MongoDB查询查找多个嵌入文档

时间:2018-03-26 15:28:55

标签: mongodb spring-boot

我尝试使用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"}}}}]}
    ]
}

2 个答案:

答案 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`