Incluir todos os campos existentes e adicionar novos campos ao documento

123

Gostaria de definir um estágio de agregação do projeto $, no qual posso instruí-lo a adicionar um novo campo e incluir todos os campos existentes, sem precisar listar todos os campos existentes.

Meu documento fica assim, com muitos campos:

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

Eu quero fazer uma operação de agregação como esta:

[
    {
        $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 algo como uma palavra-chave "incluir todos os campos" que eu possa usar neste caso ou alguma outra maneira de evitar a necessidade de listar todos os campos separadamente?

samuelluis
fonte
1
Esse recurso está disponível na próxima versão principal 2.6. Você pode experimentá-lo no ramo de desenvolvimento instável - use "$$ ROOT" para se referir a todo o documento recebido. Veja detalhes neste ticket: jira.mongodb.org/browse/SERVER-5916
Asya Kamsky
1
Esse problema também foi levantado em stackoverflow.com/questions/20497499/… , com algumas outras respostas úteis e diferentes.
Vince Bowdren

Respostas:

168

No 4.2+, você pode usar o $setoperador de pipeline de agregação, que nada mais é do que um alias a ser $addFieldsadicionado no 3.4

O $addFieldsestágio é equivalente a um $projectestágio que especifica explicitamente todos os campos existentes nos documentos de entrada e adiciona os novos campos.

db.collection.aggregate([
    { "$addFields": { "custom_field": "$obj.obj_field1" } }
])
styvane
fonte
2
Alguma idéia de como usá-lo no driver C #? Parece que não existe
Homam
Eu não estou familiarizado com o driver C #, mas $addFieldsé novo no MongoDB 3.4 que é suportado pela versão do driver C # 2.5+
styvane
Fui à procura de uma maneira de fazer isso em C # motorista, e até agora parece que a maneira de fazer isso seria usar algo comoIAggregateFluent<TResult>.AppendStage(new JsonPipelineStageDefinition<TInput, TOutput>("{ $addFields : { myField: 'myValue' }}")
sboisse
4
Eu descobri que ele pode até substituir um campo se você especificar o mesmo nome de campo
CME64 25/12/18
79

Você pode usar $$ ROOT para referenciar o documento raiz. Mantenha todos os campos deste documento em um campo e tente obtê-lo depois disso (dependendo do sistema do cliente: Java, C ++, ...)

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

        }
    },
    ... //group, match, and whatever...
]
Deka
fonte
17
Mas isso vai criar um doc incorporado chamado documentuma opção para um criado fundiu doc teria sido melhor ...
Alexander Suraphel
2
Alguém sabe da solução para passar por todos os pares de valores-chave sem criar o documento incorporado?
Ugotchi 28/09/16
11
Prenda a respiração. O MongoDB 3.4 virá com o estágio de agregação $ addFields que faz exatamente isso. Consulte stackoverflow.com/a/24557029/4653485 .
Jérôme
Essa parece ser a melhor solução atual, pois você pode (pelo menos em JS) e usar o operador de propagação para reconstruir seu objeto original com o novo custom_fieldanexo. const newObj = { ...result.document, custom_field: result.custom_field }
ffritz 13/03
7

>>> Existe algo como a palavra-chave "incluir todos os campos" que eu possa usar neste caso ou em alguma outra solução?

Infelizmente, não há operador para "incluir todos os campos" na operação de agregação. A única razão pela qual, porque a agregação é criada principalmente para agrupar / calcular dados dos campos da coleção (soma, média, etc.) e retornar todos os campos da coleção não é um objetivo direto.

Victoria Malaya
fonte
... porque a agregação é criada principalmente para agrupar / calcular dados dos campos de coleta ... Melhor resposta, de fato!
felipsmartins
1
você tem uma coleção postscom campos _id, title, body, likes. O campo de curtidas é uma matriz de usuários _id que gostam da postagem. Como você pode listar todas as postagens com todos os _id, título, corpo e likeCount? Retornar todos os campos é um objetivo direto neste caso.
doc_id 06/10
3

A partir da versão 2.6.4, o Mongo DB não possui esse recurso para o $projectpipeline de agregação. Dos documentos para $project:

Passa os documentos apenas com os campos especificados para o próximo estágio no pipeline. Os campos especificados podem ser campos existentes nos documentos de entrada ou nos campos recém-calculados.

e

O campo _id é, por padrão, incluído nos documentos de saída. Para incluir os outros campos dos documentos de entrada nos documentos de saída, você deve especificar explicitamente a inclusão em $ project.

Ghopper21
fonte
2

Para adicionar novos campos ao seu documento, você pode usar $addFields

de documentos

e para todos os campos do seu documento, você pode usar $$ROOT

db.collection.aggregate([

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

de acordo com a resposta da @Deka, para o driver do c # mongodb 2.5, você pode obter o documento agrupado com todas as teclas abaixo;

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);
mr.byte
fonte