Частичное совпадение MongoDB на объектах - PullRequest
0 голосов
/ 18 декабря 2018

Например, у меня есть такой документ:

{
   name: "Test",
   params: {
     device: "windows",
     gender: "m",
     age: 28
   }
}

Теперь я получаю следующие параметры ввода:

{
   device: "windows",
   gender: "m"
}

В этом случае я хочу найти вседокументы, которые частично совпадают с входным объектом.Есть ли простой способ решить эту проблему?Я пробовал $ elemMatch, но, похоже, это работает только с массивами.

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Вы можете использовать оператор $ или для частичного сравнения полей из объекта params .Вы можете использовать как это:

db.collection.find({
  $or: [
    {
      "params.device": "windows"
    },
    {
      "params.gender": "m"
    }
  ]
})
0 голосов
/ 18 декабря 2018

Использование проекции

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

По умолчанию запросы в MongoDB возвращают все поля всоответствующие документы.Чтобы ограничить объем данных, которые MongoDB отправляет приложениям, вы можете включить проекционный документ для указания или ограничения полей для возврата.

Например, ранее вы добавили следующие документы в базу данных:

> db.users.insertOne({"name": "Jennifer", "gender": "female", languages: ["english", "spanish"]})
> db.users.insertOne({"name": "Alex", "gender": "male", languages: ["english", "french"]})
> db.users.insertOne({"name": "Maria", "gender": "female", languages: ["english", "german"]})

И теперь вы хотите отобразить информацию, которая соответствует полю gender и имеет значение female, поэтому вы можете указать ее с помощью запроса:

db.users.find({gender: "female"}, {name: 1})

Операция возвращает следующеедокументы:

{ "name" : "Jennifer", "gender" : "female" }
{ "name" : "Maria", "gender" : "female" }

<value> может быть любым из следующих:

{ field1: <value>, field2: <value> ... }
  • 1 или true, чтобы включить поле в документы возврата.
  • 0 или false, чтобы исключить поле.
  • Выражение с использованием операторов проекции.

Более того, если вы не хотите конкретизировать выделение, но хотите отобразить вседокументы, тогда мы можем оставить первые квадратные скобки пустыми:

db.users.find({}, {name: 1, _id: 0})
0 голосов
/ 18 декабря 2018

Если я правильно понял ваш вопрос, у вас есть входной объект, который может содержать некоторые полей в объекте params основного документа, в любом порядке, например:

{
   device: "windows",
   gender: "m"
}

{
   gender: "m",
   device: "windows"
}

{
   device: "windows",
   age: 28
}

и вы хотите сопоставить, только если все поля во входном объекте существуют в основном документе:

{
   device: "linux",    // NO MATCH
   gender: "m"
}

{
   gender: "m",        // MATCH
   device: "windows"
}

{
   device: "windows",  // NO MATCH
   age: 29
}

Правильно?


Опция 1

Всегда ли ваш объект param содержит только эти три поля (device, gender и age)?

Если это так, вы можетевручную спроецируйте каждый из них на корневой уровень, сопоставьте и снова «отмените» их:

db.my_collection.aggregate([
    {
        $project: {
            name: 1, 
            params: 1, 
            device: "$params.device",  // add these three for your match stage
            gender: "$params.gender", 
            age: "$params.age"
        }
    },
    {
        $match: input_params
    },
    {
        $project: {name: 1, params: 1}  // back to original
    }
]);

Но я предполагаю, что вы хотите сделать это для любого числа полей внутри вашего params объекта.


Опция 2

Есть ли у вас способ манипулировать входящим входящим объектом?Если это так, вы могли бы предварить все поля "params.":

let input_params = 
{
   device: "windows",
   gender: "m"
};

let new_input_params =
{
   "params.device": "windows",
   "params.gender": "m"
};

Тогда ваш запрос будет просто:

db.my_collection.find(new_input_params);

Вариант 3

Если у вас нет способа изменить ввод , вы можете использовать оператор агрегирования $ replaceRoot (начиная с версии 3.4, кредит этот ответ ), чтобы сгладитьваш params в корневой документ.Поскольку это заменит корневой документ на внедренный, вам нужно сначала извлечь интересующие вас поля, по крайней мере, поле _id:

db.my_collection.aggregate([
    {
        $addFields: {"params.id": "$_id"}  // save _id inside the params doc, 
                                           // as you will lose the original one
    },
    {
        $replaceRoot: {newRoot: "$params"}
    },
    {
        $match: input_params
    },
    ...
]);

Это будет соответствовать документам и сохранит *Поле 1057 *, которое вы можете использовать, чтобы снова получить оставшуюся часть документа, например через $lookup:

    ...
    {
        $lookup: {
            from: "my_collection", 
            localField: "id", 
            foreignField: "_id", 
            as: "doc"
        }
    },
    ...

Это позволит получить ваши документы в следующем формате:

{ 
  "device" : "windows", 
  "gender" : "m", 
  "age" : 28, 
  "id" : ObjectId("XXX"), 
  "doc" : [ 
    {
      "_id" : ObjectId("XXX"), 
      "name" : "Test", 
      "params" : { 
        "device" : "windows", 
        "gender" : "m", 
        "age" : 28 
      }
    }
  ]
}

Чтобы пройти полный круг и вернуть исходный формат документа, вы можете:

    ...
    {
        $unwind: "$doc" // get rid of the [] around the "doc" object 
                        // ($lookup always results in array)
    },
    {
        $replaceRoot: {newRoot: "$doc"} // get your "doc" back to the root
    }
    ...

Пока я пишу это, я не могу поверить, что нет более чистого способа сделать это, используя только MongoDB, ноЯ не могу думать ни о чем.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...