#mongodb #lookup
Вопрос:
У меня есть три коллекции: A,B,C.
У A есть свойство с именем a, которое является идентификатором ссылки в B или C.
Что я хочу сделать, так это сначала посмотреть в B, если ничего не найдено, а затем посмотреть в C.
Что-то вроде этого:
[
{
$lookup:
{
from: 'B',
localField: 'a',
foreignField: '_id',
as: 'temp'
}
},
{
$unwind:
{
path: '$temp',
preserveNullAndEmptyArrays: true
}
},
{
$lookup:
{
if: 'temp not exist', <! just demonstrate !>
from: 'C',
localField: 'a',
foreignField: '_id',
as: 'temp'
}
},
]
Я не знаю, как это реализовать. Кто-нибудь может помочь? Спасибо.
Комментарии:
1. пожалуйста, предоставьте образцы данных для каждой коллекции
Ответ №1:
Я не думаю, что мы можем выполнять этапы условного конвейера(мы можем выполнять условные операторы, но не этапы), основываясь на информации, которой мы располагаем во время выполнения запроса.
*если бы у нас была информация при написании запроса, мы могли бы генерировать запрос динамически и включать этап или нет
Но вы можете использовать $lookup
конвейер и добавить этот фильтр, например, если temp является пустым массивом (что означает, что соединения не произошло), чтобы присоединиться только тогда.
Я не вижу причин делать раскрутку посередине, делать 2 поиска и делать это после.
Запрос
- выполните первый поиск
- второй поиск-это конвейер с фильтром, для которого требуется, чтобы временный массив первого поиска был пустым
(если вы сохраняете размотку посередине, используйте это{"$eq": [{"$type": "$temp"}, "missing"]}
вместо пустой проверки). - сохраняет как temp, temp или tempC в зависимости от того, кто не пуст
- снимите настройку tempC
- выполняется ли раскрутка сейчас (регистрация до раскрутки, я думаю, быстрее, меньше документов), также вы хотите сначала найти участников объединения, а затем раскрутиться
aggregate(
[ {"$lookup":
{"from": "B", "localField": "a", "foreignField": "_id", "as": "temp"}},
{"$lookup":
{"from": "C",
"let": {"a": "$a", "temp": "$temp"},
"pipeline":
[{"$match":
{"$expr":
{"$and": [{"$eq": ["$temp", []]}, {"$eq": ["$a", "$_id"]}]}}}],
"as": "tempC"}},
{"$set": {"temp": {"$cond": [{"$eq": ["$temp", []]}, "$tempC", "$temp"]}}},
{"$unset": ["tempC"]},
{"$unwind": {"path": "$temp", "preserveNullAndEmptyArrays": true}}])
Комментарии:
1. Большое спасибо. Насколько я понимаю, он будет искать как в коллекциях B, так и в коллекциях C. Но на самом деле в этом нет необходимости и может потребоваться дополнительное время. Есть ли у нас какое-либо решение, с помощью которого мы можем пропустить второй поиск, если первый поиск уже что-то вернул?
2. я провел сравнительный анализ, MongoDB даже если вы поставите фильтр, который всегда является ложным, для которого требуется только поле коллекции из конвейера(а не другая коллекция), вы все равно заплатите примерно 75% стоимости поиска, MongoDB не оптимизирован, я надеюсь, что это будет, но это не так. Приведенный выше код делает то, что вы хотите, но вы не можете полностью избежать затрат на поиск.