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) { testSchema.post(‘find’, function(results) { |
注意:请谨慎使用此解决方案(如果您足够疯狂),因为可能存在安全问题。例如,如果坏人知道您将 \\’.\\’ 替换为 .他们可以强制使用例如
hxxp://www.vulnerablesitethatdoesntexist.com/../../../../../../../../../../../etc/passwd
被正确转义但会被透明地转换为目录遍历类型字符串:
hxxp://www.vulnerablesitethatdoesntexist.com/../../../../../../../../../../../etc/passwd
- 看到这个答案stackoverflow.com/questions/40542336/…
- 嗨 Sergiu,感谢您的链接。我以前读过那个答案。此解决方案需要大规模更改后端数据,这很少是一个好主意。您可以通过上面的 pre 和 post 挂钩进行此替换,但解决方案与上面相同。而且您冒着将数据更改为看起来像字符但不是字符的东西的风险,即同形异义词。如果开发人员通过呈现 Unicode 的终端查看数据,这可能会导致开发人员无法确定查询失败的原因。
您应该更改文档的结构,避免使用点作为键。几年前我遇到过同样的问题。
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’]} ] } |
- 嗨,托马斯,感谢您的评论。在考虑了您的答案之后,这可能是做事的”正确”方式。但是,我想检查的部分内容是保持 JSON 结构,即前端对象和 MongoDB 后端相同。在某些情况下,如果可能的话,简单地保存前端对象会更方便。
- 想想你已经投入了多少小时,以及相比之下改变前端的工作量是多少
- 你是对的,但它正在工作,因为我现在需要它。我只是想知道这样做的正确方法是什么?对于我的用例,值得弄清楚这一点,因为它使端到端更容易一些。
- 我接受这个作为答案,因为可以说正确的方法是简化/更改架构。一周内没有其他活动。但是,如果有人想抽象出读写更改,他们可以使用我发布的代码的一些变体。
来源:https://www.codenong.com/43607645/