01.elasticsearch基本用法

elasticsearch介紹

我們建設(shè)了一個(gè)網(wǎng)站或者程序之后含衔,希望添加搜索功能职员,發(fā)現(xiàn)搜索功能的工作很難拒贱,主要有以下幾點(diǎn)

image

elasticsearch 百度介紹

image

可以看到官網(wǎng)上列出了使用 elasticsearch 的公司有哪些:https://www.elastic.co/use-cases

事實(shí)上瘾境,除了 elasticsearch 铐刘,全文搜索引擎還有很多陪每,如 solr、sphinx 等镰吵。近年來隨著 ELK 日志分析系統(tǒng)的流行檩禾,elasticsearch 才逐漸被大家所認(rèn)可
ELK 基于 JAVA 開發(fā),Lucene 是一個(gè)非常有名的底層搜索接口疤祭,但是雖然 Lucene 很出名且很好用锌订,但是使用難度還是比較高,所以 elasticsearch 對(duì) Lucene 進(jìn)行了封裝画株,讓其對(duì)開發(fā)者更友好辆飘,很多開源的搜索引擎都是基于 Lucene 來完成的

關(guān)于搜索啦辐,我們好像可以通過 like 語句去數(shù)據(jù)庫中查詢,那問什么還會(huì)有這么多獨(dú)立出來的搜索引擎呢蜈项?
通過 like 語句或者正則表達(dá)式可以滿足基本需求芹关,但是對(duì)于一些復(fù)雜的需求就無法滿足了

關(guān)系型數(shù)據(jù)庫搜索缺點(diǎn):

  • 無法打分:無法對(duì)搜索結(jié)果進(jìn)行排序
  • 無分布式:傳統(tǒng)數(shù)據(jù)庫做分布式是比較麻煩的,對(duì)開發(fā)者要求較高
  • 無法解析搜索請(qǐng)求:比如搜索 'Python'紧卒,這種搜索請(qǐng)求是比較簡單的侥衬,但搜索 '我想學(xué)習(xí) Python',這種搜索就無法解析跑芳,搜索不到結(jié)果轴总,但是在百度上搜索 '我想學(xué)習(xí) Python',就會(huì)進(jìn)行分詞博个,關(guān)系型數(shù)據(jù)庫是沒法完成這些功能的怀樟,或者需要我們自己完成,這就加大了開發(fā)難度
  • 效率低:當(dāng)數(shù)據(jù)庫中數(shù)據(jù)上億盆佣、幾十億的時(shí)候往堡,關(guān)系型數(shù)據(jù)庫單庫就無法滿足要求,如果做分布式共耍,開發(fā)成本大大提高
  • 分詞:英語用單詞表達(dá)意思虑灰,對(duì)于中文單個(gè)字很難有其具體意思,英文分詞只需要按照空格和標(biāo)點(diǎn)來就行了痹兜,但是穆咐,中文分詞是一個(gè)比較有技術(shù)含量的課題,不過現(xiàn)在市面上分詞庫非常多字旭,直接配置拿過來就可用

NoSQL 數(shù)據(jù)庫:
文檔數(shù)據(jù)庫对湃,與關(guān)系數(shù)據(jù)庫差別很大
MongoDB、Redis谐算、Elasticsearch 都是 NoSQL
MongoDB 也是 NoSQL熟尉,我們?yōu)槭裁床挥盟鼇碜鏊阉饕约按鎯?chǔ)呢
NoSQL 和 關(guān)系型數(shù)據(jù)庫應(yīng)用場景不一樣,并不存在誰優(yōu)于誰洲脂,在某些情況下是可以將 Elasticsearch 當(dāng)作 MongoDB 來使用的斤儿,比如不會(huì)頻繁的 update,因?yàn)?Elasticsearch 的更新操作實(shí)際上是比較慢的恐锦,遠(yuǎn)低于 MongoDB往果,MongoDB 的更新實(shí)際上又慢于 MySQL 等關(guān)系型數(shù)據(jù)庫的更新操作,但是 MongoDB 在插入和查詢?cè)诮^大多數(shù)情況下是優(yōu)于 MySQL 的一铅,Elasticsearch 雖然可以當(dāng)作 NoSQL 來使用陕贮,但是并不能完全取代 MongoDB 或者 MySQL,Elasticsearch 目前主要用途還是搜索引擎潘飘,實(shí)際上它既有數(shù)據(jù)的存儲(chǔ)肮之,又有數(shù)據(jù)的分析掉缺,當(dāng)我們提到搜索功能的時(shí)候,其他所有數(shù)據(jù)庫在 Elasticsearch 面前幾乎就可以當(dāng)作一個(gè)玩笑(我不是針對(duì)誰戈擒,我是說在座的各位眶明,都是垃圾...)Elasticsearch 更注重搜索

elasticsearch安裝

  1. 安裝 elasticsearch-rtf

elasticsearch-rtf 是國內(nèi)一位大神在 elasticsearch 上進(jìn)行了一系列的插件安裝之后的發(fā)型版本,更原版 elasticsearch 實(shí)際上是一樣的筐高,只是增加了插件

elasticsearch 基于 JAVA 開發(fā)搜囱,所以要先安裝 JAVA,也就是 JDK柑土,elasticsearch 5 以及以上版本需要安裝 JAVA 8 以上
JAVA 下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
選擇對(duì)應(yīng)版本下載安裝即可

image

命令行下 java -version 查看 JAVA 版本

image

elasticsearch 官網(wǎng)下載地址:https://www.elastic.co/downloads/elasticsearch

由于 elasticsearch 官方版本插件不多蜀肘,所以在 GitHub 上面下載 elasticsearch-rtf,地址:https://github.com/medcl/elasticsearch-rtf

image

elasticsearch-rtf 目錄總覽

image

bin/ 目錄存放了很多可執(zhí)行文件稽屏,包括 Linux 和 Windows 中的一些可執(zhí)行文件扮宠,elasticsearch 用于啟動(dòng) elasticsearch,elasticsearch-plugin 用來安裝插件

image

config/ 目錄诫欠,其中有一個(gè) yml 格式文件 elasticsearch.yml 涵卵,現(xiàn)在很多工具采用 yml 格式文件來作為配置文件

image

lib/ 目錄存放的都是 elasticsearch 依賴的 jar

image

modules/ 目錄存放 elasticsearch 的模塊

image

plugins/ 目錄中存放的就是 elasticsearch-rtf 里面裝的所有插件浴栽,如果安裝原版 elasticsearch 的話這些插件就要自己去裝

image

在 elasticsearch 的 bin/ 目錄下運(yùn)行 elasticsearch 或者 elasticsearch.bat 就可以啟動(dòng) elasticsearch 了

但是我這里報(bào)了一個(gè)錯(cuò)荒叼,原因是 JAVA 沒有加到環(huán)境變量中

image

新建一個(gè)用戶變量,變量名 JAVA_HOME典鸡,變量值為 JAVA 安裝目錄

image

重新打開命令行窗口被廓,運(yùn)行 elasticsearch 來啟動(dòng) elasticsearch

image

啟動(dòng)成功,控制臺(tái)輸出可以看到分別對(duì) 9300 和 9200 端口進(jìn)行了監(jiān)聽萝玷,其中 9300 是 JAVA 的調(diào)用接口嫁乘,9200 是我們?cè)L問 elasticsearch 的接口

image

瀏覽器中訪問 http://127.0.0.1:9200/ 會(huì)看到有 JSON 數(shù)據(jù)輸出

image

有上圖這樣的輸出,就代表 elasticsearch 安裝完成球碉,cluster_name 是 elasticsearch 集群名稱蜓斧,默認(rèn)是 elasticsearch

啟動(dòng) elasticsearch 后,目錄下會(huì)多出 data/ 目錄和 logs/ 目錄睁冬,分別用于存放數(shù)據(jù)以及 log 日志

image
  1. elasticsearch 的 head 插件和 kibana 的安裝

head 插件是個(gè)什么東西挎春?可以簡單的類比成 navicat。安裝了 MySQL 以后豆拨,要對(duì)數(shù)據(jù)進(jìn)行管理直奋,想看到數(shù)據(jù)需要安裝一個(gè) navicat 來管理數(shù)據(jù)庫,head 插件是一個(gè)基于瀏覽器的一個(gè)插件施禾,可以完成一種類似于 navicat 的功能脚线,可以對(duì) elasticsearch 里面的數(shù)據(jù)進(jìn)行管理,可以執(zhí)行查詢語句等

elasticsearch 是一個(gè) NoSQL 數(shù)據(jù)庫弥搞,看到它的數(shù)據(jù)比較不方便邮绿,就需要 head 插件來瀏覽存在 elasticsearch 當(dāng)中的數(shù)據(jù)

elasticsearch-head 的 GitHub 地址:https://github.com/mobz/elasticsearch-head

安裝方式

image

關(guān)于 npm:
Node.js 官方下載地址:https://nodejs.org/en/

官網(wǎng)下載后一路下一步安裝即可

image

安裝后 cmd 下測試 npm 命令能否運(yùn)行

image

由于 npm 下載依賴包的地址都是國外服務(wù)器渠旁,可以使用 cnpm 來管理依賴包的下載,cnpm 是 淘寶 NPM 鏡像船逮,地址:http://npm.taobao.org/

image
image
image

cnpm 安裝好后一死,以后所有需要用到 npm 命令的地方都可以使用 cnpm 來代替

先用 git 將 elasticsearch-head clone 下來

image

先 cd 到 elasticsearch-head 目錄下,然后運(yùn)行 cnpm install

image

運(yùn)行 cnpm install 以后傻唾,elasticsearch-head 所需依賴就安裝完成投慈,發(fā)現(xiàn)在 elasticsearch-head 目錄下多了一個(gè) node_modules/ 目錄,這個(gè)目錄就是用來存放 npm 所安裝的依賴的

image

通過 npm run start 命令啟動(dòng)服務(wù)冠骄,會(huì)監(jiān)聽 9100 端口(啟動(dòng)之前先啟動(dòng) elasticsearch)

image

瀏覽器訪問 9100 端口伪煤,會(huì)提示 集群健康值: 未連接辫封,意思就是連接不到 9200 端口碘耳,但是在瀏覽器中訪問是可以正常返回結(jié)果的

image
image

elasticsearch-head 無法連接到 elasticsearch 的 9200 端口而瀏覽器直接訪問是可以的,原因是捧挺,elasticsearch 為了安全考慮扁誓,默認(rèn)情況下不允許使用第三方的服務(wù)防泵。elasticsearch-head 就相當(dāng)于一個(gè)第三方的代理,用這個(gè)代理連接 9200 端口并進(jìn)行操作蝗敢,默認(rèn)情況下 elasticsearch 是不允許的捷泞,代理沒有權(quán)限對(duì)其進(jìn)行訪問,為了滿足 elasticsearch-head 可以連接 elasticsearch寿谴,在啟動(dòng) elasticsearch 的時(shí)候需要做一些配置
這些配置是放在 elasticsearch-rtf-master/config/elasticsearch.yml 文件中的

# 自定義配置
# 默認(rèn)情況下不允許通過第三方插件來訪問锁右,配置好后第三方插件既可以訪問
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE
http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, X-User"

image

重啟 elasticsearch

image

刷新瀏覽器 http://127.0.0.1:9100/ 頁面,現(xiàn)在可以訪問了讶泰,集群健康值: green (0 of 0)

image

kibana 嚴(yán)格來說是一個(gè)項(xiàng)目咏瑟,他并不是一個(gè) elasticsearch 的插件,這里主要使用 kibana 里的 sense 插件痪署,sense 類似于 rest 接口的一個(gè)插件码泞,sense 專門用來調(diào)用 elasticsearch 交互用的,很強(qiáng)大狼犯。
實(shí)際上 kibana 也是 elasticsearch 產(chǎn)品線中的一個(gè)產(chǎn)品余寥,也就是之前介紹的 ELK 當(dāng)中的 K,kibana 實(shí)際上是一個(gè) HTML 頁面辜王,這個(gè)頁面完成了和 elasticsearch 的交互劈狐,讓我們操作 elasticsearch 更加簡單

kibana 地址:https://www.elastic.co/downloads/kibana
下載版本一定要同之前下載的 elasticsearch 版本相一致,這里下載 kibana-5.1.1 版本
地址:https://www.elastic.co/downloads/past-releases/kibana-5-1-1

kibana 目錄結(jié)構(gòu)

image

kibana 的 bin/ 目錄

image

kibana.bat 用來運(yùn)行 kibana
kibana-plugin.bat 用來安裝 kibana 插件

運(yùn)行 kibana呐馆,會(huì)在 5601 端口監(jiān)聽

image

瀏覽器打開 http://127.0.0.1:5601

image

Dev Tools 就是之前說所的 sense肥缔,在之前的版本他就叫 sense

image

點(diǎn)擊 Get to work,出現(xiàn)的頁面就是我們需要用到的頁面

image
image

elasticsearch的基本概念

通常這些概念在一個(gè)分布式系統(tǒng)中都會(huì)出現(xiàn)

  • 集群

一個(gè)或多個(gè)節(jié)點(diǎn)組織在一起就叫做集群
elasticsearch 是一個(gè)分布式搜索引擎汹来,既然是分布式续膳,那么就會(huì)可能同時(shí)會(huì)有多個(gè) elasticsearch 實(shí)例存在改艇,比如有 3 臺(tái)服務(wù)器,這 3 臺(tái)加在一起就是一個(gè)集群

  • 節(jié)點(diǎn)

一個(gè)節(jié)點(diǎn)就是集群中的一個(gè)服務(wù)器坟岔,比如有 3 臺(tái)服務(wù)器谒兄,這 3 臺(tái)服務(wù)器都部署了一個(gè) elasticsearch,那么這 3 臺(tái)服務(wù)器都叫節(jié)點(diǎn)社付,3 個(gè)節(jié)點(diǎn)加在一起就是一個(gè)集群
節(jié)點(diǎn)是有名稱的承疲,每一個(gè)節(jié)點(diǎn)由一個(gè)名字來標(biāo)識(shí),默認(rèn)是一個(gè)隨機(jī)的漫畫角色的名字

  • 分片

將索引劃分成多份的能力鸥咖,允許水平分割和擴(kuò)展容量燕鸽,多個(gè)分片響應(yīng)請(qǐng)求,提高性能和吞吐量

這里所說的 索引 類似于數(shù)據(jù)庫啼辣,不要把 elasticsearch 中的索引當(dāng)作普通數(shù)據(jù)庫中的索引來理解啊研,它們是不一樣的
當(dāng)數(shù)據(jù)越來越多,索引就會(huì)越來越大鸥拧,索引越來越大党远,如果放在一臺(tái)服務(wù)器上,性能就會(huì)受影響富弦,所以 elasticsearch 可以將索引分成多個(gè)沟娱,比如一個(gè)索引分成 5 份,分別放在不同的地方舆声,這樣的話它的水平擴(kuò)展能力就很容易了花沉。數(shù)據(jù)有可能存在多個(gè)分片上柳爽,比如 A 這個(gè)數(shù)據(jù)是放在第 1 個(gè) 和 第 2 個(gè) 分片上的媳握,這個(gè)時(shí)候,當(dāng)我們?nèi)フ?qǐng)求 A 這個(gè)數(shù)據(jù)的時(shí)候磷脯,elasticsearch 會(huì)自動(dòng)將這個(gè)請(qǐng)求的查詢信息路由到對(duì)應(yīng)這個(gè)數(shù)據(jù)的分片上面蛾找,所以它的分片之間是有一個(gè)路由關(guān)系的,分片實(shí)際上也提高了 elasticsearch 的性能以及吞吐量

  • 副本

創(chuàng)建分片的一份或多份的能力赵誓,在一個(gè)節(jié)點(diǎn)失敗其余節(jié)點(diǎn)可以頂上

我們可以吧 副本 理解為數(shù)據(jù)的備份打毛,比如說我們?cè)O(shè)置某一個(gè)索引(或者理解為某一個(gè)數(shù)據(jù)庫)的時(shí)候,我們給他設(shè)置一個(gè)值作為它的副本俩功,比如這個(gè)值設(shè)為 2幻枉,后期它在保存數(shù)據(jù)的時(shí)候就會(huì)自動(dòng)保存兩份
副本的好處就是在某一個(gè)節(jié)點(diǎn)(服務(wù)器)失效的時(shí)候,可以頂上诡蜓,加大了分布式系統(tǒng)的可靠性

分片副本 不要搞混了熬甫,分片是將一個(gè)過大的數(shù)據(jù)分成多分,放到不同的地方蔓罚,副本是有多少分復(fù)制的數(shù)據(jù)椿肩,比如說給定一個(gè)副本的值是 2瞻颂,那么這個(gè)數(shù)據(jù)就會(huì)存兩份。

  • Elasticsearch 對(duì)比 MySQL 概念理解
Elasticsearch MySQL
index(索引) 數(shù)據(jù)庫
type(類型)
documents(文檔)
fields

elasticsearch 是一個(gè)搜索引擎郑象,我們不是通過 elasticsearch 去分析放在數(shù)據(jù)庫中的數(shù)據(jù)贡这,而是 elasticsearch 為了達(dá)到自己的搜索的目的,它的數(shù)據(jù)是自己來保存的厂榛,它不是一個(gè)中間庫盖矫,它是集合了數(shù)據(jù)保存以及數(shù)據(jù)分析的一個(gè)搜索引擎

其中,索引 有兩層含義击奶,當(dāng)作名詞理解時(shí)炼彪,就是一個(gè)數(shù)據(jù)庫,當(dāng)作為一個(gè)動(dòng)詞來理解時(shí)就不再是數(shù)據(jù)庫的概念正歼,比如我們對(duì)一個(gè) documents 進(jìn)行索引辐马,這句話的意思可以理解為在數(shù)據(jù)庫中進(jìn)行一個(gè)插入的操作(insert),也就是將這個(gè) documents 插入到這個(gè)索引的 type 當(dāng)中[話有點(diǎn)繞局义,但就是這么回事...]

  • HTTP 方法

HTTP 1.0 定義了三種請(qǐng)求方式:GET喜爷、POST、HEAD 方法
后面又新增了五中方法:OPTIONS萄唇、PUT檩帐、DELETE、TRACE另萤、CONNECT

正是由于 HTTP 提供了這些方法湃密,我們才有可能實(shí)現(xiàn) REST 接口,恰好
elasticsearch 是基于 REST 接口來完成的

elasticsearch 常用 HTTP 方法的含義

方法 描述
GET 請(qǐng)求指定的頁面信息四敞,并返回實(shí)體主體
POST 向指定的資源提交數(shù)據(jù)進(jìn)行處理請(qǐng)求泛源。數(shù)據(jù)被包含在請(qǐng)求體中。POST 請(qǐng)求可能會(huì)導(dǎo)致新的資源建立 和 / 或 已有的資源修改
PUT 向服務(wù)器傳送的數(shù)據(jù)取代指定的文檔的內(nèi)容
DELETE 請(qǐng)求服務(wù)器刪除指定的頁面

倒排索引

目前的搜索引擎當(dāng)中忿危,底層的索引存儲(chǔ)都采用的是 倒排索引
倒排索引是底層索引存儲(chǔ)的最基本的一種方式达箍,也是搜索引擎區(qū)別于已有的關(guān)系數(shù)據(jù)庫或者說其余的 NoSQL 數(shù)據(jù)庫的核心

image

假設(shè)現(xiàn)在有 A、B铺厨、C 三個(gè) documents

image

我們現(xiàn)在要查詢有哪些 documents 包含 python 這個(gè)關(guān)鍵詞

有這種查詢或者搜索需求的話缎玫,如果不通過倒排索引,按照正常的思維來理解解滓,我么要如何來做赃磨?

正常的思維方式就是:
先對(duì)文件進(jìn)行遍歷,分別遍歷 A洼裤、B、C 三個(gè)文件內(nèi)的所有內(nèi)容在扰,才能判斷每個(gè)文件中到底有沒有 python 這個(gè)關(guān)鍵詞
這樣芒珠,為了查詢一個(gè)關(guān)鍵詞,將所有的 documents 全部遍歷一遍搅裙,那么效率就太低了皱卓,如果文件上億個(gè),每次查詢都去遍歷上億個(gè)部逮,這肯定是不可能的

所以說就出現(xiàn)了倒排
倒排就是在文件進(jìn)行存儲(chǔ)之前娜汁,先對(duì)文件進(jìn)行分析,比如存儲(chǔ) A 文件兄朋,就要先對(duì)其進(jìn)行分析掐禁,對(duì)其內(nèi)容全部遍歷,遍歷完之后進(jìn)行分析颅和,B傅事、C 文件同理,也就是內(nèi)個(gè)文件在插入前都需要對(duì)其進(jìn)行遍歷并分析蹭越,這個(gè)分析就包括了 分詞买置,所以,分析后倒排數(shù)據(jù)的結(jié)構(gòu)如下圖

image

這里把 Python 寫各大聊天系統(tǒng)的屏蔽臟話功能原理 當(dāng)作一個(gè) elasticsearch 的 documents熟嫩,把這句話進(jìn)行倒排索引處理之后就是表格中的樣子景馁,上面表就是一個(gè) 倒排索引 非常基礎(chǔ)的一個(gè)表結(jié)構(gòu)僚害,可以簡單的看為一個(gè) dict门岔,左邊 關(guān)鍵詞 為 key,右邊是關(guān)鍵詞出現(xiàn)的 文章 為 value讯泣。
左側(cè)關(guān)鍵詞部分都是對(duì)這句話進(jìn)行分詞之后出現(xiàn)的關(guān)鍵詞拳锚。然后給 Python 這個(gè)詞后面加一個(gè)列表,比如說存放 Python 這個(gè)詞出現(xiàn)的 文章简卧,然后就可以得出,Python 這個(gè)關(guān)鍵詞在 文章1 和 文章3 中出現(xiàn)過,同樣 聊天 這個(gè)詞在 文章2 當(dāng)中出現(xiàn)過谬泌,依次往下分析贱鼻,就能夠直到每個(gè)詞所出現(xiàn)的 documents 有哪些。這樣的話就可以把這個(gè)表格數(shù)據(jù)看成一個(gè) dict,key 為 關(guān)鍵詞,value 是一個(gè) list蘑斧,list 里面存放的是這個(gè)關(guān)鍵詞所在的文章

當(dāng)然分詞有很多,比如 Python 寫各大聊天系統(tǒng)的屏蔽臟話功能原理 這句話里面的 要不要放到 關(guān)鍵詞里面,這種詞實(shí)際上沒有什么含義,當(dāng)然最后要不要放進(jìn)來是不需要我們?nèi)リP(guān)心的扛禽,這些 elasticsearch 都幫我們做好了

這是最簡單的一個(gè)倒排索引的模型往扔,但是,這樣就可以了嗎?貌似可以粹淋,但實(shí)際上這樣是不完善的过吻,因?yàn)檫@個(gè)關(guān)鍵詞有可能在文章中出現(xiàn)多次,比如 Python 這個(gè)關(guān)鍵詞在 文章1 中出現(xiàn) 30 次逼纸,在 文章3 中出現(xiàn)過 1 次洋措,如果我們不把它出現(xiàn)過的頻率記錄下來,后期就沒法對(duì)文本進(jìn)行打分樊展。因?yàn)槿绻粋€(gè)文本當(dāng)中吩跋,它的某一個(gè)關(guān)鍵詞出現(xiàn)頻率越高杨何,這個(gè)詞權(quán)重可能就會(huì)比較高窗宦,如果按照現(xiàn)在的結(jié)構(gòu)來存儲(chǔ)的話棍潘,就沒法存儲(chǔ)關(guān)鍵詞的權(quán)重

所以倒排索引還有更加精確的存儲(chǔ)結(jié)構(gòu)

image

左側(cè)依舊存儲(chǔ)關(guān)鍵詞歇竟,后面結(jié)構(gòu)就變了箱亿,首先還是會(huì)存儲(chǔ)文章,也就是關(guān)鍵詞所出現(xiàn)的文章冒版,比如文章1,又加了一些數(shù)據(jù),第二個(gè)數(shù)據(jù)尖括號(hào)中 <2,10> 存儲(chǔ)的是 Python 這個(gè)關(guān)鍵詞在 文章1 當(dāng)中出現(xiàn)的位置虎谢,在第 2 個(gè)單詞 和 第 10 個(gè)單詞,這個(gè)位置信息在分析的時(shí)候?qū)?elasticsearch 打分來說都是很重要的信息曹质,最后存儲(chǔ)的數(shù)字 2婴噩,代表 Python 關(guān)鍵詞在
文章1 中出現(xiàn)的頻率

其中關(guān)鍵詞出現(xiàn)次數(shù)(也叫詞頻)對(duì) elasticsearch 打分來說也是很重要的信息,我們可以把它叫做 TF

有了這樣一個(gè)倒排索引的結(jié)構(gòu)羽德,搜索就變得非常高效了几莽,我們可以對(duì)其打分

如果想了解 elasticsearch 到底是如何打分的,可以去了解 TF-IDF 這個(gè)概念玩般,數(shù)據(jù)挖掘以及機(jī)器學(xué)習(xí)也會(huì)遇到這個(gè)概念

這里分析比較簡單银觅,真正做好倒排索引是有很多問題需要解決的

image

這里面的所有問題 elasticsearch 都幫我們?nèi)ネ瓿闪?/p>

elasticsearch 基本的索引和文檔CRUD操作

  • 索引初始化操作

編寫創(chuàng)建命令后運(yùn)行

# es 的文檔、索引的 CRUD 操作

# 索引初始化操作
# 指定分片(默認(rèn)值5)和副本(默認(rèn)值1)的數(shù)量
# shards 一旦設(shè)置不能修改
PUT lagou
{
  "settings": {
    "index": {
      "number_of_shards": 5, 
      "number_of_replicas": 1
    }
  }
}

image

刷新 elasticsearch-head 已成功創(chuàng)建索引 lagou

image

可以查看索引信息

image

上面的 lagou 索引是通過 REST 接口來添加的坏为,實(shí)際上還可以在 elasticsearch-head 中添加究驴,而且圖形化,類似 navicat

image
image
image
image
image
image
image

注意:一旦索引創(chuàng)建完成匀伏,分片數(shù)量(number_of_shards)是不能修改的洒忧,副本數(shù)量(number_of_replicas)是可以修改的

獲取索引 settings 信息


# 獲取索引 settings 信息

# 獲取全部索引信息 
GET _all/_settings
GET _settings
# 獲取單個(gè)索引 settings 信息
GET lagou/_settings
# 獲取多個(gè)索引 settings 信息
# 多個(gè)索引之間用逗號(hào)作分割,且不能有空格
GET .kibana,lagou/_settings

image

更新 settings 信息


# 更新 settings 信息

# 更新副本數(shù)量
PUT lagou/_settings
{
  "number_of_replicas": 2
}

# 嘗試更新切片數(shù)量够颠,會(huì)產(chǎn)生異常
PUT lagou/_settings
{
  "number_of_shards": 3
}

image
image
image

獲取索引信息


# 獲取索引信息

# 獲取所有索引信息
GET _all
# 獲取一個(gè)索引信息
GET lagou

image

向索引中添加文檔

在關(guān)系型數(shù)據(jù)庫當(dāng)中熙侍,要存儲(chǔ)數(shù)據(jù)到數(shù)據(jù)庫當(dāng)中,是要先創(chuàng)建表,再向表中插入數(shù)據(jù)蛉抓。在 elasticsearch 中(或者很多 NoSQL 數(shù)據(jù)庫中)實(shí)際上是不用先新建表(elasticsearch 中為 type)的庆尘,可以直接向索引中插入數(shù)據(jù)。type 中我們是可以設(shè)定字段以及屬性的巷送,不過不設(shè)置也是可以的驶忌,它會(huì)默認(rèn)去猜插入的值是什么


# 向索引中添加文檔

# job 表示 type,1 是這條數(shù)據(jù)的 id
PUT lagou/job/1
{
  "title":"python分布式爬蟲開發(fā)",
  "salary_min":15000,
  "city":"北京",
  "company":{
    "name":"百度",
    "company_addr":"北京市軟件園"
  },
  "publish_date":"2017-4-16",
  "comments":15
}

image

在 elasticsearch-head 中查看數(shù)據(jù)

image

保存文檔的時(shí)候如果不指明 id 值笑跛,elasticsearch 會(huì)自動(dòng)生成 id(UUID)

image
image

獲取文檔


# 獲取文檔

# 獲取文檔全部字段 
GET lagou/job/1
GET lagou/job/1?_source
# 獲取文檔指定字段
GET lagou/job/1?_source=title
GET lagou/job/1?_source=title,city

image
image
image
image

修改文檔


# 修改文檔

# 這種方式是一種覆蓋的方式付魔,會(huì)全部覆蓋掉原來的內(nèi)容 
PUT lagou/job/1
{
  "title":"python分布式爬蟲開發(fā)",
  "salary_min":15000,
  "company":{
    "name":"百度",
    "company_addr":"北京市軟件園"
  },
  "publish_date":"2017-4-16",
  "comments":15
}

# 增量修改方式,通過POST方法飞蹂,只修改comments字段(推薦的方式)
POST lagou/job/1/_update
{
  "doc": {
    "comments":20
  }
}

image
image
image
image

可能遇到的錯(cuò)誤

ElasticSearch ClusterBlockException[blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];

解決辦法:

PUT /<yourindex>/_settings
{
  "index.blocks.read_only_allow_delete": null
}

刪除操作


# 刪除

# 刪除documents
DELETE lagou/job/1

# 刪除type几苍,無法刪除成功,會(huì)報(bào)錯(cuò)陈哑, es5已經(jīng)不支持這里通DELETE來刪除type
DELETE lagou/job

# 刪除index
DELETE lagou

image
image
image
image

以上所有 CURD 操作


# es 的文檔妻坝、索引的 CRUD 操作

# 索引初始化操作
# 指定分片和副本的數(shù)量
# shards 一旦設(shè)置不能修改
PUT lagou
{
  "settings": {
    "index": {
      "number_of_shards": 5, 
      "number_of_replicas": 1
    }
  }
}

# 獲取 settings 信息

# 獲取全部索引信息 
GET _all/_settings
GET _settings
# 獲取單個(gè)索引 settings 信息
GET lagou/_settings
# 獲取多個(gè)索引 settings 信息
# 多個(gè)索引之間用逗號(hào)作分割,且不能有空格
GET .kibana,lagou/_settings

# 更新 settings 信息

# 更新副本數(shù)量
PUT lagou/_settings
{
  "number_of_replicas": 2
}

# 嘗試更新切片數(shù)量芥颈,會(huì)產(chǎn)生異常
PUT lagou/_settings
{
  "number_of_shards": 3
}

# 獲取索引信息

# 獲取所有索引信息
GET _all
# 獲取一個(gè)索引信息
GET lagou

# 向索引中添加文檔

PUT lagou/job/1
{
  "title":"python分布式爬蟲開發(fā)",
  "salary_min":15000,
  "city":"北京",
  "company":{
    "name":"百度",
    "company_addr":"北京市軟件園"
  },
  "publish_date":"2017-4-16",
  "comments":15
}

# 不指明 id
POST lagou/job/
{
  "title":"python django 開發(fā)工程師",
  "salary_min":30000,
  "city":"上海",
  "company":{
    "name":"美團(tuán)科技",
    "company_addr":"北京軟件園A區(qū)"
  },
  "publish_date":"2018-3-30",
  "comments":20
}



# 獲取文檔

# 獲取文檔全部字段 
GET lagou/job/1
GET lagou/job/1?_source
# 獲取文檔指定字段
GET lagou/job/1?_source=title
GET lagou/job/1?_source=title,city

# 修改文檔

# 這種方式是一種覆蓋的方式惠勒,會(huì)全部覆蓋掉原來的內(nèi)容 
PUT lagou/job/1
{
  "title":"python分布式爬蟲開發(fā)",
  "salary_min":15000,
  "company":{
    "name":"百度",
    "company_addr":"北京市軟件園"
  },
  "publish_date":"2017-4-16",
  "comments":15
}

# 增量修改方式,通過POST方法爬坑,只修改comments字段(推薦方式)
POST lagou/job/1/_update
{
  "doc": {
    "comments":20
  }
}

# 刪除

# 刪除documents
DELETE lagou/job/1

# 刪除type,無法刪除成功涂臣,會(huì)報(bào)錯(cuò)盾计, es5已經(jīng)不支持這里通DELETE來刪除type
DELETE lagou/job

# 刪除index
DELETE lagou

elasticsearch的mapping映射管理

映射就是當(dāng)我們創(chuàng)建索引的時(shí)候,可以預(yù)先定義字段的類型以及相關(guān)屬性

實(shí)際上映射是創(chuàng)建在 type 上邊的赁遗,所以映射我們可以把它理解為定義數(shù)據(jù)表的時(shí)候每一個(gè)字段所定義的類型署辉。比如 ArticleSpider 爬蟲定義字段的時(shí)候,comments 字段的類型是 int器虾,對(duì)于 elasticsearch 同理干旧,在放置每一條數(shù)據(jù)的時(shí)候茸时,可以對(duì)每一個(gè)字段定義一種類型。比如之前插入文檔操作的時(shí)候材鹦,{"title":"python django 開發(fā)工程師","salary_min":30000,"city":"上海","company":{"name":"美團(tuán)科技","company_addr":"北京軟件園A區(qū)"},"publish_date":"2018-3-30","comments":20},title 就是字符串類型耕姊,salary_min 就是數(shù)字類型桶唐。我們實(shí)際上是可以先定義這里面每一個(gè)字段的類型的,以及這個(gè)類型有什么屬性茉兰,這個(gè)屬性要比 MySQL 這種關(guān)系型數(shù)據(jù)庫里面的屬性要豐富尤泽,數(shù)據(jù)庫中最多可以定義字段屬性是否可以為 NULL,但是對(duì)于 elasticsearch 來說熊咽,它的相關(guān)屬性會(huì)更加豐富横殴。

之前插入文檔的時(shí)候并沒有定義類型,elasticsearch 是如何識(shí)別這些類型的呢妇智?實(shí)際上 elasticsearch 是會(huì)去動(dòng)態(tài)的識(shí)別滥玷,根據(jù) JSON 里面?zhèn)魅脒M(jìn)來的數(shù)據(jù),它來判斷是什么類型

Elasticsearch 會(huì)根據(jù) JSON 源數(shù)據(jù)的基礎(chǔ)類型猜測你想要的字段映射巍棱。將輸入的數(shù)據(jù)轉(zhuǎn)變成可搜索的索引項(xiàng)惑畴。Mapping 就是我們自己定義的字段數(shù)據(jù)類型,同時(shí)告訴 Elasticsearch 如何索引數(shù)據(jù)(通過設(shè)置相關(guān)屬性來告訴 Elasticsearch)以及是否可以被搜索

我們?cè)O(shè)置了 Elasticsearch 的 Mapping 之后的作用是:會(huì)讓索引建立的更加細(xì)致和完善

實(shí)際上航徙,對(duì)于大多數(shù)簡單的文本來說如贷,我們自己是不用去建立 Mapping 的,因?yàn)?Elasticsearch 大部分情況都可以通過自動(dòng)猜測猜出來到踏,但是當(dāng)某些情況之下杠袱,自己要去定義的時(shí)候,自定義類型以及相關(guān)屬性就變得豐富了窝稿。

映射類型:靜態(tài)映射楣富、動(dòng)態(tài)映射

Elasticsearch 內(nèi)置類型

string類型 text,keyword(還有一個(gè)string類型,但是在es5中開始被廢棄)
數(shù)字類型 long,integer,short,byte,double,float
日期類型 date
bool類型 boolean
binary類型 binary
復(fù)雜類型 object,nested
geo類型 geo-point,geo-shape
專業(yè)類型 ip,competion
  • string 類型中的 text 和 keyword 的區(qū)別纹蝴。如果將某一個(gè) string 類型的數(shù)據(jù)設(shè)置為 text援奢,這個(gè)字段傳遞過來的數(shù)據(jù)就會(huì)被分析器進(jìn)行分析(包括分詞、抽取詞干、去除無意義的詞等)险领,如果將某一個(gè) string 類型的數(shù)據(jù)設(shè)置為 keyword,就不會(huì)做分析操作,不進(jìn)行倒排索引愁铺,只會(huì)當(dāng)作一個(gè)字符串來存取瓶竭,所以這種情況下如果想要查詢這個(gè) string 就必須完全匹配
  • 日期類型 date 不止可以代表日期荧恍,還可以解析 datetime
  • bool類型解析傳遞過來的值,這個(gè)值比較豐富世落,比如 True洲押、False专钉、Yes菇民、No 等都會(huì)被 es 識(shí)別并轉(zhuǎn)換成 bool類型
  • binary類型存放二進(jìn)制,但是二進(jìn)制數(shù)據(jù)是不會(huì)被進(jìn)行檢索的
  • geo類型是地理位置,geo-point 通過經(jīng)緯度來標(biāo)識(shí)一個(gè)位置,geo-shape 通過多個(gè)點(diǎn)標(biāo)識(shí)一片區(qū)域
  • 專業(yè)類型中 ip 就是 ip 地址,competion 是用來做搜索建議的
  • 復(fù)雜類型包括 object 和 nested
image

常用屬性以及試用類型

image

創(chuàng)建索引


# 創(chuàng)建索引
PUT lagou
{
  "mappings": {
    "job":{
      "properties": {
        "title":{
          "type": "text"
        },
        "salary_min":{
          "type": "integer"
        },
        "city":{
          "type": "keyword"
        },
        "company":{
          "properties": {
            "name":{
              "type":"text"
            },
            "company_addr":{
              "type":"text"
            },
            "employee_count":{
              "type":"integer"
            }
          }
        },
        "publish_date":{
          "type": "date",
          "format": "yyyy-MM-dd"
        },
        "comments":{
          "type": "integer"
        }
      }
    }
  }
}

image
image

添加數(shù)據(jù)


# 放入數(shù)據(jù)
PUT lagou/job/1
{
  "title":"python分布式爬蟲開發(fā)",
  "salary_min":15000,
  "company":{
    "name":"百度",
    "company_addr":"北京市軟件園",
    "employee_count":50
  },
  "publish_date":"2018-7-10",
  "comments":15
}

image
image

嘗試添加一條錯(cuò)誤的數(shù)據(jù)

image
image

可以發(fā)現(xiàn) elasticsearch 自動(dòng)將 string 類型的 salary_min 轉(zhuǎn)換成了 integer

image

獲取 mapping


# 獲取mapping 
GET lagou/_mapping
GET lagou/_mapping/job
GET _all/_mapping
GET _all/_mapping/job

image

注意:索引中一旦創(chuàng)建好了類型隅很,就不能再修改了绒尊。這是和關(guān)系型數(shù)據(jù)庫很大不同的一點(diǎn),關(guān)系型數(shù)據(jù)庫創(chuàng)建好后還是可以修改字段類型的斟冕,對(duì)于 elasticsearch 來說一旦設(shè)置了某一列的類型秀撇,就再也無法修改件相,可惜新增字段,但是已有字段無法修改,如果想要修改浪南,只能刪除索引,新建索引喷众,新建索引之后再將以前數(shù)據(jù)導(dǎo)入。所以在新建索引前一定要想好類型紧憾,后期數(shù)據(jù)越來越多到千,新建索引就會(huì)很麻煩

elasticsearch的簡單查詢

elasticsearch 是功能強(qiáng)大的搜索引擎,使用它的目的就是為了快速的查詢到需要的數(shù)據(jù)

image

添加映射


# 添加映射 
PUT lagou
{
  "mappings": {
    "job":{
      "properties": {
        "title":{
          "store": true,
          "type": "text",
          "analyzer": "ik_max_word"
        },
        "company_name":{
          "store": true,
          "type": "keyword"
        },
        "desc":{
          "type": "text"
        },
        "comments":{
          "type": "integer"
        },
        "add_time":{
          "type": "date",
          "format": "yyyy-MM-dd"
        }
      }
    }
  }
}

image

向索引中添加幾條數(shù)據(jù)


POST lagou/job/
{
  "title":"python scrapy redis分布式爬蟲基本",
  "company_name":"百度科技有限公司",
  "desc":"對(duì)scrapy的概念熟悉柿汛, 熟悉redis的基本操作",
  "comments":5,
  "add_time":"2018-7-2"
}

POST lagou/job/
{
  "title":"elasticsearch打造搜索引擎",
  "company_name":"阿里巴巴科技有限公司",
  "desc":"熟悉數(shù)據(jù)結(jié)構(gòu)算法遭商, 熟悉python的基本開發(fā)",
  "comments":15,
  "add_time":"2018-6-20"
}

POST lagou/job/
{
  "title":"python打造推薦引擎系統(tǒng)",
  "company_name":"阿里巴巴科技有限公司",
  "desc":"熟悉推薦引擎的原理以及算法,掌握C語言",
  "comments":60,
  "add_time":"2017-10-20"
}

image

查詢

match 查詢

image
image
image
image
image

term 查詢

image
image
image
image

terms查詢

image

控制查詢的返回?cái)?shù)量

image

match_all查詢

image

match_phrase查詢

image
image
image
image

multi_match查詢

image
image

指定返回字段

image
image
image

通過sort對(duì)結(jié)果進(jìn)行排序

image

范圍查詢

image
image

wildcard查詢

image

查詢操作


# 查詢

# match查詢
# match查詢會(huì)對(duì)輸入進(jìn)行分詞,會(huì)去找 match 中指名的字段里面有沒有這個(gè)詞
GET lagou/job/_search
{ 
  "query": {
    "match": {
      "title": "取"
    }
  }
}

# term查詢
# term查詢不會(huì)對(duì)輸入進(jìn)行分詞,會(huì)直接拿著輸入的數(shù)據(jù)進(jìn)行查詢(類似keyword一樣)
GET lagou/job/_search
{
  "query": {
    "term": {
       "company_name": "阿里巴巴科技有限公司"
    }
  }
}

# terms查詢
# terms查詢傳入的是一個(gè)數(shù)組隙咸,這里面只要有一個(gè)值匹配就會(huì)返回結(jié)果
GET lagou/job/_search
{
  "query": {
    "terms": {
      "title": ["工程師","django","系統(tǒng)"]
    }
  }
}

# 控制查詢的返回?cái)?shù)量
# 可以用來做分頁,from是指從第幾個(gè)開始查詢蓝谨,size是指查詢幾個(gè)
GET lagou/_search
{
  "query": {
    "match": {
      "title": "python"
    }
  },
  "from":1,
  "size": 2
}

# match_all查詢
GET lagou/_search
{
  "query": {
    "match_all": {}
  }
}

# match_phrase查詢
# 短語查詢
GET lagou/_search
{
  "query": {
    "match_phrase": {
      "title":{
        "query": "python系統(tǒng)",
        "slop":5
      }
    }
  }
}

# multi_match查詢
# 比如可以指明多個(gè)字段 
# 比如查詢title和desc這兩個(gè)字段里面包含python關(guān)鍵詞文檔
GET lagou/_search
{
  "query": {
    "multi_match": {
      "query": "python",
      "fields": ["title^3","desc"]
    }
  }
}

# 指定返回字段
GET lagou/_search
{
  "stored_fields": ["title","company_name","desc"],
  "query": {
    "match": {
      "title": "python"
    }
  }
}

# 通過sort對(duì)結(jié)果進(jìn)行排序
GET lagou/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "comments": {
        "order": "desc"
      }
    }
  ]
}

# range查詢
# 查詢范圍
GET lagou/_search
{
  "query": {
    "range": {
      "comments": {
        "gte": 10,
        "lte": 20,
        "boost": 2.0
      }
    }
  }
}

# 對(duì)時(shí)間做了一個(gè)range查詢
GET lagou/_search
{
  "query": {
    "range": {
      "add_time": {
        "gte": "2018-04-01",
        "lt": "now"
      }
    }
  }
}

# wildcard查詢
# 可以簡單的理解為模糊查詢
GET lagou/_search
{
  "query": {
    "wildcard": {
      "title": {
        "value": "pyth*n",
        "boost": 2
      }
    }
  }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末捆憎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子变抽,更是在濱河造成了極大的恐慌础拨,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绍载,死亡現(xiàn)場離奇詭異诡宗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)击儡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門塔沃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人阳谍,你說我怎么就攤上這事蛀柴。” “怎么了矫夯?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵鸽疾,是天一觀的道長。 經(jīng)常有香客問我训貌,道長制肮,這世上最難降的妖魔是什么冒窍? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮豺鼻,結(jié)果婚禮上综液,老公的妹妹穿的比我還像新娘。我一直安慰自己儒飒,他們只是感情好意乓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著约素,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笆凌。 梳的紋絲不亂的頭發(fā)上圣猎,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音乞而,去河邊找鬼送悔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛爪模,可吹牛的內(nèi)容都是我干的欠啤。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼屋灌,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼洁段!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起共郭,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤祠丝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后除嘹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體写半,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年尉咕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叠蝇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡年缎,死狀恐怖悔捶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晦款,我是刑警寧澤炎功,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站缓溅,受9級(jí)特大地震影響蛇损,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一淤齐、第九天 我趴在偏房一處隱蔽的房頂上張望股囊。 院中可真熱鬧,春花似錦更啄、人聲如沸稚疹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽内狗。三九已至,卻和暖如春义锥,著一層夾襖步出監(jiān)牢的瞬間柳沙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工拌倍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赂鲤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓柱恤,卻偏偏與公主長得像数初,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梗顺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容