Лично я бы начал агрегацию из коллекции "plates"
, где начальные условия $match
могут более четко фильтровать диапазон дат. Чтобы получить желаемый результат, достаточно просто «раскрутить» результирующие "accounts"
совпадения и «инвертировать» содержимое.
Достаточно просто с функциями MongoDB 3.6, которые вы должны иметь, чтобы использовать $lookup
с $expr
. Нам даже не нужна эта форма для $lookup
здесь:
db.plates.aggregate([
{ "$match": {
"date": {
"$gte": new Date("2016-01-01").getTime(),
"$lte": new Date("2019-01-01").getTime()
}
}},
{ "$lookup": {
"from": "accounts",
"localField": "accounts.text",
"foreignField": "_id",
"as": "accounts"
}},
{ "$unwind": "$accounts" },
{ "$group": {
"_id": "$accounts",
"plates": { "$push": { "_id": "$_id", "rego": "$rego" } }
}},
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": ["$_id", { "plates": "$plates" }]
}
}}
])
Это, конечно, «ВНУТРЕННЕЕ СОЕДИНЕНИЕ», которое будет возвращать только "accounts"
записей, где МАТ
Выполнение «соединения» из коллекции "accounts"
означает, что вам требуется дополнительная обработка для удаления несовпадающих записей из массива "accounts"
в коллекции "plates"
:
db.accounts.aggregate([
{ "$lookup": {
"from": "plates",
"let": { "account": "$_id" },
"pipeline": [
{ "$match": {
"date": {
"$gte": new Date("2016-01-01").getTime(),
"$lte": new Date("2019-01-01").getTime()
},
"$expr": { "$in": [ "$$account", "$accounts.text" ] }
}},
{ "$project": { "_id": 1, "rego": 1 } }
],
"as": "plates"
}}
])
Обратите внимание, что $match
в свойствах "date"
должны быть выражены как обычное условие запроса, а не в блоке $expr
для оптимальной производительности запроса.
$in
используется для сравнения «массива» значений "$accounts.text"
с локальной переменной, определенной для значения "_id"
документа "accounts"
, к которому присоединяется. Таким образом, первый аргумент $in
является «единственным» значением, а второй - «массивом» только "text"
значений, которые должны совпадать.
Это также, в частности, «LEFT JOIN», который возвращает все "accounts"
независимо от того, есть ли совпадения "plates"
с условиями, и, следовательно, вы можете получить пустой массив "plates"
в возвращаемых результатах , Вы можете отфильтровать их, если они вам не нужны, но в этом случае прежняя форма запроса действительно намного эффективнее, чем эта, поскольку отношение определено, и мы когда-либо имеем дело только с "plates"
, который соответствует критериям .
Любой метод возвращает тот же ответ из данных, представленных в вопросе:
{
"_id" : "Acc1",
"date" : 1516374000000,
"createdAt" : 1513810712802,
"plates" : [
{
"_id" : "Batch 1",
"rego" : "1QX-WA-123"
}
]
}
В каком направлении вы на самом деле выбираете это, зависит от того, действительно ли вам нужна форма соединения «ВЛЕВО» или «ВНУТРЕННЯЯ», а также от того, где можно создать наиболее эффективные условия запроса для элементов, которые вы на самом деле хотите выбрать.