que - MongoDB: índice único en la propiedad del elemento de matriz
mongodb español (4)
Tengo una estructura similar a esta:
class Cat {
int id;
List<Kitten> kittens;
}
class Kitten {
int id;
}
Me gustaría evitar que los usuarios creen un gato con más de un gatito con la misma identificación. He intentado crear un índice de la siguiente manera:
db.Cats.ensureIndex({''id'': 1, ''kittens.id'': 1}, {unique:true})
Pero cuando intento insertar un gato mal formateado, Mongo lo acepta.
¿Me estoy perdiendo de algo? ¿Puede esto siquiera estar terminado?
Hay un equivalente de insertar con uniquness en atributo de matriz. El siguiente comando esencialmente se inserta al tiempo que garantiza la singularidad de los gatitos (upsert lo crea para usted si el objeto con 123 ya no existe).
db.cats.update(
{ id: 123 },
{ $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {''otherfields'': ''extraval'', "field2": "value2"}},
{ upsert: true}
)
El valor resultante del objeto será
{
"id": 123,
"kittens": [456],
"otherfields": "extraval",
"field2": "value2"
}
Por lo que sé, los índices únicos solo imponen la singularidad en diferentes documentos, por lo que esto generaría un error de clave duplicado:
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
Pero esto está permitido:
db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )
No estoy seguro de si hay alguna forma de imponer la restricción que necesita en el nivel de Mongo, tal vez sea algo que pueda verificar en la lógica de la aplicación cuando inserte la actualización.
Puede escribir un método de validación de Mongoose personalizado en este caso. Puedes enganchar en post validación. Mongoose tiene validación y puede implementar un gancho antes (pre) o después (post) de validación. En este caso, puede usar la validación posterior para ver si la matriz es válida. Entonces solo asegúrate de que la matriz no tenga duplicaciones. Puede haber mejoras en la eficiencia que puede realizar en función de sus detalles. Si solo tiene ''_id'', por ejemplo, podría usar la función JS includes.
catSchema.post(''validate'',function(next) {
return new Promise((resolve,reject) => {
for(var i = 0; i < this.kittens.length; i++) {
let kitten = this.kittens[i];
for(var p = 0; p < this.kittens.length; p++) {
if (p == i) {
continue;
}
if (kitten._id == this.kittens[p]._id) {
return reject(''Duplicate Kitten Ids not allowed'');
}
}
}
return resolve();
});
});
Me gusta usar promesas en la validación porque es más fácil especificar errores.
Asegurar la singularidad de los valores individuales en un campo de matriz
Además del ejemplo anterior, hay una función en MongoDB para garantizar que cuando agrega un nuevo objeto / valor a un campo de matriz, solo realizará la actualización si el valor / objeto aún no existe.
Así que si tienes un documento que se parece a esto:
{ _id: 123, kittens: [456] }
Esto sería permitido:
db.cats.update({_id:123}, {$push: {kittens:456}})
Resultando en
{ _id: 123, kittens: [456, 456] }
sin embargo, el uso de la función $ addToSet (a diferencia de $ push ) verificará si el valor ya existe antes de agregarlo. Entonces, comenzando con:
{ _id: 123, kittens: [456] }
luego ejecutando:
db.cats.update({_id:123}, {$addToSet: {kittens:456}})
No tendría ningún efecto.
Por lo tanto, las restricciones únicas y de larga duración no validan la singularidad dentro de los elementos de valor de un campo de matriz, solo que dos documentos no pueden tener valores idénticos en los campos indexados.