管道构建复杂,如何使用MongoPlus构建$addFields阶段
$addFields
MongoDB中的$addFields
阶段用于在聚合管道中添加新的字段或修改现有字段。它允许你根据其他字段的值或计算结果添加新字段。与其他聚合阶段(如$project
)不同,$addFields
不会删除现有字段,而是将新的字段添加到文档中
{
$addFields: {
<newField>: <expression>,
...
}
}
MongoPlus的Aggregate
接口,提供了多种$addFieds
阶段的重载方法
addFields(final String field,final String value);
addFields(final SFunction<T,?> field,final String value);
addFields(final String value,final SFunction<T,?>... field);
addFields(final SFunction<T,?> field,final Object value);
addFields(final SFunction<T,?> field, final Collection<?> value);
addFields(final String field, final Collection<?> value);
addFields(final Field<?>... fields);
addFields(final List<Field<?>> fields);
addFields(final Bson bson);
参数较好理解。其中的一些方法接收了一个Field
类,此类全路径为com.mongoplus.model.aggregate.Field
,为MongoDB驱动的Field
类的增强类,支持Lambda方式构建,常用与添加多个字段到此阶段
,我们可通过其他的构造方法直接构建,因为他们的实现,也是通过Field
类进行条件构造
使用两个$addFields
阶段
名为scores
的集合包含了以下文档:
db.scores.insertMany( [
{
_id: 1,
student: "Maya",
homework: [ 10, 5, 10 ],
quiz: [ 10, 8 ],
extraCredit: 0
},
{
_id: 2,
student: "Ryan",
homework: [ 5, 6, 5 ],
quiz: [ 8, 8 ],
extraCredit: 8
}
] )
下面的操作使用两个 $addFields
阶段在输出文档中包含三个新字段:
MongoDB语句:
db.scores.aggregate( [
{
$addFields: {
totalHomework: { $sum: "$homework" } ,
totalQuiz: { $sum: "$quiz" }
}
},
{
$addFields: { totalScore:
{ $add: [ "$totalHomework", "$totalQuiz", "$extraCredit" ] } }
}
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
// 构建要添加的字段-Field
Field homeWork = Field.of("totalHomework",ConditionOperators.sum("$homework"));
Field quiz= Field.of("totalQuiz",ConditionOperators.sum("$quiz"));
// 或者使用lambda指定字段
//Field homeWork = Field.of(User::getTotalHomework,ConditionOperators.sum(User::getHomework));
//Field quiz= Field.of(User::getTotalQuiz,ConditionOperators.sum(User::getQuiz));
wrapper.addFields(homeWork,quiz);
// 构建第二个`$addFields`阶段
// 只有一个字段,所以不需要构建Field了,可以直接addFields方法
wrapper.addFields("totalScore",ConditionOperators.add("$totalHomework", "$totalQuiz", "$extraCredit"));
该操作将返回以下文档:
[
{
_id: 1,
student: "Maya",
homework: [ 10, 5, 10 ],
quiz: [ 10, 8 ],
extraCredit: 0,
totalHomework: 25,
totalQuiz: 18,
totalScore: 43
},
{
_id: 2,
student: "Ryan",
homework: [ 5, 6, 5 ],
quiz: [ 8, 8 ],
extraCredit: 8,
totalHomework: 16,
totalQuiz: 16,
totalScore: 40
}
]
为嵌入式文档添加字段
向嵌入式文档添加字段有两种方式,一种是最基础的点符号,另外是使用MongoPlus提供的FunctionUtil
类构建lambda类型的嵌入式文档
例如,创建一个包含以下文档的名为vehicles
的集合:
db.vehicles.insertMany( [
{ _id: 1, type: "car", specs: { doors: 4, wheels: 4 } },
{ _id: 2, type: "motorcycle", specs: { doors: 0, wheels: 2 } },
{ _id: 3, type: "jet ski" }
] )
以下聚合操作将新字段 fuel_type
添加到嵌入式文档 specs
。
MongoDB语句:
db.vehicles.aggregate( [
{ $addFields: { "specs.fuel_type": "unleaded" } }
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
// 只有一个字段,无需构建Field
wrapper.addFields("specs.fuel_type": "unleaded");
// 使用lambda方式,获取嵌套文档
wrapper.addFields(
FunctionUtil.builderFunction().add(User::getSpecs).add(Specs::getFuelType),
"unleaded"
);
操作返回以下结果:
[
{ _id: 1, type: "car",
specs: { doors: 4, wheels: 4, fuel_type: "unleaded" } },
{ _id: 2, type: "motorcycle",
specs: { doors: 0, wheels: 2, fuel_type: "unleaded" } },
{ _id: 3, type: "jet ski",
specs: { fuel_type: "unleaded" } }
]
覆盖现有字段
在 $addFields
操作中指定现有字段名称会导致原始字段被替换。
名为 animals
的集合包含以下文档:
db.animals.insertOne(
{ _id: 1, dogs: 10, cats: 15 }
)
以下 $addFields
操作指定 cats
字段。
MongoDB语句:
db.animals.aggregate( [
{
$addFields: { cats: 20 }
}
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
// 只有一个字段,所以不需要使用Field类
wrapper.addFields("cats",20);
该操作将返回以下文档:
[ { _id: 1, dogs: 10, cats: 20 } ]
可以用一个字段替换另一个字段
在以下示例中,item
字段替换了 _id
字段。
名为 fruit
的集合包含以下文档:
db.fruit.insertMany( [
{ _id: 1, item: "tangerine", type: "citrus" },
{ _id: 2, item: "lemon", type: "citrus" },
{ _id: 3, item: "grapefruit", type: "citrus" }
] )
以下聚合操作使用 $addFields
将每个文档的 _id
字段替换为 item
字段的值,并将 item
字段替换为静态值。
MongoDB语句:
db.fruit.aggregate( [
{
$addFields: {
_id : "$item",
item: "fruit"
}
}
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
// 构建所需Field
Field item = Field.of("_id","$item");
Field fruit = Field.of("item","fruit");
wrapper.addFields(item,fruit);
该操作返回以下内容:
[
{ _id: "tangerine", item: "fruit", type: "citrus" },
{ _id: "lemon", item: "fruit", type: "citrus" },
{ _id: "grapefruit", item: "fruit", type: "citrus" }
]
向数组添加元素
创建一个包含以下内容的示例 scores
集合:
db.scores.insertMany( [
{ _id: 1, student: "Maya", homework: [ 10, 5, 10 ], quiz: [ 10, 8 ], extraCredit: 0 },
{ _id: 2, student: "Ryan", homework: [ 5, 6, 5 ], quiz: [ 8, 8 ], extraCredit: 8 }
] )
您可以使用 $addFields
和 $concatArrays
表达式向现有数组字段添加元素。例如,以下操作使用 $addFields
将 homework
字段替换为一个新数组,该数组的元素是当前 homework
数组与另一个数组的拼接,该数组包含新分数 [ 7 ]
。
MongoDB语句:
db.scores.aggregate( [
{ $match: { _id: 1 } },
{ $addFields: { homework: { $concatArrays: [ "$homework", [ 7 ] ] } } }
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
wrapper.match(qr -> qr.eq(SqlOperationConstant._ID,1));
wrapper.addFields("homework",AggregateOperator.concatArrays(Collections.singletonList(7)));
该操作返回以下内容:
[ { _id: 1, student: "Maya", homework: [ 10, 5, 10, 7 ], quiz: [ 10, 8 ], extraCredit: 0 } ]
removeFields
您可以使用 $addFields
和 $REMOVE
变量来删除文档字段。
例如,创建 labReadings
集合:
db.labReadings.insertMany( [
{
date: ISODate("2024-10-09"),
temperature: 80
},
{
date: null,
temperature: 83
},
{
date: ISODate("2024-12-09"),
temperature: 85
}
] )
要从 labReadings
文档中删除 date
字段,请使用带有 $REMOVE
变量的 $addFields
:
MongoDB语句:
db.labReadings.aggregate( [
{
$addFields: { date: "$$REMOVE" }
}
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
wrapper.addFields("data",MongoVariableConstant.REMOVE);
输出:
[
{ _id: ObjectId('671285306fd2c3b24f2e7eaa'), temperature: 80 },
{ _id: ObjectId('671285306fd2c3b24f2e7eab'), temperature: 83 },
{ _id: ObjectId('671285306fd2c3b24f2e7eac'), temperature: 85 }
]
您也可以使用 $REMOVE
有条件地删除字段。例如,以下聚合从 date
为 null
的文档中删除 date
字段:
MongoDB语句:
db.labReadings.aggregate( [
{
$addFields:
{
date: {
$ifNull: [ "$date", "$$REMOVE" ]
}
}
}
] )
MongoPlus构建:
AggregateWrapper wrapper = new AggregateWrapper();
wrapper.addFields("data", ConditionOperators.ifNull("$data",MongoVariableConstant.REMOVE));
输出:
[
{
_id: ObjectId('671285306fd2c3b24f2e7eaa'),
date: ISODate('2024-10-09T00:00:00.000Z'),
temperature: 80
},
{ _id: ObjectId('671285306fd2c3b24f2e7eab'), temperature: 83 },
{
_id: ObjectId('671285306fd2c3b24f2e7eac'),
date: ISODate('2024-12-09T00:00:00.000Z'),
temperature: 85
}
]