it-swarm.com.ru

MongoDB точка (.) В имени ключа

Кажется, что монго не позволяет вставлять ключи с точкой (.) Или знаком доллара ($), однако, когда я импортировал файл JSON, в котором была точка, с помощью инструмента монгоимпорта, он работал нормально. Водитель жалуется на попытку вставить этот элемент.

Вот как выглядит документ в базе данных:

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

Я все делаю неправильно и не должен использовать хэш-карты, подобные этим, с внешними данными (то есть с моделями), или я могу как-то избежать точки? Может быть, я слишком много думаю о Javascript.

52
Michael Yagudaev

MongoDB не поддерживает ключи с точкой в них, поэтому вам придется предварительно обработать файл JSON, чтобы удалить/заменить их перед импортом, иначе вы будете настраивать себя на все виды проблем.

Нет стандартного обходного пути к этой проблеме, лучший подход слишком зависит от специфики ситуации. Но я бы по возможности избегал любого подхода с использованием ключевого кодера/декодера, так как вы будете продолжать оплачивать неудобства, связанные с вечностью, когда реструктуризация JSON предположительно будет единовременной.

52
JohnnyHK

В Mongo docs предлагается заменить недопустимые символы, такие как $ и ., на их эквиваленты в Юникоде.

В этих ситуациях ключи должны будут заменить зарезервированные $ и. персонажи. Любого символа достаточно, но рассмотрите возможность использования эквивалентов полной ширины Юникода: U + FF04 (т.е. «“ ») и U + FF0E (то есть« “»).

18
Martin Konecny

Как уже упоминалось в других ответах, MongoDB не допускает использование символов $ или . в качестве ключей карты из-за ограничений на имена полей . Однако, как упоминалось в Исключение оператора знака доллара, это ограничение не запрещает вам вставку документов с такими ключами, оно просто запрещает вам обновлять или запрашивать их.

Проблема простой замены . на [dot] (как упомянуто в комментариях) состоит в том, что происходит, когда пользователь законно хочет сохранить ключ [dot]?

Подход, который использует драйвер FantomafMorphia , заключается в использовании экранирующих последовательностей Unicode, аналогичных 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 но описанная последовательность замены работает так же хорошо.

12
Steve Eynon

Вы можете попробовать использовать хеш в ключе вместо значения, а затем сохранить это значение в значении 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) ];
}
11
Henry

Решение, которое я только что реализовал и которым я действительно доволен, включает разделение имени и значения ключа на два отдельных поля. Таким образом, я могу держать персонажей в точности такими же, и не беспокоиться ни о каком из этих разбирающихся кошмаров. Документ будет выглядеть так:

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

Вы все еще можете запросить это достаточно просто, просто сделав find в полях keyName и keyValue .

Так что вместо:

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

который на самом деле не будет работать, как ожидалось, вы бы запустить:

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

и он вернет ожидаемый документ.

8
Steve

Последняя стабильная версия (v3.6.1) MongoDB теперь поддерживает точки (.) В ключах или именах полей. 

Имена полей теперь могут содержать точки (.) И символы доллара ($)

8
h4ck3d

Из документов MongoDB "the". символ не должен появляться нигде в имени ключа ". Похоже, вам придётся придумать схему кодирования или обойтись без нее.

4
maerics

Вам нужно сбежать от ключей. Поскольку кажется, что большинство людей не знают, как правильно экранировать строки, вот шаги:

  1. выбрать побег (лучше всего выбрать персонажа, который редко используется). Например. '~'
  2. Чтобы убежать, сначала замените все экземпляры escape-символа некоторой последовательностью, начинающейся с вашего escape-символа (например, '~' -> '~ t'), а затем замените любой символ или последовательность, которую вы хотите экранировать, какой-нибудь последовательностью, предварительно добавленной с вашим escape-символом. , Например. '' -> '~ p'
  3. Чтобы убежать, сначала удалите escape-последовательность из всех экземпляров вашей второй escape-последовательности (например, '~ p' -> '.'), Затем преобразуйте вашу escape-последовательность в один escape-символ (например, '~ s' -> '~ «)

Кроме того, помните, что Монго также не позволяет ключам начинаться с '$', поэтому вы должны сделать что-то подобное там

Вот код, который делает это:

// 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, он может управлять преобразованием для вас. Это решение от JohnnyHK, но обработано Spring.

@Autowired
private MappingMongoConverter converter;

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

Если ваш сохраненный Json, например,

{ "axxxb" : "value" }

Через Spring (MongoClient) это будет читаться как

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

Я использую следующее экранирование в JavaScript для каждого ключа объекта:

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

Что мне нравится в этом, так это то, что он заменяет только $ в начале и не использует символы Юникода, что может быть сложно использовать в консоли. _ для меня гораздо более читабелен, чем символ юникода. Он также не заменяет один набор специальных символов ($, .) другим (unicode). Но правильно уходит с традиционным \.

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. Вы можете использовать веб-сайт livecript.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}

0
Andrey Stehno

Последние MongoDB поддерживают ключи с точкой, но Java MongoDB-драйвер не поддерживает. Поэтому, чтобы заставить его работать в Java, я вытащил код из github repo 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);
});

Сначала я подумал, что обновление документа с помощью точечного ключа также работает, но это означает, что точка является подключом!

Видя, как mongojs обрабатывает точку (подключ), я собираюсь убедиться, что мои ключи не содержат точку.

0
Sam

пары Лодаша позволит вам изменить

{ '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 ...

  $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