QueryBuilder fluent interface

33 views
Skip to first unread message

Fernando Aspiazu

unread,
Dec 28, 2016, 3:44:17 PM12/28/16
to ReactiveMongo - http://reactivemongo.org
Hi everyone,

I'm wondering if there is a way to build queries (BSONDocument) in a fluent/clever way?

For example, in order to build the following query: 
{
"userId": "USER123",
"$or": [
{ "amount": { "$gt": 1000, "$lte": 5000 } },
{ "unwanted": { "$exists": false } }
],
"categories": { "$in": [ "AAA", "BBB", "CCC" ] }
}

instead of write every BSONDocument with every operator as if it were at a low level:
val userId: BSONElement = "userId" -> "USER123"
val disjunction
: BSONElement = "$or" -> Array(
document("amount" -> document("$gt" -> 1000, "$lte" -> 5000)),
document("unwanted" -> document("$exists" -> false)))
val inCategories: BSONElement = "categories" -> document("$in" ->Set("AAA", "BBB", "CCC"))

val query
: BSONDocument = document(userId, disjunction, inCategories)
collection
.flatMap(_.find(query).collect[List])

I would want to express it as:
val userId: BSONElement = "userId" -> "USER123"
val disjunction
: BSONElement = or("amount" -> document(gt(1000), lte(5000)), "unwanted" -> exists(false))
val inCategories
: BSONElement = "categories" -> in("AAA", "BBB", "CCC")
val query: BSONDocument = document(userId, disjunction, inCategories)

collection
.flatMap(_.find(query).collect[List])

or better:
val criteria: BSONDocument = "userId" -> "USER123"
:+: or(
"amount" -> (gt(1000), lte(5000)),
"unwanted" -> exists(false))
:+: "categories" -> in("AAA", "BBB", "CCC")
val query: BSONDocument = document(criteria) // or criteria.map(document)
collection.flatMap(_.find(query).collect[List])

If there is a way to build complex query in a more compact way, I'd appreciate any idea or suggestion :-)

Thanks a lot.

Cédric Chantepie

unread,
Dec 29, 2016, 5:45:42 AM12/29/16
to ReactiveMongo - http://reactivemongo.org
Such DSL is out of the core driver/libraries. You can have a look at third-party library available for (e.g. https://github.com/osxhacker/ReactiveMongo-Criteria/ ).

Best regards

osxhacker

unread,
Jan 3, 2017, 2:16:09 PM1/3/17
to ReactiveMongo - http://reactivemongo.org
Hi Fernando,

As Cedric mentioned, you could use the Criteria DSL to formulate BSONDocument's used by the find method.  I believe the following would be an equivalent constraint to your example:


import Untyped._

val constraints
= where (
    _
.userId === "USER123" &&
   
((_.amount > 1000 && _.amount <= 5000) || !_.unwanted.exists) &&
    _
.categories.in ("AAA", "BBB", "CCC")
   
);

val filteredResults
= collection.find (constraints).collect[List];


Also, in looking at the Term methods, I noticed that my implementation for the exists method only allows for positive existence checks.  This is an oversight in the implementation and will be corrected shortly.  Most likely it will allow for syntax such as:


!criteria.some.property.exists;


As well as:


criteria.some.property.exists === false;


Currently, the project will need to be built locally as I have not yet completed the process of publishing it to Maven Central.

HTH


Fernando Aspiazu

unread,
Jan 4, 2017, 5:03:58 AM1/4/17
to ReactiveMongo - http://reactivemongo.org
Hi,

thank you very much for your useful explanation. It would surely be a good fit for me.

I'll try to integrate it.

Cheers.
Reply all
Reply to author
Forward
0 new messages