Кажется, что mongo не позволяет вставлять ключи с точкой (.) Или знаком доллара ($), однако, когда я импортировал файл JSON, содержащий точку, с помощью инструмента mongoimport, он работал нормально. Драйвер жалуется на попытку вставить этот элемент.
Вот как выглядит документ в базе данных:
{
"_id": {
"$oid": "..."
},
"make": "saab",
"models": {
"9.7x": [
2007,
2008,
2009,
2010
]
}
}
Я все делаю неправильно и не должен использовать подобные хэш-карты с внешними данными (т.е. моделями), или я могу как-то избежать точки? Может, я слишком много думаю о Javascript.
javascript
mongodb
nosql
Михаил Ягудаев
источник
источник
Ответы:
MongoDB не поддерживает ключи с точкой в них, поэтому вам придется предварительно обработать файл JSON, чтобы удалить / заменить их перед импортом, иначе вы будете настраивать себя для всех видов проблем.
Стандартного решения этой проблемы нет, лучший подход слишком зависит от специфики ситуации. Но я бы по возможности избегал использования какого-либо ключевого кодировщика / декодера, поскольку вы будете продолжать платить за неудобства, связанные с этим, на неограниченный срок, когда реструктуризация JSON, вероятно, будет единовременной платой.
источник
models
здесь), и (в) нам не нужно запрашивать их по имени ключа в Mongo. Итак, шаблон, который яJSON.stringify
выбрал, - это это поле при сохранении и JSON.parse при извлечении.Как упоминалось в других ответах, MongoDB не разрешает символы
$
или в.
качестве ключей карты из-за ограничений на имена полей . Однако, как упоминалось в разделе «Оператор знака доллара», выход из этого ограничения не мешает вам вставлять документы с такими ключами, он просто не дает вам обновлять или запрашивать их.Проблема простой замены
.
на[dot]
илиU+FF0E
(как упоминалось в другом месте на этой странице) заключается в том, что происходит, когда пользователь законно хочет сохранить ключ[dot]
илиU+FF0E
?Подход, который использует драйвер afMorphia от Fantom , заключается в использовании escape-последовательностей Unicode, аналогичных Java, но с обеспечением экранирования сначала escape-символа. По сути делаются следующие замены строк (*):
Обратная замена выполняется, когда ключи карты впоследствии считываются из 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 фактически выполняет полные / правильные правила экранирования Unicode, как указано в синтаксисе экранирования Unicode в Java, но описанная последовательность замены работает так же хорошо.
источник
//g
для замены всех вхождений, а не только первого. Кроме того, использование эквивалентов полной ширины, как в ответе Мартина Конечни, кажется хорошей идеей. Наконец, для кодирования достаточно одной обратной косой черты.key.replace(/\./g, '\uff0e').replace(/\$/g, '\uff04').replace(/\\/g, '\uff3c')
U+FF04
.Документы Mongo предлагают заменить недопустимые символы, такие как
$
и,.
их эквивалентами в Юникоде.источник
db.test.insert({"field\uff0ename": "test"})
Последняя стабильная версия (v3.6.1) MongoDB теперь поддерживает точки (.) В ключах или именах полей.
Теперь имена полей могут содержать символы точек (.) И доллара ($).
источник
mongoClient.getDatabase("mydb").getCollection("test").insertOne(new Document("value", new Document("key.with.dots", "value").append("$dollar", "value")));
Ошибка при использовании mongodb-driver.3.6.3 и MongoDB 3.6.3.mongodb-4.1.1
иpymongo-3.7.1
. Я могу добавлять документы, содержащие ключи, с.
помощью robomongo, но не frompymongo
, это порождаетInvalidDocument: key '1.1' must not contain '.'
желание, чтобы это было исправлено к настоящему времени ...Только что реализованное мной решение, которым я действительно доволен, включает разделение имени и значения ключа на два отдельных поля. Таким образом, я могу сохранить персонажей в точности такими же и не беспокоиться ни о каких кошмарах. Документ будет выглядеть так:
{ ... keyName: "domain.com", keyValue: "unregistered", ... }
Вы все еще можете запросить это достаточно просто, просто выполнив a
find
в полях keyName и keyValue .Так что вместо:
db.collection.find({"domain.com":"unregistered"})
что на самом деле не будет работать должным образом, вы запустите:
db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
и он вернет ожидаемый документ.
источник
Вы можете попробовать использовать хэш в ключе вместо значения, а затем сохранить это значение в значении 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 3.6 и более поздних версий поддерживает как точки, так и доллар в именах полей. См. Ниже JIRA: https://jira.mongodb.org/browse/JAVA-2810
Обновление Mongodb до версии 3.6+ - лучший вариант.
источник
Из документации MongoDB "." символ не должен появляться в имени ключа ". Похоже, вам придется придумать схему кодирования или обойтись без нее.
источник
Вам нужно будет сбежать от ключей. Поскольку кажется, что большинство людей не знают, как правильно экранировать строки, вот шаги:
Также помните, что 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" }
источник
Я использую следующие экранирования в JavaScript для каждого ключа объекта:
key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')
Что мне нравится в нем, так это то, что он заменяет только
$
в начале и не использует символы Unicode, которые может быть сложно использовать в консоли._
для меня гораздо более читабелен, чем символ Юникода. Он также не заменяет один набор специальных символов ($
,.
) другим (unicode). Но как следует ускользает от традиционного\
.источник
Не идеально, но сработает в большинстве ситуаций: замените запрещенные символы чем-то другим. Поскольку он находится в ключах, эти новые символы должны быть довольно редкими.
/** 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.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)
Вы также можете изменить этот код, заменив '$', так как это еще один символ, который mongo не допускает в ключе.
источник
Для 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);
источник
Вы можете сохранить его как есть и преобразовать в красивый после
Я написал этот пример на Livescript. Вы можете использовать веб-сайт livescript.net для его оценки
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}
источник
Дам вам мой совет: вы можете использовать JSON.stringify для сохранения объекта / массива, содержащего имя ключа, имеющего точки, затем проанализируйте строку для объекта с помощью JSON.parse для обработки при получении данных из базы данных
Другой обходной путь: измените структуру схемы, например:
key : { "keyName": "a.b" "value": [Array] }
источник
Последняя версия MongoDB поддерживает ключи с точкой, но java-драйвер MongoDB не поддерживает. Чтобы заставить его работать на Java, я вытащил код из репозитория github java-mongo-driver и внес соответствующие изменения в их функцию isValid Key, создал из него новую банку, используя ее сейчас.
источник
Замените точку (
.
) или доллар ($
) другими символами, которые никогда не будут использоваться в реальном документе. И восстановите точку (.
) или доллар ($
) при извлечении документа. Стратегия не повлияет на данные, которые читает пользователь.Вы можете выбрать персонажа из всех персонажей .
источник
Как ни странно, используя 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); });
Сначала я подумал, что обновление документа с помощью ключа с точкой тоже работает, но это определение точки как подключ!
Увидев, как mongojs обрабатывает точку (подключ), я собираюсь убедиться, что мои ключи не содержат точки.
источник
Как и то, что упомянул @JohnnyHK , удалите знаки препинания или "." из ваших ключей, потому что это создаст гораздо более серьезные проблемы, когда ваши данные начнут накапливаться в больший набор данных. Это вызовет проблемы, особенно когда вы вызываете агрегатные операторы, такие как $ merge, который требует доступа и сравнения ключей, что вызывает ошибку. Я усвоил это на собственном горьком опыте, пожалуйста, не повторяйте для тех, кто только начинает.
источник
/home/user/anaconda3/lib/python3.6/site-packages/pymongo/collection.py
Нашел в сообщениях об ошибках. Если вы используете
anaconda
(найдите соответствующий файл, если нет), просто измените значение сcheck_keys = True
наFalse
в файле, указанном выше. Это сработает!источник