programing

키 이름의 MongoDB 점 (.)

nasanasas 2020. 11. 4. 08:05
반응형

키 이름의 MongoDB 점 (.)


mongo는 점 (.) 또는 달러 기호 ($)가있는 키 삽입을 허용하지 않는 것 같지만 mongoimport 도구를 사용하여 점이 포함 된 JSON 파일을 가져 오면 제대로 작동했습니다. 운전자가 해당 요소를 삽입하려는 것에 대해 불평하고 있습니다.

데이터베이스에서 문서는 다음과 같습니다.

{
    "_id": {
        "$oid": "..."
    },
    "make": "saab",
    "models": {
        "9.7x": [
            2007,
            2008,
            2009,
            2010
        ]
    }
}

내가이 모든 일을 잘못하고 있고 외부 데이터 (예 : 모델)와 함께 해시 맵을 사용해서는 안됩니까? 아니면 어떻게 든 점을 이스케이프 할 수 있습니까? 어쩌면 나는 너무 많은 Javascript와 비슷하다고 생각하고 있습니다.


MongoDB는 점이 있는 키를 지원하지 않으므로 가져 오기 전에 JSON 파일을 제거 / 교체하기 위해 사전 처리해야합니다. 그렇지 않으면 모든 종류의 문제에 대해 스스로 설정해야합니다.

이 문제에 대한 표준 해결 방법은 없으며 최상의 방법은 상황의 세부 사항에 너무 의존적입니다. 하지만 가능한 한 모든 주요 인코더 / 디코더 접근 방식을 피하고 싶습니다. JSON 재구성은 아마도 일회성 비용이 될 것입니다.


몽고의 워드 프로세서 와 같은 잘못된 문자를 교체 할 것을 제안 $하고 .자신의 유니 코드 등가물.

이러한 상황에서 키는 예약 된 $ 및을 대체해야합니다. 문자. 모든 문자로 충분하지만 U + FF04 (예 : "$") 및 U + FF0E (예 : ".")와 같은 유니 코드 전체 너비를 사용하는 것이 좋습니다.


다른 답변에서 언급했듯이 MongoDB는 필드 이름대한 제한으로 인해 $또는 .문자를 맵 키로 허용하지 않습니다 . 그러나 달러 기호 연산자 에서 언급 했듯이이 제한 사항을 이스케이프 한다고해서 이러한 키가있는 문서 삽입 할 수있는 것은 아닙니다. 문서를 업데이트하거나 쿼리 할 수 ​​없습니다.

단순히 교체의 문제 .와 함께 [dot]U+FF0E(이 페이지에 다른 곳에서 언급 한 바와 같이이) 어떤 사용자가 합법적으로 키 저장하려고 할 때 발생한다 [dot]또는 U+FF0E?

Fantom의 afMorphia 드라이버 가 취하는 접근 방식 은 Java와 유사한 유니 코드 이스케이프 시퀀스를 사용하지만 이스케이프 문자가 먼저 이스케이프되도록하는 것입니다. 본질적으로 다음과 같은 문자열 대체가 이루어집니다 (*).

\  -->  \\
$  -->  \u0024
.  -->  \u002e

나중에 MongoDB 에서 맵 키를 읽을 때 역방향 교체가 수행됩니다 .

또는 Fantom 코드에서 :

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

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

사용자가 이러한 변환을 인식해야하는 유일한 시간은 해당 키에 대한 쿼리를 구성 할 때입니다.

dotted.property.names구성 목적으로 데이터베이스에 저장하는 것이 일반적이라는 점을 감안할 때 이러한 접근 방식이 모든 맵 키를 단순히 금지하는 것보다 낫다고 생각합니다.

(*) afMorphia는 실제로 Java의 유니 코드 이스케이프 구문에 언급 된대로 전체 / 적절한 유니 코드 이스케이프 규칙을 수행 하지만 설명 된 대체 시퀀스도 잘 작동합니다.


방금 구현 한 솔루션 중 정말 만족 스럽습니다. 키 이름과 값을 두 개의 개별 필드로 분할하는 것입니다. 이렇게하면 문자를 똑같이 유지할 수 있으며 구문 분석 악몽에 대해 걱정할 필요가 없습니다. 문서는 다음과 같습니다.

{
    ...
    keyName: "domain.com",
    keyValue: "unregistered",
    ...
}

keyName keyValuefind 필드 에서 a 수행하여 쉽게 쿼리 할 수 ​​있습니다 .

그래서 대신 :

 db.collection.find({"domain.com":"unregistered"})

실제로 예상대로 작동하지 않을 경우 다음을 실행합니다.

db.collection.find({keyName:"domain.com", keyValue:"unregistered"})

예상 된 문서를 반환합니다.


MongoDB의 최신 안정 버전 (v3.6.1)은 이제 키 또는 필드 이름에 점 (.)을 지원합니다.

이제 필드 이름에 점 (.) 및 달러 ($) 문자가 포함될 수 있습니다.


값 대신 키에 해시를 사용한 다음 해당 값을 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) ];
}

로부터 MongoDB를 워드 프로세서 은 " '.' 문자는 키 이름에 표시되지 않아야합니다. " 인코딩 체계를 생각해 내야하거나없이해야 할 것 같습니다.


열쇠를 탈출해야합니다. 대부분의 사람들은 문자열을 올바르게 이스케이프하는 방법을 모르는 것 같으므로 다음 단계를 따르십시오.

  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, '~')
}

늦은 답변이지만 Spring과 Mongo를 사용하는 경우 Spring은 MappingMongoConverter. JohnnyHK의 솔루션이지만 Spring에서 처리합니다.

@Autowired
private MappingMongoConverter converter;

@PostConstruct
public void configureMongo() {
 converter.setMapKeyDotReplacement("xxx");
}

저장된 Json이 다음과 같은 경우 :

{ "axxxb" : "value" }

Spring (MongoClient)을 통해 다음과 같이 읽 힙니다.

{ "a.b" : "value" }

지금 지원됩니다

MongoDb 3.6 이상 에서는 필드 이름에서 달러모두 지원 합니다. JIRA 아래 참조 : https://jira.mongodb.org/browse/JAVA-2810

Mongodb를 3.6 이상으로 업그레이드하는 것이 가장 좋은 방법입니다.


각 객체 키에 대해 JavaScript에서 다음 이스케이프를 사용합니다.

key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')

내가 좋아하는 점은 $처음 에만 교체 하고 콘솔에서 사용하기 까다로울 수있는 유니 코드 문자를 사용하지 않는다는 것입니다. _나에게 유니 코드 문자보다 훨씬 더 읽기 쉽다. 또한 한 세트의 특수 문자 ( $, .)를 다른 세트 (유니 코드)로 바꾸지 않습니다. 그러나 전통적인 \.


완벽하지는 않지만 대부분의 상황에서 작동합니다. 금지 된 문자를 다른 것으로 대체합니다. 키에 있기 때문에 이러한 새 문자는 상당히 드뭅니다.

/** 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": {}
  }
}

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);      

Lodash 쌍 을 사용하면 변경할 수 있습니다.

{ 'connect.sid': 's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' }

으로

[ [ 'connect.sid',
's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' ] ]

사용

var newObj = _.pairs(oldObj);

You can store it as it is and convert to pretty after

I wrote this example on Livescript. You can use livescript.net website to eval it

test =
  field:
    field1: 1
    field2: 2
    field3: 5
    nested:
      more: 1
      moresdafasdf: 23423
  field3: 3



get-plain = (json, parent)->
  | typeof! json is \Object => json |> obj-to-pairs |> map -> get-plain it.1, [parent,it.0].filter(-> it?).join(\.)
  | _ => key: parent, value: json

test |> get-plain |> flatten |> map (-> [it.key, it.value]) |> pairs-to-obj

It will produce

{"field.field1":1,
 "field.field2":2,
 "field.field3":5,
 "field.nested.more":1,
 "field.nested.moresdafasdf":23423,
 "field3":3}


Give you my tip: You can using JSON.stringify to save Object/ Array contains the key name has dots, then parse string to Object with JSON.parse to process when get data from database

Another workaround: Restructure your schema like:

key : {
"keyName": "a.b"
"value": [Array]
}

Latest MongoDB does support keys with a dot, but java MongoDB-driver is not supporting. So to make it work in Java, I pulled code from github repo of java-mongo-driver and made changes accordingly in their isValid Key function, created new jar out of it, using it now.


Replace the dot(.) or dollar($) with other characters that will never used in the real document. And restore the dot(.) or dollar($) when retrieving the document. The strategy won't influence the data that user read.

You can select the character from all characters.


The strange this is, using mongojs, I can create a document with a dot if I set the _id myself, however I cannot create a document when the _id is generated:

Does work:

db.testcollection.save({"_id": "testdocument", "dot.ted.": "value"}, (err, res) => {
    console.log(err, res);
});

Does not work:

db.testcollection.save({"dot.ted": "value"}, (err, res) => {
    console.log(err, res);
});

I first thought dat updating a document with a dot key also worked, but its identifying the dot as a subkey!

Seeing how mongojs handles the dot (subkey), I'm going to make sure my keys don't contain a dot.


/home/user/anaconda3/lib/python3.6/site-packages/pymongo/collection.py

Found it in error messages. If you use anaconda (find the correspondent file if not), simply change the value from check_keys = True to False in the file stated above. That'll work!

참고URL : https://stackoverflow.com/questions/12397118/mongodb-dot-in-key-name

반응형