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

Spring data Match and Filter Nested Array

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

我想提取数组项 "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);

最后一种方法: (比照

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

在论坛上转来转去,但none个帮助了我。 问题可能出在 json 结构上(使其适应轻松请求)?

谢谢

我将在这里硬编码一些值以匹配 "polaire" 的 "first" 数组索引和 "matrice" 的 "second" 数组索引以进行演示。这里注意$elemMatch in the $match aggregation pipeline stage and the usage of $map and $filter in the $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 is important to actually select the "document(s)" that meet the conditions. Without the usage of $elemMatch 表达式的 "query" 部分实际上可以匹配相同内部元素上没有正确条件的文档,并且实际上会分布在文档中存在的所有数组元素中.

过滤嵌套的数组首先使用$map since the "inner" array element is also going to be subject to its own "filtering". So both the "input" source for the $map as well as the "output" as "in" make reference to $filter条件来匹配数组的特定元素。

作为 "conditions" ( "cond" ) 到 $filter we make use of "logical aggregation expressions" such as the boolean $and as well as the other "comparison operators" 模仿其 "query operator" 同行的相同条件。这些负责将正确的数组项与 "filtered" 结果中的 return 相匹配的逻辑。


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

{
        "_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
                                        ]
                                }
                        ]
                }
        ]
}