Возвращать только попадания в массив из mongodb - PullRequest
2 голосов
/ 09 октября 2019

Итак, у меня есть коллекция mongodb с некоторыми вложенными документами. Пример документа выглядит следующим образом:

{
  "_id": "5afa9472e937b7254a306ff6",
  "import_date": "2018-05-15T08:04:02.813Z",
  "some_more_things": "foo",
  "meta": {
    "participants": [{ "name": "Ben" }, { "name": "Mary" }],
    "messages": [
      {
        "tokens": [
          { "token": "What" },
          { "token": "do" },
          { "token": "you" },
          { "token": "do" },
          { "token": "today" }
        ],
        "time": "2018-05-09T08:38:19.000Z"
      },
      {
        "tokens": [
          { "token": "Just" },
          { "token": "lying" },
          { "token": "around" }
        ],
        "time": "2018-05-09T08:40:08.000Z"
      },
      {
        "tokens": [
          { "token": "What" },
          { "token": "about" },
          { "token": "you" }
        ],
        "time": "2018-05-09T08:40:11.000Z"
      }
    ]
  }
}

Сейчас я ищу эффективный способ поиска сообщений, в которые включен определенный токен. Я делаю это с помощью следующего запроса:

db.conversations.find({'meta.messages.tokens.token': /^What$/i})
   .projection({'import_date': 1, 'meta.messages': 1})
   .sort({_id:-1})
   .limit(100)

Таким образом, я нахожу документы, которые хочу, но получаю полный массив messages. Есть ли способ получить только те элементы массива messages, которые соответствуют моему регулярному выражению? Результат должен выглядеть так (только первый и последний элемент моего примера документа).

{
  "_id": "5afa9472e937b7254a306ff6",
  "import_date": "2018-05-15T08:04:02.813Z",
  "meta": {
    "participants": [{ "name": "Ben" }, { "name": "Mary" }],
    "messages": [
      {
        "tokens": [
          { "token": "What" },
          { "token": "do" },
          { "token": "you" },
          { "token": "do" },
          { "token": "today" }
        ],
        "time": "2018-05-09T08:38:19.000Z"
      },
      {
        "tokens": [
          { "token": "What" },
          { "token": "about" },
          { "token": "you" }
        ],
        "time": "2018-05-09T08:40:11.000Z"
      }
    ]
  }
}

1 Ответ

0 голосов
/ 10 октября 2019

Вы можете использовать $ indexOfBytes , чтобы проверить, существует ли What в каждой строке. Вам также необходимо $ map с $ filter и $ anyElementTrue , чтобы построить условие фильтрации для вложенного массива:

db.collection.aggregate([
    {
        $addFields: {
            "meta.messages": {
                $filter: {
                    input: "$meta.messages",
                    as: "m",
                    cond: {
                        $anyElementTrue: {
                            $map: {
                                input: "$$m.tokens",
                                in: { $gte: [ { $indexOfBytes: [ "$$this.token", "What" ] }, 0 ] }
                            }
                        }
                    }
                }
            }
        }
    }
])

Mongo Playground

Если вам нужно Regex, вы можете взглянуть на $ regexMatch , представленный в 4.2, и использовать его в качестве замены $indexOfBytes

...