关于node.js:使用’.’的惯用方式是什么(或其他特殊字符)在 MongoDB 键中?

What is the idiomatic way to use '.' (or other special characters) in MongoDB keys?

我一直在玩 MongoDB,并希望通过域名来键入项目。问题是使用特殊字符(如句点 \\'.\\' 作为键会破坏 Mongo 并出现错误:

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

例如,我希望能够存储:

1
2
3
4
5
6
7
8
9
10
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 中完成。

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

但是...这需要一些杂乱的代码来解决 Mongoose 对象在使用 hasOwnProperty 时行为不端的问题以及首先运行 \\'.toObject()\\' 然后传递原始 \\'this\\' 指针的要求通过参考。

此解决方案有效,但我认为必须有更好的方法!任何关于正确方法的想法或指导都将被感激地接受!当您看到下面的代码时,您就会明白为什么我认为必须有更好的方法!

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

这里是使用的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 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]);
    }
});

注意:请谨慎使用此解决方案(如果您足够疯狂),因为可能存在安全问题。例如,如果坏人知道您将 \\'.\\' 替换为 .他们可以强制使用例如

hxxp://www.vulnerablesitethatdoesntexist.com/../../../../../../../../../../../etc/passwd

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


您应该更改文档的结构,避免使用点作为键。几年前我遇到过同样的问题。

1
2
3
4
5
6
yourStupidObject = {
  'www.google.com': [
    {'ip': '8.8.8.8', more: 'Other info',
    {'ip': '8.8.4.4', more: ['item1']}
    ]
}