目錄
一、DSL 查詢文檔語法
前言
1.1、DSL Query 基本語法
1.2、全文檢索查詢
1.2.1、match 查詢
1.2.2、multi_match
1.3、精確查詢
1.3.1、term 查詢
1.3.2、range 查詢
1.4、地理查詢
1.4.1、geo_bounding_box
1.4.2、geo_distance
1.5、復(fù)合查詢
1.5.1、相關(guān)性算分
1.5.2、function_score
1.5.3、boolean query
1.6、搜索結(jié)果處理
1.6.1、排序
1.6.2、分頁
重點:深度分頁問題和解決方案
1)深度分頁問題描述
2)深度分頁問題的解決方案
search after(官方推薦)
scroll(es 7.1 后,官方已經(jīng)不推薦使用了)
1.6.3、高亮
1.6.4、搜索語法匯總語法
一、DSL 查詢文檔語法
前言
本文中的案例,繼續(xù)延續(xù)上一章節(jié)的酒店數(shù)據(jù).
1.1、DSL Query 基本語法
基本查詢語法如下:
GET?/索引庫名/_search
{
??"query":?{
????"查詢類型":?{
??????"查詢條件":?"條件值"
????}
??}
}
例如查詢酒店所有數(shù)據(jù)(一般生產(chǎn)環(huán)境下不會這么做,因為數(shù)據(jù)量有可能非常大,所以查詢非常耗時,因此一般用于測試用)
GET /hotel/_search
{
"query": {
"match_all": {
//由于這里是查詢所有數(shù)據(jù),因此沒有查詢條件
}
}
}
1.2、全文檢索查詢
全文檢索查詢,會通過 分詞器 對用戶輸入內(nèi)容進行分詞處理,然后去倒排索引庫中匹配.? 經(jīng)常用于搜索框的搜索,例如百度搜索框.
1.2.1、match 查詢
全文檢索查詢中的一種,會對用戶輸入內(nèi)容進行分詞,然后去倒排索引庫中檢索,語法如下:
GET?/索引庫名/_search
{
??"query":?{
????"match":?{
??????"字段名":?"TEXT文本內(nèi)容"
????}
??}
}
例如查詢 “如家酒店” 相關(guān)的數(shù)據(jù).
GET /hotel/_search
{
"query": {
"match": {
"all": "北京如家酒店"
}
}
}
Ps:為了提高查詢效率,all 這里通過 copy_to 拷貝了多個字段,例如 name、city....
自定義 all 字段的解讀:?
- 將來 name、address、brand... 這些字段大概率都需要參與搜索,也就意味著用戶輸入的的關(guān)鍵字,我們后端都需要根據(jù)多個字來搜,并且我們可以想象以下 es 作搜索的時候, 根據(jù)多個字段去搜索的效率肯定是要比一個字段搜索效率要低,這里對比以下數(shù)據(jù)庫就清楚了~
- 最重要的是, 我們也希望用戶輸入名稱就能搜到相關(guān)的內(nèi)容, 用戶輸入品牌也能搜到相關(guān)內(nèi)容... es 就中有一個字段 "copy_to", 就是將當(dāng)前字段的值拷貝到指定字段。這里我們就將需要搜索的字段都拷貝到 all 這個字段中就 ok ,實現(xiàn)了在一個字段里, 搜索到多個字段的內(nèi)容.
- 而且這里還做了優(yōu)化,并不是真的吧文檔拷貝進去,而是創(chuàng)建索引,將來你去查的時候,是看不到這些字段,但搜卻能搜到(類似于根據(jù)指針找到數(shù)據(jù)所在位置).
?
例如以下索引庫
PUT /article
{
"mappings": {
"properties": {
"id": {
"type": "long",
"index": false
},
"boardId": {
"type": "long",
"index": false
},
"userId": {
"type": "long",
"index": false
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all" # 拷貝
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all" # 拷貝
},
"content": {
"type": "text",
"analyzer": "ik_smart",
"copy_to": "all" # 拷貝
},
"deleteState": {
"type": "integer",
"index": false
},
"visits": {
"type": "long",
"index": false
},
"likeCount": {
"type": "integer",
"index": false
},
"commentCount": {
"type": "integer",
"index": false
},
"collectCount": {
"type": "integer",
"index": false
},
"createTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss",
"index": false
},
"updateTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss",
"index": false
},
"all": { # 拷貝字段
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
1.2.2、multi_match
與 match 查詢類似,只不過允許同時查詢多個字段.?
Ps:多個字段查詢也意味著查詢性能低下,建議可以使用 copy_to 將其他字段的索引拷貝到一個字段,可以提升查詢效率.
語法如下:文章來源:http://www.zghlxwxcb.cn/news/detail-730265.html
GET?/indexName/_search
{
??"query":?{
????"multi_match":?{
??????"query":?"TEXT",
??????"fields":?["字段名1",?" 字段名2"]
????}
??}
}
例如根據(jù) name、和 city 字段查詢酒店數(shù)據(jù).
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "上海如家酒店",
"fields": ["name", "city"]
}
}
}
1.3、精確查詢
精確查詢一般是查找 keyword、數(shù)值、日期、boolean 等類型字段,因此 不會 對搜索條件進行分詞.
例如你在淘寶里買東西的時候,要通過一些信息進行篩選,比如 銷量、信譽、價格升序... 這些詞一般都是固定一個按鈕,點擊就會幫你做篩選.
1.3.1、term 查詢
term 查詢主要是根據(jù) 詞條 精確值進行查詢,一般搜索 keyword 類型、數(shù)值類型、布爾類型、日期類型字段.
語法如下:
//?term查詢
GET?/索引名/_search
{
??"query":?{
????"term":?{
??????"字段名":?{
????????"value":?"字段值"
??????}
????}
??}
}
或者簡化為
GET?/索引名/_search
{
??"query":?{
????"term":?{
??????"字段名":?"字段值"
????}
??}
}
例如我要搜索在上海的所有酒店(這里 city 就是 keyword).
GET /hotel/_search
{
"query": {
"term": {
"city": {
"value": "上海"
}
}
}
}
或者
GET /hotel/_search
{
"query": {
"term": {
"city": "上海"
}
}
}
1.3.2、range 查詢
根據(jù)數(shù)值的范圍查詢,可以是數(shù)值、日期的范圍.
語法如下:
//?range查詢
GET?/indexName/_search
{
??"query":?{
????"range":?{
??????"字段名":?{
????????"gte":?10, //大于等于 10
????????"lte":?20 //小于等于 20
??????}
????}
??}
}
或者
GET?/indexName/_search
{
??"query":?{
????"range":?{
??????"字段名":?{
????????"gt":?10, //大于 10
????????"lt":?20 //小于 20
??????}
????}
??}
}
例如我要查詢價格大于等于 161,?小于等于 300 的酒店.
1.4、地理查詢
根據(jù)經(jīng)緯度查詢,例如?
- 滴滴:搜索附近的出租出.
- 攜程:搜索附近的酒店.
- 微信:搜索附近的人.
1.4.1、geo_bounding_box
geo_bounding_box 用來查詢 geo_point 值在某個矩形范圍的所有文檔.
//?geo_bounding_box查詢
GET?/索引庫名/_search
{
??"query":?{
????"geo_bounding_box":?{
??????"字段名":?{
????????"top_left":?{
??????????"lat":?31.1, //緯度
??????????"lon":?121.5 //經(jīng)度
????????},
????????"bottom_right":?{
??????????"lat":?30.9,
??????????"lon":?121.7
????????}
??????}
????}
??}
}
Ps:這個不是很常用
1.4.2、geo_distance
查詢到指定中心點小于某個距離值的所有文檔.
語法如下:
//?geo_distance 查詢
GET?/索引庫名/_search
{
??"query":?{
????"geo_distance":?{
??????"distance":?"15km",//半徑長度
??????"FIELD":?"31.21,121.5" //緯度,經(jīng)度
????}
??}
}
例如查詢緯度 31.21,經(jīng)度 121.5?,半徑 10km 畫圓內(nèi)的酒店.
1.5、復(fù)合查詢
復(fù)合查詢,可以將其他簡單的查詢組合起來,實現(xiàn)更復(fù)雜的搜索邏輯.
例如 function score(算分函數(shù)查詢),可以控制文檔相關(guān)性算分,控制文檔排名,比如你在百度搜索 “不孕不育”,指定的肯定是廣告~? 為什么?人家給的錢到位~
1.5.1、相關(guān)性算分
當(dāng)我們使用 match 查詢的時候,查詢的結(jié)果會根據(jù)與搜索詞條的關(guān)聯(lián)度打分(_score),返回結(jié)果時按照分值降序排列.
在?elasticsearch 5.0 之前,使用 TF-IDF 算法,會隨著詞頻增加而越來越大.
在 elasticsearch 5.0 之后,使用 BM25 算法,分值會隨著詞頻增加而增大,但增長曲線會趨于水平.
1.5.2、function_score
使用 function_score ,可以修改文檔的相關(guān)性算分,根據(jù)新得的算分排序.
由于這里語法比較復(fù)雜,先給出個示例,如下
GET /hotel/_search
{
"query": {
"function_score": {
"query": {"match": {"city": "上海"}},
"functions": [
{
"filter": {"term": {"id": "1"}},
"weight": 10
}
],
"boost_mode": "multiply"
}
}
}
- "query": {"match": {"city": "上海"}}:原始查詢條件,搜索文檔并根據(jù)相關(guān)性打分.
- "filter": {"term": {"id": "1"}}:過濾條件,符合條件的文檔才會背重新算分.
- "weight": 10:算分函數(shù),將來會與 query score 進行運算,得到新算分,常見的算分函數(shù)有
- weight:給一個常量值,作為函數(shù)的結(jié)果.
- field_value_factor:用文檔中的某個字段值作為函數(shù)結(jié)果.
- random_score:隨機生成一個值,作為函數(shù)的結(jié)果.
- script_score:自定義計算公式,公式結(jié)果作為函數(shù)結(jié)果.
- "boost_mode": "multiply":加權(quán)模式,定義 function score 和 query score 運算方式,包括
- multiply(默認(rèn)):兩者相乘.
- replace:sum、avg、max、min.
例如給 “如家” 這個品牌的酒店排名靠前一些.
那么這里只需要明確以下幾個點即可:
- 品牌:"如家"
- 算分函數(shù):weight 就可以.
- 加權(quán):求和.
GET /hotel/_search
{
"query": {
"function_score": {
"query": {"match_all": {}},
"functions": [ //算分函數(shù)
{
"filter": {"term": { //需要滿足的條件: 品牌必須是如家
"brand": "如家"
}},
"weight": 3 //算分權(quán)重為 3
}
],
"boost_mode": "sum" //加和算分
}
}
}
1.5.3、boolean query
布爾查詢是一個或多個查詢子句的組合. 子查詢的組合方式有:
- must:必須匹配的查詢條件,類似 “與”.
- should:選擇性匹配的查詢條件,類似 “或”.
- must_not:必須不匹配,不參與算分,類似 “非”.
- filter:必須匹配,不參與算分.
例如,查詢城市在上海,品牌可以是 "皇冠假日" 或者是 “華美達”,價格必須不小于等于 500,并且評分大于等于 45 的.
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{"term": {
"city": "上海"
}}
],
"should": [
{"term": {"brand": "皇冠假日"}},
{"term": {"brand": "華美達"}}
],
"must_not": [
{"range": {
"price": {
"lte": 500
}
}}
],
"filter": [
{"range": {
"score": {
"gte": 45
}
}}
]
}
}
}
1.6、搜索結(jié)果處理
1.6.1、排序
es 支持對搜索結(jié)果進行排序處理,默認(rèn)是根據(jù)相關(guān)度算分(_score)來排序.? 可以排序的字段類型有(不分詞):keyword 類型、數(shù)值類型、地理坐標(biāo)類型、日期類型.
語法如下:
GET?/索引庫名/_search
{
??"query":?{
????"match_all":?{} //搜索內(nèi)容
??},
??"sort":?[
????{
??????"字段名":?"desc"??//?排序字段和排序方式ASC、DESC
????}
??]
}
如果要根據(jù)經(jīng)緯度排序,語法如下:
GET?/索引庫名/_search
{
??"query":?{
????"match_all":?{}
??},
??"sort":?[
????{
??????"_geo_distance"?:?{
??????????"字段名"?:?"緯度,經(jīng)度",
??????????"order"?:?"asc",
??????????"unit"?:?"km"
??????}
????}
??]
}
例如對酒店用戶評價降序排序,評價相同的按照價格升序排序.
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": {
"order": "desc"
},
"price": {
"order": "asc"
}
}
]
}
1.6.2、分頁
elasticsearch 默認(rèn)情況下只會返回 top 10 的數(shù)據(jù).? 如果要查詢更多數(shù)據(jù),就需要修改分頁參數(shù)了.
在 es 中通過 from(偏移量,分頁開始的位置,默認(rèn)為 0) 和?size(期望獲取的文檔總數(shù)) 參數(shù)控制返回的分頁結(jié)果,這里和 mysql 中的 limit 本質(zhì)是一樣的.
例如獲取 20 ~ 29 這 10 條數(shù)據(jù).
GET?/hotel/_search
{
??"query":?{
????"match_all":?{}
??},
??"from":?20,?//?分頁開始的位置,默認(rèn)為0
??"size":?10,?//?期望獲取的文檔總數(shù)
??"sort":?[
????{"price":?"asc"}
??]
}
重點:深度分頁問題和解決方案
1)深度分頁問題描述
ES 是支持分布式的,所以會存在深度分頁的問題.
例如按照 price 排序后,獲取 from = 990, size = 10 的數(shù)據(jù).
1. 首先在每個數(shù)據(jù)分片上都排序并查詢前 1000 條文檔.
2. 將所有節(jié)點的數(shù)據(jù)聚合(假設(shè)有 5 個分片,相當(dāng)于就有 5000 條文檔),在內(nèi)存中重新排序選出前 1000 條文檔.
3. 最后從這 1000 條文檔中,選取 990 開始往后的 10 條數(shù)據(jù).
打個比方,就類似于你在學(xué)校,有 10 個年級(10 個分片),你需要從這 10 個年級中找出排名前 100 的學(xué)生,那么由于考試的排名是以班級為單位的,因此,你就需要從每個班里都找出 排名前100 的學(xué)生,那么 10 個年級就意味著需要拿出 1000 個學(xué)生,然后再讓他們進行一次考試,才能拿到排名前 10 的學(xué)生.
那么如果搜索頁數(shù)過深,或者結(jié)果集過大,對 內(nèi)存、CPU、網(wǎng)絡(luò)帶寬(ES 也是一個單獨的服務(wù)器) 的消耗也越高,因此 es 設(shè)定結(jié)果集查詢的上限為?10000.
2)深度分頁問題的解決方案
有兩種辦法,可以使得查詢數(shù)據(jù)沒有上限:
search after(官方推薦)
步驟如下:
a)查詢進行分頁時,也要使用 sort 對數(shù)據(jù)進行排序,并且這里根據(jù)排序的值需要唯一(否則查詢出來的文檔數(shù)據(jù)就是未定義的),例如 id、創(chuàng)建時間....
例如通過 “文章的創(chuàng)建時間” 分頁查詢文章數(shù)據(jù)
GET article/_search
{
"query": {
"match_all": {}
},
"size": 2,
"sort": [
{
"createTime": {
"order": "desc"
}
}
]
}
b)查詢出來的結(jié)果中就包含了 sort 排序值,如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "article",
"_type" : "_doc",
"_id" : "8",
"_score" : null,
"_source" : {
"id" : 8,
"boardName" : "灌水",
"userId" : 1,
"title" : "分布式事務(wù)理論",
"desc" : "這是描述這是描述這是描述這是描述這是描述",
"content" : "這是正文這是正文這是正文這是正文這是正文這是正文這是正文這是正文這是正文",
"deleteState" : 0,
"createTime" : "2023-11-18 17:57:04",
"updateTime" : "2023-11-18 17:57:04",
"nickname" : "伊利小奶子",
"photoUrl" : null
},
"sort" : [
1700330224000
]
},
{
"_index" : "article",
"_type" : "_doc",
"_id" : "4",
"_score" : null,
"_source" : {
"id" : 4,
"boardName" : "生活",
"userId" : 1,
"title" : "我的生活",
"desc" : "生活是一個廣泛而深刻的主題,涵蓋了人類日常生活中的方方面面。它不僅包括人們的日常生活習(xí)慣、文化背景、價值觀和人生經(jīng)驗,還包括人們對于生活的態(tài)度和追求。",
"content" : "生活是一個復(fù)雜而美好的旅程,充滿了無數(shù)的可能性和機遇。每個人都有自己獨特的生活方式和生活經(jīng)驗,這些構(gòu)成了每個人的人生。生活不僅包括我們每天所做的事情,還包括我們的思想、情感和信仰。生活中的每一天都是一次新的開始,每一次的經(jīng)歷都會讓我們更加成熟和有智慧。在生活中,我們需要學(xué)會面對各種挑戰(zhàn)和困難,同時也要珍惜每一個美好的瞬間。",
"deleteState" : 0,
"nickname" : "伊麗莎白",
"photoUrl" : null,
"createTime" : "2023-11-15 12:40:30",
"updateTime" : "2023-11-15 12:40:30"
},
"sort" : [
1700052030000
]
}
]
}
}
c)下一步,就可以取出剛剛分頁查詢出來的結(jié)果中的最后一條數(shù)據(jù),拿到其中的 sort 排序值,用于下一次查詢時 search_after 的參數(shù),實現(xiàn)抓取下一頁的數(shù)據(jù).??
GET article/_search
{
"query": {
"match_all": {}
},
"size": 2,
"search_after": [1700052030000], # 這里是一個數(shù)組,因為可能排序的字段不止一個(此處只有一個)
"sort": [
{
"createTime": {
"order": "desc"
}
}
]
}
查詢結(jié)果如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "article",
"_type" : "_doc",
"_id" : "3",
"_score" : null,
"_source" : {
"id" : 3,
"boardName" : "筆記",
"userId" : 1,
"title" : "了解cookie",
"desc" : "Cookie是一種存儲在用戶計算機上的小型文本文件,通常用于跟蹤用戶在Web應(yīng)用程序中的活動和記錄用戶的偏好和狀態(tài)。",
"content" : "Cookie是由Web服務(wù)器發(fā)送到用戶瀏覽器并存儲在用戶計算機上的小型文本文件。當(dāng)用戶再次訪問同一個網(wǎng)站時,瀏覽器會將Cookie信息發(fā)送回服務(wù)器,以便服務(wù)器能夠識別用戶并提供個性化的內(nèi)容和功能。Cookie通常在用戶登錄時被使用。在登錄過程中,服務(wù)器會在用戶的計算機上創(chuàng)建一個新的Cookie,并將其發(fā)送到用戶的瀏覽器。",
"deleteState" : 0,
"nickname" : "王爺",
"photoUrl" : null,
"createTime" : "2023-11-15 12:30:30",
"updateTime" : "2023-11-15 12:30:30"
},
"sort" : [
1700051430000
]
},
{
"_index" : "article",
"_type" : "_doc",
"_id" : "2",
"_score" : null,
"_source" : {
"id" : 2,
"boardName" : "文章",
"userId" : 1,
"title" : "分布式Session登錄",
"desc" : "分布式Session登錄是一種在分布式系統(tǒng)中實現(xiàn)用戶登錄狀態(tài)管理的技術(shù)。它通過在多個服務(wù)器之間共享用戶的登錄信息,使得用戶在訪問不同的服務(wù)器時,能夠保持一致的登錄狀態(tài)。",
"content" : "分布式Session登錄的實現(xiàn)需要解決兩個主要問題:如何在多個服務(wù)器之間共享用戶的登錄狀態(tài),以及如何保證用戶數(shù)據(jù)的隱私和安全。一種常見的解決方案是使用Cookie和Session ID。當(dāng)用戶登錄成功后,服務(wù)器生成一個唯一的Session ID,并將其存儲在用戶的Cookie中。",
"deleteState" : 0,
"nickname" : "王爺",
"photoUrl" : null,
"createTime" : "2023-11-15 12:20:30",
"updateTime" : "2023-11-15 12:20:30"
},
"sort" : [
1700050830000
]
}
]
}
}
d)之后的每次翻頁都只需要繼續(xù)重復(fù)上述步驟.
缺陷:
但是這種方法的缺點就是 只能向后逐頁查詢,不支持隨機翻頁,適合沒有隨機翻頁需求的搜索,例如手機向下翻頁.
當(dāng)然對于想向前翻頁的問題,比較流行的一種處理方式就是無線滾動(很多網(wǎng)站都在使用,例如 CSDN、掘金、知乎......).? 例如剛開始顯示 10 條數(shù)據(jù),當(dāng)用戶滑到底部的時候,就查詢下一次分頁的結(jié)果,并且讓前端保留之前分頁的結(jié)果即可.
scroll(es 7.1 后,官方已經(jīng)不推薦使用了)
原理就是將排序數(shù)據(jù)形成快照,保存再內(nèi)存.?
缺點很明顯,就是消耗額外內(nèi)存,只適用于分頁數(shù)據(jù)不多的情況,可以實現(xiàn)隨機翻頁.
1.6.3、高亮
高亮就是指再搜索結(jié)果中把搜索關(guān)鍵字突出顯示.? 例如你在百度中搜索 "不孕不育",就會把搜索結(jié)果中所有出現(xiàn) Java 關(guān)鍵字的內(nèi)容標(biāo)紅.
原理如下:
- 給搜索結(jié)果中的關(guān)鍵字添加 HTML 標(biāo)簽處理.
- 在頁面中給標(biāo)簽添加 css 樣式.
語法如下:
GET?/索引庫/_search
{
??"query":?{
????"match":?{
??????"字段名":?"要搜索的文本" //注意!默認(rèn)情況下,搜索的字段必須要于高亮的字段一致,否則不會高亮
????}
??},
??"highlight":?{
????"fields":?{?//?指定要高亮的字段
??????"字段名":?{
????????"pre_tags":?"<em>",??//?用來標(biāo)記高亮字段的前置標(biāo)簽
????????"post_tags":?"</em>"?//?用來標(biāo)記高亮字段的后置標(biāo)簽
??????}
????}
??}
}
例如,我要搜索品牌是如家,并且讓 "如家" 字段高亮.?
Ps:默認(rèn)情況下,es 中搜索的字段必須要于高亮的字段一致,否則不會高亮
但是如果你就是想讓搜索的字段和高亮字段不一致,那可以添加?"require_field_match": "false" 的屬性,如下:
1.6.4、搜索語法匯總語法
語法如下:
GET?/hotel/_search
{
??"query":?{
????"match":?{
??????"brand":?"如家"
????}
??},
??"from":?20,?//?分頁開始的位置
??"size":?10,?//?期望獲取的文檔總數(shù)
??"sort":?[?
????{??"price":?"asc"?},?//?升序排序
????{
??????"_geo_distance"?:?{?//?距離排序
??????????"location"?:?"31.04,121.61",?
??????????"order"?:?"asc",
??????????"unit"?:?"km"
??????}
????}
??],
??"highlight":?{
????"fields":?{?//?高亮字段
??????"name":?{
? "require_field_match": "false", //高亮字段不受查詢字段限制
????????"pre_tags":?"<em>",??//?用來標(biāo)記高亮字段的前置標(biāo)簽
????????"post_tags":?"</em>"?//?用來標(biāo)記高亮字段的后置標(biāo)簽
??????}
????}
??}
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-730265.html
到了這里,關(guān)于ElasticSearch - DSL查詢文檔語法,以及深度分頁問題、解決方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!