Включите все существующие поля и добавьте новые поля в документ

123

Я хотел бы определить стадию агрегирования $ project, на которой я могу дать ему указание добавить новое поле и включить все существующие поля без необходимости перечислять все существующие поля.

Мой документ выглядит так, с множеством полей:

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

Я хочу сделать такую ​​операцию агрегирования:

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

Есть ли что-то вроде ключевого слова «включить все поля», которое я могу использовать в этом случае, или какой-то другой способ избежать необходимости перечислять каждое поле отдельно?

samuelluis
источник
1
Эта функция появится в следующем основном выпуске 2.6. Вы можете попробовать его в нестабильной ветке разработки - используйте «$$ ROOT» для ссылки на весь входящий документ. Подробности смотрите в этом билете: jira.mongodb.org/browse/SERVER-5916
Ася Камский
1
Эта проблема также поднимается на stackoverflow.com/questions/20497499/… с некоторыми другими полезными и разными ответами.
Винс Боудрен,

Ответы:

169

В 4.2+ вы можете использовать $setоператор конвейера агрегации, который представляет собой не что иное, как псевдоним, $addFieldsдобавленный в 3.4.

$addFieldsСтадия эквивалентна $projectстадии , что явно указывает все существующие поля ввода документов и добавляет новые поля.

db.collection.aggregate([
    { "$addFields": { "custom_field": "$obj.obj_field1" } }
])
styvane
источник
2
Есть идеи, как использовать его в драйвере С #? кажется, его не существует
Homam
Я не знаком с драйвером C #, но он $addFieldsявляется новым в MongoDB 3.4, который поддерживается драйвером C # версии
2.5+
Я искал способ сделать это в драйвере C #, и до сих пор похоже, что для этого нужно использовать что-то вродеIAggregateFluent<TResult>.AppendStage(new JsonPipelineStageDefinition<TInput, TOutput>("{ $addFields : { myField: 'myValue' }}")
sboisse
4
Я обнаружил, что он может даже заменить поле, если вы укажете то же имя поля
CME64
79

Вы можете использовать $$ ROOT для ссылки на корневой документ. Сохраните все поля этого документа в поле и попытайтесь получить его после этого (в зависимости от вашей клиентской системы: Java, C ++, ...)

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

        }
    },
    ... //group, match, and whatever...
]
Дека
источник
17
Но это создаст встроенный документ, называемый documentопцией создания одного объединенного документа, было бы лучше ...
Александр Сурафель 01
2
Кто-нибудь знает о решении пройти через все пары ключ-значение без создания встроенного документа?
ugotchi
11
Задержи дыхание. MongoDB 3.4 будет поставляться с этапом агрегирования $ addFields, который делает именно это. См. Stackoverflow.com/a/24557029/4653485 .
Jérôme
Кажется, это лучшее текущее решение, так как вы можете (по крайней мере, в JS) затем использовать оператор распространения, чтобы перестроить исходный объект с custom_fieldприсоединенным новым . const newObj = { ...result.document, custom_field: result.custom_field }
ffritz
7

>>> Есть что-то вроде ключевого слова "включить все поля", которое я могу использовать в этом случае или какое-то другое решение?

К сожалению, нет оператора для «включения всех полей» в операцию агрегирования. Единственная причина, почему, поскольку агрегация в основном создается для группировки / вычисления данных из полей коллекции (сумма, среднее и т.д.) и возврата всех полей коллекции, не является прямой целью.

Виктория Малая
источник
... потому что агрегация в основном создается для группировки / расчета данных из полей сбора ... Фактически, лучший ответ!
felipsmartins 01
1
у вас есть коллекция postsс полями _id, title, body, like. Поле лайков - это массив _id пользователя, которому понравился пост. Как вы могли перечислить все сообщения со всеми _id, title, body, likeCount? В этом случае прямой целью является возврат всех полей.
doc_id 06
3

Начиная с версии 2.6.4, Mongo DB не имеет такой функции для $projectконвейера агрегации. Из документов для $project:

Передает документы, содержащие только указанные поля, на следующий этап конвейера. Указанные поля могут быть существующими полями из входных документов или вновь вычисленными полями.

и

Поле _id по умолчанию включается в выходные документы. Чтобы включить другие поля из входных документов в выходные документы, вы должны явно указать включение в $ project.

Ghopper21
источник
2

Чтобы добавить новые поля в документ, вы можете использовать $addFields

из документов

и для всех полей в вашем документе вы можете использовать $$ROOT

db.collection.aggregate([

{ "$addFields": { "custom_field": "$obj.obj_field1" } },
{ "$group": {
        _id : "$field1",
        data: { $push : "$$ROOT" }
    }}
])
Дикша Шарма
источник
0

согласно ответу @Deka, для драйвера c # mongodb 2.5 вы можете получить сгруппированный документ со всеми ключами, как показано ниже;

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
источник