使用MongoPlus构建$addFields阶段

Viewed 6

管道构建复杂,如何使用MongoPlus构建$addFields阶段

1 Answers

定义

$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类进行条件构造

示例

  1. 使用两个$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
    }
    ]
    
  2. 为嵌入式文档添加字段
    向嵌入式文档添加字段有两种方式,一种是最基础的点符号,另外是使用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" } }
    ]
    
  3. 覆盖现有字段
    $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 } ]
    
  4. 可以用一个字段替换另一个字段
    在以下示例中,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" }
    ]
    
  5. 向数组添加元素
    创建一个包含以下内容的示例 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 表达式向现有数组字段添加元素。例如,以下操作使用 $addFieldshomework 字段替换为一个新数组,该数组的元素是当前 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 } ]
    
  6. 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 有条件地删除字段。例如,以下聚合从 datenull 的文档中删除 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
    	}
    ]