在 Mongodb 中聚合嵌套数组
Aggregate nested array in Mongodb
我有一个mongocollection谎言这个:
{
"_id":ObjectId("55f16650e3cf2242a79656d1"),
"user_id":11,
"push":[
ISODate("2015-09-08T11:14:18.285 Z"),
ISODate("2015-09-08T11:14:18.285 Z"),
ISODate("2015-09-09T11:14:18.285 Z"),
ISODate("2015-09-10T11:14:18.285 Z"),
ISODate("2015-09-10T11:14:18.285 Z")
]
}{
"_id":ObjectId("55f15c78e3cf2242a79656c3"),
"user_id":12,
"push":[
ISODate("2015-09-06T11:14:18.285 Z"),
ISODate("2015-09-05T11:14:18.285 Z"),
ISODate("2015-09-07T11:14:18.285 Z"),
ISODate("2015-09-09T11:14:18.285 Z"),
ISODate("2015-09-09T11:14:18.285 Z"),
ISODate("2015-09-10T11:14:18.285 Z"),
ISODate("2015-09-11T11:14:18.285 Z")
]
}
如何在单个查询中找到 user_ids timeStamps < 3 且日期 (timestamp) > (currentDate-5) 的位置。我将使用 php 并且不想将所有文档都放在内存中。
说明:
user_id : date : count
11 : 2015-09-08 : 2
2015-09-09 : 1
2015-09-10 : 2
12 : 2015-09-05 : 1
2015-09-06 : 1
2015-09-07 : 1
2015-09-09 : 2
2015-09-10 : 1
2015-09-11 : 1
如果日期设置为 2015-09-09(用户输入),它将为 user_id 11 提供 3(计数),为 user_id 12 提供 4(计数)。所以假设设置了计数到 3(用户输入)。查询应该 return 11(user_id)。如果计数设置为 2,将没有 user_id 可用,如果计数设置为 5,它应该 return 11 和 12
为了解决这个问题,您需要一个聚合管道,它首先 "filters" 将结果 "last 5 days" 然后基本上 "sums the count" 每个符合条件的文档中存在的数组项,然后查看是否"total" 是 "less than three".
所需的 $size
operator of MongoDB aggregation really helps here, as does $map
and some additional filtering via $setDifference
for the false
results returned from $map
, as doing this "in document first" and "within" the $group
阶段是处理此问题的最有效方法
$result = $collection->aggregate(array(
array( '$match' => array(
'push' => array(
'time' => array(
'$gte' => MongoDate( strtotime('-5 days',time()) )
)
)
)),
array( '$group' => array(
'_id' => '$user_id',
'count' => array(
'$sum' => array(
'$size' => array(
'$setDifference' => array(
array( '$map' => array(
'input' => '$push',
'as' => 'time',
'in' => array(
'$cond' => array(
array( '$gte' => array(
'$$time',
MongoDate( strtotime('-5 days',time()) )
)),
'$time',
FALSE
)
)
)),
array(FALSE)
)
)
)
)
)),
array( '$match' => array(
'count' => array( '$lt' => 3 )
))
));
所以首先要通过$match
找到包含满足条件的数组条目的"possible"文档,然后找到匹配数组项的"total"大小在$group
下,那么最后的$match
排除了所有总大小小于3的结果。
对于大部分 "JavaScript brains" 的人(像我自己一样,训练有素)基本上是这样的结构:
db.collection.aggregate([
{ "$match": {
"push": {
"$gte": new Date( new Date().valueOf() - ( 5 * 1000 * 60 * 60 * 24 ))
}
}},
{ "$group": {
"_id": "$user_id",
"count": {
"$sum": {
"$size": {
"$setDifference": [
{ "$map": {
"input": "$push",
"as": "time",
"in": {
"$cond": [
{ "$gte": [
"$$time",
new Date(
new Date().valueOf() -
( 5 * 1000 * 60 * 60 * 24 )
)
]},
"$$time",
false
]
}
}},
[false]
]
}
}
}
}},
{ "$match": { "count": { "$lt": 3 } } }
])
此外,MongoDB 的未来版本将提供 $filter
,这将简化整个 $map
和 $setDifference
语句部分:
db.collection.aggregate([
{ "$match": {
"push": {
"$gte": new Date( new Date().valueOf() - ( 5 * 1000 * 60 * 60 * 24 ))
}
}},
{ "$group": {
"_id": "$user_id",
"count": {
"$sum": {
"$size": {
"$filter": {
"input": "$push",
"as": "time",
"cond": {
"$gte": [
"$$time",
new Date(
new Date().valueOf() -
( 5 * 1000 * 60 * 60 * 24 )
)
]
}
}
}
}
}
}},
{ "$match": { "count": { "$lt": 3 } } }
])
同时注意到 "dates" 可能是最好的计算 "before" 管道定义作为一个单独的变量以获得最佳准确性。
我有一个mongocollection谎言这个:
{
"_id":ObjectId("55f16650e3cf2242a79656d1"),
"user_id":11,
"push":[
ISODate("2015-09-08T11:14:18.285 Z"),
ISODate("2015-09-08T11:14:18.285 Z"),
ISODate("2015-09-09T11:14:18.285 Z"),
ISODate("2015-09-10T11:14:18.285 Z"),
ISODate("2015-09-10T11:14:18.285 Z")
]
}{
"_id":ObjectId("55f15c78e3cf2242a79656c3"),
"user_id":12,
"push":[
ISODate("2015-09-06T11:14:18.285 Z"),
ISODate("2015-09-05T11:14:18.285 Z"),
ISODate("2015-09-07T11:14:18.285 Z"),
ISODate("2015-09-09T11:14:18.285 Z"),
ISODate("2015-09-09T11:14:18.285 Z"),
ISODate("2015-09-10T11:14:18.285 Z"),
ISODate("2015-09-11T11:14:18.285 Z")
]
}
如何在单个查询中找到 user_ids timeStamps < 3 且日期 (timestamp) > (currentDate-5) 的位置。我将使用 php 并且不想将所有文档都放在内存中。
说明:
user_id : date : count
11 : 2015-09-08 : 2
2015-09-09 : 1
2015-09-10 : 2
12 : 2015-09-05 : 1
2015-09-06 : 1
2015-09-07 : 1
2015-09-09 : 2
2015-09-10 : 1
2015-09-11 : 1
如果日期设置为 2015-09-09(用户输入),它将为 user_id 11 提供 3(计数),为 user_id 12 提供 4(计数)。所以假设设置了计数到 3(用户输入)。查询应该 return 11(user_id)。如果计数设置为 2,将没有 user_id 可用,如果计数设置为 5,它应该 return 11 和 12
为了解决这个问题,您需要一个聚合管道,它首先 "filters" 将结果 "last 5 days" 然后基本上 "sums the count" 每个符合条件的文档中存在的数组项,然后查看是否"total" 是 "less than three".
所需的 $size
operator of MongoDB aggregation really helps here, as does $map
and some additional filtering via $setDifference
for the false
results returned from $map
, as doing this "in document first" and "within" the $group
阶段是处理此问题的最有效方法
$result = $collection->aggregate(array(
array( '$match' => array(
'push' => array(
'time' => array(
'$gte' => MongoDate( strtotime('-5 days',time()) )
)
)
)),
array( '$group' => array(
'_id' => '$user_id',
'count' => array(
'$sum' => array(
'$size' => array(
'$setDifference' => array(
array( '$map' => array(
'input' => '$push',
'as' => 'time',
'in' => array(
'$cond' => array(
array( '$gte' => array(
'$$time',
MongoDate( strtotime('-5 days',time()) )
)),
'$time',
FALSE
)
)
)),
array(FALSE)
)
)
)
)
)),
array( '$match' => array(
'count' => array( '$lt' => 3 )
))
));
所以首先要通过$match
找到包含满足条件的数组条目的"possible"文档,然后找到匹配数组项的"total"大小在$group
下,那么最后的$match
排除了所有总大小小于3的结果。
对于大部分 "JavaScript brains" 的人(像我自己一样,训练有素)基本上是这样的结构:
db.collection.aggregate([
{ "$match": {
"push": {
"$gte": new Date( new Date().valueOf() - ( 5 * 1000 * 60 * 60 * 24 ))
}
}},
{ "$group": {
"_id": "$user_id",
"count": {
"$sum": {
"$size": {
"$setDifference": [
{ "$map": {
"input": "$push",
"as": "time",
"in": {
"$cond": [
{ "$gte": [
"$$time",
new Date(
new Date().valueOf() -
( 5 * 1000 * 60 * 60 * 24 )
)
]},
"$$time",
false
]
}
}},
[false]
]
}
}
}
}},
{ "$match": { "count": { "$lt": 3 } } }
])
此外,MongoDB 的未来版本将提供 $filter
,这将简化整个 $map
和 $setDifference
语句部分:
db.collection.aggregate([
{ "$match": {
"push": {
"$gte": new Date( new Date().valueOf() - ( 5 * 1000 * 60 * 60 * 24 ))
}
}},
{ "$group": {
"_id": "$user_id",
"count": {
"$sum": {
"$size": {
"$filter": {
"input": "$push",
"as": "time",
"cond": {
"$gte": [
"$$time",
new Date(
new Date().valueOf() -
( 5 * 1000 * 60 * 60 * 24 )
)
]
}
}
}
}
}
}},
{ "$match": { "count": { "$lt": 3 } } }
])
同时注意到 "dates" 可能是最好的计算 "before" 管道定义作为一个单独的变量以获得最佳准确性。