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"collectionname"
,这可能不是您的实际集合名称,因此您可能需要更正它。此外,序列化的输出可以直接粘贴到 mongo shell 中并针对正确的集合运行。如图所示,所有输出都对我有用。
我替换了collectionname 并且一切正常...下一步我必须替换硬代码值。非常感谢尼尔。
@anthony44 好。你是新来的,所以不要忘记accept your answers 并投票 :) 这就是我们说“谢谢!”的方式。在这里。
哎呀,我需要在名称项目(“种族”)上添加过滤器...尝试使用以下方式修改匹配子句:match(Criteria.where(“name”).is(name)。 ("sails").elemMatch(...). 你能帮我修改 AggregationExpression 吗?以上是关于Spring数据匹配和过滤嵌套数组的主要内容,如果未能解决你的问题,请参考以下文章
带有 elemMatch 的 MongoDB 查询,用于从对象内部匹配嵌套数组数据 [重复]