У меня есть коллекция монго, лежат так:
{
"_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 и имеющий дату (отметка времени)> (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 (пользовательский ввод), это даст 3 (количество) для user_id 11 и 4 (количество) для user_id 12. Так что предположим, что счетчик равен 3 (пользовательский ввод). Запрос должен вернуть 11 (user_id). Если для счетчика установлено значение 2, то user_id не будет доступен, а если для счетчика установлено значение 5, он должен вернуть как 11, так и 12
Чтобы решить эту проблему, вам нужен конвейер агрегации, который сначала «фильтрует» результаты по «последним 5 дням», а затем по существу «суммирует» количество элементов массива, присутствующих в каждом квалифицирующем документе, чтобы затем определить, меньше ли «итоговое значение», чем три».
$size
Здесь действительно помогает оператор агрегации MongoDB, а также $map
и некоторая дополнительная фильтрация через $setDifference
для false
результаты возвращены из $map
, как делать это «в документе сначала» и «внутри» $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
а затем найдите «общий» размер соответствующих элементов массива в $group
потом финал $match
исключает все результаты, которые меньше трех в общем размере.
Для в значительной степени «мозгов JavaScript» (таких как я, хорошо обученных этому) это в основном такой проект:
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 } } }
])
Также следует отметить, что «даты», вероятно, лучше всего рассчитывать «до» определения конвейера как отдельной переменной для лучшей точности.
Других решений пока нет …