2 votes

Interroger un sous-document et y sélectionner des données filtrées Mongoose

Je veux interroger un sous-document qui vit dans un document parent et seulement sélectionner (retourner dans la réponse express) un sous-document spécifique avec ses propres données, mais de la façon dont je le fais, il sélectionne/renvoie tous les sous-documents, sans filtrer celui dont j'ai besoin, que j'essaie de filtrer par le champ candidateId

Voici ma question :

interviewRouter.get(
  "/:idCandidato&:userEmail",
  async (req: Request, res: Response) => {
    const { idCandidato, userEmail } = req.params;

    parseInt(idCandidato);

    const infoCandidato = await User.find({
      email: userEmail,
      "candidates.candidateId": idCandidato,
    }).select({"_id": 0, "candidates": 1});

    infoCandidato
      ? res.status(200).json({ Candidato: infoCandidato })
      : res.status(404).json({ Candidato: "Candidato no encontrado" });
  }
);

Voici la structure de mon document et de mon sous-document :

{
    "_id" : ObjectId("61223b3c88e21fe0ee69d689"),
    "username" : "randomUserName",
    "email" : "random@email.com",
    "pictureUrl" : "some/url/to/the/user/picture/username",
    "role" : "admin",
    "candidates" : [
        {
            "_id" : ObjectId("612242cd11a7f1ebc6a18661"),
            "candidateName" : "some candidates name",
            "candidateId" : 123,
            "candidateInfo" : [
                {
                    "_id" : ObjectId("612242cd11a7f1ebc6a18662"),
                    "currentSituation" : "3",
                    "motivationToChange" : "3",
                    "postSavingDate" : ISODate("2021-08-22T12:27:57.490Z")
                },
                {
                    "_id" : ObjectId("612242cd11a7f1ebc6a04854"),
                    "currentSituation" : "6",
                    "motivationToChange" : "7",
                    "postSavingDate" : ISODate("2021-08-22T13:56:57.490Z")
                }
            ],
            "availableNow" : false,
            "mainSkills" : "MM"
        },
        {
            "_id" : ObjectId("612261abf7b68bfaf56345df"),
            "candidateName" : "some other guy",
            "candidateId" : 1234,
            "candidateInfo" : [
                {
                    "_id" : ObjectId("612261abf7b68bfaf56345e0"),
                    "currentSituation" : "dunno",
                    "motivationToChange" : "no idea",
                    "postSavingDate" : ISODate("2021-08-22T14:39:39.161Z")
                }
            ],
            "availableNow" : false,
            "mainSkills" : "JavaScript"
        }
    ],
    "__v" : 0
}

Ci-dessous, vous pouvez voir ce que ma requête renvoie, qui est en fait l'ensemble candidates au lieu des sous-documents filtrés dont j'ai besoin. :

{
    "Candidato": [
        {
            "candidates": [
        {
            "_id" : ObjectId("612242cd11a7f1ebc6a18661"),
            "candidateName" : "some candidates name",
            "candidateId" : 123,
            "candidateInfo" : [
                {
                    "_id" : ObjectId("612242cd11a7f1ebc6a18662"),
                    "currentSituation" : "3",
                    "motivationToChange" : "3",
                    "postSavingDate" : ISODate("2021-08-22T12:27:57.490Z")
                },
                {
                    "_id" : ObjectId("612242cd11a7f1ebc6a04854"),
                    "currentSituation" : "6",
                    "motivationToChange" : "7",
                    "postSavingDate" : ISODate("2021-08-22T13:56:57.490Z")
                }
            ],
            "availableNow" : false,
            "mainSkills" : "MM"
        },
        {
            "_id" : ObjectId("612261abf7b68bfaf56345df"),
            "candidateName" : "some other guy",
            "candidateId" : 1234,
            "candidateInfo" : [
                {
                    "_id" : ObjectId("612261abf7b68bfaf56345e0"),
                    "currentSituation" : "dunno",
                    "motivationToChange" : "no idea",
                    "postSavingDate" : ISODate("2021-08-22T14:39:39.161Z")
                }
            ],
            "availableNow" : false,
            "mainSkills" : "JavaScript"
        }
            ]
        }
    ]
}

Note importante : rappelez-vous que je veux juste qu'il revienne soit 123 o 1234 sous-document, en fonction du candidateId Je passe à la requête

Voici une solution de contournement que j'ai trouvée, mais je pense qu'elle est un peu sale et j'apprécierais une autre alternative :

interviewRouter.get(
  "/:idCandidato&:userEmail",
  async (req: Request, res: Response) => {
    const { idCandidato, userEmail } = req.params;

    const infoCandidato = await User.findOne(
      {
        email: userEmail,
        "candidates.candidateId": idCandidato,
      },
      function (err: any, response: any) {
        if (err) {
          res.status(400).send(err);
        } else {
          let newResponse = response["candidates"].filter((element: any) => {
            return element.candidateId === parseInt(idCandidato);
          });
          res.status(200).json({ Candidato: newResponse });
        }
      }
    );

Avec cette solution de contournement, j'obtiens ce dont j'ai besoin, mais cela semble un peu désordonné et je pense qu'il pourrait y avoir une autre solution plus propre. Voici ce que j'obtiens en retour :

{
    "Candidato": [
        {
            "_id" : ObjectId("612242cd11a7f1ebc6a18661"),
            "candidateName" : "some candidates name",
            "candidateId" : 123,
            "candidateInfo" : [
                {
                    "_id" : ObjectId("612242cd11a7f1ebc6a18662"),
                    "currentSituation" : "3",
                    "motivationToChange" : "3",
                    "postSavingDate" : ISODate("2021-08-22T12:27:57.490Z")
                },
                {
                    "_id" : ObjectId("612242cd11a7f1ebc6a04854"),
                    "currentSituation" : "6",
                    "motivationToChange" : "7",
                    "postSavingDate" : ISODate("2021-08-22T13:56:57.490Z")
                }
            ],
            "availableNow" : false,
            "mainSkills" : "MM"
        },
    ]
}

Des idées ? Merci.

Problème résolu par @eol - Note simple

Comme indiqué, @eol a résolu le problème et partagé la solution. J'ai pris sa réponse et l'ai traduite en JavaScript/Mongoose sintaxis et voilà :

interviewRouter.get(
  "/:idCandidato&:userEmail",
  async (req: Request, res: Response) => {
    const { idCandidato, userEmail } = req.params;
    const infoCandidato = await User.aggregate([
      {
        "$match": {
          email: userEmail
        }
      },
      {
        "$unwind": "$candidates"
      },
      {
        "$match": {
          "candidates.candidateId": parseInt(idCandidato)
        }
      }]);
      infoCandidato.length !== 0
      infoCandidato
      ? res.status(200).json({ Candidato: infoCandidato })
      : res.status(404).json({ Candidato: "Candidato no encontrado" });
  }
);

1voto

eol Points 2453

Vous pouvez faire une simple agrégation où vous commencez par $match avec les documents donnés email entonces $unwind les éléments candidats et le filtre ceux qui $match les id :

db.collection.aggregate([
  {
    "$match": {
      email: "random@email.com"
    }
  },
  {
    "$unwind": "$candidates"
  },
  {
    "$match": {
      "candidates.candidateId": 1234
    }
  }
])

Voici un exemple sur mongoplayground : https://mongoplayground.net/p/GPB3oYqe5em

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X