密钥名称中的MongoDB点(.)

Mic*_*aev 72 javascript mongodb nosql

似乎mongo不允许插入带点(.)或美元符号($)的键,但是当我使用mongoimport工具导入包含点的JSON文件时,它工作正常.司机抱怨试图插入该元素.

这是文档在数据库中的样子:

{
    "_id": {
        "$oid": "..."
    },
    "make": "saab",
    "models": {
        "9.7x": [
            2007,
            2008,
            2009,
            2010
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

我这样做是错误的,不应该使用像外部数据那样的哈希映射(即模型),还是可以以某种方式逃避点?也许我在想类似Javascript.

Joh*_*yHK 63

MongoDB不支持带有点的键,因此您必须预先处理JSON文件以在导入之前删除/替换它们,否则您将为各种各样的问题做好准备.

这个问题没有标准的解决方法,最好的方法太依赖于具体情况.但是如果可能的话,我会避免使用任何关键的编码器/解码器方法,因为你将继续为永久性付出不便,因为JSON重组可能是一次性成本.

  • 如果必须,您可以提供{check_keys:false}选项来绕过此问题. (13认同)
  • 再次陷入这种境地.这似乎不是因为我们可以控制并且经常需要查询的app键名,而是用嵌套数据结构中的用户提供的数据,这是我们无法控制的,但是(a)想要存储在Mongo中,(b)我们知道这可能发生在哪些特定字段中(例如,这里的"模型"),以及(c)我们不需要在Mongo中按密钥名称查询它们.所以我决定使用的模式是在保存时将此字段设置为"JSON.stringify",并在检索时使用"JSON.parse". (8认同)
  • @TzuryBarYochay OMG你发现MongoDB相当于西北通道.我认为这应该是公认的答案. (4认同)
  • @emarel db.collection_foo.update({this: "that"}, {$set: {a:"b"}}, {check_keys: false}) (2认同)

Mar*_*cny 18

蒙戈文档建议替换非法字符,如$.他们的Unicode的等价物.

在这些情况下,密钥需要替换保留的$和.字符.任何字符都足够了,但考虑使用Unicode全宽等价:U + FF04(即"$")和U + FF0E(即".").

  • 这听起来像是一个大规模调试头痛的秘诀. (65认同)
  • @SergioTulentsev我让他们删除了建议:)https://github.com/mongodb/docs/commit/3cb0c576256d27266a1ab5475dac715b746c157a (6认同)
  • -1 A.这是一个可怕的想法 - 如果有人真的试图将这些unicode字符用作密钥怎么办?然后你有一个无声的错误,谁会知道你的系统是什么.不要使用这样的模糊转义方法.B. mongo docs不再这么说了,可能是因为有人意识到这是一个可怕的想法 (4认同)
  • @AndrewMedico,@ atamlyn - 我认为文档的含义类似于`db.test.insert({"field\uff0ename":"test"})` (2认同)
  • @BT:给你小费,先生:) (2认同)

Ste*_*non 17

正如其他答案中所提到的,由于字段名称的限制, MongoDB不允许$.字符作为映射键.但是,如美元符号运算符中所述,转义此限制并不会阻止您插入具有此类键的文档,它只会阻止您更新或查询它们.

简单地更换问题.[dot](如在评论中提到),当用户合法想要存储密钥,会发生什么U+FF0E

该办法魅影的 afMorphia驱动程序需要的是用类似的Java的Unicode转义序列,但要确保任何转义字符转义第一.本质上,进行以下字符串替换(*):

\  -->  \\
$  -->  \u0024
.  -->  \u002e
Run Code Online (Sandbox Code Playgroud)

随后 MongoDB 读取映射键时进行反向替换.

或者在Fantom代码中:

Str encodeKey(Str key) {
    return key.replace("\\", "\\\\").replace("\$", "\\u0024").replace(".", "\\u002e")
}

Str decodeKey(Str key) {
    return key.replace("\\u002e", ".").replace("\\u0024", "\$").replace("\\\\", "\\")
}
Run Code Online (Sandbox Code Playgroud)

用户需要了解此类转换的唯一时间是构造对此类密钥的查询.

鉴于[dot]出于配置目的而存储在数据库中是很常见的,我认为这种方法比简单地禁止所有这样的映射键更可取.

(*)afMorphia实际上执行完整/正确的unicode转义规则,如Java中的Unicode转义语法中所述,但所描述的替换序列也可以正常工作.

  • 事实证明,Mongodb 在其最新版本中支持点和美元。请参阅:- /sf/answers/3997467561/ (2认同)

h4c*_*k3d 11

MongoDB的最新稳定版本(v3.6.1)现在支持键或字段名称中的点(.).

字段名称现在可以包含点(.)和美元($)字符

  • 即使服务器现在支持它,驱动程序仍然会检查键中的$和点,并且不接受它们。因此,Mongo理论上仅支持点和美元字符。实际上这还不可用:( (6认同)

Ste*_*eve 10

我刚刚实现的解决方案,我真的很满意,涉及将密钥名称和值拆分为两个单独的字段.这样,我可以保持角色完全相同,而不用担心任何解析噩梦.该文档看起来像:

{
    ...
    keyName: "domain.com",
    keyValue: "unregistered",
    ...
}
Run Code Online (Sandbox Code Playgroud)

您仍然可以通过find对字段keyName keyValue执行a 来轻松查询.

所以代替:

 db.collection.find({"domain.com":"unregistered"})
Run Code Online (Sandbox Code Playgroud)

哪个实际上不会按预期工作,你会运行:

db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
Run Code Online (Sandbox Code Playgroud)

它将返回预期的文件.


Hen*_*nry 9

您可以尝试在键中使用散列而不是值,然后将该值存储在JSON值中.

var crypto = require("crypto");   

function md5(value) {
    return crypto.createHash('md5').update( String(value) ).digest('hex');
}

var data = {
    "_id": {
        "$oid": "..."
    },
    "make": "saab",
    "models": {}
}

var version = "9.7x";

data.models[ md5(version) ] = {
    "version": version,
    "years" : [
        2007,
        2008,
        2009,
        2010
    ]
}
Run Code Online (Sandbox Code Playgroud)

然后,您将使用散列访问模型.

var version = "9.7x";
collection.find( { _id : ...}, function(e, data ) {
    var models = data.models[ md5(version) ];
}
Run Code Online (Sandbox Code Playgroud)

  • 使用散列作为键的问题在于,不能保证它们是唯一的,并且它们经常会产生[冲突](http://crypto.stackexchange.com/questions/15873/what-is-the-md5-与最小的输入值冲突)。加上每次您想访问地图时都计算加密哈希对于我来说似乎并不是最理想的解决方案。 (2认同)
  • 为什么这比用特殊字符或序列替换句点更好? (2认同)

Abh*_*mon 8

现在支持

MongoDb 3.6以后的字段名称支持美元。请参阅以下 JIRA:https : //jira.mongodb.org/browse/JAVA-2810

将您的 Mongodb 升级到 3.6+ 听起来是最好的方法。

  • 3.6 可以存储它们,是的,但它*不*尚受支持,可能会引发驱动程序错误,并且可能会破坏查询/更新:[限制](https://docs.mongodb.com/manual/reference/limits/#Restrictions -on-Field-Names): “MongoDB 查询语言不能总是对字段名称包含这些字符的文档进行有意义的查询(请参阅 SERVER-30575)。在查询语言中添加支持之前,在字段中使用 $ 和 .不建议使用名称,并且官方 MongoDB 驱动程序**不支持**。” (7认同)

B T*_*B T 5

你需要逃脱钥匙。由于似乎大多数人不知道如何正确转义字符串,因此步骤如下:

  1. 选择一个转义字符(最好选择很少使用的字符)。例如。'~'
  2. 要转义,首先将转义字符的所有实例替换为前面带有转义字符的某个序列(例如“~”->“~t”),然后将需要转义的任何字符或序列替换为前面带有转义字符的某个序列。例如。'.' -> '~p'
  3. 要取消转义,首先从第二个转义序列的所有实例中删除转义序列(例如 '~p' -> '.'),然后将转义字符序列转换为单个转义字符(例如 '~s' -> '~ ')

另外,请记住 mongo 也不允许键以“$”开头,因此您必须在那里执行类似的操作

这是一些执行此操作的代码:

// returns an escaped mongo key
exports.escape = function(key) {
  return key.replace(/~/g, '~s')
            .replace(/\./g, '~p')
            .replace(/^\$/g, '~d')
}

// returns an unescaped mongo key
exports.unescape = function(escapedKey) {
  return escapedKey.replace(/^~d/g, '$')
                   .replace(/~p/g, '.')
                   .replace(/~s/g, '~')
}
Run Code Online (Sandbox Code Playgroud)