TransWikia.com

Filtrar coleccion de mongo por datos de un arreglo

Stack Overflow en español Asked by Alejandro Castellanos on December 26, 2020

estoy trabajando en un proyecto con node con mongoose como ORM y tengo la siguiente coleccion:
introducir la descripción de la imagen aquí

lo que quiero hacer es consultar todos los documentos donde el ultimo objeto del arreglo de ‘agents’ sea igual a determinado ObjectId que yo ingrese en la consulta, hay alguna manera de hacerlo con mongoose?

EDIT

si, en efecto lo que necesito es que la consulta me devuelva los documentos donde el ultimo objeto del arreglo de ‘agents’ sea igual al que yo le introduzca en la consulta, es necesario que sea el ultimo

Esta es la coleccion en texto

{
  "_id": {
    "$oid": "5edeb7a158414800295c8fa5"
  },
  "consumer": ["[email protected]"],
  "newinteractions": 1,
  "flows": [
  ],
  "agents": [{
    "0": "OjectId(5eb493a40752445d59d03a40)"
  }, {
    "1": "OjectId(5eb493a20752445d59d03a3f)"
  }],
  "currentInputInteraction": [],
  "topics": ["5e715959d02920001e9c53b9_DLUNNAS_SERVICIO_CLIENTE"],
  "project": {},
  "currentConsumer": "[email protected]",
  "currentChannel": "WHATSAPP",
  "channels": {
    "WHATSAPP": "[email protected]"
  },
  "variables": {
    "CONSUMER_NAME": {
      "label": "Nombre",
      "value": "Greyss"
    },
    "CONSUMER_PROFILE_PICTURE": {
      "label": "Imagen de perfil"
    },
    "OBTAIN_OPTION": {
      "label": "Opcion escogida en menú principal",
      "value": "2"
    },
    "SATISFACTION_LEVEL": {
      "label": "Nivel de satisfacción con equipo de servicio al cliente",
      "value": "3"
    }
  },
  "currentNode": {
    "id": "inicio",
    "type": "OPTION",
    "delay": 200,
    "text": ["¿Ahora por favor califica nuestro servicio, ingresa una de las opciones? "],
    "next": "@FLOW:Dlunna"
  },
  "currentFlow": {
    "$oid": "5e7448ee65100a002c9e9736"
  },
  "createdAt": {
    "$date": "2020-06-08T22:11:45.381Z"
  },
  "updatedAt": {
    "$date": "2020-07-27T22:12:18.079Z"
  },
  "__v": 7,
  "flowResponse": {
    "5e72f7a033880500294e9c64": {
      "dlunna_init": "2"
    },
    "5e715980d02920001e9c53ba": {
      "inicio": "3"
    }
  },
  "currentTopic": "5e715959d02920001e9c53b9_DLUNNAS_SERVICIO_CLIENTE",
  "currentAgent": {},
  "status": "RESOLVED"
}

a lo que he llegado es

let conversations = await Conversation.find({
  agents: {
    $slice: -1
  }
}, {
  $or: [{
      status: SystemConversationStatus.RESOLVED
    },
    {
      status: SystemConversationStatus.AWATING_RESPONSE
    },
  ]
}).select("currentChannel currentConsumer currentTopic newinteractions project status updatedAt variables")

se que de esta manera solo obtendria el ultimo valor del arreglo, pero no se como hacer la comparacion

One Answer

PROBLEMA

Se desea filtrar una colección en MongoDB de tal forma que la consulta realizada devuelva todos los documentos que teniendo un campo de tipo Array, contengan en dicho Array un documento que satisfaga las siguientes 2 condiciones:

  1. El elemento tiene un campo _id que debe corresponder con el valor pasado en la consulta.
  2. El elemento cuyo campo _id corresponde al valor pasado en la consulta debe ser el último elemento de la lista. (Ocupa la última posición en el Array).

SOLUCIÓN

La solución es simple y tenemos al menos 2 opciones, usando una consulta (query) sencilla y luego filtrar el resultado o usando una consulta que me devuelva el resultado directamente desde la Base de Datos

Usando una query y filtrando

Puedo pedir en mi consulta todos los documentos que contengan en el campo agents al menos 1 documento cuyo campo _id coincida con el que estoy buscando. Luego, una vez recibidos todos los datos, puedo filtrar para quedarme solamente con aquellos que tengan como último elemento de la lista el _id buscado.

Una consulta sencilla sería algo parecida a esta:

// el valor _id que deseo que aparezca en el campo agents:
const filterId = '5eb493a20752445d59d03a3f';
// mi documento de consulta para el método find
const query = {
  'agents._id': mongoose.Types.ObjectId(filterId),
  //... otras condiciones o filtros
}

// el resultado me garantiza elementos que contengan en su campo agents elementos
// con el id buscado, ahora puedo filtrar para quedarme sólo con los que tienen
// como último elemento el id buscado.

Conversation.find(query)
.then(results => {
  let filtered = results.filter(item => {
    return item.agents[item.agents.length - 1]._id.toString() === filterId; // <- condición: último elemento de la lista coincide con fiterId.
  });
  console.log(filtered.agents); // muestra elementos cuyo último valor es el _id deseado
})
.catch(errorHandler);

Como se aprecia en el código, hago uso del método toString() del tipo ObjectId. La razón es muy sencilla, el valor de filterId es un tipo String y el valor del campo _id devuelto por la consulta es de tipo ObjectId. La comparación siempre daría false si no hago la conversión de tipo (casting) adecuada. En este caso obtengo el valor del campo _id como un tipo String y lo comparo con filterId.

También hago la conversión de tipo adecuada para pasar el valor en el documento de consulta:

'agents._id': mongoose.Types.ObjectId(filterId)

ya que Mongoose no hará la conversión por nosotros. Si no hacemos esta conversión, no se devolverán resultados, ya que la comparación de String con ObjectId siempre dará false.

De esta forma, los documentos obtenidos luego de aplicar el filtro son los que cumplen con las condiciones establecidas.

Usando una consulta directa

Podemos apoyarnos en las capacidades de la base de datos para filtrar los resultados durante la consulta. Para ello haremos uso del operador posicional $ usado en un documento de proyección de nuestra consulta find().

Nuestro documento de proyección sería el siguiente:

const projection = {
  'agents.$': -1
}

En este caso indico que deseo realizar una comparación con el último elemento de la lista. Las listas (array) en MongoDB se enumeran desde el valor 0. Sin embargo, usar un índice negativo indica que deseamos recorrerla en orden inverso. Por lo tanto -1 indica el último elemento de la lista, y -2 sería el penúltimo y así sucesivamente.

Ahora la consulta sería parecida a la siguiente:

const filterId = '5eb493a20752445d59d03a3f';
const query = {
  'agents._id': mongoose.Types.ObjectId(filterId),
  // otros filtros
}
const projection = {
  'agents.$': -1
}

Conversation.find(query, projection)
.then(results => {
  console.log(results); // <- Sólo tendrá documentos que contengan en la última posición del campo agents el _id buscado
  //...
})
.catch(errorHandler);

Espero que esto te ayude a resolver el problema.

Answered by Mauricio Contreras on December 26, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP