2

After reviewing this page, specifically this query

db.scores.find(
   { results: { $elemMatch: { $gte: 80, $lt: 85 } } }
)

I used the following imports

import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.elemMatch;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Projections.excludeId;
import static com.mongodb.client.model.Projections.fields;
import static com.mongodb.client.model.Projections.include;

And came up with the following code to perform a similar operation (ARRAY_FIELD_NAME = "myArray")

MongoCollection<Document> collection = mongoDB.getCollection(COLLECTION_NAME);

Bson filters = and(eq("userId", userId), elemMatch(ARRAY_FIELD_NAME, eq("id", id)));
Bson projections = fields(include(ARRAY_FIELD_NAME), excludeId());

List<Document> results = (List<Document>) collection.find(filters).projection(projections).first().get(ARRAY_FIELD_NAME);
if (CollectionUtils.isEmpty(results)) {
    return null;
}
if (results.size() > 1) {
    throw new ApiException(String.format("Multiple results matched (User ID: %s, Array item ID: %s)", userId, id));
}
return results.get(0);

To filter documents that have the following structure

{
    "_id": {
        "$oid": "588899721bbabc26865f41cc"
    },
    "userId": 55,
    "myArray": [
        {
            "id": "5888998e1bbabc26865f41d2",
            "title": "ABC"
        },
        {
            "id": "5888aaf41bbabc3200e252aa",
            "title": "ABC"
        }
    ]
}

But instead of getting a single or no item from the myArray field, I always get both items !

The only code that worked for me is the following

MongoCollection<Document> collection = mongoDB.getCollection(COLLECTION_NAME);

List<Bson> aggregationFlags = new ArrayList<>();
aggregationFlags.add(new Document("$unwind", "$" + ARRAY_FIELD_NAME));
aggregationFlags.add(new Document("$match", new Document("userId", userId).append(ARRAY_FIELD_NAME + ".id", id)));
aggregationFlags.add(new Document("$project", new Document("_id", 0).append(ARRAY_FIELD_NAME, "$" + ARRAY_FIELD_NAME)));

return (Document) collection.aggregate(aggregationFlags).first().get(ARRAY_FIELD_NAME);

So why does the first piece of code that should behave the same as the query shown at the beginning of the question, not filter results as expected ?

I do not need to "aggregate" results, I need to "filter" them using the user ID and array item id.

1 Answer 1

1

You need to use $elemMatch(projection). Something like below should work.

import static com.mongodb.client.model.Projections.elemMatch;

Bson filters = and(eq("userId", userId));
Bson projections = fields(elemMatch(ARRAY_FIELD_NAME, eq("id", id)), excludeId());
Sign up to request clarification or add additional context in comments.

1 Comment

At first, I couldn't see how your answers addresses my problem. But I kept reading the elemMatch documentation for projections and filters and only now I understand. Filters.elemMatch filters the documents with the matching array filter. While Projections.elemMatch, limits the document's array items ! This was very confusing !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.