我尝试使用MongoDB构建我的第一个非平凡的查询作为通知系统的一部分,并且我试图了解如何检查多个嵌入文档是否符合我的标准,或者不要存在的。 (另外,我尝试使用Spring Boot Mongo api构建查询。)
我有订阅文件,其标准地图如下:
{
...,
criteria: [
{ key: "order.companyId", value: "ABC" },
{ key: "order.status", value: "NEW" }
]
}
{
...,
criteria: [
{ key: "order.status", value: "NEW" }
]
}
{
...,
criteria: [
{ key: "order.companyId", value: "ABC" }
]
}
当公司ABC的新订单进入时,我想生成一个与上述所有三个订阅相匹配的查询。我似乎需要$and
,$elemMatch
和$in
的组合,但我找不到如何将它们拼凑在一起的好例子。谁能给我一些关于如何开始这个的建议?
在我最初的尝试中,我一直在生成这样的标准:Criteria.where("criteria").elemMatch(Criteria.where("key").is(key).and("value").in(value, null))
但它似乎并不喜欢同一属性的多个条件。
利用dsharew's advice,我现在正在使用此方法为每个键/值对建立一系列标准:
private Criteria equalsOrUnspecified(String key, Object value) {
Criteria valueMatch = Criteria.where("criteria")
.elemMatch(Criteria.where("key").is(key).and("value").is(value));
Criteria notSpecified = Criteria.where("criteria").not().elemMatch(Criteria.where("key").is(key));
return new Criteria().orOperator(valueMatch, notSpecified);
}
然后使用
组合它们 q.addCriteria(new Criteria().andOperator(criteria.toArray(new Criteria[criteria.size()])));
但这似乎并不匹配任何东西。以下是其中一个查询的示例:
{
"isEnabled" : true ,
"eventType" : "order::positionUpdate" ,
"$and" : [
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.bolNumber" , "value" : "51166350"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.bolNumber"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.consRefNumber" , "value" : "AGVS"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.consRefNumber"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.operationsUser.id" , "value" : "janedoe"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.operationsUser.id"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.bookingUser.id" , "value" : "janedoe"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.bookingUser.id"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.status" , "value" : "NEW"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.status"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.billingCustomer.id" , "value" : "EXQUOTE"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.billingCustomer.id"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.bookingCustomer.id" , "value" : "EXBE63A"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.bookingCustomer.id"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "order.id" , "value" : "5849005"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "order.id"}}}}]} ,
{ "$or" : [ { "criteria" : { "$elemMatch" : { "key" : "companyId" , "value" : "ABC"}}} , { "criteria" : { "$not" : { "$elemMatch" : { "key" : "companyId"}}}}]}
]
}
答案 0 :(得分:1)
我可以看到你已根据实际需要简化了问题的查询。从你的评论和你的笔记"但它似乎不喜欢同一属性的多个标准。"。是的,你不能多次使用一把钥匙。
你应该尝试这样的事情:
List<Criteria> criterias = new ArrayList<>();
criterias.add(Criteria.where("criteria.key").is(key1))
criterias.add(Criteria.where("criteria.key").is(key2))
criterias.add(Criteria.where("criteria.value").in(values))
如果您需要上述条件的OR
,则应执行以下操作:
Criteria criteria = new Criteria().orOperator(criterias.toArray(new Criteria[criterias.size()]))
如果您需要AND
上述查询,请执行以下操作:
new Criteria().andOperator(criterias.toArray(new Criteria[criterias.size()]))
此外,您可以将其中一些与AND
合并,其中一些与OR
合并,以符合您的确切查询。
答案 1 :(得分:0)
这是我最终需要的。 public Criteria createCriteria(Map<String,?> criteriaMap) {
List<Criteria> criteria= new ArrayList<>();
for (Map.Entry<String,Object> entry : criteriaMap) {
list.add(equalsOrUnspecified(entry.getKey(), entry.getValue()));
}
return new Criteria().andOperator(criteria.toArray(new Criteria[criteria.size()]));
}
/**
* Find a subscription where {@code key} equals {@code value}
* or where {@code key} is not present.
*/
private void equalsOrUnspecified(String key, Object value) {
Criteria valueMatch = Criteria.where("criteria")
.elemMatch(Criteria.where("key").is(key).and("value").is(value));
Criteria notSpecified = Criteria.where("criteria").not().elemMatch(Criteria.where("key").is(key));
return new Criteria().orOperator(valueMatch, notSpecified);
}
包含事件的属性,我们希望为其找到匹配的订阅。 (它具有“order.bookingUser.id”=&gt;“janedoe”之类的映射。)如果订阅未指定给定属性的值,则将其视为通配符并匹配该属性的所有值。
SELECT
SUM(`a`.`credits`) as `credits`
FROM (
SELECT
MAX(`course`.`credits`) AS `credits`
FROM `student`
JOIN `takes`
ON `takes`.`ID` = `student`.`ID`
JOIN `section`
USING (`course_id`,`sec_id`,`semester`,`year`)
JOIN `course`
ON `course`.`course_id` = `section`.`course_id`
WHERE `student`.`ID` = 20084
GROUP BY `section`.`course_id`
ORDER BY `takes`.`grade` DESC
) AS `a`