1

Below is my DOCUMENT:

@Document(collection = "products")
@Data
@EqualsAndHashCode
public class Product {

    @Id
    private String id;

    @Field("lang_content_list")
    private List<ProductLangContent> contentList;

    @Data
    public static class ProductLangContent {
        @Field("lang")
        private String lang;
    }

}

I want to get only those contentList where lang = 'en'. lang is unique within innner list.

Note: I am using Mongotemplate

My sample json is:

{
    "_id" : ObjectId("5d2040f9f7c5ac1e9d8ef712"),
    "lang_content_list" : [ 
        {
            "lang" : "en"
        }, 
        {
            "lang" : "np"
        }
    ]
    "_class" : "com.sn.application.model.Product"
}

Desired query result is:

{
    "_id" : ObjectId("5d2040f9f7c5ac1e9d8ef712"),
    "lang_content_list" : [ 
        {
            "lang" : "en"
        }
    ]
}

I tried couple of queries but got no luck:

Aggregation aggregation = newAggregation(
                   project().and(filter("contentList")
                     .as("item")
                     .by(valueOf(
                          "item.lang")
                           .equalToValue(
                          "en")))
                  .as("contentList")
        );
        List<Product> results = mongoTemplate.aggregate(aggregation, Product.class, Product.class).getMappedResults();

output is: contentList is null.

Tried:

Criteria elementMatchCriteria = Criteria.where("contentList").elemMatch(Criteria.where("lang").is("en"));

It gives all elements in contentList. I don't want that. I want only one object in inner list where lan = 'en' .

Huge Thank you in advance.

Tried:

AggregationOperation match = Aggregation.match(Criteria.where("contentList.lang").is("en"));
        AggregationOperation unwind = Aggregation.unwind("contentList");
        AggregationOperation group = Aggregation.group("id")            
                .push("contentList").as("contentList");

        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(match);
        operations.add(unwind);
        operations.add(match);
        operations.add(group);
        Aggregation aggregation = Aggregation.newAggregation(operations);
        List<Product> results = mongoTemplate.aggregate(aggregation, Product.class, Product.class).getMappedResults();
        System.out.println(results.get(0).getContentList() != null);

output is: false. Inner array object is coming as null.

1 Answer 1

3

Your document has an array field "contentList" which will be having multiple "lang". I'm assuming you want to filter/get all those documents in which atleast one "lang" in "contentList" is "en". Then use :

Criteria elementMatchCriteria = Criteria.where("contentList.lang").is("en"));

If you want only that object in the inner array where lang='en', you need to use aggregation pipeline like:

Link: https://mongoplayground.net/p/JaJ7420i4qJ

db.collection.aggregate([
  {
    $match: {
      "lang_content_list.lang": "en"
    }
  },
  {
    $unwind: "$lang_content_list"
  },
  {
    $match: {
      "lang_content_list.lang": "en"
    }
  },
  {
    $group: {
      "_id": "$_id",
      "_class": {
        $first: "$_class"
      },
      "lang_content_list": {
        $push: "$lang_content_list"
      }
    }
  }
])

The reason for using the last group stage is that in your object, contentList is an array so we need to wrap lang object as array, otherwise not needed if you can change return type object.

In Spring MongoTemplate code:

AggregationOperation match = Aggregation.match(Criteria.where("lang_content_list.lang").is("en"));
AggregationOperation unwind = Aggregation.unwind("lang_content_list");
AggregationOperation group = Aggregation.group("_id")             
        .first("_class").as("_class")                
        .push("lang_content_list").as("lang_content_list");

List<AggregationOperation> operations = new ArrayList<>();
operations.add(match);
operations.add(unwind);
operations.add(match);
operations.add(group);
Aggregation aggregation = Aggregation.newAggregation(operations);
List<Product> results = mongoTemplate.aggregate(aggregation, Product.class, Product.class).getMappedResults();
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you. But i want only that object in the inner array where lang='en'. Is there a way to filter it out? I have updated question for better understanding.
updated answer for only that object in the inner array where lang='en'
Sorry for interruption, can you update query for syntax MongoTemplate package i'm using is: org.springframework.data.mongodb.core.MongoTemplate;
Hey thanks a lot. I found a solution after trying for sometime from your suggestion. Thanks alot.
Hi, @Rajat Goel ,can you help me out once again. I have question posted in following link: stackoverflow.com/questions/57132939/…

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.