Scala MongoDB Driver does not find Objects - mongodb

In my Scala Application, I am trying to find() all Objects for a List of ObjectIds.
My DB Structure looks like that:
case class Parent(_id: ObjectId, name: String)
case class Child(_id: ObjectId, name: String, parents: List[ObjectId])
Now, I have a list of ParentIds and want to find all Parents.find(in("_id", foundChild.parents)).
Unfortunately, parents will be empty. I have searched the Internet for a in("_id", List[ObjectId])-Example, but couldn't find a single one.
Reading the documentation, it seems like it is not supported to check for lists, but only for TItem*. Is it possible to achieve a "is in List" Check somehow?

Pass in the List as varargs:
Parents.find(in("_id", foundChild.parents: _*))

Related

Converting a nested List in using Gson.tojson

I'm using Scala, but this applies to Java as well - it appears Gson is having issues converting a List nested within a case class/within another object:
case class Candy(name:String, price:Double)
case class PersonCandy(name:String, age:Int, candyList:List[Candy])
val candies = List(Candy("M&M's", 1.99), Candy("Snickers", 1.59), Candy("Butterfinger", 2.33))
val personC = PersonCandy("Mark", 19, candies)
val pollAsJson = new Gson().toJson(personC)
The REPL shows the resulting pollAsJson as follows:
pollAsJson: String = {"name":"Mark","age":19,"candyList":{}}
My workaround could be to convert the nested candyList, convert the personC and then since both are just Strings, manually hack them toether, but this is less than ideal. Reading blogs and usages and the like seems that Gson can extract and convert nested inner classes just fine, but when a Collection is the nested class, it seems to have issues. Any idea?
The problem is not related with case classes or nesting, but rather with Scala collection types which are not supported properly in Gson.
To prove that, let's change PersonCandy to
case class PersonCandy(name:String, age:Int, candyList: java.util.List[Candy])
And convert candies to a Java List:
import scala.collection.JavaConverters._
val candies = List(/* same items */).asJava
And the JSON is OK:
{"name":"Mark","age":19,"candyList":[{"name":"M\u0026M\u0027s","price":1.99},{"name":"Snickers","price":1.59},{"name":"Butterfinger","price":2.33}]}
And if you try to produce a JSON for the original candies list, it will produce:
{"head":{"name":"M\u0026M\u0027s","price":1.99},"tl":{}}
Which reflects the head :: tail structure of the list.
Gson is a lib primarily used with Java code.
For Scala, there is a variety of choices. I'd suggest to try some from the answers to this question.
You can use lift-json module to render json strings from case classes. It is a nice easy to use library with a really good DSL to create JSON strings without the need of case classes. You can find more about it at Lift-Json Github page

Update deeply nested case class with Options

I have a 3-level nested case class model with a bunch of options that represents some data in a database. It's essentially:
case class User(settings: Option[Settings])
case class Settings(keys: Option[List[KeySet]])
case class KeySet(privateKey: String, publicKey: String)
I understand how to get deeply nested fields out of this using some for comprehension or chains of flatMap (Scala Option object inside another Option object) and I also understand how to update it using a lens library, but I want to figure out how to update the fields even if some stuff in the tree is None and automatically make Somes of those if they don't exist yet.
For example, how would I handle the case where I want to add to the keys List but the user hasn't yet set any settings? Is it possible to, in some sense, automatically create a Some(settings) field and also a Some(keys) field?
I have an idea of how to do it with a lot of pattern matching, but this seems wrong because of 1. rightward drift of code and 2. not using map or flatMap very much with the options.
Is this possible using a lens library on its own? I read here that it might not be possible: https://github.com/julien-truffaut/Monocle/issues/215 as in the case of Monocle it can't update an Option that is a None. Maybe I need to think about the problem another way?
Thanks
I'm not sure why you use Option[List[KeySet]]. Is there an important distinction between None and an empty List?
In any case, I find fold to be a handy tool when working with Options.
def updateUser(u :User, ks :KeySet) :User = {
u.copy(settings =
Some(u.settings.fold(Settings(Some(ks::Nil))) (stngs =>
stngs.copy(keys = Some(stngs.keys.fold(ks::Nil) (ks::_))))))
}
val pat = updateUser(User(None), KeySet("a","b"))
//pat: User = User(Some(Settings(Some(List(KeySet(a,b))))))
val upat = updateUser(pat, KeySet("c","d"))
//upat: User = User(Some(Settings(Some(List(KeySet(c,d), KeySet(a,b))))))

Preserving some null in Json4s

I'm trying to marshall a case class in json which is easy enough with json4s.
case class container(id: String, `type`: Option[String], things: List[???])
Json4s does an excellent job of omitting fields that are null. The issues is I'd like options at the top level of container to be preserved but everything in things (it's filled with nested options) to preserve these empty options and serve them as Nulls.
The follow question pointed me in the right direction Json4s ignoring None fields during seriallization (instead of using 'null')
But using implicit val f = DefaultFormats.preservingEmptyValues targets all of the case classes in scope. Is there anyway I can just target fields within things. I can't specifically target fields as there's quite a few and doing this could become unmanageable.
Thanks in advance

Option as a singleton collection - real life use cases

The title pretty much sums it up. Option as a singleton collection can sometimes be confusing, but sometimes it allows for an interesting application. I have one example on top of my head, and would like to learn more of such examples.
My only example is running for comprehension on the Option[List[T]]. We can do the following:
val v = Some(List(1, 2, 3))
for {
list <- v.toList
elem <- list
} yield elem + 1
Without having Option.toList, it wouldn't be possible to stay in the same for comprehension, and I'd be forced to write something like this:
for {
list <- v
} yield for {
elem <- list
} yield elem + 1
The first example is cleaner, and it's an advantage of Option being a collection. Of course, the result type will be different in these 2 examples, but let's assume it doesn't matter for the sake of discussion.
Any other examples? I'd especially like to concentrate on collection-like usage, and not usage of Option's monadic properties - those are pretty much obvious. In other words, map and flatMap functions are out of scope of this question. They're definitely very useful, just coming from elsewhere.
I find that working with Option[T] as a collection's main benefit is that you get to use operations defined on a collection, such as map, flatmap, filter, foreach etc. This makes it easier to do operations on a given option, instead of using pattern matching or checking Option[T].isDefined to see if a value exists.
For example, let's take the user repository example from Daniel Westheide blog post about Option[T]:
Say you have a UserRepository object which returns users based on their ID. The user may or may not exist, hence it returns an Option[Person]. Now let's say we want to search a person by id and then filter their age. We can do:
val age: Some[Int] = UserRepository.findById(1).map(_.age)
Now let's say that a Person also has a gender property of type Option[String]. If you wanted to extract that out, you could use map:
val gender: Option[Option[String]] = UserRepository.findById(1).map(_.gender)
But working with nested options isn't too convenient. For that, you have flatMap:
val gender: Option[String] = UserRepository.findById(1).flatMap(_.gender)
And if we want to print out the gender if it exists, we can use foreach:
gender.foreach(println)
You'll find yourself working with scala types that have nested Option[T] fields defined and it's really handy to have collection like methods which help you remove out boilerplate and noise for extracting the actual value out of the operation.
A more real life use case I just encountered the other day was working with the awscala SDK, where I wanted to retrieve an object from S3 storage:
val bucket: Option[Bucket] = s3.bucket(amazonConfig.bucketName)
val result: Option[S3Object] = bucket.flatMap(_.get(amazonConfig.offsetKey))
result.flatMap(s3Object =>
Source.fromInputStream(s3Object.content).mkString.decodeOption[Array[KafkaOffset]])
So what happens here is that you query the S3 service for a bucket, which may or may not exist. Then, you want to extract an S3Object out of it which actually contains the data, but the API itself returns an Option[S3Object], so it's handy to use flatMap to flat out get an Option[S3Object] instead of Option[Option[S3Object]]. Finally, I want to deserialize the S3Object which actually contains a JSON, and using the Argonaut library, it returns an Option[MyObject], so then again using flatMap to the rescue of extracting the inner option type.
Edit:
As you pointed out, map and flatMap belong to the monadic property of Option[T]. I've written a blog post describing the reduction of two options where the final solution was:
def reduce[T](a: Option[T], b: Option[T], f: (T, T) => T): Option[T] = {
(a ++ b).reduceLeftOption(f)
}
Which takes advantage of the ++ operator defined on any collection which is also specifically defined on Option[T], being a collection.
I'd suggest to take a look at the corresponding chapter of The Neophyte's Guide to Scala.
In my experience, most useful use-cases of Option-as-collection are to filter an option and to make flatMap that implicitly filters None values.

Scala Play ReactiveMongo - Arbitrary list of query parameters

I'm trying to support arbitrary filters for a REST API that fetches a list of documents from MongoDB.
For instance
//example.com/users <- list all
//example.com/users?age=30 <- all users who are 30
//example.com/users?age=30&name=John <- all users who are 30 and called John
...
I'm using Play-ReactiveMongo and dealing with JSONCollection objects only.
So in my routes I put
GET /users controllers.Users.list(id: Option[String], name: Option[String], age: Option[Int])
But there are two problem with that, first I'll need to have a pretty long list of optional parameters, and then in my controller I need to use pattern matching on all of them to check whether they're empty or not, and also build the selector that I use to filter my collection.
var filters = JsObject(Nil)
name match {
case Some(x) => filters += ("name" -> JsString(x))
case None => None
}
I realized that I can get the full query string from the request object, which is a Map[String, Seq[String]]. But then I don't know a good way to check whether values are String or something else.
Is there another better and idiomatic way to do what I want?
Possible solution can be:
Use POST instead of GET:
POST /example.com/users
"data"={"age":25, "name":"xyz", ... }
OR single param in GET:
GET /example.com/users?filter={"age":25, "name":"xyz", ... }
On the server side, just validate against your model class OR just pass the same json in your reactivemongo find method.
Maybe request binders will help you to create complex objects from varying request parameters.
https://www.playframework.com/documentation/2.5.x/ScalaRequestBinders#QueryStringBindable
For example, you could build something like this (from the docs):
case class AgeRange(from: Int, to: Int)
for requests like this:
/age?from=1&to=10
Now you could change these attributes to Option and create a function that creates a reactivemongo query based on the values at hand.