Javascript 键名中的MongoDB点(.)
mongo似乎不允许插入带有点(.)或美元符号($)的键,但是当我使用mongoimport工具导入一个包含点的JSON文件时,它工作得很好。驱动程序正在抱怨试图插入该元素 这是文档在数据库中的外观:Javascript 键名中的MongoDB点(.),javascript,mongodb,nosql,Javascript,Mongodb,Nosql,mongo似乎不允许插入带有点(.)或美元符号($)的键,但是当我使用mongoimport工具导入一个包含点的JSON文件时,它工作得很好。驱动程序正在抱怨试图插入该元素 这是文档在数据库中的外观: { "_id": { "$oid": "..." }, "make": "saab", "models": { "9.7x": [ 2007, 2008, 200
{
"_id": {
"$oid": "..."
},
"make": "saab",
"models": {
"9.7x": [
2007,
2008,
2009,
2010
]
}
}
我这样做是不是都错了,不应该对外部数据(即模型)使用那样的散列映射,或者我可以以某种方式逃出点吗?可能我想的Javascript太多了。来自“the.”字符不能出现在键名的任何地方。看起来你必须想出一个编码方案,否则就不需要了。对于PHP,我用HTML值代替句点。那是
”.;“
它在MongoDB中的存储方式如下:
"validations" : {
"4e25adbb1b0a55400e030000" : {
"associate" : "true"
},
"4e25adb11b0a55400e010000" : {
"associate" : "true"
}
}
还有PHP代码
$entry = array('associate' => $associate);
$data = array( '$set' => array( 'validations.' . str_replace(".", `"."`, $validation) => $entry ));
$newstatus = $collection->update($key, $data, $options);
MongoDB不支持它们,因此在导入之前,您必须对JSON文件进行预处理以删除/替换它们,否则您将面临各种问题
这个问题没有一个标准的解决方法,最好的方法过于依赖于具体情况。但如果可能的话,我会尽量避免使用任何键编码器/解码器方法,因为您将继续永久性地为此付出不便,因为JSON重构可能是一次性的成本。您可以尝试在键中使用散列而不是值,然后将该值存储在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
]
}
随后,您将使用散列访问模型
var version = "9.7x";
collection.find( { _id : ...}, function(e, data ) {
var models = data.models[ md5(version) ];
}
建议将非法字符(如$
和
)替换为unicode等效字符
在这些情况下,密钥将需要替换保留的$and。人物。任何字符都是足够的,但是考虑使用Unicode全宽度等效项:U+FF04(即“$”)和U+FF0E(即“。”)。
正如在其他答案中所提到的,由于以下原因,MongoDB不允许
$
或
字符作为映射键。但是,如中所述,此限制并不阻止您插入带有此类密钥的文档,它只是阻止您更新或查询它们。
简单地用[dot]
或U+FF0E
替换
的问题是,当用户合法地想要存储密钥[dot]
或U+FF0E
时会发生什么
需要采取的一种方法是使用类似于Java的unicode转义序列,但要确保首先转义转义字符。本质上,进行了以下字符串替换(*):
当随后从MongoDB读取映射键时,会进行反向替换
或在代码中:
用户唯一需要知道此类转换的时间是在为此类密钥构造查询时
鉴于在数据库中存储dotterd.property.names
以进行配置是很常见的,我认为这种方法比简单地禁止所有此类映射键更可取
(*)afMorphia实际上执行完整/正确的unicode转义规则,如中所述,但所述的替换序列同样有效。将允许您进行更改
{ 'connect.sid': 's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' }
进入
使用
var newObj = _.pairs(oldObj);
我刚刚实现了一个我非常满意的解决方案,它将键名和键值拆分为两个单独的字段。这样,我可以保持字符完全相同,而不用担心任何解析噩梦。文档看起来像:
{
...
keyName: "domain.com",
keyValue: "unregistered",
...
}
您仍然可以很容易地查询它,只需在keyName和keyValue字段上执行find
因此,不是:
db.collection.find({"domain.com":"unregistered"})
这实际上不会像预期的那样工作,您可以运行:
db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
它将返回预期的文档。我在JavaScript中为每个对象键使用以下转义:
key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')
我喜欢它的地方在于,它在开始时只替换了
$
,并且它不使用unicode字符,这在控制台中可能会很难使用<代码>\uu对我来说比unicode字符更具可读性。它也不会将一组特殊字符($
,
)替换为另一组(unicode)。但是使用传统的\
可以正确地逃逸,您可以按原样存储它,并在以后转换为pretty
我在Livescript上写了这个例子。您可以使用livescript.net网站对其进行评估
测试=
字段:
字段1:1
字段2:2
第3场:5场
嵌套的:
更多:1
moresdafasdf:23423
字段3:3
get plain=(json,父级)->
|类型!json是\Object=>json |>obj to pairs |>map->get plain it.1[parent,it.0]。过滤器(->it?).join(\)
|_u=>key:parent,value:json
测试|>获取普通|>展平|>映射(->[it.key,it.value])|>对到obj
它将产生
{“field.field1”:1,
“field.field2”:2,
“字段3”:5,
“field.nested.more”:1,
“field.nested.moresdafasdf”:23423,
“field3”:3}
您需要退出按键。由于大多数人似乎不知道如何正确地转义字符串,以下是步骤:
// 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, '~')
}
答案很晚,但是如果您使用Spring和Mongo,Spring可以使用
MappingMongoConverter
为您管理转换。这是解决方案
db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')
// 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, '~')
}
@Autowired
private MappingMongoConverter converter;
@PostConstruct
public void configureMongo() {
converter.setMapKeyDotReplacement("xxx");
}
{ "axxxb" : "value" }
{ "a.b" : "value" }
key : {
"keyName": "a.b"
"value": [Array]
}
/** This will replace \ with ⍀, ^$ with '₴' and dots with ⋅ to make the object compatible for mongoDB insert.
Caveats:
1. If you have any of ⍀, ₴ or ⋅ in your original documents, they will be converted to \$.upon decoding.
2. Recursive structures are always an issue. A cheap way to prevent a stack overflow is by limiting the number of levels. The default max level is 10.
*/
encodeMongoObj = function(o, level = 10) {
var build = {}, key, newKey, value
//if (typeof level === "undefined") level = 20 // default level if not provided
for (key in o) {
value = o[key]
if (typeof value === "object") value = (level > 0) ? encodeMongoObj(value, level - 1) : null // If this is an object, recurse if we can
newKey = key.replace(/\\/g, '⍀').replace(/^\$/, '₴').replace(/\./g, '⋅') // replace special chars prohibited in mongo keys
build[newKey] = value
}
return build
}
/** This will decode an object encoded with the above function. We assume the structure is not recursive since it should come from Mongodb */
decodeMongoObj = function(o) {
var build = {}, key, newKey, value
for (key in o) {
value = o[key]
if (typeof value === "object") value = decodeMongoObj(value) // If this is an object, recurse
newKey = key.replace(/⍀/g, '\\').replace(/^₴/, '$').replace(/⋅/g, '.') // replace special chars prohibited in mongo keys
build[newKey] = value
}
return build
}
var nastyObj = {
"sub.obj" : {"$dollar\\backslash": "$\\.end$"}
}
nastyObj["$you.must.be.kidding"] = nastyObj // make it recursive
var encoded = encodeMongoObj(nastyObj, 1)
console.log(encoded)
console.log( decodeMongoObj( encoded) )
{
sub⋅obj: {
₴dollar⍀backslash: "$\\.end$"
},
₴you⋅must⋅be⋅kidding: {
sub⋅obj: null,
₴you⋅must⋅be⋅kidding: null
}
}
[12:02:47.691] {
"sub.obj": {
$dollar\\backslash: "$\\.end$"
},
"$you.must.be.kidding": {
"sub.obj": {},
"$you.must.be.kidding": {}
}
}
db.testcollection.save({"_id": "testdocument", "dot.ted.": "value"}, (err, res) => {
console.log(err, res);
});
db.testcollection.save({"dot.ted": "value"}, (err, res) => {
console.log(err, res);
});
db.getCollection('mycollection').aggregate([
{$match: {mymapfield: {$type: "object" }}}, //filter objects with right field type
{$project: {mymapfield: { $objectToArray: "$mymapfield" }}}, //"unwind" map to array of {k: key, v: value} objects
{$match: {mymapfield: {k: "my.key.with.dot", v: "myvalue"}}} //query
])
def mongo_jsonify(dictionary):
new_dict = {}
if type(dictionary) is dict:
for k, v in dictionary.items():
new_k = k.replace('.', '-')
if type(v) is dict:
new_dict[new_k] = mongo_jsonify(v)
elif type(v) is list:
new_dict[new_k] = [mongo_jsonify(i) for i in v]
else:
new_dict[new_k] = dictionary[k]
return new_dict
else:
return dictionary
if __name__ == '__main__':
with open('path_to_json', "r") as input_file:
d = json.load(input_file)
d = mongo_jsonify(d)
pprint(d)