J'ai une collection appelée post avec des références à deux autres collections: statut, format
post:
{
"_id" : ObjectId("5d7c1c4549c5e277395871ad"),
"status" : {
"_id" : ObjectId("status123"),
"name" : "Draft"
},
"format" : {
"_id" : ObjectId("format123"),
"name" : "Standard"
}
}
Je voudrais que la requête de publication renvoie ce qui suit:
post:
{
"_id" : ObjectId("5d7c1c4549c5e277395871ad"),
"status" : {
"$ref" : "status",
"$id" : ObjectId("status123")
},
"format" : {
"$ref" : "format",
"$id" : ObjectId("format123")
}
}
status:
{
"_id" : ObjectId("status123"),
"name" : "Draft"
}
format:
{
"_id" : ObjectId("format123"),
"name" : "Standard"
}
Que doit être ma requête Mongo? Je suis nouveau dans l'utilisation de MongoDB, toute aide est grandement appréciée.
Merci, Shashi
3 Réponses :
Malheureusement, vous ne pouvez pas utiliser $ dans vos noms de champs.
Mais si nous utilisons ceci à la place:
{
"_id" : ObjectId("5d7c1c4549c5e277395871ad"),
"status" : {
"_id" : ObjectId("5d7c921a32c55e3d729b552f"),
"name" : "Draft"
},
"format" : {
"_id" : ObjectId("5d7c921a32c55e3d729b5530"),
"name" : "Standard"
}
}
alors cette requête:
db.post.aggregate([
{$lookup: {"from": "status",
localField: "status.id",
foreignField: "_id",
as: "STATUS" }}
,{$lookup: {"from": "format",
localField: "format.id",
foreignField: "_id",
as: "FORMAT" }}
,{$project: {
"status": {$arrayElemAt: [ "$STATUS",0 ] },
"format": {$arrayElemAt: [ "$FORMAT",0 ] }
}}
]);
donne le résultat souhaité:
var sid1 = new ObjectId();
var fid1 = new ObjectId();
var postdoc =
{
"_id" : ObjectId("5d7c1c4549c5e277395871ad"),
"status" : {
"ref" : "status",
"id" : sid1
},
"format" : {
"ref" : "format",
"id" : fid1
}
};
var statusdoc =
{
"_id" : sid1,
"name" : "Draft"
};
var formatdoc =
{
"_id" : fid1,
"name" : "Standard"
};
db.post.insert(postdoc);
db.status.insert(statusdoc);
db.format.insert(formatdoc);
À votre question
vous pouvez le faire comme ceci
{ "_id" : ObjectId("5d7c1c4549c5e277395871ad"),
"status" : {
"_id" : ObjectId("5d7c4af7c45316e35251a499"),
"name" : "Draft"
},
"format" : {
"_id" : ObjectId("5d7c4b21c45316e35251a49a"),
"name" : "Standard"
}
}
le résultat ressemble à
db.post.aggregate([
{$project :
{ status:
{$arrayElemAt:
[{$objectToArray:"$status"},1]
},
format:
{$arrayElemAt:
[{$objectToArray:"$format"},1]
}}},
{$project:
{status:"$status.v" ,
format:"$format.v"}
},
{$lookup :
{from :"status" ,
localField : "status",
foreignField:"_id" ,
as :"status"}
},
{$lookup:
{from :"format" ,
localField:"format",
foreignField:"_id",
as:"format"}
},
{$unwind :"$status"},
{$unwind:"$format"}
])
expliquer
étape1: utilisez $ objectToArray pour Convertir un document en tableau, nous l'utilisons pour convertir la DBref;
étape2: utilisez $ arrayElemAt avec $ project pour obtenir le "_id" lié.
étape3: utilisez $ lookup pour joindre deux collections
étape4: utilisez $ unwind pour obtenir le document interne. Déconstruit un champ de tableau à partir des documents d'entrée pour générer un document pour chaque élément.
Pourquoi utiliser $ objectToArray ...? Un peu exagéré dans cette situation, non?
@HbnKing lorsque j'utilise $ unwind, je vois que tous mes messages ne reviennent pas et que certains messages apparaissent plusieurs fois dans le résultat. Je voudrais que tous mes messages soient retournés une et une seule fois. Puis-je supprimer $ unwind, qui semble résoudre mon objectif?
@BuzzMoschetti Qu'entendez-vous par $ objectToArray, c'est exagéré? Avez-vous une meilleure solution en tête?
? Oui, posté ci-dessous hier. Attention: j'ai changé status. $ Id et format. $ Id en status.id et format.id I Je ne sais pas si l'OP ne peut pas faire la même chose, mais une conception de données avec $ en tête dans les noms de champ se dirige vers des problèmes lors de l'utilisation des opérateurs d'agrégation et des fonctions de chemin de champ.
utilisez $ unwind avec une option comme {$ unwind: {path: "$ tailles", preserveNullAndEmptyArrays: true}}
I could get the desired output using variables.
var post = db.post.findOne({"_id":ObjectId("5d7c8fb8ba38a9196e5fc195")})
var dbRef = post.status
var status = db[dbRef.$ref].findOne({"_id":(dbRef.$id)})
var dbRef1 = post.format
var format = db[dbRef1.$ref].findOne({"_id":(dbRef1.$id)})
db.post.aggregate({ $project : { "_id" : post._id , "status" : status , "format" : format}})
You can check MongoDB database references link for further information.
https://docs.mongodb.com/manual/reference/database-references/index.html
C'est 3 allers-retours séparés vers la base de données.