Tampaknya mongo tidak mengizinkan penyisipan kunci dengan titik (.) Atau tanda dolar ($) namun ketika saya mengimpor file JSON yang berisi titik di dalamnya menggunakan alat mongoimport, itu berfungsi dengan baik. Pengemudi mengeluh tentang mencoba memasukkan elemen itu.
Seperti inilah tampilan dokumen di database:
{
"_id": {
"$oid": "..."
},
"make": "saab",
"models": {
"9.7x": [
2007,
2008,
2009,
2010
]
}
}
Apakah saya melakukan ini semua salah dan seharusnya tidak menggunakan peta hash seperti itu dengan data eksternal (yaitu model) atau dapatkah saya melarikan diri dari titik entah bagaimana? Mungkin saya terlalu banyak berpikir seperti Javascript.
javascript
mongodb
nosql
Michael Yagudaev
sumber
sumber
Jawaban:
MongoDB tidak mendukung kunci dengan titik di dalamnya sehingga Anda harus memproses file JSON Anda untuk menghapus / menggantinya sebelum mengimpornya atau Anda akan menyiapkan diri untuk segala macam masalah.
Tidak ada solusi standar untuk masalah ini, pendekatan terbaik terlalu bergantung pada spesifik situasi. Tapi saya akan menghindari pendekatan encoder / decoder kunci jika memungkinkan karena Anda akan terus membayar ketidaknyamanan itu untuk selamanya, di mana restrukturisasi JSON mungkin akan menjadi biaya satu kali.
sumber
models
sini), dan (c) kita tidak perlu menanyakannya dengan nama kunci di Mongo. Jadi pola yang sayaJSON.stringify
tetapkan adalah bidang ini di simpan, dan 'JSON.parse` saat diambil.Seperti yang disebutkan dalam jawaban lain, MongoDB tidak mengizinkan
$
atau.
karakter sebagai kunci peta karena batasan pada nama bidang . Namun, seperti yang disebutkan dalam Dollar Sign Operator Escaping pembatasan ini tidak mencegah Anda memasukkan dokumen dengan kunci seperti itu, ini hanya mencegah Anda untuk memperbarui atau menanyakannya.Masalah hanya
.
dengan mengganti dengan[dot]
atauU+FF0E
(seperti yang disebutkan di tempat lain di halaman ini) adalah, apa yang terjadi ketika pengguna secara sah ingin menyimpan kunci[dot]
atauU+FF0E
?Pendekatan yang diambil oleh pengemudi afMorphia dari Fantom , adalah dengan menggunakan urutan pelolosan unicode yang mirip dengan yang ada di Jawa, tetapi memastikan karakter pelolosan tersebut lolos terlebih dahulu. Intinya, penggantian string berikut dilakukan (*):
Penggantian terbalik dilakukan ketika kunci peta kemudian dibaca dari MongoDB.
Atau dalam kode Fantom :
Str encodeKey(Str key) { return key.replace("\\", "\\\\").replace("\$", "\\u0024").replace(".", "\\u002e") } Str decodeKey(Str key) { return key.replace("\\u002e", ".").replace("\\u0024", "\$").replace("\\\\", "\\") }
Satu-satunya saat pengguna perlu menyadari konversi tersebut adalah saat membuat kueri untuk kunci tersebut.
Mengingat umum untuk menyimpan
dotted.property.names
dalam database untuk tujuan konfigurasi, saya yakin pendekatan ini lebih disukai daripada hanya melarang semua kunci peta tersebut.(*) afMorphia sebenarnya menjalankan aturan pelolosan unicode penuh / tepat seperti yang disebutkan dalam sintaks pelolosan Unicode di Java, tetapi urutan penggantian yang dijelaskan juga berfungsi dengan baik.
sumber
//g
untuk mengganti semua kejadian dan bukan hanya yang pertama. Juga, menggunakan ekuivalen lebar penuh seperti pada jawaban Martin Konecny tampaknya merupakan ide yang bagus. Akhirnya, satu garis miring terbalik cukup untuk pengkodean.key.replace(/\./g, '\uff0e').replace(/\$/g, '\uff04').replace(/\\/g, '\uff3c')
U+FF04
.Dokumen Mongo menyarankan untuk mengganti karakter ilegal seperti
$
dan.
dengan padanan unicode mereka.sumber
db.test.insert({"field\uff0ename": "test"})
Versi stabil terbaru (v3.6.1) dari MongoDB mendukung titik (.) Di kunci atau nama kolom sekarang.
Nama bidang sekarang dapat berisi titik (.) Dan karakter dolar ($)
sumber
mongoClient.getDatabase("mydb").getCollection("test").insertOne(new Document("value", new Document("key.with.dots", "value").append("$dollar", "value")));
Gagal menggunakan mongodb-driver.3.6.3 dan MongoDB 3.6.3.mongodb-4.1.1
danpymongo-3.7.1
. Saya dapat menambahkan dokumen yang berisi kunci dengan.
dengan robomongo tetapi tidak daripymongo
, itu sill menimbulkanInvalidDocument: key '1.1' must not contain '.'
Berharap itu telah diperbaiki sekarang ...Solusi yang baru saja saya terapkan yang membuat saya sangat senang melibatkan pemisahan nama dan nilai kunci menjadi dua bidang terpisah. Dengan cara ini, saya dapat menyimpan karakter yang persis sama, dan tidak khawatir tentang mimpi buruk penguraian itu. Dokumen tersebut akan terlihat seperti:
{ ... keyName: "domain.com", keyValue: "unregistered", ... }
Anda masih bisa membuat kueri ini cukup mudah, hanya dengan melakukan
find
di bidang keyName dan keyValue .Jadi, alih-alih:
db.collection.find({"domain.com":"unregistered"})
yang sebenarnya tidak berfungsi seperti yang diharapkan, Anda akan menjalankan:
db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
dan itu akan mengembalikan dokumen yang diharapkan.
sumber
Anda dapat mencoba menggunakan hash di kunci alih-alih nilainya, lalu simpan nilai itu dalam nilai 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 ] }
Anda kemudian akan mengakses model menggunakan hash nanti.
var version = "9.7x"; collection.find( { _id : ...}, function(e, data ) { var models = data.models[ md5(version) ]; }
sumber
Ini didukung sekarang
MongoDb 3.6 dan seterusnya mendukung titik dan dolar dalam nama bidang. Lihat JIRA di bawah: https://jira.mongodb.org/browse/JAVA-2810
Mengupgrade Mongodb Anda menjadi 3.6+ terdengar seperti cara terbaik untuk melakukannya.
sumber
Dari dokumen MongoDB "the '.' karakter tidak boleh muncul di mana pun dalam nama kunci ". Sepertinya Anda harus membuat skema encoding atau tanpa skema.
sumber
Anda harus melepaskan kunci. Karena tampaknya kebanyakan orang tidak tahu cara melepaskan string dengan benar, inilah langkah-langkahnya:
Juga, ingat bahwa mongo juga tidak mengizinkan kunci untuk dimulai dengan '$', jadi Anda harus melakukan sesuatu yang serupa di sana
Berikut beberapa kode yang melakukannya:
// 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, '~') }
sumber
Jawaban yang terlambat, tetapi jika Anda menggunakan Spring dan Mongo, Spring dapat mengatur konversi untuk Anda dengan
MappingMongoConverter
. Ini solusinya oleh JohnnyHK tetapi ditangani oleh Spring.@Autowired private MappingMongoConverter converter; @PostConstruct public void configureMongo() { converter.setMapKeyDotReplacement("xxx"); }
Jika Json Anda yang disimpan adalah:
{ "axxxb" : "value" }
Melalui Spring (MongoClient) akan dibaca sebagai:
{ "a.b" : "value" }
sumber
Saya menggunakan escaping berikut di JavaScript untuk setiap kunci objek:
key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')
Yang saya suka dari ini adalah ia hanya menggantikan
$
di awal, dan tidak menggunakan karakter unicode yang bisa rumit untuk digunakan di konsol._
bagi saya jauh lebih mudah dibaca daripada karakter unicode. Ini juga tidak mengganti satu set karakter khusus ($
,.
) dengan yang lain (unicode). Tapi lolos dengan benar dengan tradisional\
.sumber
Tidak sempurna, tetapi akan berfungsi dalam banyak situasi: gantilah karakter terlarang dengan yang lain. Karena ada dalam kunci, karakter baru ini seharusnya cukup langka.
/** 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 }
Ini tesnya:
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) )
dan hasilnya - perhatikan bahwa nilainya tidak diubah:
{ 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": {} } }
sumber
Ada beberapa cara buruk untuk membuat kueri yang tidak disarankan untuk digunakan dalam aplikasi daripada untuk tujuan debug (hanya berfungsi pada objek yang disematkan):
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 ])
sumber
Seperti yang disebutkan pengguna lain, encoding / decoding ini dapat menjadi masalah di masa mendatang, jadi mungkin lebih mudah untuk mengganti semua kunci yang memiliki titik. Ini adalah fungsi rekursif yang saya buat untuk mengganti kunci dengan '.' kejadian:
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)
Anda juga dapat mengubah kode ini untuk mengganti '$', karena itu adalah karakter lain yang tidak diizinkan mongo dalam sebuah kunci.
sumber
Untuk PHP saya mengganti nilai HTML untuk periode tersebut. Begitulah
"."
.Ini menyimpan di MongoDB seperti ini:
"validations" : { "4e25adbb1b0a55400e030000" : { "associate" : "true" }, "4e25adb11b0a55400e010000" : { "associate" : "true" } }
dan kode PHP ...
$entry = array('associate' => $associate); $data = array( '$set' => array( 'validations.' . str_replace(".", `"."`, $validation) => $entry )); $newstatus = $collection->update($key, $data, $options);
sumber
Pasangan Lodash akan memungkinkan Anda untuk berubah
{ 'connect.sid': 's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' }
ke
[ [ 'connect.sid', 's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' ] ]
menggunakan
var newObj = _.pairs(oldObj);
sumber
Anda dapat menyimpannya apa adanya dan mengubahnya menjadi cantik setelahnya
Saya menulis contoh ini di Livescript. Anda dapat menggunakan situs web Livescript.net untuk mengevaluasi itu
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
Itu akan menghasilkan
{"field.field1":1, "field.field2":2, "field.field3":5, "field.nested.more":1, "field.nested.moresdafasdf":23423, "field3":3}
sumber
Beri Anda tip saya: Anda dapat menggunakan JSON.stringify untuk menyimpan Objek / Array berisi nama kunci memiliki titik, lalu parsing string ke Objek dengan JSON.parse untuk diproses saat mendapatkan data dari database
Solusi lain: Atur ulang skema Anda seperti:
key : { "keyName": "a.b" "value": [Array] }
sumber
MongoDB terbaru mendukung kunci dengan titik, tetapi java MongoDB-driver tidak mendukung. Jadi untuk membuatnya bekerja di Java, saya menarik kode dari github repo java-mongo-driver dan membuat perubahan sesuai dengan fungsi kunci isValid mereka, membuat jar baru darinya, menggunakannya sekarang.
sumber
Ganti titik (
.
) atau dolar ($
) dengan karakter lain yang tidak akan pernah digunakan dalam dokumen sebenarnya. Dan kembalikan titik (.
) atau dolar ($
) saat mengambil dokumen. Strategi tersebut tidak akan memengaruhi data yang dibaca pengguna.Anda dapat memilih karakter dari semua karakter .
sumber
Anehnya, dengan menggunakan mongojs, saya dapat membuat dokumen dengan titik jika saya menyetel _id sendiri, namun saya tidak dapat membuat dokumen saat _id dibuat:
Berhasil:
db.testcollection.save({"_id": "testdocument", "dot.ted.": "value"}, (err, res) => { console.log(err, res); });
Tidak bekerja:
db.testcollection.save({"dot.ted": "value"}, (err, res) => { console.log(err, res); });
Saya pertama kali berpikir bahwa memperbarui dokumen dengan kunci titik juga berfungsi, tetapi itu mengidentifikasi titik sebagai subkunci!
Melihat bagaimana mongojs menangani titik (subkunci), saya akan memastikan kunci saya tidak mengandung titik.
sumber
Seperti yang @JohnnyHK sebutkan, hapus tanda baca atau '.' dari kunci Anda karena ini akan menciptakan masalah yang jauh lebih besar ketika data Anda mulai terakumulasi menjadi kumpulan data yang lebih besar. Ini akan menyebabkan masalah terutama ketika Anda memanggil operator agregat seperti $ merge yang memerlukan akses dan perbandingan kunci yang akan menimbulkan kesalahan. Saya telah mempelajarinya dengan cara yang sulit, tolong jangan ulangi untuk mereka yang baru memulai.
sumber
/home/user/anaconda3/lib/python3.6/site-packages/pymongo/collection.py
Ditemukan dalam pesan kesalahan. Jika Anda menggunakan
anaconda
(temukan file koresponden jika tidak), cukup ubah nilainya daricheck_keys = True
menjadiFalse
dalam file yang disebutkan di atas. Itu akan berhasil!sumber