Spring Data MongoDB 教程

聚合框架示例7

本例使用条件投影,类似我们熟悉的 if-else 语法,或者是三目运算符。条件投影对应 MongoDB 的 $cond 命令。$cond 含义如下:

$cond 命令用来根据指定的布尔表达式结果,返回 $cond 命令指定的两个返回表达式之一。$cond 命令有两种语法:

{ $cond: { if: <boolean-expression>, then: <true-case>, else: <false-case> } }

或者

{ $cond: [ <boolean-expression>, <true-case>, <false-case> ] }

注意:$cond 的任意一个语法都需要所有三个参数,即 <boolean-expression>、<true-case>、<false-case>。如果 <boolean-expression> 表达式计算结果为 true,则 $cond 计算并返回 <true-case> 表达式的值。否则 $cond 计算并返回 <false-case> 表达式的值。并且,$cond 的参数可以是任何有效的表达式。

该示例关键代码:

(1)输入文档实体

public class InventoryItem {
   @Id
   private int id;
   private String item;
   private String description;
   private int qty;
}

(2)输出文档实体

public class InventoryItemProjection {
   @Id
   private int id;
   private String item;
   private String description;
   private int qty;
   private int discount;
}

(3)投影关键代码

TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
   project("item")
       // 如果 qty 大于等于 250,则为 30,否则为 20
       .and("discount").applyCondition(
           ConditionalOperators.Cond.newBuilder().when(Criteria.where("qty").gte(250)
       ).then(30).otherwise(20))
       // 如果 description 为 null,则为 Unspecified,否则返回原值
       .and(ConditionalOperators.ifNull("description")
               .then("Unspecified")).as("description")
);

AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg, InventoryItemProjection.class);
List<InventoryItemProjection> stateStatsList = result.getMappedResults();
for(InventoryItemProjection doc : stateStatsList) {
   System.out.println(JSONObject.toJSONString(doc));
}

上面代码对 inventoryItem 集合使用投影操作。通过对 qty 字段大于或等于 250 的所有文档使用条件操作来计算 discount 新字段,并对 description 字段执行第二个条件投影。如果我们未指定 description 字段,则使用 “Unspecified” 作为默认字段。否则,直接使用 description 字段的值。

注意,从 MongoDB 3.6 开始,可以通过使用条件表达式从投影中排除字段。例如:

TypedAggregation<Book> agg = Aggregation.newAggregation(Book.class,
 project("title")
   .and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle")     // (1)
       .equalToValue(""))                                                          // (2)
       .then("$$REMOVE")                                                           // (3)
       .otherwiseValueOf("author.middle")                                          // (4)
   )
   .as("author.middle"));

(1)如果字段 author.middle 的值

(2)不包含值,即等于空字符串,

(3)然后使用 $$REMOVE 排除该字段。

(4)否则,请添加 author.middle 的字段值。

完整示例

(1)application.properties 配置

# Log
logging.level.root=debug

# MongoDB
spring.data.mongodb.uri=mongodb://localhost:27017/test

(2)AppConfig.java 配置类

package com.hxstrive.springdata.mongodb.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;

/**
* 配置 MongoTemplate
* @author hxstrive.com 2022/12/23
*/
@Slf4j
@Configuration
public class AppConfig {

   @Bean
   public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) {
       log.info("mongoTemplate({}, {})", mongoDatabaseFactory);
       return new MongoTemplate(mongoDatabaseFactory);
   }

}

(3)输入文档实体

import lombok.Builder;
import lombok.Data;
import org.springframework.data.annotation.Id;

/**
* 输入文档类型实体
* @author hxstrive.com
*/
@Data
@Builder
public class InventoryItem {
   @Id
   private int id;
   private String item;
   private String description;
   private int qty;
}

(4)投影输出结果实体

import lombok.Data;
import org.springframework.data.annotation.Id;

/**
* 投影结果
* @author hxstrive.com
*/
@Data
public class InventoryItemProjection {
   @Id
   private int id;
   private String item;
   private String description;
   private int qty;
   private int discount;
}

(5)客户端代码

import com.alibaba.fastjson.JSONObject;
import com.hxstrive.springdata.mongodb.entity.demo7.InventoryItem;
import com.hxstrive.springdata.mongodb.entity.demo7.InventoryItemProjection;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import java.util.List;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;

/**
* 聚合框架示例7
* @author hxstrive.com
*/
@SpringBootTest
public class AggregationFrameworkDemo7 {

   @Autowired
   private MongoTemplate mongoTemplate;

   @BeforeEach
   public void init() {
       mongoTemplate.dropCollection(InventoryItem.class);

       // 准备数据
       mongoTemplate.insert(InventoryItem.builder().id(1).item("A").description("description1").qty(120).build());
       mongoTemplate.insert(InventoryItem.builder().id(2).item("B").description("").qty(300).build());
       mongoTemplate.insert(InventoryItem.builder().id(3).item("C").description(null).qty(400).build());
   }

   @Test
   public void contextLoads() {
       TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
           project("item")
               // 如果 qty 大于等于 250,则为 30,否则为 20
               .and("discount").applyCondition(
                   ConditionalOperators.Cond.newBuilder().when(Criteria.where("qty").gte(250)
               ).then(30).otherwise(20))
               // 如果 description 为 null,则为 Unspecified,否则返回原值
               .and(ConditionalOperators.ifNull("description")
                       .then("Unspecified")).as("description")
       );

       AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg, InventoryItemProjection.class);
       List<InventoryItemProjection> stateStatsList = result.getMappedResults();
       for(InventoryItemProjection doc : stateStatsList) {
           System.out.println(JSONObject.toJSONString(doc));
       }
       // 结果:
       // {"description":"description1","discount":20,"id":1,"item":"A","qty":0}
       // {"description":"","discount":30,"id":2,"item":"B","qty":0}
       // {"description":"Unspecified","discount":30,"id":3,"item":"C","qty":0}

       // 执行的语句如下:
       // [{ "$project" : { "item" : 1, "discount" : {
       //    "$cond" : { "if" : { "$gte" : ["$qty", 250]}, "then" : 30, "else" : 20}},
       //    "description" : { "$ifNull" : ["$description", "Unspecified"]}}
       // }]
   }

}
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号