【问题标题】:$divide elements of Embedded Documents - MongoDB Aggregation$divide 嵌入文档的元素 - MongoDB 聚合
【发布时间】:2020-01-16 19:09:52
【问题描述】:

我正在尝试创建聚合 MongoDB 查询。

数据结构:

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

目标:在value1/total > 0.5value2/total > 0.25total > 100 的位置找到对象名称。 以这种方式构造数据以提供 value_name 和 value 字段的索引。

我尝试过的 - 与以下管道聚合:

$match: 过滤总数 > 100 的文档:

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

$project:只获取我们需要的 value_names(有近 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 也匹配名称。

如果有更简单的解决方案,我将非常感谢您的意见。

【问题讨论】:

  • 所以您想要拥有所有这三个value1/total > 0.5value2/total > 0.25total > 100 满意的文档并且响应仅包含values 数组中的那些对象?
  • 是的,我要满足三个条件。我唯一关心的返回字段是“object_name”。

标签: mongodb aggregation


【解决方案1】:

请试试这个:

  1. 我们正在过滤值数组包含一个对象的文档 name : total & value > 100.
  2. 使用name : total 添加对象 记录。
  3. 只留下符合条件的对象 value1/total > 0.5value2/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"
}

【讨论】:

  • 这是一个非常明确的解决方案。谢谢你的解释!
【解决方案2】:

您可以使用$arrayToObject 运算符将数组转换为对象,并添加tmp 字段以便轻松访问value1value2total

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

【讨论】:

  • $arrayToObject$map 是这个问题的关键:) 效果很好,甚至可以在不添加临时值的情况下工作。非常感谢!
  • 是的,你是对的,优化你的管道。不客气
猜你喜欢
  • 2013-09-18
  • 1970-01-01
  • 2017-07-31
  • 1970-01-01
  • 2019-02-16
  • 2019-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多