MongoDB dot (.) По ключевому названию
Кажется, что манго не позволяет вводить ключи с точкой (.) или знаком доллара ($), однако, когда я импортировал файл JSON, содержащий точку в нем, используя инструмент mongoimport, он работал нормально. Водитель жалуется на попытку вставить этот элемент.
Вот как выглядит документ в базе данных:
{
"_id": {
"$oid": "..."
},
"make": "saab",
"models": {
"9.7x": [
2007,
2008,
2009,
2010
]
}
}
Я делаю все это неправильно и не должен использовать хеш-карты, подобные этим с внешними данными (т.е. с моделями), или я могу как-то избежать точки? Возможно, я слишком много думаю о Javascript.
Ответы
Ответ 1
MongoDB не поддерживает ключи с точкой в них, поэтому вам придется предварительно обработать ваш JSON файл, чтобы удалить/заменить их перед импортом или вы "Я буду настраивать себя на всевозможные проблемы.
В этой проблеме нет стандартного решения, лучший подход слишком зависит от специфики ситуации. Но, если это возможно, я избегаю любого подхода кодера/декодера, поскольку вы будете продолжать неустанно выполнять неудобства, когда реструктуризация JSON предположительно будет одноразовой.
Ответ 2
Mongo docs предлагают заменить нелегальные символы, такие как $
и .
своими эквивалентами в формате unicode.
В этих ситуациях ключи должны будут заменить зарезервированные $и. персонажи. Любой символ достаточен, но рассмотрим использование эквивалентов полной ширины Unicode: U + FF04 (т.е. "$" ) И U + FF0E (т.е. "." ).
Ответ 3
Как уже упоминалось в других ответах, MongoDB не допускает $
или .
символы в качестве ключей карты из-за ограничений на имена полей. Однако, как уже упоминалось в " Операторе знака доллара", исключение этого ограничения не запрещает вам вставлять документы с такими ключами, оно просто запрещает вам обновлять или запрашивать их.
Проблема простой замены .
с [dot]
или U+FF0E
(как упоминалось в другом месте на этой странице), что происходит, когда пользователь законно хочет сохранить ключ [dot]
или U+FF0E
?
Подход, используемый драйвером Fantom afMorphia, заключается в использовании escape-последовательностей в юникоде, аналогичных 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, но описанная последовательность замены работает так же хорошо.
Ответ 4
Решение, которое я только что реализовал, что я действительно доволен, включает разделение имени и значения ключа на два отдельных поля. Таким образом, я могу держать персонажей в точности одинаковыми и не беспокоиться ни о одном из этих синтаксических кошмаров. Документ будет выглядеть так:
{
...
keyName: "domain.com",
keyValue: "unregistered",
...
}
Вы все еще можете запросить это достаточно просто, просто выполнив find
в полях keyName и keyValue.
Поэтому вместо:
db.collection.find({"domain.com":"unregistered"})
который на самом деле не работает так, как ожидалось, вы запускаете:
db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
и он вернет ожидаемый документ.
Ответ 5
Последняя стабильная версия (v3.6.1) MongoDB теперь поддерживает точки (.) В именах ключей или полей.
Имена полей могут содержать точки (.) И доллар ($).
Ответ 6
Вы можете попробовать использовать хэш в ключе вместо значения, а затем сохранить это значение в значении 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) ];
}
Ответ 7
Из MongoDB docs "the.". символ не должен появляться нигде в имени ключа ". Похоже, вам придется придумать схему кодирования или обойтись.
Ответ 8
Поздний ответ, но если вы используете 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" }
Ответ 9
Вам нужно будет избежать ключей. Поскольку кажется, что большинство людей не знают, как правильно избежать строк, выполните следующие шаги:
- выберите escape-символ (лучше всего выбрать символ, который редко используется). Например. '~'
- Чтобы сбежать, сначала замените все экземпляры escape-символа на некоторую последовательность, добавленную вашим escape-символом (например, '~' → '~ t'), затем замените любой символ или последовательность, которые вам нужно выполнить, с некоторой последовательностью, добавленной с помощью ваш побег. Например. '' → '~ p'
- Чтобы unescape, сначала удалите escape-последовательность из всего экземпляра вашей второй escape-последовательности (например, '~ p' → '.'), затем преобразуйте последовательность escape-символов в один escape-символ (например, '~ 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, '~')
}
Ответ 10
Я использую следующее экранирование в JavaScript для каждого ключа объекта:
key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')
Мне нравится, что он заменяет только $
в начале и не использует символы Unicode, которые могут быть сложными для использования в консоли. _
для меня гораздо читабельнее, чем символ юникода. Он также не заменяет один набор специальных символов ($
, .
) другим (unicode). Но правильно сбегает с традиционным \
.
Ответ 11
Не идеально, но будет работать в большинстве ситуаций: замените запрещенные символы чем-то другим. Поскольку это в ключах, эти новые символы должны быть довольно редкими.
/** 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": {}
}
}
Ответ 12
Партии Lodash позволит вам изменить
{ 'connect.sid': 's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' }
в
[ [ 'connect.sid',
's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' ] ]
используя
var newObj = _.pairs(oldObj);
Ответ 13
Вы можете сохранить его как есть и преобразовать в красивое после
Я написал этот пример в 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}
Ответ 14
Дайте мне мой совет: вы можете использовать JSON.stringify для сохранения объекта/массива, содержащего имя ключа, с точками, затем проанализировать строку Object с JSON.parse для обработки, когда получить данные из базы данных
Другое обходное решение:
Перестройте свою схему следующим образом:
key : {
"keyName": "a.b"
"value": [Array]
}
Ответ 15
Для 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);
Ответ 16
Последний MongoDB поддерживает ключи с точкой, но java MongoDB-драйвер не поддерживает. Поэтому, чтобы заставить его работать на Java, я вытащил код из github repo java-mongo-driver и внес соответствующие изменения в их функцию isValid Key, создал из него новую банку, используя ее сейчас.
Ответ 17
Замените точку (.
) Или доллар ($
) другими символами, которые никогда не будут использоваться в реальном документе. И восстановите точку (.
) Или доллар ($
) при получении документа. Стратегия не будет влиять на данные, которые пользователь читает.
Вы можете выбрать символ из всех символов.
Ответ 18
Странно то, что используя 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 обрабатывает точку (подключ), я собираюсь убедиться, что мои ключи не содержат точку.
Ответ 19
Сейчас поддерживается
MongoDb 3.6 и далее поддерживает как точки, так и доллары в именах полей. Смотрите ниже JIRA: https://jira.mongodb.org/browse/JAVA-2810
Обновление вашего Mongodb до 3. 6+ звучит как лучший путь.
Ответ 20
/home/user/anaconda3/lib/python3.6/site-packages/pymongo/collection.py
Обнаружено это в сообщениях об ошибках. Если вы используете anaconda
(найдите соответствующий файл, если нет), просто измените значение от check_keys = True
до False
в указанном выше файле. Это сработает!