# mongo-crud **Repository Path**: Anntly/mongo-crud ## Basic Information - **Project Name**: mongo-crud - **Description**: 项目没有实际用过mongodb,自己按照简单的物流捋了一下需求,将常见crud,位置,嵌套文档,聚合等常见操作做了下整理,算是练手吧,后期深入学习原理再更新文档 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-04-06 - **Last Updated**: 2023-04-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mongo-crud ### 介绍 项目没有实际用过mongodb,自己按照简单的物流捋了一下需求,将常见crud,位置,嵌套文档,聚合等常见操作做了下整理,算是练手吧,后期深入学习原理再更新文档 常见的 增删改查、地理位置、嵌套文档、聚合函数 ### 新建项目 ![image.png](https://cdn.nlark.com/yuque/0/2023/png/2355575/1680833064647-47c36750-9334-4668-b446-5509a188b51d.png#averageHue=%23222b34&clientId=uea493eb0-78be-4&from=paste&height=637&id=u3d7c8bb1&name=image.png&originHeight=796&originWidth=984&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=76275&status=done&style=none&taskId=ub6ebf62d-997a-48c6-9503-e8ef8dc55d9&title=&width=787.2) ![image.png](https://cdn.nlark.com/yuque/0/2023/png/2355575/1680833085817-eaeb890a-8793-42b8-b0d8-514c85030d1a.png#averageHue=%23222b34&clientId=uea493eb0-78be-4&from=paste&height=637&id=u952f1bea&name=image.png&originHeight=796&originWidth=984&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=71345&status=done&style=none&taskId=u31ced9e2-2d7d-4b0b-b21d-c4da4e81727&title=&width=787.2) 修改 application.yml 添加 mongodb数据源信息 ```yaml spring: data: mongodb: uri: mongodb://jboom:123456@127.0.0.1:27017/logistics auto-index-creation: true ``` ### 案例代码 以物流订单为例(非真实项目设计) #### LogisticsOrder 物流订单 ```java /** * 物流订单 */ @Data @Document("logistics_order") @ToString public class LogisticsOrder { /** * 物流订单id */ @MongoId @AutoId private Long orderId; /** * 用户id */ private Long userId; /** * 订单价格 */ private BigDecimal price; /** * 订单总重 */ private BigDecimal weight; /** * 目的地 */ private GeoJsonPoint to; /** * 出发地 */ private GeoJsonPoint from; /** * 当前位置 */ @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE,useGeneratedName = true) private GeoJsonPoint location; /** * 距离 */ private transient Double distance; /** * 住址信息 */ private Address address; /** * 运送物品 */ private List items; /** * 0 已发货 * 1 待收件 * 2 已揽收 * 3 运输中 * 4 配送中 * 5 已签收 */ private Integer status; /** * 发出时间 */ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private LocalDateTime sendTime; /** * 发出时间 */ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private LocalDateTime receiveTime; /** * 操作者 */ private String operator; /** * 操作时间 */ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private LocalDateTime operateTime; /** * 备注 */ private String remark; } ``` #### OrderItem 订单货物 ```java /** * 物流货物 */ @Data @ToString public class OrderItem { /** * 货物id */ private Long itemId; /** * 货物信息 */ private String info; /** * 商品毛重 */ private BigDecimal weight; /** * 商品价格 */ private BigDecimal price; } ``` #### Address 地址信息 ```java @Data @ToString public class Address { private String province; private String city; private String counties; private String detail; private String postCode; } ``` ### CRUD 案例直接使用的MongoTemplate ```java @Resource private MongoTemplate mongoTemplate; ``` #### 新增订单 新增很简单 ```java /** * 新增订单 * * @param order * @return */ public Long addOrder(LogisticsOrder order) { return mongoTemplate.insert(order).getOrderId(); } ``` #### 查询订单,同时返回距离当前位置的距离 使用 NearQuery 包一层用于返回 GeoResult 获取 distance ```java /** * mongodb一个集合最好只设计一个地理索引,多个会冲突 * 查询指定经纬度与数据的距离 * * @param orderId * @param longitude 收货地精度 * @param latitude 收货地纬度 -- 用于计算距离 * @return */ public LogisticsOrder getOrder(Long orderId, double longitude, double latitude) { Criteria criteria = Criteria.where("_id").is(orderId); Query query = new Query(criteria); NearQuery nearQuery = NearQuery .near(new GeoJsonPoint(longitude, latitude), Metrics.MILES) .query(query); GeoResults results = mongoTemplate.geoNear(nearQuery, LogisticsOrder.class); for (GeoResult result : results) { LogisticsOrder content = result.getContent(); double distance = result.getDistance().getValue(); content.setDistance(distance); } // 处理距离 return results.getContent().get(0).getContent(); } ``` #### 查询坐标范围内的订单 ```java /** * 获取用户 range 公里范围(km)内的订单 * * @param userId * @param longitude * @param latitude * @param range * @return */ public List getUserRangeOrder(Long userId, double longitude, double latitude, Integer range) { GeoJsonPoint point = new GeoJsonPoint(longitude, latitude); Distance distance = new Distance(range, Metrics.KILOMETERS); // 圆圈范围内 Circle circle = new Circle(point, distance); Criteria criteria = Criteria.where("userId").is(userId) .and("location").withinSphere(circle); Query query = new Query(criteria); return mongoTemplate.find(query, LogisticsOrder.class); } ``` #### 修改订单 ```java /** * 操作员操作,发货、揽收、配送、签收等操作 * * @param orderId * @param operator * @param longitude * @param latitude * @param status * @return */ public boolean operateOrder(Long orderId, String operator, double longitude, double latitude, Integer status) { Criteria criteria = Criteria.where("orderId").is(orderId); Query query = new Query(criteria); Update update = new Update().set("operator", operator) .set("location", new GeoJsonPoint(longitude, latitude)) .set("status", status); UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class); return result.getMatchedCount() != 0; } ``` #### 修改地址信息,嵌套子文档 ```java /** * 修改收货地址 * 修改嵌套子文档字段 * * @param orderId * @return */ public boolean modifyReceiveAddress(Long orderId, String province, String city, String counties, String detail, String postCode) { Criteria criteria = Criteria.where("_id").is(orderId); Query query = new Query(criteria); Update update = new Update(); Optional.ofNullable(province).ifPresent(p -> update.set("address.province", p)); Optional.ofNullable(city).ifPresent(c -> update.set("address.city", c)); Optional.ofNullable(counties).ifPresent(c -> update.set("address.counties", c)); Optional.ofNullable(detail).ifPresent(d -> update.set("address.detail", d)); Optional.ofNullable(postCode).ifPresent(p -> update.set("address.postCode", p)); return mongoTemplate.updateFirst(query, update, LogisticsOrder.class).getMatchedCount() > 0; } ``` #### 修改货物信息,嵌套子文档(数组) ```java /** * 更新货物信息 * 更新嵌套数组对象字段信息 * * @param orderId * @param itemId * @param weight * @param price * @param info * @return */ public boolean modifyItem(Long orderId, Long itemId, BigDecimal weight, BigDecimal price, String info) { Criteria criteria = Criteria.where("_id").is(orderId) .and("items.itemId").is(itemId); Query query = new Query(criteria); Update update = new Update(); Optional.ofNullable(weight).ifPresent(w -> update.set("items.$.weight", w)); Optional.ofNullable(price).ifPresent(p -> update.set("items.$.price", p)); Optional.ofNullable(info).ifPresent(i -> update.set("items.$.info", i)); UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class); return result.getMatchedCount() != 0; } ``` #### 新增货物,嵌套子文档(数组) ```java /** * 添加货物 * 嵌套数组添加元素 * * @param orderId * @param item * @return */ public boolean addItem(Long orderId, OrderItem item) { Criteria criteria = Criteria.where("_id").is(orderId); Query query = new Query(criteria); Update update = new Update(); // 不重复就用addToSet update.push("items", item); UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class); return result.getMatchedCount() != 0; } ``` #### 删除货物,嵌套子文档(数组) ```java /** * 清除货物 * 嵌套数组删除元素 * * @param orderId * @param itemId * @return */ public boolean removeItem(Long orderId, Long itemId) { Criteria criteria = Criteria.where("_id").is(orderId); Query query = new Query(criteria); BasicDBObject dbObject = new BasicDBObject(); dbObject.put("itemId", itemId); Update update = new Update(); update.pull("items", dbObject); UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class); return result.getMatchedCount() != 0; } ``` #### 统计用户不同状态的订单数,聚合 ```java /** * 统计用户每个状态的订单数 * * @param userId * @return */ public void userStatusOrderNum(Long userId) { AggregationOperation match = Aggregation.match(Criteria.where("userId").is(userId)); AggregationOperation group = Aggregation.group("status").count().as("orderNum"); Aggregation aggregation = Aggregation.newAggregation(match, group); AggregationResults aggregate = mongoTemplate.aggregate(aggregation, LogisticsOrder.class, Map.class); for (Map map : aggregate) { System.out.println(map); } } ```