【问题标题】:mongodb foreach doc find min of subset and update itmongodb foreach doc 找到子集的最小值并更新它
【发布时间】:2014-04-11 23:28:08
【问题描述】:

产品和变体价格

在所有products.Variants.Price 中找到尺寸较小的最小值并将其更新 15%

    {
        "_id" : 23,
        "name" : "Polo Shirt",
        "Variants" : [ 
            {
                "size" : "Large",
                "Price" : 82.42
            }, 
            {
                "size" : "Medium",
                "Price" : 20.82 // this should get increased by 15%
            }, 
            {
                "size" : "Small",
                "Price" : 42.29
            }
        ]
    },
{
        "_id" : 24,
        "name" : "Polo Shirt 2",
        "Variants" : [ 
            {
                "size" : "Large",
                "Price" : 182.42
            }, 
            {
                "size" : "Medium",
                "Price" : 120.82  // this should get increased by 15%
            }, 
            {
                "size" : "Small",
                "Price" : 142.29
            }
        ]
    }

我开始了这样的事情。不确定这是否是正确的开始

db.products.find().forEach(function(product){
    var myArr = product.Variants;
    print(myArr.min());
});

【问题讨论】:

  • 如果您需要在 Variants 中查找 min,为什么还要遍历集合中的文档?
  • 我需要更新最小的。
  • 集合中所有文档的最小值还是文档中所有变体的最小值?
  • @SalvadorDali 集合中每个文档的最低变体价格
  • 尺寸较小的集合中每个文档的最低变体价格

标签: node.js mongodb mongo-shell


【解决方案1】:

这里有一个问题,您不能在单个更新语句中识别数组中的“最小”值以用于位置更新,因此您当前的方法是正确的。

有争议的是,更好的方法是预先确定哪个元素是最小元素,然后将其传递给更新。您可以使用.aggregate()

var result = db.products.aggregate([
    { "$unwind": "$Variants" },
    { "$sort": { "_id": 1, "Variants.price" } }
    { "$group": {
        "_id": "$_id",
        "size":  { "$first": "$Variants.size" },
        "price": { "$first": "$Variants.price" }
    }},
    { "$project": {
        "size": 1,
        "price": 1,
        "adjusted": { "$multiply": [ "$price", 1.15 ] }
    }}
])

当然,这只是每个产品的最低 Variant 项目详细信息的结果,但您可以使用如下结果:

result.result.forEach(function(doc) {
    db.products.update(
        { 
            "_id": doc._id, 
            "Variants": { "$elemMatch": {
                "size": doc.size,
                "price": doc.price
            }}
        },
        {
            "$set": { 
                "Variants.$.price": doc.adjusted
             }
        } 
    }
})

这不是最好的形式,但它至少消除了迭代数组的一些开销,并允许在服务器硬件上进行计算,这可能是来自客户端的更高规格。

在您采用 MongoDB 2.6 及更高版本的一些可用功能之前,它仍然看起来并不算多。值得注意的是,聚合获得了响应的光标,您现在也可以执行"bulk updates"。所以表格可以这样改变:

var cursor = db.products.aggregate([
    { "$unwind": "$Variants" },
    { "$sort": { "_id": 1, "Variants.price" } }
    { "$group": {
        "_id": "$_id",
        "size":  { "$first": "$Variants.size" },
        "price": { "$first": "$Variants.price" }
    }},
    { "$project": {
        "size": 1,
        "price": 1,
        "adjusted": { "$multiply": [ "$price", 1.15 ] }
    }}
]);

var batch = [];

while ( var doc = cursor.next() ) {

    batch.push({
        "q": {
            "_id": doc._id, 
            "Variants": { "$elemMatch": {
                "size": doc.size,
                "price": doc.price
            }}
        },
        "u": {
            "$set": { 
                "Variants.$.price": doc.adjusted
             }
        }
    });

    if ( batch.length % 500 == 0 ) {
        db.command({ "update": "products", "updates": batch });
    }
}

db.command({ "update": "products", "updates": batch });

所以这真的很好,因为当您仍在迭代列表时,流量和等待网络上的响应确实被最小化了。最好的部分是每 500 个项目仅发生一次的批量更新(通过数学用法)。批处理项的最大大小实际上是 16MB 的 BSON 限制,因此您可以酌情调整。

如果您目前正在开发产品以迁移到 2.6 版本,这提供了一些很好的理由。

考虑到您正在处理“价格”,我要添加的唯一最后一个脚注是尽量不要为此使用浮点数学,而是寻找使用整数的形式,因为它可以避免很多问题。

【讨论】:

    【解决方案2】:

    这就是我成功的方式。

        var result = db.Products.aggregate( 
        [       { "$unwind":"$Variants" },{"$match":{"Variants.size":"small"}},
            { "$group":
                {"_id":"$_id","minprice":
                    {"$min":"$Variants.price" }}},
                   {$sort:{ _id: 1}} ] )
    
    
                    result.result.forEach(function(doc) {
    
                        db.Products.update( { "_id": doc._id }, 
                               { "$pull": { "Variants" : { 
                                                  "price":doc.minprice, 
                                                  "size":"small"
                               } } } ,
                               { $addToSet: { "Variants":{ 
                                                  "price":doc.minprice*1.15, 
                                                  "size":"small"
                               }  }
    
       );
    
    });
    

    【讨论】:

      猜你喜欢
      • 2013-06-10
      • 2019-01-13
      • 2018-08-04
      • 1970-01-01
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      • 2012-02-26
      • 2017-08-30
      相关资源
      最近更新 更多