cms查詢系統(一)背景以及需求分析與設計

本人想作一個cms查詢框架,用於解決實際的業務問題,順便鍛鍊下能力 #1 背景介紹算法

在一個配置管理系統即cms系統中,有不少的實體,實體間有不少的關聯關係,這些實體就是構建成了一張網。以下圖所示: cms系統中實體間的關係圖sql

目前面臨的問題是,如何輕鬆應對其餘用戶對實體的各類各樣的查詢需求?數據庫

#2 需求整理json

站在用戶的角度來分析需求緩存

##2.1 用戶的輸入服務器

###2.1.1 用戶要查詢的數據框架

  • 可能只須要獲取某些想要的字段
  • 可能想獲取每一個實體的全部字段

###2.1.2 查詢條件線程

  • 各類各樣的查詢條件,如debug

    如 =、 !=、>、<、like、in、exists 等查詢條件,時間段查詢等
  • 查詢條件間的and or 關係,以及多層條件嵌套設計

    如 a>2 and (b>3 or c<4)

##2.2 查詢輸出

###2.2.1 字段對應的值的格式化

如某個表的type字段的值爲0或1,須要將這些0或1轉化成有意義的值,如 0表示online 1表示offline

###2.2.2 返回的數據形式的格式化

sql查詢的結果是平鋪的形式,然而返回給用戶的但願是一個格式良好的形式,因此要求必須具有格式化的功能。

目前可能的格式化形式以下所示:

  • 形式1 a下的全部的b(外層主體內容是a,而後包含一個b的集合)

    {
    	"aName":"name1",
    	"bs":[
    		{
    			"bName":"name2"
    		},
    		{
    			"bName":"name3"
    		}
    	]
    }
  • 形式2 b的信息(外層主體是b,而後包含一個a的信息)

    {
    	"bName":"name2",
    	"a":{
    		"aName":"name1"
    	}
    }
  • 形式3 a下全部的b、c(外層主體是a,而後包含一個b的集合,也包含一個c的集合)

    {
    	"aName":"name1",
    	"bs":[
    		{
    			"bName":"name2"
    		},
    		{
    			"bName":"name3"
    		}
    	],
    	"cs":[
    		{
    			"cName":"name4"
    		},
    		{
    			"cName":"name5"
    		}
    	]
    }

#3 大概的設計

  • 1 用戶查詢封裝成QueryBody

  • 2 對QueryBody進行解析,解析成sql

  • 3 根據sql查詢出對應的結果

  • 4 對sql查詢的結果進行值的格式化和形式的格式化,返回滿意的結果

##3.1 用戶查詢封裝成QueryBody

QueryBody就是配置用戶需求的地方。它的來源有兩種方式,分別以下:

  • 方式1 用戶配置QueryBody的一些參數直接進行http請求

    這種方式的狀況下,用戶須要瞭解QueryBody的配置意義,同時用戶能夠自行進行各類各樣的查詢

  • 方式2 根據用戶的查詢需求,在後臺配置QueryBody的參數,並映射對應的key,而後讓用戶拿着key來進行查詢,以下

    {
    	"key1":{QueryBody1的配置},
    	"key2":{QueryBody2的配置}
    }

    用戶拿着key1來查詢,即咱們使用key1對應的QueryBody配置來進行查詢。這種方式,用戶不須要關心QueryBody的配置,但只能按照咱們後臺所配置的參數進行查詢了。方式1就不須要維護信息,方式2就須要維護key對應的QueryBody的配置信息

以上的這兩種方式均可能會出現,因此都要支持。

##3.2 對QueryBody進行解析,解析成sql

這一部分其實就是一個sql生成器。這裏須要說明的是,咱們並非去設計一個複雜的sql生成器,而是針對cms系統常見的查詢操做可以生成sql就好了。因此不要期望這個查詢框架能自動幫你生成複雜的sql。 可是有不少地方要注意:

###3.2.1 對解析要進行緩存

方便下一次相同的請求直接跳過解析過程,加快搜索。然而一旦服務器關閉,緩存就消失,因此是否要考慮將解析緩存持久化起來,在服務器啓動的時候,就去先加載解析緩存。

同時容許清空緩存等操做

###3.2.2 普通查詢sql的幾個要素

普通sql以下所示:

select 表1.column_a,表1.column_b,表2.column_c,表3.column_d 
from  表1 表2 表3 
where 表1.column_e>4 and 表2.column_f='test'

主要分紅三大部分:

  • 第一部分 要查詢的column

    這一部分,可讓用戶本身輸入,還要進行下配置映射,避免對外暴漏數據庫中的表和字段名

    上述方式通常不多,因此大部分的時候仍是,查詢表1 表2 表3 的所有有用字段的信息,因此須要在後臺配置哪些表的哪些字段須要對外暴漏。

    上面的兩種方式也都是要支持的

  • 第二部分 表之間的鏈接關係

    一種方式就是,直接配置表與表之間的鏈接關係(簡單粗暴,可是會有不少的重複配置信息)

    另外一種方式就是,只配置兩兩表之間的鏈接關係,經過一個針對圖的算法模塊來找到表之間的鏈接關係。如博客開頭的圖片中,算法模塊可以自動計算出entity1和entity4之間的鏈接關係是 entity1->entity2->entity3->entity4。這種方式大大減小了配置的冗餘度。

    雖然算法這一塊是美好的,仍然會存在不少的問題。算法要找出最短路徑,可是最短路徑的鏈接關係不必定是用戶想要的,因此有時候又不能來依靠算法,還須要人爲的干預配置。如上圖中的entity1到entity7有2條途徑,算法只能給咱們推薦一個最短的路徑,但這並不必定是用戶想要的,因此在這個時候,咱們是須要配置使用哪條路徑的

  • 第三部分 查詢參數部分

    用戶的查詢條件就是一個json對象,咱們要把這個json對象轉化成sql中的where部分

    用戶的查詢條件是各類各樣的,同時查詢條件是能夠嵌套的,以下兩種查詢條件

    {
    	"a.name":"lg",
    	"b.age@>":24
    }

    這裏就表示查詢 where a.name='lg' and b.age>24

    {
    	"a.name":"lg",
    	"$or":{
    		"b.age@>":24,
    		"c.id@in":[1,2,3]
    	}
    }

    這裏就表示查詢 where a.name='lg' and (b.age>24 or c.id in (1,2,3) )

    因此但願可以作到上述的查詢效果,這就須要設計一系列內置的查詢參數解析器,同時方便用戶自定義擴展查詢參數解析器,來支持更加複雜的查詢

##3.3 根據sql查詢出對應的結果

這一塊就須要藉助於如Spring的JdbcTemplate來執行相應的sql,或者藉助於其餘,這就須要思考如何更加方便的接入呢?

##3.4 對sql查詢的結果進行值的格式化和形式的格式化,返回滿意的結果

sql的查詢結果是平鋪的,這時候就須要進行聚合操做,聚合操做就須要依據外層實體的主鍵做爲聚合的重要依據了。

對數據進行格式化處理,就須要遍歷查詢結果,一一進行處理。而後返回給用戶,若是用戶還要進行相應的處理操做,又要遍歷一次,形成浪費,因此該框架還要支持用戶配置一些處理操做。

#4 還涉及的問題

##4.1 日誌

  • 該框架應該不能定死所使用的日誌系統,因此須要採用slf4j這個統一的日誌接口

  • 對於程序中哪部分的日誌採用debug級別,哪部分的日誌採用info級別須要仔細考量。簡單來講就是,程序的主要執行過程採用info級別,使得咱們可以迅速定位錯誤緣由就能夠了,而一些詳細的解析過程採用debug級別就夠了。

##4.2 配置文件的監控模塊

爲了避免用重啓服務器,就能方便的發佈新的API、或者改動老的API,就須要對這些配置文件進行監控。

由於這些各類各樣的配置文件會不少,因此就須要把監控單獨寫成一個模塊,方便外界隨意的添加監控項。

同時還須要監控的總開關和每一個監控項的子開關,來隨時關閉或者打開某個監控項。

##4.3 上下文環境Context

在解析的過程當中,用戶的查詢QueryBody,會在不少地方都要用到,若是都是做爲方法的參數傳遞來傳遞去,將很是難看和難以維護,很明顯這裏就須要用到ThreadLocal形式了,將相似QueryBody和一些對應的緩存存儲到綁定的對應的線程中,經過ThreadLocal對象來隨時隨地獲取這些數據。最好是弄一個上下文環境來封裝數據。

##4.4 異常處理

配置文件加載、解析產生的異常要進行規範的自定義處理

##4.5 集成與配置

如何更加方便的使用與配置這個cms查詢框架

#5 最終達成的效果

仍是如文章最上面的圖

1 用戶輸入以下:

{
	"entites":["entity1","entity2s@listentity2","entity3s@listentity3"],
	"params":{
		這裏放置查詢參數
	}
}

則表明查詢的是全部的entity1,以及它所包含的entity2和entity3,返回的數據格式是以下形式的:

{
	"entity1Name":"aaa",
	"entity2s":[
		{"entity2Name":"xxx"},
		{"entity2Name":"xxx"}
	],
	"entity3s":[
		{"entity3Name":"xxx"}
	]
}

2 若是用戶輸入以下:

{
	"entites":["entity2","entity1@mapentity1"],
	"params":{
		這裏放置查詢參數
	}
}

則表示用戶想查詢entity2的信息,而且想知道每一個entity2所屬的entity1信息,是以下格式的:

{
	"entity2Name":"aaa",
	"entity1":{
		"entity1Name":"xxx"
	}
}
相關文章
相關標籤/搜索