安装完成ElasticSearch环境,接下来就开始初步搭建一个查询服务。
为了方便理解,针对ES中关于数据的术语与MySQL和MongoDB的对应关系为:
需要注意的是, ElasticSearch的所有字段相当于都是索引过的(indexed)。
数据导入
在数据导入之前,需要先明确一下ES的数据格式要求。
在ES中,文档(Document)是一个JSON对象,不过特殊性在于,Document指顶层或者根对象,若是嵌套的JSON结构,可以理解成每个根对象的值,被序列化后保存在ES中。
通常的数据导入有以下几种方式:
- 通过API实现数据录入
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
其中,_index=website,_type=blog,_id=123。
若数据中没有可以作为UniqueID的列,导入过程中,可以通过ES自动生成ID,具体操作如下:
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
通过这样的API接口,可以实现通过代码或脚本进行批量的数据录入。
若已经具有JSON格式的数据文件,可以通过文件直接进行数据导入,具体导入方式可参考:
curl -H "Content-Type: application/json" -XPOST "xx.xx.xx.xx:9200/index_name/_bulk?pretty&refresh" --data-binary "@esdata.json"
curl "xx.xx.xx.xx:9200/_cat/indices?v"
- 通过ElasticDump实现数据录入
Elastic提供批量导入导出工具,不过需要额外进行安装。
npm install elasticdump -g
利用ElasticDump进行数据导出:
# 导出索引Mapping数据
elasticdump
--input=http://xx.xx.xx.xx:9200/index_name/index_type
--output=/data/my_index_mapping.json
--type=mapping
# 导出索引数据
./bin/elasticdump
--input=http://xx.xx.xx.xx:9200/index_name/index_type
--output=/data/my_index.json
--type=data
利用ElasticDump进行数据导入:
# Mapping数据导入至索引
./bin/elasticdump
--output=http://xx.xx.xx.xx:9200/index_name
--input=/data/my_index_mapping.json
--type=mapping
# ES文档数据导入至索引
./bin/elasticdump
--output=http://xx.xx.xx.xx:9200/index_name
--input=/data/my_index.json
--type=data
由于我的数据均放在数据库中,这里使用了Python来操作ES,实现内容的填充。
# -*- coding: utf-8 -*-
import pandas as pd
from sqlalchemy import create_engine
from elasticsearch import Elasticsearch
sql = 'select `bookId`, `name`, `publish`, `author`, `seriesName`, `status` from t_book'
engine = create_engine('mysql+pymysql://root:welcome@localhost:3306/turing')
df = pd.read_sql_query(sql, engine)
es = Elasticsearch(['xx.xx.xx.xx:9200'])
result = es.indices.create(index='books', ignore=400)
for idx, item in df.iterrows():
doc = {
'name': item['name'],
'publish': item['publish'],
'author': item['author'],
'seriesName': item['seriesName'],
'status': item['status']
}
print(idx, doc)
result = es.create(index='books', doc_type='sample', id=item['bookId'], body=doc)
print(result)
小插曲 - cluster_block_exception
在使用Python进行数据导入的过程中,发现ES在报错,导致数据无法被导入。
报错内容如下:
elasticsearch.exceptions.TransportError: TransportError(429, 'cluster_block_exception', 'index [books] blocked by: [TOO_MANY_REQUESTS/12/index read-only / allow delete (api)];')
这时,我发现_index的状态为“read_only”,但是我手动修改该状态值发现无法修改成功,一直对外显示“read_only”。
curl -XPUT 'xx.xx.xx.xx:9200/books/_settings' -H 'Content-Type: application/json' -d '{"index.blocks.read_only_allow_delete": null}'
最终Baidu了一下才知道,当磁盘剩余空间小于5%时,index会自动设置为read_only,并无法进行修改。处理的方法有两种:
- 清空磁盘空间,保证在5%,在执行命令,修改_index状态
- 通过修改配置文件,降低index可写入的磁盘下限
为了安全起见,我选择解决方案1,最终可以实现数据的正确导入。
数据搜索
在数据搜索前,可以先对数据是否导入成功进行一次确认。
通过_count,查看插入了多少条数据:
curl -XGET 'xx.xx.xx.xx:9200/index_name/index_type/_count' -H 'Content-Type: application/json'
使用空搜索,查看导入数据是否正确:
curl -XGET 'xx.xx.xx.xx:9200/index_name/index_type/_search' -H 'Content-Type: application/json'
当数据确认已经导入完成后,可以实现内容的检索,通常使用的检索方式有两种:
- 轻量搜索
在最开始的时候说过,在ES中,相当于对所有的字段均建立了索引,因此可以很轻松的实现全文检索。
具体可以参考官网文档,“搜索——最基本的工具”。
例如:在type下全文检索“mary”
curl -XGET 'xx.xx.xx.xx:9200/index_name/index_type/_search?q=mary'
- 请求体搜索
ES通过查询表达式(Query DSL)来支持更加灵活的查询方式,具体查询通过JSON格式进行。
具体可以参考官网文档,“请求体查询”。
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
在我的绘本数据中,期望可以实现通过绘本名称(name),作者(author),出版社(publish),绘本系列名称(series name)实现绘本搜索。具体代码如下所示:
# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch
qstr = "原则"
query = {
"query": {
"bool": {
"must": [
{
"term": {
"status": 1
}
}
],
"should": [
{
"match": {
"name": {
"query": qstr
}
}
},
{
"match": {
"author": {
"query": qstr
}
}
},
{
"match": {
"publish": {
"query": qstr
}
}
},
{
"match": {
"seriesName": {
"query": qstr
}
}
}
]
}
},
"from": 0,
"size": 10
}
es = Elasticsearch(['192.168.1.135:9200'])
result = es.search(index="books", doc_type="sample", body=query)
在这段检索当中,会将输入的名称qstr分别在name,author,publish,seriesName上进行检索,同时要求对应的status字段必须为1,即已经被审核。
到这里,就完成了一个简单的图书检索服务,输出结果如下:
+-----------+----------------+----------------------------------------------------------------------+--------------------+------------------------+
| Score | Name | Author | Publish | SeriesName |
+-----------+----------------+----------------------------------------------------------------------+--------------------+------------------------+
| 17.760134 | 树真好 | 贾尼思·梅·伍德里 文 马可·塞蒙 图 舒杭丽 译 | 21世纪出版社 | # |
| 14.487474 | 真高兴 | 木木树文化 | 新时代出版社 | # |
| 13.899454 | 有你真好 | 童悦早教 编著 | 北方妇女儿童出版社 | 好孩子是夸出来的 |
| 13.111786 | 洗澡真好玩儿 | 童悦早教 | 北方妇女儿童出版社 | 卫生好习惯 |
| 12.519405 | 和平树 | (美)珍妮特·温特 文/图 徐泓 译 | 北京联合出版公司 | 一个来自非洲的真实故事 |
| 12.476025 | 橡树图书馆 | (美)直子·巢冬符 著 李璐 译 | 北方妇女儿童出版社 | 橡树图书馆 |
| 12.390741 | 失踪的小橘子 | (法) 纳迪娜·布兰-科姆 文 (法) 奥利维耶·塔莱克 图 邢培健 译 | 新疆青少年出版社 | 有你真好 |
| 12.390741 | 来自远方的朋友 | (法) 纳迪娜·布兰-科姆 文 (法) 奥利维耶·塔莱克 图 邢培健 译 | 新疆青少年出版社 | 有你真好 |
| 12.390741 | 送你一场流星雨 | (法) 纳迪娜·布兰-科姆 文 (法) 奥利维耶·塔莱克 图 邢培健 译 | 新疆青少年出版社 | 有你真好 |
| 11.763252 | 整体认读帮帮忙 | 周霞 著 段张取艺 绘 | 电子工业出版社 | 拼音真好玩 |
+-----------+----------------+----------------------------------------------------------------------+--------------------+------------------------+