Spring数据匹配和过滤嵌套数组

Posted

技术标签:

【中文标题】Spring数据匹配和过滤嵌套数组【英文标题】:Spring data Match and Filter Nested Array 【发布时间】:2017-11-12 07:09:48 【问题描述】:

如何从嵌套数组中提取数据?

我想提取数组项“values”,其中 wind_speed 参数值介于 vitRange.min 和 vitRange.max 之间(twaRange 和风向的条件相同)

数据:


    "name" : "race"
    ,"polaire" : [
        
            "voile" : "foc"
            , "matrice" :[
                
                    "vitRange" :  "min" : 0, "max" : 4
                    ,"twaRange" :  "min" : 0, "max" : 30
                    ,"values" : [0, 0, 0, 2.4]
                ,
                
                    "vitRange" :  "min" : 4, "max" : 6
                    ,"twaRange" :  "min" : 30, "max" : 33
                    ,"values" : [0, 0, 2.4, 3.7]
                
            ]
        ,
        
            "voile" : "spi"
            , "matrice" :[
                
                    "vitRange" :  "min" : 0, "max" : 4
                    ,"twaRange" :  "min" : 0, "max" : 30
                    ,"values" : [0, 0, 0, 1.4]
                ,
                
                    "vitRange" :  "min" : 4, "max" : 6
                    ,"twaRange" :  "min" : 30, "max" : 33
                    ,"values" : [0, 0, 1.4, 2.2]
                
            ]
        
    ]

第一种方法:

Query query = new Query(
  Criteria.where("name").is(name)
  .andOperator(
    Criteria.where("polaire.voile").is(sail),
    Criteria.where("polaire.matrice.twaRange.max").lt(wind_direction),
    Criteria.where("polaire.matrice.twaRange.min").gte(wind_direction),
    Criteria.where("polaire.matrice.vitRange.max").lt(wind_speed),
    Criteria.where("polaire.matrice.vitRange.min").gte(wind_speed)
  )
);
query.fields().include("polaire.matrice.values");
Polaires data = mongoTemplate.findOne(query, Polaires.class);

第二种方法:

Criteria findPolaireCriteria = Criteria.where("name").is(name);
Criteria findValueCriteria = Criteria.where("polaire").elemMatch(Criteria.where("voile").is(sail))
      .andOperator(
            Criteria.where("polaire.matrice.twaRange").elemMatch(Criteria.where("max").lt(wind_direction)),
            Criteria.where("polaire.matrice.twaRange").elemMatch(Criteria.where("min").gte(wind_direction)),
            Criteria.where("polaire.matrice.vitRange").elemMatch(Criteria.where("max").lt(wind_speed)),
            Criteria.where("polaire.matrice.vitRange").elemMatch(Criteria.where("min").gte(wind_speed)));

BasicQuery query = new BasicQuery(findPolaireCriteria.getCriteriaObject(), findValueCriteria.getCriteriaObject());

query.fields().include("polaire.matrice.values");
Polaires data = mongoTemplate.findOne(query, Polaires.class);

最后一种方法: (参见Query a document and all of its subdocuments that match a condition in mongodb (using spring))

Aggregation aggregation = newAggregation(
        match(Criteria.where("name").is(name)
                .and("polaire").elemMatch(Criteria.where("voile").is(sail))),
        project( "_id", "matrice")
            .and(new AggregationExpression() 
            @Override
            public DBObject toDbObject(AggregationOperationContext aggregationOperationContext ) 
                DBObject filter = new BasicDBObject("input", "$matrice")
                    .append("as", "result")
                    .append("cond",
                        new BasicDBObject("$and", Arrays.<Object> asList(
                                new BasicDBObject("$gte", Arrays.<Object> asList("$$result.vitRange.min", 0)),
                                new BasicDBObject("$lt", Arrays.<Object> asList("$$result.vitRange.max", 4))
                                )
                        )
                );
                return new BasicDBObject("$filter", filter);
            
        ).as("matrice")
);

List<BasicDBObject> dbObjects = mongoTemplate.aggregate(aggregation, "collectionname", BasicDBObject.class).getMappedResults();     

或者另一个...

List<AggregationOperation> list = new ArrayList<AggregationOperation>();
list.add(Aggregation.match(Criteria.where("name").is(name)));
list.add(Aggregation.unwind("polaire"));
list.add(Aggregation.match(Criteria.where("polaire.voile").is(sail)));
list.add(Aggregation.unwind("polaire.matrice"));
list.add(Aggregation.match(Criteria.where("polaire.matrice.twaRange").elemMatch(Criteria.where("max").lt(wind_direction))));
list.add(Aggregation.match(Criteria.where("polaire.matrice.twaRange").elemMatch(Criteria.where("min").gte(wind_direction))));
list.add(Aggregation.match(Criteria.where("polaire.matrice.vitRange").elemMatch(Criteria.where("max").lt(wind_speed))));
list.add(Aggregation.match(Criteria.where("polaire.matrice.vitRange").elemMatch(Criteria.where("min").gte(wind_speed))));
list.add(Aggregation.group("id", "polaire.matrice").push("polaire.matrice.values").as("values"));
list.add(Aggregation.project("polaire.matrice","values"));

TypedAggregation<Polaires> agg = Aggregation.newAggregation(Polaires.class, list);
List<BasicDBObject> dbObjects = mongoTemplate.aggregate(agg, "collectionname", BasicDBObject.class).getMappedResults();     

在论坛上一次又一次地转,但没有一个能帮助我。 问题可能在于处理 json 结构(使其适应轻松请求)?

谢谢

【问题讨论】:

$elemMatch。这是您想要匹配数组元素中的多个条件的操作。否则,条件将应用于所有个可能的数组位置。您想要一个“奇异”元素来匹配条件。或者至少所有这些条件都适用于满足它们的所有“奇异”元素。 您的问题没有显示$elemMatch 条件,因此这可能会导致错误的结果,需要更正。但是你还有另一个问题,你也有嵌套数组。 positional $ operator 只能匹配 "first" 和 "outer" 数组位置,这使得投影任何内部数组成为不可能。因此,在修复初始查询后,您需要使用 $filter 或类似的聚合操作来匹配它。 参见Retrieve only the queried element in an object array in MongoDB collection了解更多细节和示例。您需要在此处进行一些研究以了解其工作原理。 另外,当您的问题实际上并不存在这样的尝试时,请不要做出诸如 “我尝试这个但没有成功...”之类的声明。如果您“尝试过”,则在代码中包含Edit。然后我们都可以看到你真正尝试了什么,然后告诉你是对还是错。 【参考方案1】:

我将在这里硬编码一些值以匹配"polaire" 的“第一个”数组索引和"matrice" 的“第二个”数组索引以进行演示。这里注意$elemMatch$match聚合管道阶段的使用以及$map$filter$project管道阶段的使用:

Aggregation aggregation = newAggregation(
  match(
    Criteria.where("name").is("race").and("polaire").elemMatch(
      Criteria.where("voile").is("foc")
        .and("matrice").elemMatch(
          Criteria.where("vitRange.min").lt(5)
            .and("vitRange.max").gt(5)
            .and("twaRange.min").lt(32)
            .and("twaRange.max").gt(32)
        )
    )
  ),
  project("name")
    .and(new AggregationExpression() 
      @Override
      public DBObject toDbObject(AggregationOperationContext context) 
        return new BasicDBObject("$map",
          new BasicDBObject("input",new BasicDBObject(
            "$filter", new BasicDBObject(
              "input", "$polaire")
              .append("as","p")
              .append("cond", new BasicDBObject("$eq", Arrays.asList("$$p.voile","foc")))
          ))
          .append("as","p")
          .append("in", new BasicDBObject(
            "voile", "$$p.voile")
            .append("matrice",new BasicDBObject(
              "$filter", new BasicDBObject(
                "input", "$$p.matrice")
                .append("as","m")
                .append("cond", new BasicDBObject(
                  "$and", Arrays.asList(
                    new BasicDBObject("$lt", Arrays.asList("$$m.vitRange.min", 5)),
                    new BasicDBObject("$gt", Arrays.asList("$$m.vitRange.max", 5)),
                    new BasicDBObject("$lt", Arrays.asList("$$m.twaRange.min", 32)),
                    new BasicDBObject("$gt", Arrays.asList("$$m.twaRange.max", 32))
                  )
                ))
            ))
          )
        );
      
    ).as("polaire")
);

转化为这个序列化:

[
   "$match": 
    "name": "race",
    "polaire": 
      "$elemMatch": 
        "voile": "foc",
        "matrice": 
          "$elemMatch": 
            "vitRange.min":  "$lt": 5 ,
            "vitRange.max":  "$gt": 5 ,
            "twaRange.min":  "$lt": 32 ,
            "twaRange.max":  "$gt": 32 
          
        
      
    
  ,
   "$project": 
    "name": 1,
    "polaire": 
       "$map": 
         "input": 
           "$filter": 
             "input": "$polaire",
             "as": "p",
             "cond":  "$eq": [ "$$p.voile", "foc" ] 
            
         ,
         "as": "p",
         "in": 
           "voile": "$$p.voile",
           "matrice": 
             "$filter": 
               "input": "$$p.matrice",
               "as": "m",
               "cond": 
                 "$and": [
                    "$lt": [ "$$m.vitRange.min", 5 ] ,
                    "$gt": [ "$$m.vitRange.max", 5 ] ,
                    "$lt": [ "$$m.twaRange.min", 32 ] ,
                    "$gt": [ "$$m.twaRange.max", 32 ] 
                 ]
               
             
           
         
       
     
  
]

并生成匹配的文档输出为:


    "_id" : ObjectId("593bc2f15924d4206cc6e399"),
    "name" : "race",
    "polaire" : [
        
            "voile" : "foc",
            "matrice" : [
                    
                            "vitRange" : 
                                    "min" : 4,
                                    "max" : 6
                            ,
                            "twaRange" : 
                                    "min" : 30,
                                    "max" : 33
                            ,
                            "values" : [
                                    0,
                                    0,
                                    2.4,
                                    3.7
                            ]
                    
            ]
        
    ]

$match 的“查询”部分对于实际选择满足条件的“文档”很重要。如果不使用$elemMatch,表达式实际上可以匹配没有正确条件的相同内部元素的文档,并且实际上会分布在文档中存在的所有数组元素中。

过滤嵌套的数组首先使用$map,因为“内部”数组元素也将受到它自己的“过滤”。因此$map"input" 源以及"in" 的“输出”都引用$filter 条件以匹配数组的特定元素。

作为$filter 的“条件”("cond"),我们使用“逻辑聚合表达式”,例如布尔值$and 以及其他"comparison operators" 来模拟它们的“查询”的相同条件运营商”的同行。这些负责匹配正确的数组项以在“过滤”结果中返回的逻辑。


作为参考,这是从中获得结果的源数据,应该与问题中发布的相同:


        "_id" : ObjectId("593bc2f15924d4206cc6e399"),
        "name" : "race",
        "polaire" : [
                
                        "voile" : "foc",
                        "matrice" : [
                                
                                        "vitRange" : 
                                                "min" : 0,
                                                "max" : 4
                                        ,
                                        "twaRange" : 
                                                "min" : 0,
                                                "max" : 30
                                        ,
                                        "values" : [
                                                0,
                                                0,
                                                0,
                                                2.4
                                        ]
                                ,
                                
                                        "vitRange" : 
                                                "min" : 4,
                                                "max" : 6
                                        ,
                                        "twaRange" : 
                                                "min" : 30,
                                                "max" : 33
                                        ,
                                        "values" : [
                                                0,
                                                0,
                                                2.4,
                                                3.7
                                        ]
                                
                        ]
                ,
                
                        "voile" : "spi",
                        "matrice" : [
                                
                                        "vitRange" : 
                                                "min" : 0,
                                                "max" : 4
                                        ,
                                        "twaRange" : 
                                                "min" : 0,
                                                "max" : 30
                                        ,
                                        "values" : [
                                                0,
                                                0,
                                                0,
                                                1.4
                                        ]
                                ,
                                
                                        "vitRange" : 
                                                "min" : 4,
                                                "max" : 6
                                        ,
                                        "twaRange" : 
                                                "min" : 30,
                                                "max" : 33
                                        ,
                                        "values" : [
                                                0,
                                                0,
                                                1.4,
                                                2.2
                                        ]
                                
                        ]
                
        ]

【讨论】:

非常感谢您的解释...我粘贴您的代码并添加 List dbObjects = mongoTemplate.aggregate(aggregation, "collectionname", Polaires.class).getMappedResults()得到结果但是 dbObjects 是空的 @anthony44 这里的所有内容都是从您在问题中提供的数据的运行操作中剪切和粘贴的。再次仔细检查并确保您正在针对具有匹配数据的正确集合运行。我注意到您正在输入"collectionname",这可能不是您的实际集合名称,因此您可能需要更正它。此外,序列化的输出可以直接粘贴到 mongo shell 中并针对正确的集合运行。如图所示,所有输出都对我有用。 我替换了collectionname 并且一切正常...下一步我必须替换硬代码值。非常感谢尼尔。 @anthony44 好。你是新来的,所以不要忘记accept your answers 并投票 :) 这就是我们说“谢谢!”的方式。在这里。 哎呀,我需要在名称项目(“种族”)上添加过滤器...尝试使用以下方式修改匹配子句:match(Criteria.where(“name”).is(name)。 ("sails").elemMatch(...). 你能帮我修改 AggregationExpression 吗?

以上是关于Spring数据匹配和过滤嵌套数组的主要内容,如果未能解决你的问题,请参考以下文章

基于 n 嵌套数组按名称过滤

带有 elemMatch 的 MongoDB 查询,用于从对象内部匹配嵌套数组数据 [重复]

将所有数据保存在数组中,过滤掉重复的数据,比较数组之间的数据并删除匹配的数据

查找与顺序无关紧要的嵌套数组的完全匹配

匹配 MongoDB 中嵌套子文档的嵌套数组

MongoDB通过***属性和嵌套数组键查找文档,并返回匹配文档的一部分