Includere tutti i campi esistenti e aggiungere nuovi campi al documento


123

Vorrei definire una fase di aggregazione di $ project in cui posso istruirlo per aggiungere un nuovo campo e includere tutti i campi esistenti, senza dover elencare tutti i campi esistenti.

Il mio documento ha questo aspetto, con molti campi:

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

Voglio fare un'operazione di aggregazione come questa:

[
    {
        $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...
]

C'è qualcosa come una parola chiave "includi tutti i campi" che posso usare in questo caso, o qualche altro modo per evitare di dover elencare ogni campo separatamente?


1
Questa funzionalità sarà disponibile nella prossima major release 2.6. Puoi provarlo sul ramo di sviluppo instabile - usa "$$ ROOT" per fare riferimento all'intero documento in arrivo. Vedi i dettagli in questo ticket: jira.mongodb.org/browse/SERVER-5916
Asya Kamsky

1
Questo problema viene sollevato anche su stackoverflow.com/questions/20497499/… , con altre risposte utili e diverse.
Vince Bowdren

Risposte:


168

In 4.2+, puoi usare l' $setoperatore pipeline di aggregazione che non è altro che un alias da $addFieldsaggiungere in 3.4

La $addFieldsfase è equivalente a una $projectfase che specifica esplicitamente tutti i campi esistenti nei documenti di input e aggiunge i nuovi campi.

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

2
Qualche idea su come usarlo nel driver C #? sembra che non esista
Homam

Non ho familiarità con il driver C # ma $addFieldsè nuovo in MongoDB 3.4 che è supportato dalla versione 2.5+ del driver C #
styvane

Ho cercato un modo per farlo nel driver C #, e finora sembra che il modo per farlo sarebbe usare qualcosa comeIAggregateFluent<TResult>.AppendStage(new JsonPipelineStageDefinition<TInput, TOutput>("{ $addFields : { myField: 'myValue' }}")
sboisse

4
Ho scoperto che può persino sostituire un campo se specifichi lo stesso nome di campo
CME64

79

È possibile utilizzare $$ ROOT per fare riferimento al documento radice. Mantieni tutti i campi di questo documento in un campo e prova a recuperarlo dopo (a seconda del tuo sistema client: Java, C ++, ...)

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

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

17
Ma questo creerà un documento incorporato chiamato documentun'opzione per creare un documento unito sarebbe stato più bello ...
Alexander Suraphel

2
Qualcuno conosce la soluzione per passare attraverso tutte le coppie chiave-valore senza creare il documento incorporato?
ugotchi

11
Trattieni il fiato. MongoDB 3.4 verrà fornito con la fase di aggregazione $ addFields che fa proprio questo. Vedi stackoverflow.com/a/24557029/4653485 .
Jérôme

Questa sembra essere la migliore soluzione attuale, poiché puoi (almeno in JS) quindi utilizzare l'operatore di diffusione per ricostruire il tuo oggetto originale con il nuovo custom_fieldallegato. const newObj = { ...result.document, custom_field: result.custom_field }
ffritz

7

>>> C'è qualcosa come "includi tutti i campi" che posso usare in questo caso o in qualche altra soluzione?

Sfortunatamente, non esiste un operatore per "includere tutti i campi" nell'operazione di aggregazione. L'unico motivo, perché, poiché l'aggregazione è principalmente creata per raggruppare / calcolare i dati dai campi di raccolta (somma, media, ecc.) E restituire tutti i campi della raccolta non è scopo diretto.


... perché l'aggregazione è principalmente creata per raggruppare / calcolare i dati dai campi di raccolta ... La migliore risposta in effetti!
felipsmartins

1
hai una raccolta postscon campi _id, titolo, corpo e Mi piace. Il campo Mi piace è un array di _id utente a cui piace il post. Come potresti elencare tutti i post con tutti i _id, title, body, likeCount? La restituzione di tutti i campi è uno scopo diretto in questo caso.
doc_id

3

A partire dalla versione 2.6.4, Mongo DB non dispone di tale funzionalità per la $projectpipeline di aggregazione. Dai documenti per $project:

Passa i documenti con solo i campi specificati alla fase successiva nella pipeline. I campi specificati possono essere campi esistenti dai documenti di input o campi appena calcolati.

e

Il campo _id è, per impostazione predefinita, incluso nei documenti di output. Per includere gli altri campi dei documenti di input nei documenti di output, è necessario specificare esplicitamente l'inclusione in $ project.


2

Per aggiungere nuovi campi al tuo documento puoi usare $addFields

da documenti

ea tutti i campi del tuo documento puoi usare $$ROOT

db.collection.aggregate([

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

0

secondo la risposta di @Deka, per il driver c # mongodb 2.5 puoi ottenere il documento raggruppato con tutti i tasti come sotto;

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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.