Как выполнить условный поиск $в MongoDB

#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 не оптимизирован, я надеюсь, что это будет, но это не так. Приведенный выше код делает то, что вы хотите, но вы не можете полностью избежать затрат на поиск.