【问题标题】:MongoDB aggregation pipeline filtering two arraysMongoDB聚合管道过滤两个数组
【发布时间】:2016-05-03 23:45:42
【问题描述】:

你能给我建议吗?我有这样的文件:

{
    "_id" : ObjectId("569620270d3ac01895316edb"),
    "customerId" : NumberLong("2000900000000000022"),
    "gender" : "MALE",
    "birthDate" : ISODate("1976-01-06T23:00:00Z"),
    "someArray" : [
            {
                    "id" : 5411,
                    "firstDate" : ISODate("2014-08-05T16:17:50Z"),
                    "lastDate" : ISODate("2015-10-31T11:55:51Z"),
                    "sumOfAll" : 5677.35,
                    "minAmount" : 9.75,
                    "maxAmount" : 231.72,
                    "innerArray" : [
                            {
                                    "count" : 4,
                                    "amount" : 449.33
                            },
                            {
                                    "count" : 3,
                                    "amount" : 401.31
                            },
                            {
                                    "count" : 7,
                                    "amount" : 617.8000000000001
                            },
                            {
                                    "count" : 4,
                                    "amount" : 465.28999999999996
                            },
                            {
                                    "count" : 2,
                                    "amount" : 212.95999999999998
                            },
                            {
                                    "count" : 4,
                                    "amount" : 497.53999999999996
                            },
                            {
                                    "count" : 3,
                                    "amount" : 278.23
                            },
                            {
                                    "count" : 3,
                                    "amount" : 383.15999999999997
                            },
                            {
                                    "count" : 6,
                                    "amount" : 459.63
                            },
                            {
                                    "count" : 9,
                                    "amount" : 677.19
                            },
                            {
                                    "count" : 4,
                                    "amount" : 393.85
                            }
                    ]
            },
            {
                    "id" : 5812,
                    "firstDate" : ISODate("2014-09-03T17:16:32Z"),
                    "lastDate" : ISODate("2015-11-04T22:59:59Z"),
                    "sumOfAll" : 275.6,
                    "minAmount" : 15,
                    "maxAmount" : 69,
                    "innerArray" : [
                            {
                                    "count" : 1,
                                    "amount" : 17
                            },
                            {
                                    "count" : 1,
                                    "amount" : 15.4
                            },
                            {
                                    "count" : 1,
                                    "amount" : 69
                            },
                            {
                                    "count" : 1,
                                    "amount" : 53.7
                            },
                            {
                                    "count" : 2,
                                    "amount" : 84
                            }
                    ]
            },
            {
                    "id" : 7399,
                    "firstDate" : ISODate("2015-01-12T22:59:59Z"),
                    "lastDate" : ISODate("2015-03-16T22:59:59Z"),
                    "sumOfAll" : 144.73,
                    "minAmount" : 0.84,
                    "maxAmount" : 24.98,
                    "innerArray" : [
                            {
                                    "count" : 5,
                                    "amount" : 50.379999999999995
                            },
                            {
                                    "count" : 5,
                                    "amount" : 55.45
                            },
                            {
                                    "count" : 10,
                                    "amount" : 38.900000000000006
                            }
                    ]
            },
    ]

}

我想过滤两个内部数组并投影它们。我正在尝试这个查询:

db.sandbox.aggregate([
{ $match: {
           'gender': {$eq : 'MALE'},
           $or: [
                { $and: [{'someArray.id': {$eq: 5411}}, {'someArray.innerArray.count': 4}, {'someArray.innerArray.amount': {$gte: 2}}]},
                { $and: [{'someArray.id': {$eq: 5812}}, {'someArray.innerArray.count': 5}, {'someArray.innerArray.amount': {$gte: 50}}]},
           ]
          }
},
{ $project: {
    gender: 1,
    customerId: 1,
    someArray: { $filter: {
        input: '$someArray',
        as: 'item',
        cond: {
        $and: [ 
            { $or: [
                {$and: [{$eq: ['$$item.id', 5411]}, {$eq: ['$$item.innerArray.count', 4]}, {$gte: ['$$item.innerArray.amount', 2]}]},
                {$and: [{$eq: ['$$item.id', 5812]}, {$eq: ['$$item.innerArray.count', 5]}, {$gte: ['$$item.innerArray.amount', 50]}]},
            ]},
        ]
      }
    }},
}}

]).pretty()

我收到了 someArray 中没有数据的结果:

{
    "_id" : ObjectId("569620270d3ac01895316edb"),
    "customerId" : NumberLong("2000900000000000022"),
    "gender" : "MALE",
    "someArray" : [ ]

}

我想收到:

{
    "_id" : ObjectId("569620270d3ac01895316edb"),
    "customerId" : NumberLong("2000900000000000022"),
    "gender" : "MALE",
    "birthDate" : ISODate("1976-01-06T23:00:00Z"),
    "someArray" : [
            {
                    "id" : 5411,
                    "firstDate" : ISODate("2014-08-05T16:17:50Z"),
                    "lastDate" : ISODate("2015-10-31T11:55:51Z"),
                    "sumOfAll" : 5677.35,
                    "minAmount" : 9.75,
                    "maxAmount" : 231.72,
                    "innerArray" : [
                            {
                                    "count" : 4,
                                    "amount" : 449.33
                            },
                            {
                                    "count" : 4,
                                    "amount" : 465.28999999999996
                            },
                            {
                                    "count" : 4,
                                    "amount" : 497.53999999999996
                            },
                            {
                                    "count" : 4,
                                    "amount" : 393.85
                            }
                    ]
            }
    ]

}

如果我将 $eq 更改为 $gte,我将收到接收结果,但我也想投影 innerArray。我该如何实施?我应该使用自己的 MapReduce 作业,还是可以使用聚合管道来做到这一点?

MongoDB 3.2 版。我还观察到当我尝试对数组使用多个谓词并仅投影一个元素时,例如:

db.sandbox.find(  {$and: [{'someArray.id': 7399}, {'someArray.sumOfAll': {$gte: 5000}}]}, {'customerId': 1, 'someArray.$': 1}).pretty()

但它返回我:

{
    "_id" : ObjectId("569620270d3ac01895316edb"),
    "customerId" : NumberLong("2000900000000000022"),
    "someArray" : [
            {
                    "id" : 5411,
                    "firstDate" : ISODate("2014-08-05T16:17:50Z"),
                    "lastDate" : ISODate("2015-10-31T11:55:51Z"),
                    "sumOfAll" : 5677.35,
                    "minAmount" : 9.75,
                    "maxAmount" : 231.72,
                    "innerArray" : [
                            {
                                    "count" : 4,
                                    "amount" : 449.33
                            },
                            {
                                    "count" : 3,
                                    "amount" : 401.31
                            },
                            {
                                    "count" : 7,
                                    "amount" : 617.8000000000001
                            },
                            {
                                    "count" : 4,
                                    "amount" : 465.28999999999996
                            },
                            {
                                    "count" : 2,
                                    "amount" : 212.95999999999998
                            },
                            {
                                    "count" : 4,
                                    "amount" : 497.53999999999996
                            },
                            {
                                    "count" : 3,
                                    "amount" : 278.23
                            },
                            {
                                    "count" : 3,
                                    "amount" : 383.15999999999997
                            },
                            {
                                    "count" : 6,
                                    "amount" : 459.63
                            },
                            {
                                    "count" : 9,
                                    "amount" : 677.19
                            },
                            {
                                    "count" : 4,
                                    "amount" : 393.85
                            }
                    ]
            }
    ]

}

这对我来说是不正确的。我什么都不期待。

【问题讨论】:

  • 您能否以书面形式而不是作为查询来描述您想要实现的目标?
  • 我认为第二部分正是我想要做的。
  • 为什么在第二个查询的结果中会出现带有"sumOfAll" : 144.73someArray 元素?您在查询中指定此字段应大于或等于 5000。
  • 好的,您提供的查询太多,在一个问题中不起作用。您希望我们帮助您解决哪一个问题?或者哪个普遍问题?
  • 对此感到抱歉。一般问题是 - 我想根据一些谓词同时过滤两个数组,我想根据这些谓词投影结果。

标签: arrays mongodb


【解决方案1】:

首先,您在$match 中使用条件的方式不会产生您想要的结果。

{ $and: [{'someArray.id': {$eq: 5411}}, {'someArray.innerArray.count': 4}, {'someArray.innerArray.amount': {$gte: 2}}]}

上面的行将分别验证每个条件,而不是同时检查每个 innerArray 元素的 countamount 条件。如果这是您想要的,您应该查看 $elemMatch 运算符。

其次,我不相信您可以像在二级阵列上那样使用$filter。你应该先放松someArray

db.sandbox.aggregate(
{
    $match:
    {
        gender: { $eq: 'MALE' },
        "someArray.id":
        {
            $in: [5411, 5812]
        }
    }
},
{
    $unwind: "$someArray",
},
{
    $project:
    {
        gender: 1,
        customerId: 1,
        someArray:
        {
            id: 1,
            firstDate: 1,
            lastDate: 1,
            sumOfAll: 1,
            minAmount: 1,
            maxAmount: 1,
            innerArray:
            {
                $filter:
                {
                    input: '$someArray.innerArray',
                    as: 'item',
                    cond:
                    {
                        $or:
                        [
                            {
                                $and:
                                [
                                    { $eq: ['$$item.count', 4] },
                                    { $gte: ['$$item.amount', 2] }
                                ]
                            },
                            {
                                $and:
                                [
                                    { $eq: ['$$item.count', 5] },
                                    { $gte: ['$$item.amount', 50] }
                                ]
                            }
                        ]
                    }
                }
            }
        },
    }
})

如果需要,您还可以返回 $group someArray 元素。

【讨论】:

  • 非常感谢!首先我使用了$(projection),但我没有查看条件限制,所以在这种情况下我需要切换到 elemMatch。对于第二个问题,查询看起来真的很难看,可能是我需要更改我的文档架构以更好地使用。
  • 这里需要另外一个匹配,在展开之后你会得到多余的结果。
  • 如果可能的话,展开后如果没有匹配,您的代码可能会返回不相关的答案。为了得到正确的答案,我在 unwind 和 projection 之间添加了 { $match: { "someArray.id": { $in: [7011,5812,5411,5651,6011,5542,5691] } } }
猜你喜欢
  • 2020-08-13
  • 2014-12-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-09
  • 2020-12-12
  • 2020-09-05
  • 2020-09-23
  • 1970-01-01
相关资源
最近更新 更多