使用'。'的惯用方法是什么? MongoDB密钥中的(或其他特殊字符)?

时间:2017-04-25 10:07:22

标签: node.js mongodb

我一直在使用MongoDB,并希望按域名键入项目。问题是使用像'''这样的特殊字符。 for keys打破了Mongo的错误:

错误:密钥www.google.com不得包含'。'

例如,我希望能够存储:

stupidObject = {
    'www.google.com': {
        '8.8.8.8': 'Other info',
        '8.8.4.4': ['item1', 'item2', ... , 'itemN']
        },
    'www.othersite.com': {
        '8.8.8.8': 'Other info',
        '8.8.4.4': ['item1', 'item2', ... , 'itemN']
        },
    }

我见过的所有解决方案都有一些变体:在保存之前更改密钥,使用Unicode表示,在保存之前散列密钥。例如见答案:MongoDB dot (.) in key name

所有这些解决方案都会导致问题,并且难以维护代码。程序员有责任记住进行过滤并始终如一地进行过滤。这是一个可怕的解决方案。

我虽然关于散列但是碰撞是一种风险(几乎不可能调试)并且再次对程序员负责。想象一下这些解决方案对国际开发团队的影响。

我的问题很简单:在MongoDB中执行此操作的正确方法是什么?

我最终得到了一个自定义解决方案,我递归地(警钟!)导航结构并替换特殊字符。这是通过利用pre('save')和post('find')钩子在Mongoose Schema中完成的。

这意味着程序员不必关心他们使用密钥和他们保存的域名的特殊字符,数据库层可以透明地处理所有内容。这对我来说似乎是一个更好的解决方案。

然而......这需要一些凌乱的代码来解决使用hasOwnProperty时Mongoose对象行为不端的问题以及首先运行'.toObject()'然后通过引用传递原始'this'指针的要求。 / p>

这个解决方案有效,但我认为必须有更好的方法!我们非常感谢您对正确方法的任何想法或指导!当你看到下面的代码时,你会发现为什么我认为必须有更好的方法!

我应该提一下,我不想安装任何库或有其他依赖项来解决这个问题。

以下是使用代码的示例:

// Recursive function to replace character||string in keys that may cause violations
// Same code can be used to reverse the change
//
var replaceStringInKeys = function (stringToReplace, newString, regExp, thisObj, thisPtr) {
    for(property in thisObj) {
        if (thisObj.hasOwnProperty(property)) {
            if(property.indexOf(stringToReplace) > -1) {
                // Replace the '.'s with URL escaped version. Delete old object.
                var newproperty = property.replace(regExp, newString);
                thisObj[newproperty] = thisObj[property];
                thisPtr[newproperty] = thisPtr[property];
                delete thisObj[property];
                delete thisPtr[property];
                // Pass the new property too
                if (thisObj[newproperty].constructor === Object) {
                    thisObj[newproperty] = replaceStringInKeys(stringToReplace, newString, regExp, thisObj[newproperty], thisPtr[newproperty]);
                    thisPtr[newproperty] = thisObj[newproperty];
                }
                continue;
            }
            if (thisObj[property].constructor === Object) {
                thisObj[property] = replaceStringInKeys(stringToReplace, newString, regExp, thisObj[property], thisPtr[property]);
                thisPtr[property] = thisObj[property];
            }
        }
    }
    return thisObj;
};

testSchema.pre('save', function(next) {
    // Calling '.toObject' allows for hasOwnProperty to work
    var thisObj = this.toObject();
    console.log('Pre save record...');
    // Duplicate the this pointer as mongo is too shit to use hasOwnProperty properly
    replaceStringInKeys('.', '[whateveryouwantinsteadofdot]', /\./g, thisObj, this);
    next();
});

testSchema.post('find', function(results) {
    console.log('post find record...');
    // Undo the changes made by the pre-save hook
    var i;
    for(i = 0; i < results.length; i++) {
        var thisObj = results[i].toObject();
        replaceStringInKeys('[whateveryouwantinsteadofdot]', '.', /\[whateveryouwantinsteadofdot\]/g, thisObj, results[i]);
    }
});

注意:请小心使用此解决方案(如果您足够疯狂),因为可能存在安全问题。例如,如果一个坏人知道你替换'。'使用%2E并且他们可以强制使用例如

hxxp://www.vulnerablesitethatdoesntexist.com/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E %2E /%2E%2E /%2E%2E /%2E%2E / etc / passwd

正确转义,但会透明地转换为目录遍历类型字符串:
  hxxp://www.vulnerablesitethatdoesntexist.com/../../../../../../../../../../../etc/passwd

1 个答案:

答案 0 :(得分:0)

您应该使用点作为键将文档的结构更改为无效。几年前我遇到了同样的问题。

yourStupidObject = {
  'www.google.com': [
    {'ip': '8.8.8.8', more: 'Other info',
    {'ip': '8.8.4.4', more: ['item1']}
    ]
}