MongoDB:使用动态字段名称在嵌套对象中搜索最小值,最大值

时间:2015-10-22 13:18:40

标签: mongodb mongodb-query aggregation-framework

我有一个查询,可以在下面的示例数据集中获取minimum querymaximum query。在我的案例中,字段名称是动态的,例如product_1下方,product_2 ...

{
    "_id" : NumberLong(540),
    "product_1" : {
            "orderCancelled" : 0,
            "orderDelivered" : 6
    },
    "product_2" : {
            "orderCancelled" : 3,
            "orderDelivered" : 16
    },
    "product_3" : {
            "orderCancelled" : 5,
            "orderDelivered" : 11
    }
}

我不知道我如何才能在Mongo中执行此操作,其中字段名称是动态的,意味着将来可能还会创建其他产品product_4product_5同一{ {1}}。

我需要一个查询,它为id提供最小值,为orderDelivered提供最大值,例如在上面的文档中,结果将为orderCancelled& orderDelivered:16
谢谢你的任何想法。

2 个答案:

答案 0 :(得分:2)

您需要通过更新文档结构来更改它们。您需要使用.forEach方法循环浏览每个文档,然后$unsetproduct_开头的所有字段名称。在那里,您需要使用$set更新运算符添加新字段products,这是一系列产品。话虽如此,您应该使用"bulk"操作来更新文档以获得最高效率

var bulkOp = db.collection.initializeOrderedBulkOp();
var count = 0;
db.collection.find().forEach(function(doc) {
    var allproducts = [];
    for(var key in doc) {   
        if(Object.prototype.hasOwnProperty.call(doc, key) && /^product_\d+/.test(key)) { 
            var product = {}; 
            product["name"] = key;    
            product["orderCancelled"] = doc[key]["orderCancelled"]; 
            product["orderDelivered"] = doc[key]["orderDelivered"];
            allproducts.push(product);
            var unsetField = {}; 
            unsetField[key] = ""; 
            bulkOp.find({"_id": doc._id}).update({ "$unset": unsetField });
        }; 
        count++; 
    };
    bulkOp.find({"_id": doc._id}).update({ 
        "$set": { "products": allproducts } 
    });
    count++; 
    if(count % 500 === 0) { 
        // Execute per 500 operations and re-init
        bulkOp.execute(); 
        bulkOp = db.collection.initializeOrderedBulkOp();
    }
})

// clean up queues

if(count > 0) {
    bulkOp.execute(); 
}

您的文档现在看起来像这样:

{
        "_id" : NumberLong(542),
        "products" : [
                {
                        "name" : "product_1",
                        "orderCancelled" : 0,
                        "orderDelivered" : 6
                },
                {
                        "name" : "product_2",
                        "orderCancelled" : 3,
                        "orderDelivered" : 16
                },
                {
                        "name" : "product_3",
                        "orderCancelled" : 5,
                        "orderDelivered" : 11
                }
        ]
}

然后使用.aggregate()方法进行聚合查询:

db.collection.aggregate([ 
    { "$match": { "_id": 542 } }, 
    { "$unwind": "$products" }, 
    { "$group": { 
        "_id": "$_id", 
        "maxOrderCancelled": { "$max": "$products.orderCancelled"}, 
        "minOrderDelivvered": { "$min": "$products.orderDelivered"}
    }}
])

返回:

{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivvered" : 6 }

version 3.2开始,您可以在$project阶段使用$max$min,这是更好的方法,因为不需要{{3}你的数组首先。

db.collection.aggregate([ 
    { "$match": { "_id": 542 } }, 
    { "$project": { 
        "maxOrderCancelled": { 
            "$max": { 
                "$map": { 
                    "input": "$products", 
                    "as": "order", 
                    "in": "$$orc.orderCancelled" 
                } 
            } 
        }, 
        "minOrderDelivered": { 
            "$min": { 
                "$map": { 
                    "input": "$products", 
                    "as": "orc", 
                    "in": "$$orc.orderDelivered" 
                } 
             } 
        } 
    }} 
])

哪个收益率:

{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivered" : 6 }

答案 1 :(得分:1)

您应该重新构建文档,以便所有产品文档都在一个数组中:

{
    "_id": NumberLong(540),
    products: [
        {
            "name": "product_1",
            "orderCancelled": 0,
            "orderDelivered": 6
        },
        {
            "name": "product_2",
            "orderCancelled": 3,
            "orderDelivered": 16
        },
        {
            "name": "product_3",
            "orderCancelled": 5,
            "orderDelivered": 11
        }
    ]
}

然后您就可以发出正常的最大/最小查询:

db.test.aggregate([
    {   
        $match: { "_id" : NumberLong(540) }
    },
    {
        $unwind: "$products"
    },
    {
        $group: {
            _id: "$_id",
            minDelivered: { $min: "$products.orderDelivered" },
            maxCancelled: { $max: "$products.orderCancelled" }
        }
    }
])