在单个查询匹配键从两个查询(Match on key from two queries in a s

2019-10-23 13:10发布

我在MongoDB的时间序列数据如下:

{ 
    "_id" : ObjectId("558912b845cea070a982d894"),
    "code" : "ZL0KOP",
    "time" : NumberLong("1420128024000"),
    "direction" : "10", 
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d895"), 
    "code" : "AQ0ZSQ", 
    "time" : NumberLong("1420128025000"), 
    "direction" : "10",
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d896"),
    "code" : "AQ0ZSQ",
    "time" : NumberLong("1420128003000"),
    "direction" : "10", 
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d897"), 
    "code" : "ZL0KOP",
    "time" : NumberLong("1420041724000"),
    "direction" : "10",
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d89e"),
    "code" : "YBUHCW",
    "time" : NumberLong("1420041732000"),
    "direction" : "10",
    "siteId" : "0002"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d8a1"),
    "code" : "U48AIW",
    "time" : NumberLong("1420041729000"),
    "direction" : "10",
    "siteId" : "0002"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d8a0"), 
    "code" : "OJ3A06",
    "time" : NumberLong("1420300927000"),
    "direction" : "10",
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d89d"),
    "code" : "AQ0ZSQ",
    "time" : NumberLong("1420300885000"),
    "direction" : "10",
    "siteId" : "0003"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d8a2"),
    "code" : "ZLV05H",
    "time" : NumberLong("1420300922000"),
    "direction" : "10",
    "siteId" : "0001"
}
{
    "_id" : ObjectId("558912b845cea070a982d8a3"),
    "code" : "AQ0ZSQ",
    "time" : NumberLong("1420300928000"),
    "direction" : "10", 
    "siteId" : "0000"
}

符合两个或两个以上条件的代码需要被过滤掉。 例如:

condition1: 1420128000000 < time < 1420128030000,siteId == 0000
condition2: 1420300880000 < time < 1420300890000,siteId == 0003

结果第一个条件:

{ 
    "_id" : ObjectId("558912b845cea070a982d894"),
    "code" : "ZL0KOP",
    "time" : NumberLong("1420128024000"),
    "direction" : "10",
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d895"),
    "code" : "AQ0ZSQ",
    "time" : NumberLong("1420128025000"),
    "direction" : "10",
    "siteId" : "0000"
}
{ 
    "_id" : ObjectId("558912b845cea070a982d896"),
    "code" : "AQ0ZSQ",
    "time" : NumberLong("1420128003000"),
    "direction" : "10",
    "siteId" : "0000"
}

结果为第二个条件:

{ 
    "_id" : ObjectId("558912b845cea070a982d89d"),
    "code" : "AQ0ZSQ", "time" : NumberLong("1420300885000"),
    "direction" : "10",
    "siteId" : "0003"
}

这配衬以上所有条件的唯一代码应该是:

{"code" : "AQ0ZSQ", "count":2}

“伯爵”的意思,代码“AQ0ZSQ”出现在两个条件

我能想到的唯一的解决办法是使用两个querys。 例如,使用Python

result1 = list(db.codes.objects({'time': {'$gt': 1420128000000,'$lt': 1420128030000}, 'siteId': "0000"}).only("code"))
result2 = list(db.codes.objects({'time': {'$gt': 1420300880000,'$lt': 1420300890000}},{'siteId':'0003'}).only("code"))

然后发现在两个结果共享代码。

问题是,现在有成千上万的集合中的文件,都可以查询很容易超过16MB的限制。

因此,它是可以做到的是,在一个查询? 或者我应该改变文档的结构?

Answer 1:

你问这里需要的使用聚合框架 ,以便计算,有在服务器上的结果之间的交集。

逻辑的第一部分是你需要一个$or两个条件查询,然后会有一些额外的投影和过滤这些结果:

db.collection.aggregate([
    // Fetch all possible documents for consideration
    { "$match": {
        "$or": [
            { 
                "time": { "$gt": 1420128000000, "$lt": 1420128030000 },
                "siteId": "0000"
            },
            {
                "time": { "$gt": 1420300880000, "$lt": 1420300890000 },
                "siteId": "0003"
            }
        ]
    }},

    // Locigically compare the conditions agaist results and add a score
    { "$project": {
        "code": "$code",
        "score": { "$add": [
            { "$cond": [ 
                { "$and":[
                    { "$gt": [ "$time", 1420128000000 ] },
                    { "$lt": [ "$time", 1420128030000 ] },
                    { "$eq": [ "$siteId", "0000" ] }
                ]},
                1,
                0
            ]},
            { "$cond": [ 
                { "$and":[
                    { "$gt": [ "$time", 1420300880000 ] },
                    { "$lt": [ "$time", 1420300890000 ] },
                    { "$eq": [ "$siteId", "0003" ] }
                ]},
                1,
                0
            ]}
        ]}
    }},

    // Now Group the results by "code"
    { "$group": {
        "_id": "$code",
        "score": { "$sum": "$score" }
    }},

    // Now filter to keep only results with score 2
    { "$match": { "score": 2 } }
])

因此,打破下来,看看它是如何工作的。

首先,你想用一个查询$match来获取所有可能的文件为您的“交集”条件“所有”。 这正是$or表达式允许在这里通过考虑匹配的文档必须满足任一组。 你需要所有的人都制定出“交叉点”在这里。

在第二$project流水线阶段的你条件布尔测试与每个组进行。 注意的使用$and在这里以及其他布尔运算符的聚合框架的是,查询使用形式略有不同。

在聚合框架形式(外的$match其使用正常的查询运算符)这些运算符采取的参数阵列,典型地表示用于比较而不是操作“二”的值被分配给字段名的“右”。

由于这些条件是逻辑或“布尔”我们要返回的结果为“数字”,而不是true/false值。 这是$cond在这里所做的。 那么,该条件为真对文档检查的分数1被发射,否则是0时返回false。

最后,在此$project表达您的两个条件都包裹着$add ,形成“分数”的结果。 所以,如果没有的条件($比赛后是不可能的)是不正确的比分会是0,如果“一”为真,那么1,或“既”是真的,那么2。

注意到这里要求此处的具体情况将永远不会得分以上1为单个文档,因为没有文件可以具有重叠范围或“二”,“网站ID”的值作为存在这里。

现在重要的部分是$group的“代码”值与$sum的分值一共拿到每个“代码”。

这使得最后$match的管道过滤阶段,只保留这些文件以“分数”值等于你要求条件的数量。 在这种情况下2


有一种然而,在哪里有是在任一条件的匹配“代码”超过一个值(有),那么“分数”在这里是不正确的故障出现。

引入到聚合使用逻辑运算符的原则后,所以,你可以通过实质上“标记”每个结果逻辑哪个条件“设置”,它适用于修复故障。 然后你就可以主要考虑其“代码”出现在“都”套在这种情况下:

db.collection.aggregate([
    { "$match": {
        "$or": [
            { 
                "time": { "$gt": 1420128000000, "$lt": 1420128030000 },
                "siteId": "0000"
            },
            {
                "time": { "$gt": 1420300880000, "$lt": 1420300890000 },
                "siteId": "0003"
            }
        ]
    }},

    // If it's the first logical condition it's "A" otherwise it can
    // only be the other, therefore "B". Extend for more sets as needed.
    { "$group": {
        "_id": {
            "code": "$code",
            "type": { "$cond": [ 
                { "$and":[
                    { "$gt": [ "$time", 1420128000000 ] },
                    { "$lt": [ "$time", 1420128030000 ] },
                    { "$eq": [ "$siteId", "0000" ] }
                ]},
                "A",
                "B"
            ]}
        }
    }},

    // Simply add up the results for each "type"
    { "$group": {
        "_id": "$_id.code",
        "score": { "$sum": 1 }
    }}

    // Now filter to keep only results with score 2
    { "$match": { "score": 2 } }
])

这可能是一个有点采取,如果这是一个使用聚合框架的第一次。 请花时间看作为与链接这里定义的运营商,也看聚合管道运营商一般。

除了简单的数据选择,这是你应该使用MongoDB中时,可以达到最频繁的工具,所以你会很好地了解所有都是可能的操作。



文章来源: Match on key from two queries in a single query