0

I've below mongoDB aggregation which is filtering tests array in my mongodb collection item.

Sample collection item : {,...tests:[ {} , {"someField":"yesIamHere"} ] }

Below query worked well and returned only tests collection which contains someField

db.getCollection('yourcollection')
.aggregate([
{"$match": {"tests.someField": {"$exists": true}}},
{ $project:{"tests": {"$filter": {"input": "$tests", "as": "item", 
    "cond": {"$ne": ["$$item.someField", undefined]}}}}
},
])

However,

While using java BasicDBObject is taking "undefined" as string not JS undefined

    BasicDBObject projectionFilterInput=new BasicDBObject("input","$tests")
        .append("as", "item")
        .append("cond",new BasicDBObject("$ne", Arrays.asList("$$item.someField","undefined")));

So, this interprets "cond": {"$ne": ["$$item.vidaptorCode", "undefined"]}}}} "undefined" not undefined. So, this doesn't filter the items as intended.

Is there any constant defined for this specific undefined value in mongodb java driver base ? This is the main question.

Whoever curious...

Why am I not using ODM ?

Actually we do use Spring Data for MongoDB, but it doesn't support this aggregation cond.

    MatchOperation matchStage = Aggregation.match(new Criteria("tests.someField").exists(true));        
    ProjectionOperation projection = Aggregation.project("tests");  

Aggregation aggregation 
      = Aggregation.newAggregation(matchStage, projection);
    
    AggregationResults<LabConfiguration> output 
      = mongoTemplate.aggregate(aggregation, "yourcollection", YourClass.class);

Morphia ODM

I liked Morphia fluent syntax however they're using different annotations than Spring Data Mongo and its dependent MongoDB libs are different. In short, two ODM don't work together. The biggest problem is for repository implementation you need to implement BasicDAO<C,K> and it's not very practical, it's mongo oriented and Spring Data Mongo does great job with MongoRepository<C,K>

    Projections filterProjection = projection(
            "tests",
            expression(
                    "$filter",
                    new BasicDBObject("input","$tests")
                    .append("as", "item")
                    .append("cond",new BasicDBObject("$ne", Arrays.asList("$$item.someField","undefined")))
            )
    );

Hence, I ended up with Mongo driver base syntax for this problem that's why I need to pass undefined to BasiDBObject but not as a string covered by double quotes.

I'm also open to hear your overall advices. What we have now is QueryDSL and Spring Data for MongoDB.

Davut Gürbüz
  • 5,526
  • 4
  • 47
  • 83
  • You cannot check for "undefined", in the aggregation''s `$filter` operation - use `$ifNull` aggregation operator to construct your condition for the `cond`. – prasad_ Feb 22 '21 at 09:56
  • @prasad_ thanks for your comment, I think ifNull isn't the same thing. We don't have that field in some docs at all. undefined check works with DB tools, there must be a way – Davut Gürbüz Feb 22 '21 at 11:56
  • Hi @DavutGürbüz, Java works with primitive type and can't understand javascript undefined, It would always take it as a string value. I concur with @prasad suggestion, official documentation states that $ifNull `covers the undefined value as well as missing field`. Please check https://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/ – Rahul Kumar Feb 22 '21 at 19:59
  • If you must not want to use above suggestion, there is always this hack to convert json directly into DBObject, however its more of a hack than implementation and there is no assurance that it will work cent percent. Check `https://stackoverflow.com/questions/16333549/converting-json-structure-to-basicdbobject/48234498` – Rahul Kumar Feb 22 '21 at 20:11
  • Hi @RahulKumar thank you. It really works. I was expecting something like $ifNotNull :) – Davut Gürbüz Feb 23 '21 at 07:22

1 Answers1

0

As commented by others in my OP, the alternative is using $ifNull.

Actually, I was expecting using an {extists:true} but it's not a valid aggregation cond operator or something like $ifNotNull would be nice, however it's also achievable with $ifNull.

As you know projection works with 0 and 1, i.e. {name:1,_id:0}

So, I decided to return 0 when $ifNull and it worked!

Java MongoDB Core Driver way

BasicDBObject projectionFilterInput=new BasicDBObject("input","$tests")
.append("as", "item")
.append("cond",new BasicDBObject("$ifNull",Arrays.asList("$$item.someField",0)));
Davut Gürbüz
  • 5,526
  • 4
  • 47
  • 83