MongoDB.live, free & fully virtual. June 9th - 10th. Register Now

Java driver - $filter aggregation operator

Is the $filter aggregation operator supported by the java driver?

I’ve looked in tutorials, stack overflow, and these two files: https://github.com/mongodb/mongo-java-driver/blob/master/driver-core/src/main/com/mongodb/client/model/Projections.java and https://github.com/mongodb/mongo-java-driver/blob/master/driver-core/src/main/com/mongodb/client/model/Aggregates.java , but haven’t found anything on it. I assumed it to be supported based on https://docs.mongodb.com/ecosystem/drivers/java/#mongodb-compatibility and the “version 3.2” note in the $filter docs. Any help is apprecciated.

In particular, I’m looking to do

$project: {
    "FieldNameA" : {
        $filter: {
            input: "$FieldNameB", as: "item",
            cond: { $in: [ "{B}", "$$item.List" ] }
        } 
    }
}

I am assuming an input document like this:

{
        "_id" : 1,
        "FieldNameB" : [
                {
                        "List" : [ "one",  "two", "three" ]
                },
                {
                        "List" : [ "five", "six" ]
                }
        ]
}

And the aggregation based on your post (may not be exact, but similar) using MongoDB v4.2.3:

db.collection.aggregate([
{
  $project: {
    "FieldNameA" : {
        $filter: {
            input: "$FieldNameB", as: "item",
            cond: { $in: [ "six", "$$item.List" ] }
        } 
    }
  }
}
] )

The Java code using MongoDB Java Driver v3.12.2:

MongoClient mongoClient = MongoClients.create("mongodb://localhost/");
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("collection");
Bson projection = project(
					computed(
						"FieldNameA", 
							eq("$filter", 
								and(
									eq("input", "$FieldNameB"), 
									eq("as", "item"), 
									in("cond", Arrays.asList("six", "$$item.List"))
								)
							)
					)
);

List<Bson> pipeline = Arrays.asList(projection);									
List<Document> results = new ArrayList<>();
collection.aggregate(pipeline).into(results); 	
results.forEach(doc -> System.out.println(doc.toJson()));

The output:

{
        "_id" : 1,
        "FieldNameA" : [
                {
                        "List" : [
                                "five",
                                "six"
                        ]
                }
        ]
}


NOTE: You can also build the aggregation and export the pipeline to Java programming language; see Aggregation Pipeline Builder.

Thank you!

I never would have guessed it was possible like that. Out of sheer curiosity, could you explain how that works? Is it taking advantage of the way computed/eq/and are represented by the java driver to use a projection/aggregation not “natively” supported by the higher-level API?

Is it taking advantage of the way computed/eq/and are represented by the java driver to use a projection/aggregation

Yes, using the driver and its API calls.

I am using MongoDB Java driver APIs to build the Java code to connect to the MongoDB database server, build and run the aggregation query (thats the only way, as I know, to access the MongoDB database from Java).

In general, if you are running the aggregation from the mongo shell, there is a mongo shell’s associated driver. All client programs, like mongo shell or Java or Python programs/scripts, access MongoDB via a driver and are coded using relevant APIs.

I wasn’t really asking that, though thank you for the answer, I was curious how you seem to be manually creating the bson query object using “eq” and “and” in the java code, specifically. I looked for how to do this a decent amount, and e.g the docs for projections for the java driver don’t even mention the $filter operator: https://mongodb.github.io/mongo-java-driver/3.0/builders/projections/

I’d been looking for something like Projections.filter, but apparently since it’s not there it required manually building the bson query. If I understand correctly, the “eq” filter is being used/abused to represent arbitrary key-value pairs, and the “and” filter to represent arbitrary bson objects, allowing you to manually construct the query? Without using the higher level constructs from Aggregates.* or Projections.*

Your solution worked perfectly, and if I understand the way you created the bson query manually using “eq” and “and” correctly I should be able to do the same for anything else I find missing in the “dumb” top-level api. Thank you!

I looked for how to do this a decent amount, and e.g the docs for projections for the java driver don’t even mention the $filter operator…

There is no specific API for the $filter aggregation array operator; I haven’t seen one yet. If you are looking for such constructs the Spring MongoDB APIs have those; e.g using the ArrayOperators.Filter:

The following aggregation stage with $project and $filter,

{ 
  $project: { 
      details: {
          $filter: {
              input: "$details",
              as: "d",
              cond: { $eq: [ "$$d.state", "Active" ] }
          }
      } 
  } 
}

translates to (in Java using Spring APIs):

project()
    .and(
        filter("details")
            .as("d")
            .by(Eq.valueOf("d.state").equalToValue("Active" )))
    .as("details")
1 Like