Hace no mucho, estaba trabajando en un proyecto donde necesitábamos encontrar documentos en MongoDB que tuvieran un array con más de un elemento. Una necesidad súper común, la verdad. La primera reacción de muchos es irse por la solución más obvia y, de hecho, la que más te aparece si buscas rápido: usar $where. Pero, en mi experiencia, eso es pedirle peras al olmo si quieres rendimiento.
Pucha que me frustra cuando veo que se sacrifica performance por una supuesta "flexibilidad" que en realidad es pereza de buscar una solución nativa. El problema con $where es que ejecuta JavaScript en el servidor y eso es lento, no aprovecha índices y no escala bien. Si tienes miles o millones de documentos, te va a doler.
La forma eficiente: Indexado de arrays
Cuando me topé con este problema, sabía que tenía que haber una forma mejor. Y la hay, una que es mucho más elegante y performante, aprovechando cómo MongoDB maneja los índices y los arrays. Se trata de consultar la existencia de un elemento en una posición específica del array.
Si quieres saber si un array name tiene al menos dos elementos, basta con chequear si existe el elemento en el índice 1 (recordemos que los arrays son 0-indexed). Es una solución que me costó un poco descubrir, pero una vez que la tienes clara, te ahorra muchos dolores de cabeza.
// Encuentra todos los documentos que tienen al menos dos elementos en el array 'name'
db.accommodations.find({'name.1': {$exists: true}})
Lo mejor de todo es que puedes optimizar esta consulta creando un índice. Para versiones de MongoDB 3.2+, puedes incluso usar un partialFilterExpression para que el índice sea más pequeño y eficiente, cubriendo solo los documentos que realmente nos importan:
// Índice para documentos con al menos dos elementos en el array 'name'
db.accommodations.createIndex(
{'name.1': 1},
{partialFilterExpression: {'name.1': {$exists: true}}}
);
¿Y qué de otras opciones?
De hecho, he visto otra solución que, si bien funciona, no me convence tanto por la sobreingeniería: añadir un campo extra al documento que almacene la longitud del array. Así, puedes indexar ese campo y consultarlo directamente. Por ejemplo, si tienes un campo NamesArrayLength:
db.accommodations.find({"NamesArrayLength": {$gt: 1} });
Sí, esto sería rápido porque se puede indexar, pero implica que cada vez que modifiques el array name, tienes que acordarte de actualizar NamesArrayLength. Es un riesgo de inconsistencia y una carga extra en las operaciones de escritura que, francamente, no siempre vale la pena.
En mi opinión, la primera opción, usando 'name.1': {$exists: true}, es la más limpia y eficiente para la mayoría de los casos. Aprovecha las capacidades nativas de MongoDB y se integra bien con los índices. Es simple, directa y funciona. El error que veo constantemente es que la gente se va por la solución que parece más fácil a primera vista (como $where) sin entender el impacto real en el rendimiento. Ojo, que lo "flexible" no siempre es lo mejor en producción. Siempre hay que buscar la forma más nativa y performante que el motor de base de datos nos ofrece.