Запрос Mongo для фильтрации внутренних элементов Arraylist в Spring Boot с использованием шаблона Mongo

Ниже мой ДОКУМЕНТ:

@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;
    }

}

Я хочу получить только те contentList, где lang = 'en'. lang уникален во внутреннем списке.

Примечание. Я использую Mongotemplate.

Мой образец json:

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

Желаемый результат запроса:

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

Я попробовал пару запросов, но не повезло:

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();

вывод: contentList равен null.

Пытался:

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

Он дает все элементы в contentList. Я не хочу этого. Мне нужен только один объект во внутреннем списке, где lan = 'en' .

Заранее огромное спасибо.

Пытался:

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);

вывод: false. Объект внутреннего массива имеет значение null.


person Nabin Kumar Khatiwada    schedule 07.07.2019    source источник


Ответы (1)


В вашем документе есть поле массива «contentList», которое будет иметь несколько «языков». Я предполагаю, что вы хотите отфильтровать/получить все те документы, в которых хотя бы один «lang» в «contentList» равен «en». Затем используйте:

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

Если вам нужен только этот объект во внутреннем массиве, где lang='en', вам нужно использовать конвейер агрегации, например:

Ссылка: 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"
      }
    }
  }
])

Причина использования последнего группового этапа заключается в том, что в вашем объекте contentList является массивом, поэтому нам нужно обернуть объект lang как массив, в противном случае он не нужен, если вы можете изменить объект возвращаемого типа.

В весеннем коде MongoTemplate:

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();
person Rajat Goel    schedule 07.07.2019
comment
Спасибо. Но мне нужен только тот объект во внутреннем массиве, где lang='en'. Есть ли способ отфильтровать это? Я обновил вопрос для лучшего понимания. - person Nabin Kumar Khatiwada; 08.07.2019
comment
обновленный ответ только для того объекта во внутреннем массиве, где lang='en' - person Rajat Goel; 08.07.2019
comment
Извините за прерывание, не могли бы вы обновить запрос на синтаксис пакета MongoTemplate, который я использую: org.springframework.data.mongodb.core.MongoTemplate; - person Nabin Kumar Khatiwada; 08.07.2019
comment
Эй, большое спасибо. Я нашел решение, попробовав какое-то время из вашего предложения. Большое спасибо. - person Nabin Kumar Khatiwada; 08.07.2019
comment
Привет, @Rajat Goel, не могли бы вы помочь мне еще раз. У меня есть вопрос, размещенный по следующей ссылке: stackoverflow.com/questions/57132939/ - person Nabin Kumar Khatiwada; 21.07.2019