java mongodb 多表關聯查詢,多條件查詢,分頁,排序

前言:

        由於最近項目趕,版本迭代快,不知道大BOSS從哪裏聽別人說MongoDB用來做關係型數據庫好,而且速度快,性能高;聽到這話的我,立馬就反駁了回去:“MongoDB不支持事物”!
        結果查閱了大量資料,反被大BOSS啪啪打臉…,哎…,最終還是本着爲大BOSS服務和學習的態度,技術選型上,關係型數據庫由MySQL轉變成爲MongoDB-4.2
        由於一時半會兒還不太適應MongoDB的一些寫法,查閱了大量的資料才搞定MongoDB在java中的實現方式,此次運用到的是spring的一個插件spring-data-mongodb,版本號:2.2.6.RELEASE

使用spring-data-mongodb對mongo進行聯表分頁查詢

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

1、一對一:兩張表關聯查詢,帶多條件查詢,分頁,排序

傳統的MongoDB的SQL語句實現,如下:

db.shop.aggregate([
    {
        "$lookup":{
            "from":"member",		// 從表表名
            "localField":"userId",	// 如shop被查詢主表的userId,相對於member表的外鍵
            "foreignField":"_id",   // 如member從表的主鍵_id,相對於member表的主鍵
            "as":"docs_member"		// 聯合查詢出的別名,用於多條件查詢表明前綴,相當於SQL中的臨時表名
        }
    },
    {
        "$match":{
            "shopStatus":1,
            "blockade":{
            	// in包含查詢
                "$in":[
                    0,
                    null
                ]
            },
            "dataFlag":1
        }
    },
    {
        "$skip":{
            "$numberLong":"0"
        }
    },
    {
        "$limit":{
            "$numberLong":"10"
        }
    },
    {
        "$sort":{
            "modifyTime":-1
        }
    }
])

Java中實現以上寫法,如下:

package /**這裏是包名**/;

import com.sszh.mall.shop.entity.ShopDO;
import org.bson.types.ObjectId;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.regex.Pattern;

import org.springframework.data.mongodb.core.MongoTemplate;

public class ShopServiceImpl {

    @Autowired
    protected MongoTemplate mongoTemplate;

    // 一對一:兩表關聯查詢
    private List<ShopDO> twoTableQuery(String keyword, int pageNum, int pageSize) {
        // TODO 店長是否實名認證
        // 構建 Aggregation:添加查詢條件
        Aggregation aggregation = Aggregation.newAggregation(
                // 關聯member表
                Aggregation.lookup(
                        "member",           // 從表表名
                        "userId",           // 如shop被查詢主表的userId,相對於member表的外鍵
                        "_id",              // 如member從表的主鍵_id,相對於member表的主鍵
                        "docs_member"       // 聯合查詢出的別名,用於多條件查詢表明前綴,相當於SQL中的臨時表名
                ),
                // ============================================= 以上內容可舉一反三 =============================================
                // 查詢條件
                null == keyword || "".equals(keyword.trim())
                        ?
                        Aggregation.match(
                                Criteria.where("shopStatus").is(1)                                                      // 店鋪狀態: 1:已審覈
                                        .and("blockade").in(0, null)                                                    // 店鋪是否有效: 0=解封  -1=封鎖
                                        .and("dataFlag").is(1)                                                          // 店鋪是否有效: 1:有效
                                        .and("docs_member.telephone").is("8615323710096")                               // 添加member表查詢條件,如用戶手機號,此處可舉一反三
                        )
                        :
                        Aggregation.match(
                                Criteria.where("shopStatus").is(1)
                                        .and("blockade").in(0, null)
                                        .and("dataFlag").is(1)
                                        // 根據店鋪名稱模糊搜索
                                        .andOperator(Criteria.where("shopName").regex(Pattern.compile(keyword, Pattern.CASE_INSENSITIVE)))
                        ),
                // 分頁:頁碼
                Aggregation.skip(Long.valueOf(pageNum)),
                // 分頁:條數
                Aggregation.limit((long) pageSize),
                // 排序
                Aggregation.sort(Sort.Direction.DESC,"modifyTime")
        );
        // 執行查詢,這裏的shop必須是查詢的主表名
        AggregationResults<ShopDO> results = mongoTemplate.aggregate(aggregation, "shop", ShopDO.class);
        return results.getMappedResults();
    }

補充語法:

一對多關聯查詢(商品表爲主表、取商品規格集合):

db.mall_goods.aggregate([
   {
     $lookup:
       {
         from: "mall_specs",
         localField: "id",
         foreignField: "goods_id",
         as: "inventory_docs"
       }
  }
]);

一對多關聯查詢(訂單表爲主表,根據商品ID取商品名稱、商品規格集合):

db.mall_order.aggregate([
  {
    $lookup:
      {
        from: "mall_goods",
        localField: "id",
        foreignField: "goods_id",
        as: "goods"
      }
 },
  {
    $lookup:
      {
        from: "mall_specs",
        localField: "goods_id",
        foreignField: "goods_id",
        as: "specs"
      }
 }
])

2、高級聚合查詢:排序:根據商品【銷售量 + 虛擬交易量】之和,進行倒敘排序(DESC):-1 降序 1 升序; 再根據價格,由小到大排序

傳統的MongoDB的SQL語句實現,如下:

db.product.aggregate([
    {
        $project: {
            _id : 1,
            // ---------------- 部分代碼省略 ----------------
            shopId: 1,
            "saleNumCount" : {
                $sum: ["$saleNum", "$salesVolumeNum"]
            }
        }
    },
    {
        $sort: {"saleNumCount" : -1},
        $sort: {"price" : 1}
    }
    // ---------------- 分頁代碼省略(注意:這整塊SQL語句都有先後順序要求) ----------------
])

備註:

  • $project 屬性:設置返回指定字段
  • 語句【 _id : 1 】解釋:此處的 1 代表返回,0 代表不返回
  • 語句【 $sort: {“saleNumCount” : -1} 】解釋:根據統計結果從大到小排序
  • 語句【 $sum: ["$saleNum", “$salesVolumeNum”] 】解釋:計算 【虛擬交易量 + 交易量】,發回字段 saleNumCount

Java中實現以上寫法,如下:

package /*包名省略*/

import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.sszh.common.core.MongoOperator;
import com.sszh.mall.product.entity.ProductDO;	/*這個是返回的實體對象,不指定也可以*/
import com.sszh.mall.product.repository.ProductRepository;
import com.sszh.mall.shop.entity.ShopDO;
import com.sszh.mongodb.springdata.BaseMongoRepository;
import com.sszh.utils.DateUtil;
import com.sszh.utils.StringUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.*;

public class ProductRepositoryImpl implements ProductRepository {

	@Autowired
    protected MongoTemplate mongoTemplate;

    public MongoTemplate getDatastore(){
        return mongoTemplate;
    }

	@Override
	public List<ProductDO> getProductListByWhere(int categoryId,int pageNum,int pageSize) {
        List<Document> pipeline = new ArrayList<>();
        // 設置返回指定字段,1 返回   0/不指定 不返回
        Document basicDBObject = new Document();
        basicDBObject.put("_id", 1);
        basicDBObject.put("shopId", 1);
        basicDBObject.put("categoryId", 1);
        basicDBObject.put("shieldSearch", 1);
        basicDBObject.put("productName", 1);
        basicDBObject.put("productPic", 1);
        basicDBObject.put("price", 1);
        basicDBObject.put("fare", 1);
        basicDBObject.put("profit", 1);
        basicDBObject.put("warnStock", 1);
        basicDBObject.put("productStock", 1);
        basicDBObject.put("isSale", 1);
        basicDBObject.put("isBest", 1);
        basicDBObject.put("productDescTitle", 1);
        basicDBObject.put("productDescEnd", 1);
        basicDBObject.put("saleNum", 1);
        basicDBObject.put("saleTime", 1);
        basicDBObject.put("dataFlag", 1);
        basicDBObject.put("praiseNum", 1);
        basicDBObject.put("forwardNum", 1);
        basicDBObject.put("attributes", 1);
        basicDBObject.put("forwardCommission", 1);
        basicDBObject.put("createTime", 1);
        basicDBObject.put("modifyTime", 1);
        basicDBObject.put("salesVolumeNum", 1);
        // 計算 【虛擬交易量  + 交易量】之和,返回字段 saleNumCount
        basicDBObject.put("saleNumCount", new BasicDBObject("$sum", Arrays.asList("$saleNum", "$salesVolumeNum")));
        Document project = new Document(MongoOperator.PROJECT, basicDBObject);
        pipeline.add(project);
        // 排序:根據商品【銷售量 + 虛擬交易量】之和,進行倒敘排序(DESC):-1 降序  1 升序;  再根據價格,由小到大排序
        basicDBObject = new Document();
        basicDBObject.put("saleNumCount", -1);
        basicDBObject.put("price", 1);
        Document sort = new Document(MongoOperator.SORT, basicDBObject);
        pipeline.add(sort);
        // 查詢條件
        basicDBObject = new Document();
        basicDBObject.put("isSale", 1);		//查詢條件,狀態是否可用
        basicDBObject.put("dataFlag", new BasicDBObject(MongoOperator.NE, -1));		//商品是否被刪除
        basicDBObject.put("shieldSearch", new BasicDBObject(MongoOperator.NE, 1));	//商品是否可以搜索
        Document match = new Document(MongoOperator.MATCH, basicDBObject);		//將查詢條件添加到match中
        pipeline.add(match);
        // 分頁:開始位置
        Document skip = new Document(MongoOperator.SKIP, (pageNum * pageSize));
        pipeline.add(skip);
        // 分頁:結束位置
        Document limit = new Document(MongoOperator.LIMIT, ((pageNum + 1) * pageSize));
        pipeline.add(limit);
        // 執行聚合查詢操作:獲取數據庫連接、指定表名:product,發回結果集:cursor 
        final MongoCursor<Document> cursor = getDatastore().getCollection("product").aggregate(pipeline).iterator();
        // 處理結果集
        List<ProductDO> list = new ArrayList<>();
        ProductDO productDO;
        while (cursor.hasNext()) {
            Document dbObject = cursor.next();
            productDO = JSONObject.toJavaObject(JSONObject.parseObject(JSONObject.toJSONString(dbObject)), ProductDO.class);
            productDO.setId((ObjectId)dbObject.get("_id"));
            list.add(productDO);
        }
        return list;
    }



}

mongodb 關鍵詞常量類:

package com.sszh.common.core;

public final class MongoOperator {

    // id
    public static final String ID = "_id";

    // query condition
    public static final String GT = "$gt";
    public static final String GTE = "$gte";
    public static final String LT = "$lt";
    public static final String LTE = "$lte";
    public static final String NE = "$ne";
    public static final String IN = "$in";
    public static final String NIN = "$nin";
    public static final String MOD = "$mod";
    public static final String ALL = "$all";
    public static final String SLICE = "$slice";
    public static final String SIZE = "$size";
    public static final String EXISTS = "$exists";
    public static final String WHERE = "$where";
    public static final String REGEX ="$regex";

    // query logic
    public static final String AND = "$and";
    public static final String OR = "$or";

    // 2d and geo
    public static final String NEAR = "$near";
    public static final String WITHIN = "$within";
    public static final String CENTER = "$center";
    public static final String BOX = "$box";

    // update
    public static final String SET = "$set";
    public static final String UNSET = "$unset";
    public static final String INC = "$inc";
    public static final String MUL = "$mul";
    public static final String PUSH = "$push";
    public static final String PULL = "$pull";
    public static final String EACH = "$each";
    public static final String POP = "$pop";
    public static final String MIN = "$min";
    public static final String MAX = "$max";
    public static final String BIT = "$bit";

    // aggregation
    public static final String PROJECT = "$project";
    public static final String MATCH = "$match";
    public static final String LIMIT = "$limit";
    public static final String SKIP = "$skip";
    public static final String UNWIND = "$unwind";
    public static final String GROUP = "$group";
    public static final String SORT = "$sort";

}
































參考文獻:
spring-data-mongodb官方參考文檔:
https://docs.spring.io/spring-data/mongodb/docs/3.0.0.RELEASE/reference/html/#mongodb.repositories.queries.aggregation

mongoTemplate.aggregate() 聚合查詢,關聯查詢:
https://blog.csdn.net/C18298182575/article/details/100698885

使用spring-data-mongodb對mongo進行聯表分頁查詢:
https://blog.csdn.net/qazwsx081/article/details/97897187

Spring Data Mongodb多表關聯查詢:
https://blog.csdn.net/zhang135123/article/details/85273957

SpringBoot整合MongoDB實現聚合查詢(多表聯查)以及一套簡單CRUD:
https://blog.csdn.net/weixin_44530530/article/details/91901631

MongoDB的多表關聯查詢:
https://blog.csdn.net/watersprite_ct/article/details/78500997

Spring Data Mongodb多表關聯查詢:
https://blog.csdn.net/zhang135123/article/details/85273957

Java使用mongodb進行數據存儲及多表關聯,多條件查詢:
https://blog.csdn.net/lchq1995/article/details/102586243

Java mongodb複雜多條件查詢:
https://blog.csdn.net/xiaTianCsDN/article/details/96116338?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

Spring Data MongoDB-參考文檔:
https://docs.spring.io/spring-data/mongodb/docs/2.2.3.RELEASE/reference/html/#mapping-usage


























注:以上內容僅提供參考和交流,請勿用於商業用途,如有侵權聯繫本人刪除!


持續更新中…

如有對思路不清晰或有更好的解決思路,歡迎與本人交流,微信:seesun2012(非緊急項目請加QQ羣解答),QQ羣:273557553
你遇到的問題是小編創作靈感的來源!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章