Есть ли способ найти ключевые поля, которые содержат определенную подстроку в MongoDB?

#mongodb #find #key #document #substr

Вопрос:

Я рассмотрел множество решений, но все они включают поиск подстрок в значениях полей. У меня есть коллекция, в которой более 3 миллионов документов, и в некоторых из них есть поля даты, но не все, и не одинаковое количество полей в каждом. Например, у одного может быть «Дата первого дня» и «Дата последнего дня», другие могут содержать только «Дату», а другие могут вообще не содержать полей даты.

Что я хочу сделать, так это запросить все документы, в которых в ключах полей есть слово/подстрока «дата». Другими словами, запросите все ключевые поля, содержащие подстроку «дата». Например:

 db.collection.find({ *date* : { $exists : true } })
   .projection({})
   .sort({_id:-1})
 

(Я знаю, что * неправильно, но это просто для иллюстрации)

Я пробовал использовать .find() , .aggregate() и $regex , но все синтаксисы, которые я пробовал, вообще не имеют смысла…

Ответ №1:

Это может быть достигнуто с помощью конвейера агрегирования. Давайте предположим, что ваша коллекция выглядит так:

 [
  {
    "_id": 1,
    property: true,
    
  },
  {
    "_id": 2,
    firstDate: "First date"
  },
  {
    "_id": 3,
    lastDate: "Last name"
  },
  {
    "_id": 4,
    date: "date"
  },
  {
    "_id": 5,
    property: true
  },
  {
    "_id": 6,
    name: "Name"
  }
]
 

Сначала вам нужно создать свойство с массивом ключей и значений, чтобы позже вы могли фильтровать по ключам. Это можно сделать, используя $objectToArray в an $addFields .

 db.collection.aggregate([
  {
    $addFields: {
      keysAndValues: {
        $objectToArray: "$ROOT",
      }
    }
  }
]);
 

Если вы перейдете $$ROOT к нему, вы создадите массив со всеми ключами и значениями документов. Таким образом, ваши данные на данный момент будут выглядеть следующим образом:

 [
  {
    "_id": 1,
    "keysAndValues": [
      {
        "k": "_id",
        "v": 1
      },
      {
        "k": "property",
        "v": true
      }
    ],
    "property": true
  },

  ...

  {
    "_id": 6,
    "keysAndValues": [
      {
        "k": "_id",
        "v": 6
      },
      {
        "k": "name",
        "v": "Name"
      }
    ],
    "name": "Name"
  }
]
 

Таким образом, теперь вы можете добавить $match этап, используя $regex фильтр по ключам, например:

 db.collection.aggregate([
  {
    $addFields: {
      keysAndValues: {
        $objectToArray: "$ROOT",
        
      }
    }
  },
  {
    $match: {
      "keysAndValues.k": {
        $regex: "date",
        $options: "i",
        
      }
    }
  }
]);
 

На этом этапе у вас уже был бы желаемый результат, все документы, в которых есть слово date в ключах, тогда вы могли бы просто отключить созданное вами свойство, чтобы сделать возможной фильтрацию keysAndValues , для очистки данных.

 db.collection.aggregate([
  {
    $addFields: {
      keysAndValues: {
        $objectToArray: "$ROOT",
        
      }
    }
  },
  {
    $match: {
      "keysAndValues.k": {
        $regex: "date",
        $options: "i",
        
      }
    }
  },
  { $unset: 'keysAndValues' },
]);
 

После этого ваш окончательный результат будет:

 [
  {
    "_id": 2,
    "firstDate": "First date"
  },
  {
    "_id": 3,
    "lastDate": "Last name"
  },
  {
    "_id": 4,
    "date": "date"
  }
]
 

Вы можете увидеть рабочий пример на этой игровой площадке:
https://mongoplayground.net/p/U5FjTOVQIwb