Как указать поля в MongoDB с несколькими условиями поиска запроса? - PullRequest
0 голосов
/ 10 октября 2018

Это отдельный документ:

{
   _id: "...",
   firstName: "john",
   lastName:"Doe",
   cars: [
       {
           "_id": "...",
           "carName": "BMW",
           "carModel": "330",
           "carColor": "silver"
       },
       {
           "_id": "...",
           "carName": "Lexus",
           "carModel": "IS300",
           "carColor": "white"
       },
       {
           "_id": "...",
           "carName": "LADA",
           "carModel": "2106",
           "carColor": "blue"
       }
   ]
}

Я пытаюсь выбрать только "carColor" Джона БМВ.Примерно так:

db.persons.findOne(
        { "firstName": "John", "cars.carName": "BMW" },
        { "_id": 0, "cars.$.carColor": 1 }
      );

Но этот запрос возвращает полный объект, подобный этому:

{
    cars: [
      {
         "_id": "...",
         "carName": "BMW",
         "carModel": "330",
         "carColor": "silver"
      }
}

Я пробовал другой запрос уже без. $.символ:

db.persons.findOne(
            { "firstName": "John", "cars.carName": "BMW" },
            { "_id": 0, "cars.carColor": 1 }
          );

Эта версия возвращает только свойства "carColor", но без фильтрации "carName".Как это:

{
   cars: [
       {
          "carColor": "silver"
       },
       {
          "carColor": "white"
       },
       {
          "carColor": "blue"
       }
   ]
}

Есть идеи?

Ответы [ 3 ]

0 голосов
/ 10 октября 2018
db.persons.find({
  firstName: 'john',
  cars: {
    $elemMatch: {
      carName: 'BMW'
    }
  }
},
{
  'cars.$': 1
})
0 голосов
/ 10 октября 2018

Если вы точно знаете, что в вашем массиве есть не более одного значения «BMW», то вот способ получить результат, используя один $project этап:

db.getCollection('collection').aggregate([{
    $match: {
        "firstName": "john"
        /* for performance reasons, you may want to include the following line which, however, is not required */
        /* this makes sense if you have lots of "john"s with different sets of cars in your database */
        , "cars.carName": "BMW" // this will use an index on "cars.carName" if available
    }
}, {
    $project: {
        _id: 0, // do not return the _id field
        color: {
            $reduce: { // transform the filtered input array
                "input": {
                    $filter: { // remove all non-"BMW" cars from the "cars" array
                        input: "$cars",
                        as: "car",
                        cond: { $eq: [ "$$car.carName", "BMW" ] }
                    }
                },
                "initialValue": null,
                "in": "$$this.carColor" // just return the color value, nothing else
            }
        }
    }
}])
0 голосов
/ 10 октября 2018

Почему это не работает?

{"firstName": "John", "cars.carName": "BMW"}

означает «где имя Джон и где по крайней мере одна запись в массиве автомобилей, где carName -« BMW »».Но он возвращает полный документ без массива фильтрации.

{"_id": 0, "cars.carColor": 1}

не проецирует _id, нопроекты carColor всех записей массива автомобилей.

РЕШЕНИЕ

Фактически, вы не можете достичь именно того, что вы хотите, с помощью методов поиска и проекции.Лучше всего добавить оператор проекции $ , например:

db.collection.find({
  firstName: "john",
  "cars.carName": "BMW"
},
{
  _id: 0,
      "cars.$": 1
    })

**RESULT**

[
  {
    "cars": [
      {
        "_id": "...",
        "carColor": "silver",
        "carModel": "330",
        "carName": "BMW"
      }
    ]
  }
]

Но у этого метода есть недостатки:

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

ЛУЧШЕЕ РЕШЕНИЕ

К счастью, MongoDB предоставляет другой способ достижения этой цели, с помощью механизма агрегирования и оператора $ filter :

db.collection.aggregate([
  {
    $match: {
      firstName: "john"
    }
  },
  {
    $project: {
      cars: {
        $filter: {
          input: "$cars",
          as: "cars",
          cond: {
            $eq: [
              "$$cars.carName",
              "BMW"
            ]
          }
        }
      }
    }
  },
  {
    $project: {
      _id: 0,
      "colors": "$cars.carColor"
    }
  }
])

Вы можете попробовать этоздесь.

РЕДАКТИРОВАТЬ: Другое решение

вы можете попробовать это тоже, с размоткой / групповые этапы:

db.collection.aggregate([
  {
    $match: {
      firstName: "john"
    }
  },
  {
    $unwind: "$cars"
  },
  {
    $match: {
      "cars.carName": "BMW"
    }
  },
  {
    $group: {
      "_id": null,
      colors: {
        $push: "$cars.carColor"
      }
    }
  }
])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...