Sequelize query con una cláusula where en un include de un include

8

Estoy luchando por crear una consulta con secuela.

Algún contexto

Tengo los siguientes modelos:

  • A Manifestationpuede tener [0..n]Event
  • Un Eventpertenece a uno Manifestation(un Eventno puede existir sin un Manifestation)
  • A Placepuede tener [0..n]Event
  • Un Eventpertenece a uno Place(un Eventno puede existir sin un Place)
  • A Manifestationpuede tener [1..n]Place
  • A Placepuede tener [0..n]Manifestation

Modelo las relaciones de la siguiente manera:

Manifestation.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Manifestation)

Place.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Place)

Manifestation.belongsToMany(Place, { through: 'manifestation_place' })
Place.belongsToMany(Manifestation, { through: 'manifestation_place' })

Para mí parece bastante correcto, pero no dudes si tienes comentarios.

La pregunta

Estoy tratando de consultar el Placepara obtener todo Manifestationy Eventsuceder en un determinado Place. Pero para los Eventque quiero incluirlos dentro de ellos, Manifestationincluso si Manifestationno sucede en el dado Place.

A continuación se muestra la estructura "JSON" que estoy tratando de lograr:

{
  id: 1,
  name: "Place Name",
  address: "Place address",
  latitude: 47.00000,
  longitude: -1.540000,
  manifestations: [
    {
      id: 10,
      title: "Manifestation one",
      placeId: 1,
      events: []
    },
    {
      id: 11,
      title: "Manifestation two",
      placeId: 3,
      events: [
        id: 5,
        title: "3333",
        manifestationId: 11,
        placeId: 1
      ]
    }
  ]
}

Entonces quiero incluir el Manifestationcon id: 11, porque uno de ellos Eventocurre en el dado Place(con id: 1)

Actualización (06/04/20): por ahora confío en javascript para obtener el resultado esperado

Pensé que sería bueno si publicaba mi solución actual antes de preguntar.

router.get('/test', async (req, res) => {
  try {
    const placesPromise = place.findAll()
    const manifestationsPromise = manifestation.findAll({
      include: [
        { model: event },
        {
          model: place,
          attributes: ['id'],
        },
      ],
    })

    const [places, untransformedManifestations] = await Promise.all([
      placesPromise,
      manifestationsPromise,
    ])

    const manifestations = untransformedManifestations.map(m => {
      const values = m.toJSON()
      const places = values.places.map(p => p.id)
      return { ...values, places }
    })

    const result = places
      .map(p => {
        const values = p.toJSON()
        const relatedManifestations = manifestations
          .filter(m => {
            const eventsPlaceId = m.events.map(e => e.placeId)
            return (
              m.places.includes(values.id) ||
              eventsPlaceId.includes(values.id)
            )
          })
          .map(m => {
            const filteredEvents = m.events.filter(
              e => e.placeId === values.id
            )
            return { ...m, events: filteredEvents }
          })
        return { ...values, manifestations: relatedManifestations }
      })
      .filter(p => p.manifestations.length)

    return res.status(200).json(result)
  } catch (err) {
    console.log(err)
    return res.status(500).send()
  }
})

Pero estoy bastante seguro de que podría hacerlo directamente con la secuencia. ¿Alguna idea o recomendación?

Gracias

xavier.seignard
fuente

Respuestas:

0

Esto no es óptimo. Pero puedes probarlo:

const findPlace = (id) => {
    return new Promise(resolve => {
        db.Place.findOne({
            where: {
                id: id
            }
        }).then(place => {
            db.Manefestation.findAll({
                include: [{
                    model: db.Event,
                    where: {
                        placeId: id
                    }
                }]

            }).then(manifestations => {
                const out = Object.assign({}, {
                    id: place.id,
                    name: place.name,
                    address: place.address,
                    latitude: place.latitude,
                    longitude: place.longitude,
                    manifestations: manifestations.reduce((res, manifestation) => {
                        if (manifestation.placeId === place.id || manifestation.Event.length > 0) {
                            res.push({
                                id: manifestation.id,
                                title: manifestation.id,
                                placeId: manifestation.placeId,
                                events: manifestation.Event
                            })
                        }
                        return res;
                    }, [])
                })
            })
            resolve(out);
        })
    })
}

A partir de esto, obtienes todas las manifestaciones asignadas para colocar o tener cualquier evento que asigne. Todos los eventos incluidos en las manefestations se asignan al lugar.

Editar: También podrá usar el siguiente:

const findPlace = (id) => {
    return new Promise(resolve => {
        db.Place.findOne({
            include: [{
                model: db.Manefestation,
                include: [{
                    model: db.Event,
                    where: {
                        placeId: id
                    }
                }]

            }],
            where: {
                id: id
            }
        }).then(place => {
            db.Manefestation.findAll({
                include: [{
                    model: db.Event,
                    where: {
                        placeId: id
                    }
                }],
                where: {
                    placeId: {
                        $not: id
                    }
                }

            }).then(manifestations => {
                place.Manefestation = place.Manefestation.concat(manifestations.filter(m=>m.Event.length>0))
                resolve(place);// or you can rename, reassign keys here
            })
        })
    })
}

Aquí tomo solo manifestaciones directas en la primera consulta. Luego, manifestaciones que no se incluyen y concatenan.

Nilanka Manoj
fuente
Gracias por su respuesta, eso es lo que realmente hago, y estaba buscando una forma secuencial de hacerlo en lugar de una simple js.
xavier.seignard
Sin embargo
Nilanka Manoj
Después de mirar su solución, tampoco hace lo que estoy buscando. Vuelva a leer cuidadosamente mi pregunta
xavier.seignard
Lo siento,
edité
Hubo algunos errores tipográficos en la primera respuesta y lo edité. Además, también agregué una nueva respuesta
Nilanka Manoj