Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Failing to add a second '$or' expression

I am trying to query a mongodb collection with two 'or' clauses via Java API. The following query works fine when run through any mongo client:

{
  $and : [{
    $or : [ { “field1” : “value1”}, {“field1” : “value2”}]
 },{
    $or : [ { “field2” : “value3”} , { “field2” : “value4”}]
 }]
}

But when I try to run this using Java API, I get the following error:

org.springframework.data.mongodb.InvalidMongoDbApiUsageException: Due to limitations of the com.mongodb.BasicDBObject, you can't add a second '$or' expression specified as '$or : [ { “field1” : “value1”}, {“field1” : “value2”}]'. Criteria already contains '$or : [ { “field2” : “value3”} , { “field2” : “value4”}]'.
at org.springframework.data.mongodb.core.query.Criteria.setValue(Criteria.java:640)
at org.springframework.data.mongodb.core.query.Criteria.getCriteriaObject(Criteria.java:573)
at org.springframework.data.mongodb.core.query.Criteria.createCriteriaList(Criteria.java:630)
at org.springframework.data.mongodb.core.query.Criteria.andOperator(Criteria.java:539)
like image 721
Dikshit Luthra Avatar asked Jul 22 '16 06:07

Dikshit Luthra


2 Answers

You can do it through Java API by

1) preparing a list of your 'or' criterias.

2) create new criteria object.

3) 'feed' the list created in (1) into the object created in (2), using the criteria object's 'andOperator' method.

Something like this ->

    Query query = new Query();
    Map<String, String> params; // let's say this arrived with some values
    List<Criteria> criterias = new ArrayList<>();

    for (String k : params.keySet()) {
        String paramVal = params.get(k);
        switch (k) {
        case "someVal1":
            criterias.add(new Criteria().orOperator(Criteria.where("metadata.field1").is(paramVal),
                    Criteria.where("metadata.field2").is(paramVal)));

            break;
        case "someVal2":
            criterias.add(new Criteria().orOperator(Criteria.where("metadata.arrayField3").in(paramVal),
                    Criteria.where("metadata.arrayField4").in(paramVal),
                    Criteria.where("metadata.arrayField5").in(paramVal)));
            break;
        default:
            break;
        }
    }

    if (criterias.size() > 0){
        query.addCriteria(new Criteria().andOperator(criterias.toArray(new Criteria[criterias.size()])));
    }
like image 102
knopch1425 Avatar answered Sep 22 '22 17:09

knopch1425


Try splitting that into an $in operator. Let's break down the first $or expression:

{
    "$or": [ 
        { "filed1" : "value1" }, 
        { "filed1" : "value2" }
    ]
}

This can be converted into an $in operator as

{ "field1": { "$in": ["value1", "value2"] } }

Similarly

{
    "$or": [ 
        { "filed2" : "value3" }, 
        { "filed2" : "value4" }
    ]
}

can be expressed as

{ "field2": { "$in": ["value3", "value4"] } }

Combining the two expressions into one implicit AND query yields:

db.collection.find({
    "field1": { "$in": ["value1", "value2"] },
    "field2": { "$in": ["value3", "value4"] }
})
like image 38
chridam Avatar answered Sep 23 '22 17:09

chridam