Массив с уникальными значениями по всем документам одной коллекции
Ситуация:
У меня есть несколько документов одного и того же набора (счет), каждый из которых имеет атрибут типа array (string) с именем uniquestrings.
Проблема: Каждая запись в uniquestrings должна быть уникальной по всем документам в mongodb. Похоже, что MongoDB/Mongoose не предлагает такой проверки (ни addToSet¹, ни index: {unique: true} ² решить проблему). Есть ли шаблон для реструктуризации моей схемы документов, чтобы убедиться, что сам mongodb может проверить его? В настоящий момент программное обеспечение проверяет его перед обновлением документа.
например.
account {
_id: 111,
uniquestrings: ["a", "b", "c"]
}
account {
_id: 222,
uniquestrings: ["d", "e", "f"]
}
например. предотвратите account(222).uniquestrings.push("a");
, выбросив дублирующую ошибку из mongo.
¹ Уникальности в массиве недостаточно.
² Каждый элемент массива должен быть уникальным в коллекции
Update1:
Другие примеры. Затронутая запись схемы выглядит следующим образом:
var Account = new Schema({
...
uniquestrings: [{type: String, index: {unique: true}}]
...
});
Теперь при создании 4 учетных документов. Я хочу, чтобы только 1 и 2 были в порядке, и отдых должен потерпеть неудачу.
var accountModel1 = new Account.Model({uniquestrings: ["a", "b"]});
accountModel1.save(); // OK
var accountModel2 = new Account.Model({uniquestrings: ["c", "d"]});
accountModel2.save(); // OK
var accountModel3 = new Account.Model({uniquestrings: ["c", "d"]});
accountModel3.save(); // FAIL => Not unique, so far so good
var accountModel4 = new Account.Model({uniquestrings: ["X", "d"]});
accountModel4.save(); // OK => But i Want this to faile because "d" is alreay in use.
Ответы
Ответ 1
Возможно, возможно, если вы захотите сохранить уникальные значения в другой коллекции. Он будет структурирован следующим образом:
{ "uniquestring" : "a", "account" : 111 }
{ "uniquestring" : "b", "account" : 111 }
{ "uniquestring" : "c", "account" : 111 }
{ "uniquestring" : "d", "account" : 222 }
{ "uniquestring" : "e", "account" : 222 }
{ "uniquestring" : "f", "account" : 222 }
Я не эксперт в Mongoose, но я считаю, что вы можете определить Модели для объединения коллекций вместе с полем учетной записи здесь, ссылающимся на поле _id коллекции учетных записей.
Теперь вы можете обеспечить уникальность с помощью простого индекса:
db.uniquestrings.createIndex( { "uniquestring" : 1 } , { unique : true } )
Теперь вашему приложению потребуется немного больше работы при сохранении данных (его необходимо сохранить в коллекции уникальных объектов, а также в коллекции учетных записей), но теперь у вас есть возможность соблюдения на уровне базы данных уникальности этих по всей базе данных.
Редактирование PS приветствуется от кого-либо, у кого более подробные знания о том, как реализовать и использовать такие модели в мангусте.
Ответ 2
В соответствии с этот MongoDB Doc, не существует способа заставить MongoDB применять уникальную политику индекса в рамках одного документа, но есть способ принудительного исполнения в отдельных документах.
db.collection.createIndex("a.b");
... обеспечит уникальность для них на a.b
...
db.collection.insert({ a: [{b: 1}] });
db.collection.insert({ a: [{b: 1}] });
... но не будет обеспечивать уникальность для этого...
db.collection.insert({ a: [{b: 1},{b: 1}] ]);
... НО, если вы строго используете $addToSet
с индексом...
db.collection.upsert({ $addToSet: { a: { b: 1 } } });
... и вы ставите компромисс, не создавая исключение, а скорее upsert спокойно игнорирует дубликат, который не то, что вы хотите, но ближе.
До сих пор мы рассмотрели то, что ответили в другом вопросе SO, но продолжайте читать, и, возможно, вы получите то, что вам нужно.
Теперь, чтобы добиться того, что ваш запрос с помощью собственных запросов MongoDB невозможен из коробки, но вы могли бы ensureIndex
и использовать охватываемый запрос для поиска индексированного массива и выкидывания ошибки, если вы его найдете, в противном случае upsert, как указано выше.
Итак...
// Ensure index
db.collection.createIndex({ 'a.b': 1 });
// Test for existence and throws up if not unique
function insertUnique(newVal) {
var exists = db.collection.find({'a.b': newVal});
if (exists) {
throw "Element is not unique in the collection: " + newVal;
} else {
db.collection.upsert({ $addToSet: { a: { b: 1 } } });
}
}
// Use it later...
try {
insertUnique(1);
insertUnique(1); // it should barf
} catch (e) {
console.warn(e);
}
Наконец, в зависимости от того, какой клиент вы используете, вы можете продлить прототип (в JS) с помощью метода insertUnique
, и вскоре вы забудете, что вы не могли бы этого сделать с самого начала.