2014-09-02 315 views
1

我必须处理MongoDB集合中不一致的文档,其中某些字段可能是数字或可能具有NaN值。我需要用$ inc更新它。但看起来像是NaN值$ inc没有效果。原子文档更新有哪些可用选项?

+0

如果值为NaN,预期的行为是什么?它应该被视为0并增加?给出一些不是数字的字段值的示例数据。 – BatScream 2014-09-02 06:03:45

回答

3

那么这似乎导致了两个合乎逻辑的结论。首先是如果有一个字段中存在NaN值,那么如何识别它们?考虑下面的示例,让我们称之为集“nantest”

{ "_id" : ObjectId("54055993b145d1c015a1ad41"), "n" : NaN } 
{ "_id" : ObjectId("540559e8b145d1c015a1ad42"), "n" : Infinity } 
{ "_id" : ObjectId("54055b59b145d1c015a1ad43"), "n" : 1 } 
{ "_id" : ObjectId("54055ea1b145d1c015a1ad44"), "n" : -Infinity } 

因此,无论NaNInfinity-Infinity代表已经在某种程度上你的数据出现了“非数字”的。找到这些文档的最佳方式就是使用运算符来计算JavaScript评估的查询条件。效率不高,但你得到了:

db.nantest.find({ 
    "$where": "return isNaN(this.n) || Math.abs(this.n) == Infinity" 
}) 

因此,这给出了一种方法来找到问题的数据。从这里你可以跳过箍,并决定在遇到这种情况时,你只需在递增之前将它重置为0,基本上发出两条更新语句,如果值正确,则第一条语句不匹配文档以更新:

db.nantest.update(
    { "$where": "return isNaN(this.n) || Math.abs(this.n) == Infinity" }, 
    { "$set": { "n": 0 } } 
); 
db.nantest.update(
    { }, 
    { "$inc": { "n": 1 } } 
); 

但是,当你看到这个时,你为什么要修补你的代码以迎合这个问题呢?于是顺理成章的事情,最终得出结论:仅仅是更新一个声明中的所有Nan,并可能Infinity值标准复位数字:

db.nantest.update(
    { "$where": "return isNaN(this.n) || Math.abs(this.n) == Infinity" }, 
    { "$set": { "n": 0 } }, 
    { "multi": true } 
); 

运行一个语句,然后你不必改变你的代码,只是过程按照您通常的预期递增。

如果你的麻烦是知道哪些领域具有存在的Nan值,以调用更新来解决这些问题,然后再考虑沿着这MapReduce的过程线的东西来检查字段:

db.nantest.mapReduce(
    function() { 

    var doc = this; 
    delete doc._id; 

    Object.keys(doc).forEach(function(key) { 
     if (isNaN(doc[key]) || Math.abs(doc[key]) == Infinity) 
     emit(key, 1); 
    }); 

    }, 
    function (key,values) { 
    return Array.sum(values); 
    }, 
    { "out": { "inline": 1 } } 
) 

对于您可能需要为更多嵌套文档添加一些复杂性,但是这会告诉您哪些字段可能包含错误值,以便您可以构造更新语句来修复它们。

这似乎不是弯曲你的代码,以满足这个你“应该”做的:

  1. 查找导致数字出现,修复源。

  2. 标识字段或包含这些值

  3. 过程一次性更新语句来一次全部固定的数据字段。

与代码最小的混乱,它既修复了问题的“源头”,也修复了引入的数据损坏的“结果”。