Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/413.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 键名中的MongoDB点(.)_Javascript_Mongodb_Nosql - Fatal编程技术网

Javascript 键名中的MongoDB点(.)

Javascript 键名中的MongoDB点(.),javascript,mongodb,nosql,Javascript,Mongodb,Nosql,mongo似乎不允许插入带有点(.)或美元符号($)的键,但是当我使用mongoimport工具导入一个包含点的JSON文件时,它工作得很好。驱动程序正在抱怨试图插入该元素 这是文档在数据库中的外观: { "_id": { "$oid": "..." }, "make": "saab", "models": { "9.7x": [ 2007, 2008, 200

mongo似乎不允许插入带有点(.)或美元符号($)的键,但是当我使用mongoimport工具导入一个包含点的JSON文件时,它工作得很好。驱动程序正在抱怨试图插入该元素

这是文档在数据库中的外观:

{
    "_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",
    ...
}
您仍然可以很容易地查询它,只需在keyNamekeyValue字段上执行
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}
您需要退出按键。由于大多数人似乎不知道如何正确地转义字符串,以下是步骤:

  • 选择转义字符(最好选择很少使用的字符)。例如~'
  • 若要转义,请首先将转义字符的所有实例替换为转义字符前面的某个序列(例如“~”->“~t”),然后将需要转义的任何字符或序列替换为转义字符前面的某个序列。例如,“->”~p”
  • 要取消转义,请首先从第二个转义序列的所有实例中删除转义序列(例如'~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, '~')
    }
    

    答案很晚,但是如果您使用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)