Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare 2 fields in Spring Data MongoDB using query object

What seems almost natural in simple SQL is impossible in mongodb.

Given a simple document:

{
    "total_units" : 100,
    "purchased_unit" : 60
}

I would like to query the collection, using spring data Criteria class, where "total_units > purchased_units".

To my understanding it should be as trivial as any other condition.

Found nothing to support this on Spring api.

like image 572
Hanan Bareket Avatar asked Mar 26 '15 13:03

Hanan Bareket


People also ask

What is the difference between MongoOperations and MongoTemplate?

MongoTemplate provides a simple way for you to save, update, and delete your domain objects and map those objects to documents stored in MongoDB. You can save, update and delete the object as shown below. MongoOperations is the interface that MongoTemplate implements.

How do I capture a specific field in MongoDB?

You can select a single field in MongoDB using the following syntax: db. yourCollectionName. find({"yourFieldName":yourValue},{"yourSingleFieldName":1,_id:0});

Can I use Spring data JPA with MongoDB?

Yes, DataNucleus JPA allows it, as well as to many other databases. You make compromises by using the JPA API for other types of datastores, but it makes it easy to investigate them.


2 Answers

You can use the following pattern:

Criteria criteria = new Criteria() {
    @Override
    public DBObject getCriteriaObject() {
        DBObject obj = new BasicDBObject();
        obj.put("$where", "this.total_units > this.purchased_units");
        return obj;
    }
};

Query query = Query.query(criteria);
like image 112
Andrew Onischenko Avatar answered Sep 23 '22 19:09

Andrew Onischenko


I don't think Spring Data API supports this yet but you may need to wrap the $where query in your Java native DbObject. Note, your query performance will be fairly compromised since it evaluates Javascript code on every record so combine with indexed queries if you can.

Native Mongodb query:

db.collection.find({ "$where": "this.total_units > this.purchased_units" });

Native Java query:

DBObject obj = new BasicDBObject();
obj.put( "$where", "this.total_units > this.purchased_units");

Some considerations you have to look at when using $where:

Do not use global variables.

$where evaluates JavaScript and cannot take advantage of indexes. Therefore, query performance improves when you express your query using the standard MongoDB operators (e.g., $gt, $in). In general, you should use $where only when you can’t express your query using another operator. If you must use $where, try to include at least one other standard query operator to filter the result set. Using $where alone requires a table scan. Using normal non-$where query statements provides the following performance advantages:

MongoDB will evaluate non-$where components of query before $where statements. If the non-$where statements match no documents, MongoDB will not perform any query evaluation using $where. The non-$where query statements may use an index.

As far as I know you can't do query.addCriteria(Criteria.where("total_units").gt("purchased_units"));

but would go with your suggestion to create an additional computed field say computed_units that is the difference between total_units and purchased_units which you can then query as:

Query query = new Query();
query.addCriteria(Criteria.where("computed_units").gt(0));

mongoOperation.find(query, CustomClass.class);
like image 34
chridam Avatar answered Sep 22 '22 19:09

chridam