una - que es mongodb
matriz con valores únicos sobre todos los documentos de una colección (2)
Situación: Tengo varios documentos de la misma colección (cuenta), cada uno tiene un atributo de tipo array (string) llamado uniquestrings.
Problema: cada entrada en uniquestrings debe ser única sobre todos los documentos en mongodb. Parece que MongoDB / Mongoose no ofrece dicha validación (ni addToSet¹ ni index: {unique: true} ² resuelven el problema). ¿Existe un patrón para reestructurar mi esquema de documentos para asegurarme de que mongodb pueda validarlo? Por el momento, el software lo verifica antes de actualizar el documento.
P.ej
account {
_id: 111,
uniquestrings: ["a", "b", "c"]
}
account {
_id: 222,
uniquestrings: ["d", "e", "f"]
}
Por ejemplo, prevenir account(222).uniquestrings.push("a");
arrojando un error duplicado de mongo.
¹ La singularidad en una matriz no es suficiente
² Cada elemento en conjunto debe ser único en toda la colección
ACTUALIZACIÓN1:
Más ejemplos. La entrada del esquema afectado se ve así:
var Account = new Schema({
...
uniquestrings: [{type: String, index: {unique: true}}]
...
});
Ahora cuando creas 4 documentos de cuenta. Solo quiero que 1 y 2 estén bien, y el resto debería fallar.
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.
Podría ser posible, si está dispuesto a almacenar los valores únicos en una colección diferente . Se estructuraría así:
{ "uniquestring" : "a", "account" : 111 }
{ "uniquestring" : "b", "account" : 111 }
{ "uniquestring" : "c", "account" : 111 }
{ "uniquestring" : "d", "account" : 222 }
{ "uniquestring" : "e", "account" : 222 }
{ "uniquestring" : "f", "account" : 222 }
No soy un experto con Mongoose, pero creo que puede definir Modelos para vincular colecciones, con el campo de la cuenta aquí haciendo referencia al campo _id de la colección de cuentas.
Ahora puede aplicar la singularidad con un índice directo:
db.uniquestrings.createIndex( { "uniquestring" : 1 } , { unique : true } )
Ahora, su aplicación tendrá un poco más de trabajo que hacer al guardar los datos (debe guardarse en la colección uniquestrings así como en la colección de cuentas), pero ahora tiene la aplicación a nivel de base de datos de la singularidad de estas cadenas, a través la base de datos.
Las ediciones de PD son bienvenidas por cualquier persona con un conocimiento más detallado de cómo implementar y usar dichos modelos en mangosta.
De acuerdo con este MongoDB Doc , no hay forma de forzar a MongoDB a aplicar una política de índice única dentro de un solo documento, pero hay una manera de aplicarlo en documentos separados .
db.collection.createIndex("a.b");
... impondrá unicidad para estos en ab
...
db.collection.insert({ a: [{b: 1}] });
db.collection.insert({ a: [{b: 1}] });
... pero no hará cumplir la singularidad para esto ...
db.collection.insert({ a: [{b: 1},{b: 1}] ]);
... PERO si usas estrictamente $addToSet
con el índice ...
db.collection.upsert({ $addToSet: { a: { b: 1 } } });
... y te comprometes al no tener una excepción, sino que el upsert ignora silenciosamente el duplicado, que no es lo que quieres sino que está más cerca.
Hasta ahora, hemos cubierto lo que se respondió en otra pregunta de SO , pero sigue leyendo y tal vez obtendrás lo que buscas.
Ahora, para lograr lo que pides con las solicitudes nativas de MongoDB no es posible de ensureIndex
, puedes ensureIndex
y usar una consulta cubierta para buscar la matriz indexada y lanzar un error si la encuentras, de lo contrario, resérvalo como se indicó anteriormente.
Asi que...
// 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);
}
Por último, dependiendo del cliente que use, puede extender el prototipo (en JS) con el método insertUnique
, y pronto se olvidará de que no podría hacer esto para comenzar.