Sequenciar a consulta com uma cláusula where em um include de um include

8

Estou lutando para criar uma consulta com sequelize.

Algum contexto

Eu tenho os seguintes modelos:

  • A Manifestationpode ter [0..n]Event
  • Um Eventpertence a um Manifestation( Eventnão pode existir sem a Manifestation)
  • A Placepode ter [0..n]Event
  • Um Eventpertence a um Place( Eventnão pode existir sem a Place)
  • A Manifestationpode ter [1..n]Place
  • A Placepode ter [0..n]Manifestation

Eu modelo as relações da seguinte maneira:

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 mim, parece bastante correto, mas não hesite se tiver comentários.

A questão

Estou tentando consultar o Place, a fim de obter tudo Manifestatione Eventacontecendo em um determinado Place. Mas para Eventos que eu quero incluí-los dentro deles, Manifestationmesmo que Manifestationisso não aconteça no dado Place.

Abaixo está a estrutura "JSON" que estou tentando obter:

{
  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
      ]
    }
  ]
}

Então, eu quero incluir o Manifestationcom id: 11, porque um deles Eventocorre no dado Place(com id: 1)

Atualização (04/06/20): Por enquanto, dependo de javascript para obter o resultado esperado

Eu descobri que seria bom se eu postasse minha solução atual antes de perguntar.

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()
  }
})

Mas tenho certeza de que poderia fazer isso diretamente com a sequência. Alguma idéia ou recomendação?

obrigado

xavier.seignard
fonte

Respostas:

0

Isso não é o ideal. Mas você pode experimentar:

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 disso, você obtém todas as manifestações atribuídas ao local ou possui qualquer evento atribuído. Todos os eventos incluídos nas manifestações são atribuídos ao local.

Editar: você também poderá usar o seguinte:

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
            })
        })
    })
}

Aqui, tomo apenas manifestações diretas na primeira consulta. Depois, manifestações que não foram incluídas e concatenadas.

Nilanka Manoj
fonte
Obrigado pela sua resposta, é o que eu realmente faço, e eu estava procurando uma maneira sequencial de fazê-lo, em vez de um simples js.
xavier.seignard 03/04
você pode executar consulta SQL db embora
Nilanka Manoj 03/04
Depois de analisar a sua solução, ela também não faz o que estou procurando. Releia cuidadosamente minha pergunta
xavier.seignard 04/04
Desculpe por isso, editei a resposta agora
Nilanka Manoj 04/04
Houve alguns erros de digitação na primeira resposta e eu a editei. Além disso, eu adicionei uma nova resposta também
Nilanka Manoj