Inclure tous les champs existants et ajouter de nouveaux champs au document


123

Je voudrais définir une étape d'agrégation $ project où je peux lui demander d'ajouter un nouveau champ et d'inclure tous les champs existants, sans avoir à lister tous les champs existants.

Mon document ressemble à ceci, avec de nombreux champs:

{
    obj: {
        obj_field1: "hi",
        obj_field2: "hi2"
    },
    field1: "a",
    field2: "b",
    ...
    field26: "z"
}

Je veux faire une opération d'agrégation comme celle-ci:

[
    {
        $project: {
            custom_field: "$obj.obj_field1",
            //the next part is that I don't want to do
            field1: 1,
            field2: 1,
            ...
            field26: 1
        }
    },
    ... //group, match, and whatever...
]

Existe-t-il quelque chose comme un mot-clé "inclure tous les champs" que je peux utiliser dans ce cas, ou une autre façon d'éviter d'avoir à lister chaque champ séparément?


1
Cette fonctionnalité sera disponible dans la prochaine version majeure 2.6. Vous pouvez l'essayer sur la branche de développement instable - utilisez "$$ ROOT" pour faire référence à l'ensemble du document entrant. Voir les détails dans ce billet: jira.mongodb.org/browse/SERVER-5916
Asya Kamsky

1
Ce problème est également soulevé sur stackoverflow.com/questions/20497499/… , avec d'autres réponses utiles et différentes.
Vince Bowdren

Réponses:


168

Dans 4.2+, vous pouvez utiliser l' $setopérateur de pipeline d'agrégation qui n'est rien d'autre qu'un alias $addFieldsajouté dans 3.4

L' $addFieldsétape équivaut à une $projectétape qui spécifie explicitement tous les champs existants dans les documents d'entrée et ajoute les nouveaux champs.

db.collection.aggregate([
    { "$addFields": { "custom_field": "$obj.obj_field1" } }
])

2
Une idée comment l'utiliser dans le pilote C #? semble qu'il n'existe pas
Homam

Je ne connais pas le pilote C # mais il $addFieldsest nouveau dans MongoDB 3.4 qui est pris en charge par le pilote C # version 2.5+
styvane

J'ai cherché un moyen de faire cela dans le pilote C #, et jusqu'à présent, il semble que la façon de le faire serait d'utiliser quelque chose commeIAggregateFluent<TResult>.AppendStage(new JsonPipelineStageDefinition<TInput, TOutput>("{ $addFields : { myField: 'myValue' }}")
sboisse

4
J'ai découvert qu'il peut même remplacer un champ si vous spécifiez le même nom de champ
CME64

79

Vous pouvez utiliser $$ ROOT pour faire référence au document racine. Conservez tous les champs de ce document dans un champ et essayez de l'obtenir par la suite (en fonction de votre système client: Java, C ++, ...)

 [
    {
        $project: {
            custom_field: "$obj.obj_field1",
            document: "$$ROOT"

        }
    },
    ... //group, match, and whatever...
]

17
Mais cela créera un document intégré appelé documentune option pour créer un document fusionné aurait été plus agréable ...
Alexander Suraphel

2
Quelqu'un connaît-il la solution pour passer à travers toutes les paires valeur / clé sans créer le document incorporé?
ugotchi

11
Retiens ta respiration. MongoDB 3.4 viendra avec l' étape d'agrégation $ addFields qui fait exactement cela. Voir stackoverflow.com/a/24557029/4653485 .
Jérôme

Cela semble être la meilleure solution actuelle, car vous pouvez (au moins dans JS) utiliser l'opérateur de propagation pour reconstruire votre objet d'origine avec le nouveau custom_fieldattaché. const newObj = { ...result.document, custom_field: result.custom_field }
ffritz

7

>>> Il y a quelque chose comme le mot-clé "inclure tous les champs" que je peux utiliser dans ce cas ou une autre solution?

Malheureusement, il n'y a pas d'opérateur pour "inclure tous les champs" dans l'opération d'agrégation. La seule raison, pourquoi, parce que l'agrégation est principalement créée pour regrouper / calculer les données des champs de collection (somme, moyenne, etc.) et retourner tous les champs de la collection n'est pas un but direct.


... car l'agrégation est principalement créée pour regrouper / calculer des données à partir des champs de collecte ... La meilleure réponse en fait!
felipsmartins

1
vous avez une collection postsavec des champs _id, title, body, likes. Le champ J'aime est un tableau de _id d'utilisateur qui aiment le message. Comment pouvez-vous lister tous les messages avec tous les _id, title, body, likeCount? Le renvoi de tous les champs est un objectif direct dans ce cas.
doc_id

3

Depuis la version 2.6.4, Mongo DB ne dispose pas d'une telle fonctionnalité pour le $projectpipeline d'agrégation. À partir de la documentation pour $project:

Passe les documents avec uniquement les champs spécifiés à l'étape suivante du pipeline. Les champs spécifiés peuvent être des champs existants des documents d'entrée ou des champs nouvellement calculés.

et

Le champ _id est, par défaut, inclus dans les documents de sortie. Pour inclure les autres champs des documents d'entrée dans les documents de sortie, vous devez spécifier explicitement l'inclusion dans $ project.


2

Pour ajouter de nouveaux champs à votre document, vous pouvez utiliser $addFields

à partir de documents

et pour tous les champs de votre document, vous pouvez utiliser $$ROOT

db.collection.aggregate([

{ "$addFields": { "custom_field": "$obj.obj_field1" } },
{ "$group": {
        _id : "$field1",
        data: { $push : "$$ROOT" }
    }}
])

0

selon @Deka reply, pour c # mongodb driver 2.5, vous pouvez obtenir le document groupé avec toutes les touches comme ci-dessous;

var group = new BsonDocument
{
 { "_id", "$groupField" },
 { "_document", new BsonDocument { { "$first", "$$ROOT" } } }
};

ProjectionDefinition<BsonDocument> projection = new BsonDocument{{ "document", "$_document"}};
var result = await col.Aggregate().Group(group).Project(projection).ToListAsync();

// For demo first record 
var fistItemAsT = BsonSerializer.Deserialize<T>(result.ToArray()[0]["document"].AsBsonDocument);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.