MongoDB: Получить счетчик внутреннего объекта массива с совпадением вложенного элемента массива - PullRequest
0 голосов
/ 27 апреля 2020

У меня есть коллекция mon go с ответами на вопросы, представленные каждым пользователем. Я хотел бы получить количество пользователей, выбранных в качестве опции. Только один пользователь выбрал опцию O12 . Выход должен быть 1.

{
    "_id" : ObjectId("5ea179eb39ff117948f19266"),
    "_class" : "model.survey.Answer",
    "survey_id" : "5ea178c239ff117948f19265",
    "survey_user" : [ 
        {
            "user_id" : 1072,
            "user_option" : [ 
                {
                    "question_id" : "Q1",
                    "option_id" : "O11"
                }, 
                {
                    "question_id" : "Q2",
                    "option_id" : "O21"
                }, 
                {
                    "question_id" : "Q3",
                    "option_id" : "O31"
                }, 
                {
                    "question_id" : "Q4",
                    "option_id" : "O41"
                }
            ]
        }, 
        {
            "user_id" : 1073,
            "user_option" : [ 
                {
                    "question_id" : "Q1",
                    "option_id" : "O12"
                }, 
                {
                    "question_id" : "Q2",
                    "option_id" : "O21"
                }, 
                {
                    "question_id" : "Q3",
                    "option_id" : "O31"
                }, 
                {
                    "question_id" : "Q4",
                    "option_id" : "O41"
                }
            ]
        }
    ]
}

1 Ответ

0 голосов
/ 27 апреля 2020

Вы можете сделать это, используя агрегационный конвейер MongoDB :

Различные способы сделать это. Один из способов - использовать $unwind:

Тип 1 - Запрос 1:

db.collection.aggregate([
    /** Optional but will be good on huge collections to lessen data for further stages */
    {
      $match: { "survey_user.user_option.option_id": "O12" }
    },
    {
      $unwind: "$survey_user"
    },
    /** When you unwind a each object/element in array gets it's own document after `unwind` stage */
    {
      $match: { "survey_user.user_option.option_id": "O12" }
    },
    /** After match you'll only have objects which met the criteria in `survey_user` array */
    /** group on `_id` & push entire original doc to data field */
    {
      $group: { _id: "$_id", survey_user: { $push: "$survey_user" }, data: {  $first: "$$ROOT" } }
    },
    /** Add `survey_user` array to `data.survey_user` & it's size to `data.optedCount` field */
    {
      $addFields: { "data.survey_user": "$survey_user", "data.optedCount": {  $size: "$survey_user" } }
    },
    /** Make `data` as new root to doc */
    {
      $replaceRoot: { newRoot: "$data" }
    }
  ])

Тест: mongoplayground

На всякий случай, если вам нужен только подсчет, но не нужен для всего do c, который должен быть возвращен, в приведенном выше запросе будут небольшие изменения:

Тип 1 - Запрос 2:

db.collection.aggregate([
    {
      $match: { "survey_user.user_option.option_id": "O12" }
    },
    {
      $unwind: "$survey_user"
    },
    {
      $match: { "survey_user.user_option.option_id": "O12" }
    },
    /** Just group on `_id` & count no.of docs, maintain `survey_id` */
    {
      $group: { _id: "$_id", optedCount: { $sum: 1 }, survey_id: { $first: "$survey_id" } }
    }
  ])

Тест: mongoplayground

Использование итератора массива $reduce, что может быть полезно, если данные ваших коллекций настолько велики, что раскрутка взорвет ваши документы.

Тип 2 - Запрос:

db.collection.aggregate([
  {
    $match: {
      "survey_user.user_option.option_id": "O12",
    },
  },
  /** Instead of `$addFields`, you can use `$project` to project fewer needed fields (which can be help improve query with performance benefits ) */
  {
    $addFields: {
      optedCount: {
        $reduce: {
          input: "$survey_user",
          initialValue: 0,
          in: {
            $cond: [
              { $in: ["O12", "$$this.user_option.option_id"] },
              { $add: ["$$value", 1] },
              "$$value",
            ]
          }
        }
      }
    }
  }
]);

Тест: mongoplayground

...