it-swarm.com.de

MongoDB-Punkt (.) Im Schlüsselnamen

Es scheint, dass Mongo das Einfügen von Schlüsseln mit einem Punkt (.) Oder einem Dollarzeichen ($) nicht zulässt. Als ich jedoch eine JSON-Datei importierte, die einen Punkt mit dem Mongoimport-Tool enthielt, funktionierte es einwandfrei. Der Fahrer beschwert sich, dass er versucht hat, dieses Element einzufügen.

So sieht das Dokument in der Datenbank aus:

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

Mache ich das alles falsch und sollte solche Hash-Karten nicht mit externen Daten (d. H. Den Modellen) verwenden, oder kann ich dem Punkt irgendwie entgehen? Vielleicht denke ich zu viel nach Javascript.

52

MongoDB unterstützt keine Schlüssel mit einem Punkt . Sie müssen also Ihre JSON-Datei vorverarbeiten, um sie vor dem Import zu entfernen oder zu ersetzen, oder Sie bereiten sich auf alle möglichen Probleme vor.

Es gibt keine Standardlösung für dieses Problem, der beste Ansatz ist zu sehr von den Besonderheiten der Situation abhängig. Aber ich würde jeden Key-Encoder/Decoder-Ansatz möglichst vermeiden, da Sie die Unbequemlichkeit für die Ewigkeit, in der eine JSON-Umstrukturierung vermutlich einmalige Kosten verursachen würde, weiterhin zu tragen haben.

52
JohnnyHK

Die Mongo-Dokumente schlagen vor, illegale Zeichen wie $ und . durch ihre Unicode-Entsprechungen zu ersetzen.

In diesen Situationen müssen Schlüssel die reservierten $ und ersetzen. Zeichen. Jedes Zeichen ist ausreichend, jedoch sollten Sie die Unicode-Äquivalente mit voller Breite verwenden: U + FF04 (d. H. "$") und U + FF0E (d. H. ".").

18
Martin Konecny

Wie in anderen Antworten erwähnt, erlaubt MongoDB aufgrund von Einschränkungen für Feldnamen keine $- oder .-Zeichen als Kartenschlüssel. Wie in Dollar Sign Operator Escapingerwähnt, verhindert diese Einschränkung jedoch nicht, dass Einfügen Dokumente mit solchen Schlüsseln angezeigt werden.

Das Problem, einfach . durch [dot] (wie in den Kommentaren erwähnt) zu ersetzen, ist, was passiert, wenn der Benutzer den Schlüssel [dot] legitim speichern möchte?

Ein Ansatz, den FantomsafMorphia-Treiber verwendet, besteht darin, Unicode-Escape-Sequenzen ähnlich der von Java zu verwenden, wobei jedoch sichergestellt wird, dass alle Escape-Zeichen zuerst mit Escapezeichen versehen werden. Im Wesentlichen werden die folgenden Zeichenfolgen ersetzt (*):

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

Eine umgekehrte Ersetzung wird durchgeführt, wenn Kartenschlüssel anschließend from MongoDB gelesen werden.

Oder in Fantom code:

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

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

Der Benutzer muss sich nur über solche Konvertierungen informieren, wenn er Abfragen für solche Schlüssel erstellt.

Da es üblich ist, dotted.property.names zu Konfigurationszwecken in Datenbanken zu speichern, ist dieser Ansatz meiner Meinung nach vorzuziehen, wenn Sie einfach alle derartigen Kartenschlüssel verbieten.

(*) afMorphia führt tatsächlich vollständige/richtige Unicode-Escape-Regeln aus, wie in Unicode-Escape-Syntax in Java beschrieben, aber die beschriebene Ersetzungssequenz funktioniert genauso gut.

12
Steve Eynon

Sie können versuchen, einen Hash im Schlüssel anstelle des Werts zu verwenden, und diesen Wert dann im JSON-Wert speichern.

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
    ]
}

Sie würden dann später über den Hash auf die Modelle zugreifen.

var version = "9.7x";
collection.find( { _id : ...}, function(e, data ) {
    var models = data.models[ md5(version) ];
}
11
Henry

Eine Lösung, die ich gerade implementiert habe und mit der ich sehr zufrieden bin, besteht darin, den Namen und den Wert des Schlüssels in zwei separate Felder aufzuteilen. Auf diese Weise kann ich die Charaktere genau gleich halten und muss mir keine Sorgen um einen dieser Albträume machen. Das Dokument würde folgendermaßen aussehen:

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

Sie können dies dennoch leicht genug abfragen, indem Sie einfach eine find für die Felder keyName und keyValue ausführen.

Also statt:

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

was nicht wie erwartet funktionieren würde, würden Sie laufen:

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

und das erwartete Dokument wird zurückgegeben.

8
Steve

Die neueste stabile Version (v3.6.1) von MongoDB unterstützt jetzt Punkte (.) In den Schlüsseln oder Feldnamen. 

Feldnamen können jetzt Punkte (.) Und Dollar ($) Zeichen enthalten

8
h4ck3d

Aus den MongoDB-Dokumenten "der". " Zeichen darf an keiner Stelle des Schlüsselnamens stehen ". Es sieht so aus, als müssten Sie ein Kodierungsschema ausarbeiten oder ganz verzichten.

4
maerics

Sie müssen die Schlüssel entziehen. Da es scheint, dass die meisten Leute nicht wissen, wie sie Strings richtig umgehen, sind hier die Schritte:

  1. wählen Sie ein Escape-Zeichen (am besten wählen Sie ein Zeichen, das selten verwendet wird). Z.B. '~'
  2. Um zu entkommen, ersetzen Sie zunächst alle Instanzen des Escape-Zeichens durch eine Sequenz, die mit Ihrem Escape-Zeichen vorangestellt ist (z. B. '~' -> '~ t'), und ersetzen Sie dann das zu fluchende Zeichen oder die Sequenz mit einer Sequenz, die Ihr Escape-Zeichen enthält . Z.B. '.' -> '~ p'
  3. Um die Flucht aufzuheben, entfernen Sie zuerst die Escape-Sequenz aus allen Instanzen Ihrer zweiten Escape-Sequenz (z. B. '~ p' -> '.'), Und transformieren Sie dann die Escape-Zeichenfolge in ein einzelnes Escape-Zeichen (z. B. '~ s' -> '~) ')

Denken Sie auch daran, dass der mongo es nicht zulässt, dass die Schlüssel mit '$' beginnen, sodass Sie dort etwas Ähnliches tun müssen

Hier ist ein Code, der es tut:

// 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

Eine späte Antwort, aber wenn Sie Spring mit Mongo verwenden, kann es die Konvertierung für Sie übernehmen. Es ist die Lösung von JohnnyHK, die jedoch von Spring gehandhabt wird.

@Autowired
private MappingMongoConverter converter;

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

Wenn Ihr gespeicherter Json zum Beispiel ist

{ "axxxb" : "value" }

Durch den Frühling (MongoClient) wird es gelesen als

{ "a.b" : "value" }
2
PomPom

Es wird jetzt unterstützt

MongoDb 3.6 unterstützt sowohl Punkte als auch Dollar in Feldnamen. Siehe unten JIRA: https://jira.mongodb.org/browse/Java-281

Ein Upgrade Ihres Mongodb auf 3.6+ klingt nach dem besten Weg.

2
Abhidemon

Ich benutze in JavaScript für jeden Objektschlüssel die folgende Escape-Funktion:

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

Was mir dabei gefällt, ist, dass es anfangs nur $ ersetzt, und es werden keine Unicode-Zeichen verwendet, deren Verwendung in der Konsole schwierig sein kann. _ ist für mich viel lesbarer als ein Unicode-Zeichen. Es ersetzt auch keinen Satz von Sonderzeichen ($, .) durch einen anderen (Unicode). Aber mit traditionellen \.

1
Mitar

Nicht perfekt, funktioniert aber in den meisten Situationen: Ersetzen Sie die verbotenen Zeichen durch etwas anderes. Da es sich um Schlüssel handelt, sollten diese neuen Zeichen ziemlich selten sein.

/** 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
}

Hier ist ein Test:

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

und die Ergebnisse - Beachten Sie, dass die Werte nicht geändert werden:

{
  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

Sie können es so speichern wie es ist und danach in hübsch konvertieren

Ich habe dieses Beispiel auf Livescript geschrieben. Sie können die Webseite livescript.net verwenden, um es auszuwerten

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

Es wird produzieren 

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

0
Andrey Stehno

Die neueste Version von MongoDB unterstützt Schlüssel mit einem Punkt, Java MongoDB-Treiber werden jedoch nicht unterstützt. Damit es in Java funktioniert, habe ich Code aus github repo von Java-mongo-driver abgerufen und entsprechende Änderungen in der isValid-Key-Funktion vorgenommen, ein neues Jar daraus erstellt und es jetzt verwendet.

0
ANDY MURAY

Das Seltsame dabei ist, dass ich mit Hilfe von Mongojs ein Dokument mit einem Punkt erstellen kann, wenn ich die _id selbst setze. Ich kann jedoch kein Dokument erstellen, wenn die _id generiert wird:

Funktioniert:

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

Funktioniert nicht:

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

Ich dachte zuerst, dass das Aktualisieren eines Dokuments mit einem Punktschlüssel auch funktionierte, aber es kennzeichnet den Punkt als einen Unterschlüssel!

Da mongojs mit dem Punkt (Unterschlüssel) umgeht, werde ich sicherstellen, dass meine Schlüssel keinen Punkt enthalten.

0
Sam

Lodash-Paare können Sie ändern

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

in

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

mit 

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

Für PHP ersetze ich den HTML-Wert für den Zeitraum. Das ist ".".

Es speichert in MongoDB wie folgt:

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

und der PHP Code ...

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

Tipp: Sie können JSON.stringify verwenden, um zu speichern. Objekt/Array enthält den Schlüsselnamen mit Punkten. Anschließend wird der String in Object mit JSON.parse analysiert, um Daten zu verarbeiten, wenn Daten aus der Datenbank abgerufen werden

Eine andere Problemumgehung: Umstrukturieren Sie Ihr Schema wie folgt:

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

Ersetzen Sie den Punkt (.) oder den Dollar ($) durch andere Zeichen, die niemals im realen Dokument verwendet werden. Und stellen Sie den Punkt (.) oder den Dollar ($) wieder her, wenn Sie das Dokument abrufen. Die Strategie hat keinen Einfluss auf die Daten, die der Benutzer liest.

Sie können das Zeichen aus alle Zeichen auswählen.

0
Simin Jie