我在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}
&#34;计数&#34;意思是代码&#34; AQ0ZSQ&#34;出现在两个条件下
我能想到的唯一解决方案是使用两个查询。例如,使用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的限制。
那么在一个查询中可以这样做吗?或者我应该更改文档结构?
答案 0 :(得分:1)
您在这里要求的是要求使用aggregation framework来计算服务器上的结果之间存在交集。
逻辑的第一部分是您需要对这两个条件进行$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
的使用以及聚合框架的其他boolean operators与查询使用表单的使用略有不同。
在聚合框架形式(使用普通查询运算符的$match
之外)中,这些运算符采用一组参数,通常表示“两个”值进行比较,而不是分配给“右”的操作字段名称。
由于这些条件是逻辑或“布尔”,我们希望将结果返回为“数字”而不是true/false
值。这就是$cond
在这里做的事情。因此,如果检查的文档的条件为真,则会发出1
的分数,否则为0
。
最后,在此$project
表达式中,您的所有条件都包含$add
以形成“得分”结果。因此,如果没有任何条件(在$ match之后不可能)不为真,则分数将为0,如果“one”为真,则为1,或者“both”为真,则为2。
请注意,此处要求的具体条件永远不会超过单个文档的1
,因为此处不存在重叠范围或“两个”“siteId”值。
现在,重要的部分是$group
的“代码”值和$sum
得分值,以获得每个“代码”的总数。
这使得管道的最后$match
过滤阶段只保留那些文件的“得分”值等于您要求的条件数。在这种情况下2
。
但是有一个失败的原因是,在任何条件的匹配中存在多个“代码”值(因为有),那么这里的“得分”将是不正确的。
因此,在介绍了在聚合中使用逻辑运算符的原则之后,您可以通过在逻辑上“标记”每个结果来确定它应用于哪个“set”条件来修复该错误。那么你基本上可以考虑在这种情况下“两个”集中出现的“代码”:
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 } }
])
如果这是您第一次使用聚合框架,可能会有一点需要考虑。请花些时间查看此处使用链接定义的运算符,并一般查看Aggregation Pipeline Operators。
除了简单的数据选择之外,这是您在使用MongoDB时应该最常接触的工具,因此您最好能够理解所有可能的操作。