$ разделяют элементы встраиваемых документов - MongoDB Aggregation - PullRequest
0 голосов
/ 16 января 2020

Я пытаюсь создать агрегирующий запрос MongoDB.

Структура данных:

{
    "object_name": Example,
    "values": [ {"name":"value1", "value":1}, 
                {"name":"value2", "value":10},
                {"name":"total", "value":105}
}

Цель: Найти имен объектов , где value1/total > 0.5 и value2/total > 0.25 и total > 100. Данные структурированы таким образом, чтобы обеспечить индексы для полей value_name и value.

То, что я пробовал - объединить со следующими конвейерами:

$ match: фильтровать документы с общим количеством> 100:

$match: { values: { $elemMatch: { value_name: "total", value: {$gte: 100 }

$ project: захватите только те имена-значения, которые нам нужны (есть около 200 различных имен)

$project: {
            values: {
                $filter: {
                    input: "$values",
                    as: "value",
                    cond: { $or: [
                        { $eq: [ "$$value.name", "name1"] },
                        { $eq: [ "$$value.name", "name2"] },
                        { $eq: [ "$$value.name", "total"] },
                    ] }
                }
            },
            name: 1
        }

затем, { $unwind: "$values" }

И вот, Я мог бы от $group до $divide: name1/total, name2/total однако я застрял на том, как получить эти значения. Я не могу просто сделать stats.value:, потому что он не знает, какое значение я имею в виду. Я полагаю, что $group не может сделать $elemMatch, чтобы также соответствовать имени.

Если есть более простые решения, такие, я был бы очень признателен за ваш вклад.

Ответы [ 2 ]

1 голос
/ 17 января 2020

Пожалуйста, попробуйте это:

  1. Мы фильтруем документы, где массив значений имеет объект с name : total & value > 100.
  2. Добавление объекта с name : total в документ .
  3. Оставлять в массиве значений только объекты, соответствующие критериям value1/total > 0.5 и value2/total > 0.25.
  4. Если размер этого массива больше 1, то эти два условия выполняются.
  5. Окончательно только проецирование object_name

Запрос:

db.yourCollectionName.aggregate([{ $match: { values: { $elemMatch: { name: "total", value: { $gte: 100 } } } } }, 
    {
        $addFields: {
            totalValue: {
                $arrayElemAt: [{
                    $filter: {
                        input: "$values",
                        as: "item",
                        cond: { $eq: ["$$item.name", 'total'] }
                    }
                }, 0]
            }
        }
    },
    {
        $project: {
            values: {
                $filter: {
                    input: "$values",
                    as: "value",
                    cond: {
                        $or: [
                            { $cond: [{ $eq: ["$$value.name", "value1"] }, { $gt: [{ $divide: ["$$value.value", '$totalValue.value'] }, 0.5] }, false] },
                            { $cond: [{ $eq: ["$$value.name", "value2"] }, { $gt: [{ $divide: ["$$value.value", '$totalValue.value'] }, 0.25] }, false] }
                        ]
                    }
                }
            }, object_name: 1
        }
    }, {
        $match: {
            $expr: { $gt: [{ $size: "$values" }, 1] }
        }
    }, { $project: { object_name: 1, _id: 0 } }])

Сбор данных:

/* 1 */
{
    "_id" : ObjectId("5e20bd94d02e05b694d55fa5"),
    "object_name" : "Example",
    "values" : [ 
        {
            "name" : "value1",
            "value" : 1
        }, 
        {
            "name" : "value2",
            "value" : 10
        }, 
        {
            "name" : "total",
            "value" : 105
        }, 
        {
            "name" : "total1",
            "value" : 105
        }
    ]
}

/* 2 */
{
    "_id" : ObjectId("5e20bdb1d02e05b694d56490"),
    "object_name" : "Example2",
    "values" : [ 
        {
            "name" : "value1",
            "value" : 1
        }, 
        {
            "name" : "value2",
            "value" : 10
        }, 
        {
            "name" : "total",
            "value" : 5
        }, 
        {
            "name" : "total1",
            "value" : 5
        }
    ]
}

/* 3 */
{
    "_id" : ObjectId("5e20d1b7d02e05b694d7c57a"),
    "object_name" : "Example3",
    "values" : [ 
        {
            "name" : "value1",
            "value" : 100
        }, 
        {
            "name" : "value2",
            "value" : 100
        }, 
        {
            "name" : "total",
            "value" : 200
        }, 
        {
            "name" : "total1",
            "value" : 205
        }
    ]
}

/* 4 */
{
    "_id" : ObjectId("5e20d1cad02e05b694d7c71c"),
    "object_name" : "Example4",
    "values" : [ 
        {
            "name" : "value1",
            "value" : 200
        }, 
        {
            "name" : "value2",
            "value" : 40
        }, 
        {
            "name" : "total",
            "value" : 200
        }, 
        {
            "name" : "total1",
            "value" : 205
        }
    ]
}

/* 5 */
{
    "_id" : ObjectId("5e20d1e2d02e05b694d7c933"),
    "object_name" : "Example5",
    "values" : [ 
        {
            "name" : "value1",
            "value" : 150
        }, 
        {
            "name" : "value2",
            "value" : 100
        }, 
        {
            "name" : "total",
            "value" : 200
        }, 
        {
            "name" : "total1",
            "value" : 205
        }
    ]
}

Результат:

/* 1 */
{
    "object_name" : "Example5"
}
1 голос
/ 17 января 2020

Вы можете преобразовать ваш массив в объект с помощью оператора $ arrayToObject и добавить поле tmp, чтобы иметь легкий доступ к value1, value2, total значениям

db.collection.aggregate([
  {
    $addFields: {
      tmp: {
        $arrayToObject: {
          $map: {
            input: "$values",
            as: "value",
            in: {
              k: "$$value.name",
              v: "$$value.value"
            }
          }
        }
      },
      name: 1
    }
  },
  {
    $match: {
      $expr: {
        $and: [
          {
            $gt: [
              {
                $divide: [
                  "$tmp.value1",
                  "$tmp.total"
                ]
              },
              0.5
            ]
          },
          {
            $gt: [
              {
                $divide: [
                  "$tmp.value2",
                  "$tmp.total"
                ]
              },
              0.25
            ]
          },
          {
            $gt: [
              "$tmp.total",
              100
            ]
          }
        ]
      }
    }
  },
  {
    $project: {
      tmp: 0
    }
  }
])

MongoPlayground

...