Могу ли я использовать String в качестве типа идентификатора для документа mongodb?

Я использую java/morphia для работы с mongodb. ObjectId по умолчанию не очень удобно использовать на уровне Java. Я хотел бы сделать его типом String, сохраняя при этом процесс генерации ключа с использованием ObjectId, скажем, _id = new ObjectId.toString().

Я хочу знать, есть ли какие-либо побочные эффекты, делая это таким образом? Например, повлияет ли это на производительность базы данных или каким-либо образом вызовет конфликты ключей? Повлияет ли это на среду шардинга...


person Gelin Luo    schedule 18.06.2012    source источник
comment
Можете ли вы объяснить, почему ObjectId неудобен? Вы можете легко воссоздать его из строки, например id= new ObjectId(str).   -  person Nic Cottrell    schedule 20.06.2012
comment
String — это тип, используемый в каждой программе Java, а ObjectId — нет. Я не хочу вводить новый тип в другие компоненты, использующие мою библиотеку. вероятно, потребуется импортировать библиотеки morphia, mongodb и bson, все же лучше, если люди, использующие мою библиотеку, прозрачны для типов mongodb, включая ObjectId   -  person Gelin Luo    schedule 21.06.2012
comment
Хм... но если ваша библиотека хранит данные в Mongo, разве mongo.jar не должен быть в пути к классам? Кроме того, существуют накладные расходы на создание и сбор мусора большого количества строк.   -  person Nic Cottrell    schedule 21.06.2012
comment
Если вы хотите получить новое строковое значение для идентификатора, используйте (new ObjectId).valueOf() вместо (new ObjectId).toString() (изменено в MongoDB версии 2.2).   -  person Mika Vatanen    schedule 12.09.2015


Ответы (5)


Вы можете использовать любой тип значения для поля _id (кроме массивов). Если вы решите не использовать ObjectId, вам придется как-то гарантировать уникальность значений (подойдет приведение ObjectId к строке). Если вы попытаетесь вставить дубликат ключа, произойдет ошибка, и вам придется с ней бороться.

Я не уверен, как это повлияет на сегментированный кластер, когда вы попытаетесь вставить два документа с одинаковым _id в разные сегменты. Я подозреваю, что это позволит вам вставить, но это вас потом укусит. (Я должен буду проверить это).

Тем не менее, у вас не должно быть проблем с _id = (new ObjectId).toString().

person Sergio Tulentsev    schedule 18.06.2012
comment
Ваше второе и третье предложения противоречат друг другу. Третий правильный. Не подозревайте в ответах. Кроме того, _ids может содержать значение типа array. - person Dyin; 02.07.2014
comment
Как именно они противоречат друг другу? - person Sergio Tulentsev; 02.07.2014
comment
Если вы хотите получить новое строковое значение для идентификатора, используйте (new ObjectId).valueOf() вместо (new ObjectId).toString() (изменено в MongoDB версии 2.2). - person Mika Vatanen; 12.09.2015

На самом деле я сделал то же самое, потому что у меня возникла проблема с преобразованием ObjectId в JSON.

Затем я сделал что-то вроде

@Id
private String id;
public String getId() {
    return id();
}
public void setId(String id) {
    this.id = id;
}

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

Хуже того, у нового документа был тот же идентификатор, что и у ранее вставленного, что я считал невозможным.

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

@Id
private ObjectId id;
public String getId() {
    return id.toString();
}
public void setId(String id) {
    this.id = new ObjectId(id);
}
person Destino    schedule 19.01.2014

Да, вы можете использовать строку в качестве _id.

Я бы рекомендовал его, только если у вас есть какое-то значение (в документе), которое, естественно, является хорошим уникальным ключом. Я использовал этот дизайн в одной коллекции, где был строковый геотег вида «xxxxyyyy»; это уникальное для документа поле ДОЛЖНО быть в документе, и мне нужно было создать для него индекс... так почему бы не использовать его в качестве ключа? (Это позволило избежать одной дополнительной пары «ключ-значение» И избежать второго индекса в коллекции, поскольку MondoDB естественным образом строит индекс по «_id». Учитывая размер коллекции, оба из них в сумме дают серьезную экономию места.)

Однако, судя по тону вашего вопроса («Идентификаторы объектов не очень удобны»), если единственная причина, по которой вы хотите использовать строку, заключается в том, что вы не хотите заморачиваться выяснением того, как аккуратно управлять идентификаторами объектов... Я' d предполагают, что стоит потратить время на то, чтобы обдумать их. Я уверен, что они не доставят вам хлопот... как только вы разберетесь с ними.

В противном случае: каковы ваши варианты? Будете ли вы придумывать строковые идентификаторы КАЖДЫЙ РАЗ, когда будете использовать MongoDB в будущем?

person Dan H    schedule 02.02.2016
comment
Эй, спасибо, что вернулись к моему вопросу, заданному почти четыре года назад. Думаю, с тех пор я стал лучше понимать MongoDB. И теперь я считаю, что хранить ObjectId в виде строки — действительно плохая идея, потому что ObjectId — это буквально три целых числа, а его строковое представление — 24 символа, что неэффективно с точки зрения пространства и времени. - person Gelin Luo; 03.02.2016

Вы также можете использовать строку в качестве идентификатора из Java. Вот пример метода и соответствующий модульный тест, вставляющий один объект со строковым идентификатором в коллекцию MongoDB:

public Document insert(String json, String collectionName) {
    MongoCollection<Document> collection = database.getCollection(collectionName);
    BasicDBObject document = BasicDBObject.parse(json);
    Document doc = new Document(document);
    collection.insertOne(doc);
    return doc;
}

@Test
void whenInsert_ShouldInsertOne() {
    final String uuid = UUID.randomUUID().toString();
    final String collection = "test_collection";
    final Document doc = app.insert(String.format("{\"_id\":\"%s\", \"name\": \"test\"}", 
        uuid), collection);
    assertThat(doc).isNotNull();
    final String json = app.getById(uuid, collection);
    assertThat(json).contains(String.format("\"_id\": \"%s\"", uuid));
}
person gil.fernandes    schedule 20.07.2020

Я хотел бы добавить, что не всегда хорошая идея использовать автоматически сгенерированный BSON ObjectID в качестве уникального идентификатора, если он передается приложению: пользователь потенциально может манипулировать им.

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

ОБНОВЛЕНИЕ: начиная с версии 3.4+ идентификаторы объектов больше не генерируются постепенно. См. документы 3.2 и последние документы

Таким образом, использование идентификаторов типа UUID обеспечит уровень безопасности через неясность. Конечно, авторизация (разрешен ли этому пользователю доступ к запрашиваемому ресурсу) обязательна, но вы должны знать о вышеупомянутой функции ObjectID.

Чтобы получить лучшее из обоих миров, сгенерируйте UUID, который соответствует длине вашего ObjectID (12 или 24 символа), и используйте его для создания собственного _id типа ObjectID.

person Tony Sepia    schedule 31.10.2016
comment
если вы не сможете реализовать необходимую авторизацию, вы все равно обречены. Неизвестность — это не безопасность. Только обманывай себя, что мы в безопасности. - person Piohen; 14.02.2017
comment
Таким образом, использование идентификаторов типа UUID обеспечит уровень безопасности через неясность. Фактически это пароль для каждого документа. - person GAllan; 16.05.2019
comment
-1, потому что ObjectID НЕ генерируются последовательно, даже если они могут показаться на первый взгляд. Они включают метку времени, случайное значение, а затем счетчик docs.mongodb.com/manual/reference. /метод/Идентификатор_объекта - person Dushyant Bangal; 17.02.2021
comment
@DushyantBangal это немного грубо, так как ответ был дан, когда версия 3.4 еще не была выпущена. Ответ был правильным для этой версии. Лучше было бы выделить изменение и продвигать ответ для обновления (тем более, что эта незначительная деталь не меняет сути ответа). - person Tony Sepia; 17.02.2021
comment
@TonySepia у него все еще было 4-байтовое значение, представляющее секунды с эпохи Unix, так что оно все еще не было последовательным в том смысле, в котором вы сказали, что пользователь может просто увеличить значение, которое у него есть. Прошу прощения, если сочту это грубым, но обычно люди даже не оправдывают -1 и не дают кому-то шанс исправить ответ. - person Dushyant Bangal; 03.03.2021
comment
Также это полностью меняет суть ответа. Совершенно нормально использовать ObjectId в качестве уникального идентификатора, тогда как вы говорите, что это не так. Ответ даже не касается вопроса ОП - могут ли они использовать строку и будет ли это иметь какой-либо побочный эффект производительности. - person Dushyant Bangal; 03.03.2021