如何对对象数组的值小于给定值但最大值为

时间:2016-03-11 09:34:16

标签: mongodb mongodb-query aggregation-framework

鉴于此类文件:

{
    "_id" : "16",
    "name" : "SB15",
    "category_id" : 3,
    "level" : 2,
    "sizes" : [ 
        {
            "v" : 34.78
        }, 
        {
            "v" : 32.15
        }, 
        {
            "v" : 35.86
        }, 
        {
            "v" : 39.4
        }, 
        {
            "v" : 24.01
        }
    ]
}

我需要为每个“category_id”值找到“size.v”具有小于给定数字的值的文档,并且该级别等于给定数字,并且对于每个category_id将文档限制为4

如果我找一个小于35的“size.v”

,结果应该是这样的
{
    "_id" : 3, /* the category_id */
    "models" : [
        {
            "name": "SB15",
            "level": 2,
            "size":  {
                "v" : 34.78, /* the higher value in sizes array less than 35 */ 
                "h" : 6.4,
                "w" : 0.20,
                "t" : 0.03
            } 
        }
        ...,
        ...,
        ...,
    ] 
}

1 个答案:

答案 0 :(得分:1)

一般的想法是找出与目标值的差异,然后对结果进行排序:

对于MongoDB 2.6以上版本,$map之前阵列上的$unwind可能最好,因为处理开销较少:

db.collection.aggregate([
    // Calculate the difference
    { "$project": {
        "name": 1,
        "category_id": 1,
        "level": 1,
        "sizes": {
            "$map": {
                "input": "$sizes",
                "as": "el",
                "in": {
                    "v": "$$el.v",
                    "diff": {
                        "$cond": [
                            { "$gt": [ 35, "$$el.v" },
                            999999,
                            { "$subtract": [ 35, "$$el.v" ] }
                        ]
                     }
                }
            }
        }
    }},

    // Unwind the array
    { "$unwind": "$sizes" },

    // Sort to smallest difference 
    { "$sort": { "_id": 1, "sizes.diff": 1 } },

    // Get the $first after sort
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "category_id": { "$first": "$category_id" },
        "level": { "$first": "$level" },
        "size_v": { "$first": "$sizes.v" }
    }},

    // Since you cannot have nested paths in a $group
    { "$project": {
        "name": 1,
        "category_id": 1,
        "level": 1,
        "size": {
            "v": "$size_v"
        }
    }},

    // Group up by category
    { "$group": {
        "_id": "$category_id",
        "models": { 
            "$push": {
                "name": "$name",
                "level": "$level",
                "size": "$size"
            }
        }
    }}
])

在早期版本中$unwind首先:

db.collection.aggregate([
    { "$unwind": "$sizes" },
    { "$project": {
        "name": 1,
        "category_id": 1,
        "level": 1,
        "sizes": {
            "v": "$sizes.v",
            "diff": {
                "$cond": [
                    { "$gt": [ 35, "$sizes.v" ] },
                    99999,
                    { "$subtract": [ 35, "$sizes.v" ] }
                ]
            }
        }
    }},
    { "$sort": { "_id": 1, "sizes.diff": 1 } },
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "category_id": { "$first": "$category_id" },
        "level": { "$first": "$level" },
        "size_v": { "$first": "$sizes.v" }
    }},
    { "$project": {
        "name": 1,
        "category_id": 1,
        "level": 1,
        "size": {
            "v": "$size_v"
        }
    }},
    { "$group": {
        "_id": "$category_id",
        "models": { 
            "$push": {
                "name": "$name",
                "level": "$level",
                "size": "$size"
            }
        }
    }}
])

主要的计算是在$cond内完成的,首先你要看看这个值是否大于目标,如果是,那么分配一个"差异"这是一个很大的价值。进一步远离可能的间隔越好。

它实际上小于目标,$subtract来自目标的值。结果中的数字越小,越接近。

但基本情况是计算差异然后$sort,因此最小值在顶部。然后,您可以再次$first使用$group选择该值。

另请注意,$group不能将嵌套对象作为键,因此总计首先为"size_v",然后在稍后的"size.v"阶段重命名为$project以容纳变换。