web-dev-qa-db-ja.com

キー名のMongoDBドット(。)

Mongoでは、ドット(。)またはドル記号($)を使用したキーの挿入は許可されていないようですが、mongoimportツールを使用して、ドットを含むJSONファイルをインポートしたときは正常に機能しました。ドライバーは、その要素を挿入しようとして不満を言っています。

これは、データベース内のドキュメントの外観です。

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

私はこれをすべて間違っていますか、外部データ(つまりモデル)でそのようなハッシュマップを使用するべきではありませんか、どうにかドットをエスケープできますか?多分、Javascriptのように考えすぎています。

67

MongoDBは ドット付きキー をサポートしないため、インポートする前にJSONファイルを前処理して削除/置換する必要があります。そうしないと、あらゆる種類の設定を行うことになります。問題。

この問題に対する標準的な回避策はありません。最適なアプローチは状況の詳細に依存しすぎています。ただし、JSONの再構築には1回限りのコストがかかる可能性があるため、キーエンコーダー/デコーダーのアプローチは可能な限り回避します。

62
JohnnyHK

Mongo docs は、$.などの不正な文字を同等のUnicodeに置き換えることを提案しています。

これらの状況では、キーは予約済みの$およびを置き換える必要があります。文字。任意の文字で十分ですが、Unicodeの同等の全幅を使用することを検討してください:U + FF04(つまり「$」)およびU + FF0E(つまり「。」).

18
Martin Konecny

他の回答で述べたように、MongoDBは、フィールド名の 制限 により、$または.文字をマップキーとして許可しません。ただし、 Dollar Sign Operator Escapingで説明したように、この制限は挿入そのようなキーを持つドキュメントを防ぐことはできません。それらを更新または照会できなくなります。

.[dot]またはU+FF0Eに単純に置き換える問題(このページの別の箇所で説明します)は、ユーザーが合法的にキー[dot]またはU+FF0Eを保存したい場合にどうなりますか?

Fantom'safMorphia driver がとるアプローチは、Javaのそれに似たユニコードエスケープシーケンスを使用することですが、エスケープ文字が最初にエスケープされるようにします。本質的に、次の文字列置換が行われます(*):

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

マップキーがその後読み取られると、逆の置換が行われますfrom 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 のUnicodeエスケープ構文で説明されているように、完全/適切なUnicodeエスケープ規則を実際に実行しますが、説明されている置換シーケンスも同様に機能します。

17
Steve Eynon

実装したばかりのソリューションで、本当に満足しているのは、キー名と値を2つの別々のフィールドに分割することです。この方法で、キャラクターをまったく同じに保つことができ、それらの解析する悪夢について心配する必要はありません。ドキュメントは次のようになります。

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

フィールドでfindを実行するだけで、これを簡単に照会できますkeyNameand- keyValue

代わりに:

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

実際に期待どおりに動作しない場合は、次を実行します。

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

そして、期待されるドキュメントを返します。

10
Steve

MongoDBの最新の安定バージョン(v3.6.1)は、キーまたはフィールド名でドット(。)をサポートするようになりました。

フィールド名にはドット(。)およびドル($)文字を含めることができます

10
h4ck3d

値の代わりにキーでハッシュを使用してみて、その値を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) ];
}
9
Henry

MongoDB docs "the '。'から文字はキー名のどこにも現れてはなりません」。エンコードスキームを考え出す必要があるようです。

4
maerics

キーをエスケープする必要があります。ほとんどの人は文字列を適切にエスケープする方法を知らないようですので、手順は次のとおりです。

  1. エスケープ文字を選択します(めったに使用されない文字を選択するのが最適です)。例えば。 「〜」
  2. エスケープするには、まずエスケープ文字のすべてのインスタンスをエスケープ文字を先頭に追加したシーケンス(たとえば「〜」->「〜t」)に置き換え、次にエスケープする必要がある文字またはシーケンスをエスケープ文字を先頭に追加したシーケンスに置き換えます。例えば。 「。」 -> '〜p'
  3. エスケープを解除するには、最初に2番目のエスケープシーケンスのすべてのインスタンスからエスケープシーケンスを削除し(例: '〜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, '~')
}
3
B T

遅い答えですが、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" }
3
PomPom

現在サポートされています

MongoDb 3.6以降は、フィールド名でdotsdollarの両方をサポートします。以下のJIRAを参照してください: https://jira.mongodb.org/browse/Java-281

Mongodbを3.6+にアップグレードするのが最善の方法のように思えます。

2
Abhidemon

オブジェクトキーごとにJavaScriptで次のエスケープを使用します。

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

私が気に入っているのは、最初の$のみを置き換え、コンソールで使用するのが難しいユニコード文字を使用しないことです。 _は、ユニコード文字よりも読みやすいです。また、特殊文字のセット($.)を別のセット(ユニコード)に置き換えません。しかし、従来の\で適切にエスケープします。

1
Mitar

完全ではありませんが、ほとんどの状況で機能します。禁止されている文字を別の文字に置き換えます。キーにあるため、これらの新しい文字はかなりまれです。

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

あなたはそれをそのまま保存し、かなり後に変換することができます

この例をLivescriptで書きました。 livescript.net Webサイトを使用して評価できます

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

生産します

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

最新のMongoDBはドット付きのキーをサポートしていますが、Java MongoDB-driverはサポートしていません。Javaで動作させるため、 githubリポジトリからコードを引き出しました。 Java-mongo-driver を使用して、isValid Key関数に適宜変更を加え、それを使用して新しいjarを作成しました。

0
ANDY MURAY

奇妙なことに、mongojsを使用して、_idを自分で設定するとドット付きのドキュメントを作成できますが、_idが生成されるとドキュメントを作成できません。

動作します:

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

最初にdatを使ってドキュメントをドットキーで更新することも有効だと思っていましたが、ドットをサブキーとして識別します。

Mongojsがドット(サブキー)を処理する方法を見て、キーにドットが含まれていないことを確認します。

0
Sam

Lodashペア を変更できます

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

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

を使用して

var newObj = _.pairs(oldObj);
0
steampowered

PHPでは、ピリオドをHTML値に置き換えます。それは"."

次のようにMongoDBに保存します。

  "validations" : {
     "4e25adbb1b0a55400e030000" : {
     "associate" : "true" 
    },
     "4e25adb11b0a55400e010000" : {
       "associate" : "true" 
     } 
   } 

およびPHP code ...

  $entry = array('associate' => $associate);         
  $data = array( '$set' => array( 'validations.' . str_replace(".", `"."`, $validation) => $entry ));     
  $newstatus = $collection->update($key, $data, $options);      
0
JRForbes

ヒントを教えてください:JSON.stringifyを使用して、ドットを含むキー名を含むオブジェクト/配列を保存し、JSON.parseでオブジェクトに文字列を解析して、データベースからデータを取得するときに処理できます

別の回避策:次のようにスキーマを再構築します。

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

ドット(.)またはドル($)実際のドキュメントでは決して使用されない他の文字を使用します。そして、ドットを復元します(.)またはドル($)ドキュメントを取得するとき。この戦略は、ユーザーが読み取るデータには影響しません。

すべての文字 から文字を選択できます。

0
Simin Jie