Агрегация MongoDB - оператор для чтения в документах

Поскольку Mongo поддерживает только одно $text поле на конвейер агрегации (внутри первого $match этапа), это означает, что вы не можете выполнить логическое И, поскольку вы не можете $and результаты нескольких $text поисков.

// Fails due to "too many text expressions"
db.Employees.aggregate([
    {$match: {$and: [
        {$text: {$search: "senior"}},
        {$text: {$search: "manager"}}
    ]}}
])

Поэтому мне нужно выполнить несколько отдельных поисков $text, объединить результаты в моем коде NodeJS и передать этот набор результатов обратно в конвейер агрегации для дальнейшей обработки (например, $addFields, $match, $sort).

Есть ли способ сделать что-то вроде...

let results1 = db.Employees.find({"$text":{"$search":"senior"}}, {"score":{"$meta":"textScore"}})
let results2 = db.Employees.find({"$text":{"$search":"manager"}}, {"score":{"$meta":"textScore"}})
let combinedResults = _.intersectionWith(results1, results2, _.isEqual)
let finalResults = /* pass combinedResults into aggregation pipeline and execute it */

Что-то вроде противоположности оператору $out, где я вместо этого я читал результирующий набор.

Я использую NestJS и Mongoose, если это поможет.


person yev    schedule 13.08.2020    source источник
comment
Вы ищете поиск по фразе, где документ содержит Senior manager?   -  person Gibbs    schedule 14.08.2020
comment
Я изучил поиск по фразе docs.mongodb.com/manual /reference/operator/query/text/#phrases, но я хочу, чтобы он совпадал, даже если термины не совпадают друг с другом, поэтому документ должен содержать senior в каком-то поле и manager в каком-то поле. Они могут находиться в одном поле, но не обязательно. Лучшим примером может быть поиск, например, Джеймс И менеджер   -  person yev    schedule 14.08.2020
comment
Есть ли способ напрямую передать предопределенный массив документов в конвейер агрегации?   -  person yev    schedule 14.08.2020
comment
К сожалению, для каждой агрегации допускается только один текст. Вы можете использовать регулярное выражение?   -  person Gibbs    schedule 14.08.2020
comment
Я мог бы использовать какое-то регулярное выражение - позволит ли это мне объединить все в агрегацию?   -  person yev    schedule 14.08.2020


Ответы (1)


В $text есть ограничения, которые вы уже знаете,

Существует вариант, если у вас есть ограниченные поля, используя $regexMatch, я не уверен, в скольких полях у вас есть текстовый индекс, но с этим вы можете комбинировать условия совпадения с оператором $and для нескольких полей,

Пример данных:

[
  { _id: 1, f1: "senior", f2: "manager" },
  { _id: 2, f1: "junior", f2: "manager" },
  { _id: 3, f1: "fresher", f2: "developer" },
  { _id: 4, f1: "manager", f2: "senior" }
]

Агрегационный запрос 1:

  • $addFields чтобы добавить новое поле matchResult для сопоставления статуса в логическом значении
db.collection.aggregate([
  {
    $addFields: {
      matchResult: {
        $and: [
  • первое $or условие соответствует, если f1 или f2 поля соответствуют senior, то возвращает true иначе возвращает false
          {
            $or: [
              { $regexMatch: { input: "$f1", regex: "senior", options: "x" } },
              { $regexMatch: { input: "$f2", regex: "senior", options: "x" } }
            ]
          },
  • второе условие $or соответствует, если поля f1 или f2 соответствуют manager, то вернуть true иначе вернуть false
          {
            $or: [
              { $regexMatch: { input: "$f1", regex: "manager", options: "x" } },
              { $regexMatch: { input: "$f2", regex: "manager", options: "x" } }
            ]
          }
        ]
      }
    }
  },
  • $match условие возвращает результат, у которого matchResult равно true
  { $match: { matchResult: true } }
])

Игровая площадка

Агрегационный запрос 2:

  • если вы не используете поля массива, то это способ сортировки, напрямую вы можете объединить все поля в одном поле, здесь я объединил f1 и f2 с пробелом в allField
db.collection.aggregate([
  {
    $addFields: {
      allField: { $concat: ["$f1", " ", "$f2"] }
    }
  },
  • это будет соответствовать условию $and при совпадении обоих слов, если оба верны, то вернет true, иначе false
  {
    $addFields: {
      matchResult: {
        $and: [
          { $regexMatch: { input: "$allField", regex: "senior", options: "x" } },
          { $regexMatch: { input: "$allField", regex: "manager", options: "x" } }
        ]
      }
    }
  },
  • $match условие возвращает результат, у которого matchResult равно true
  { $match: { matchResult: true } }
])

Игровая площадка

Примечание. Это альтернативный подход для ограниченного числа полей, но отображение более 5 полей влияет на скорость и производительность запроса.

person turivishal    schedule 15.08.2020
comment
Черт, самая большая проблема в том, что у нас много полей в документе, поэтому $text в основном необходим для поиска во всех них одновременно (и для всех его причудливых слов и т. д.). - person yev; 17.08.2020
comment
Я могу просто продолжать использовать несколько операций find() с $text, чтобы получить результаты. Вернемся к основному вопросу, я думаю, у меня есть больше несвязанных этапов обработки дальше по конвейеру агрегации (в основном больше $addFields для изменения оценки результата на основе других критериев). Есть ли этап для импорта результатов $text в этот конвейер, или агрегирование всегда должно начинаться со всей коллекции? - person yev; 17.08.2020
comment
да, вы правы, я уже упоминал, что это вариант для ограниченных полей, а с $text это невозможно. - person turivishal; 17.08.2020
comment
я не знаю, но может быть ваши 2 запроса - хорошее решение ... и есть много ограничений на текст $, который вы можете прочитать в ссылке на вопрос. - person turivishal; 17.08.2020