Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎.
集成了Lucene, 只接收json格式的数据
参考资料
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
https://zhuanlan.zhihu.com/p/387442083
https://www.cnblogs.com/clonen/p/6674922.html
官网
https://www.elastic.co/cn/start
下载
Elasticsearch 和 Kibana
Kibana
如果es是数据库, Kibana就是navicat
正常解压安装
启动elasticsearch 和 Kibana
访问http://127.0.0.1:9200 正常显示json, 表示es正常启动
访问http://127.0.0.1:5601, 进入主界面, 表示kibana正常启动
MaxDirectMemorySize may have been miscalculated due to JDK-8074459
因为windows系统中配置着jdk, 这里默认使用的是环境变量中的JAVA_HOME, 由于elasticsearch版本太高, jdk版本太低, 所以导致了这个问题, 在elasticsearch7.x之后的版本, 已经集成了自带的jdk, 在根目录下的jdk文件夹中
需要修改bin/elasticsearch-env.bat启动文件的配置内容, 设置系统使用elasticsearch自带的jdk版本
在elasticsearch-env.bat文件中找到关于JAVA_HOME的配置, 注释掉if…else判断的东西, 替换为如下三行代码
删除代码段
if defined ES_JAVA_HOME (
set JAVA="%ES_JAVA_HOME%\bin\java.exe"
set JAVA_TYPE=ES_JAVA_HOME
) else if defined JAVA_HOME (
rem fallback to JAVA_HOME
echo "warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME" >&2
set JAVA="%JAVA_HOME%\bin\java.exe"
set "ES_JAVA_HOME=%JAVA_HOME%"
set JAVA_TYPE=JAVA_HOME
) else (
rem use the bundled JDK (default)
set JAVA="%ES_HOME%\jdk\bin\java.exe"
set "ES_JAVA_HOME=%ES_HOME%\jdk"
set JAVA_TYPE=bundled JDK
)
增加代码段
set JAVA="%ES_HOME%\jdk\bin\java.exe"
set JAVA_HOME="%ES_HOME%\jdk"
set JAVA_TYPE=bundled JDK
修改 elasticsearch/config/jvm.options, 增加一行字
-Dfile.encoding=GBK
修改 kibana-7.13.2-windows-x86_64\config, 增加i18n
i18n.locale: "zh-CN"
ES远程
修改config/elasticsearch.yml
找到#network.host: 192.168.0.1
改为
network.host: 0.0.0.0
cluster.initial_master_nodes: ["node-1"]
kibana远程
修改config/kibana.yml, 找到
#server.host: "localhost"
改为
server.host: "0.0.0.0"
Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒)。
一个集群就是由一个或多个节点组织在一起,它们共同持有你整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群。在产品环境中显式地设定这个名字是一个好习惯,但是使用默认值来进行测试/开发也是不错的。
一个节点是你集群中的一个服务器,作为集群的一部分,它存储你的数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
一个索引就是一个相似特征的文档的集合。比如说,你可以有一个客户数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。索引类似于关系型数据库中table的概念。在一个集群中,如果你想,可以定义任意多的索引。
7.0之后, index即一个table, 只能存一种类型的数据; 而之前的版本, index下有type, 相当于库和表的关系
一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。
在一个index/type里面,只要你想,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。文档类似于关系型数据库中Record的概念。实际上一个文档除了用户定义的数据外,还包括_index、_type和_id字段。
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。
7.0以前, 在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。类型类似于关系型数据库中Table的概念。
7.0之后, type被删除, 一个index只能存一种类型, 原来的 index-type-document的关系, 被简化成index-document的关系
一个索引就类似一个库, 一个文档就等于一行数据
1.下载测试数据, 例如shakespeare_6.0.json
测试数据格式, 格式为 newline delimited JSON (NDJSON)
{
"line_id": INT,
"play_name": "String",
"speech_number": INT,
"line_number": "String",
"speaker": "String",
"text_entry": "String",
}
2.Kibana左侧菜单 “开发工具” - “控制台”, 创建索引
API: https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
PUT /shakespeare
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"speaker": {"type": "keyword"},
"play_name": {"type": "keyword"},
"line_id": {"type": "integer"},
"speech_number": {"type": "integer"}
}
}
}
支持的字段类型, 见 https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
常用类型 | 说明 |
---|---|
text | 用于存储长文本, 会被分词, 不排序和聚合 |
keyword | 用于文本精确匹配, 排序, 过滤, 不会被分词; 用于结构化内容,如ID、电子邮件地址、主机名、状态码、邮政编码或标记。 |
constant_keyword | 用于始终包含相同值的关键字字段, 如类型或者枚举, debug, info, error |
wildcard | 通配符字段, 如quitelengthy |
boolean | true, false |
binary | base64字符串, 如blob类型 |
integer | 有符号整数, 范围同java; 类似类型有byte, short, long, unsigned_long |
double | 有符号小数, 范围同java; 类似类型有float, half_float, scaled_float |
date | 日期型, 可以是"2015-01-01", “2015-01-01T12:10:30Z”, 1420070400001等格式; 类似类型有date_nanos |
object | json格式对象 |
ip | ipv4或v6 |
range | 包括long_range, double_range, date_range, ip_range 等等 |
注意
创建索引时, 如不指定分词器, 则使用默认. 索引创建后不可修改, 只能删掉重建再导数据
删除索引
DELETE /shakespeare
3.用命令行导入样品数据文件
API: https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
cmd
cd C:\Users\81814\Downloads
curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/shakespeare/_bulk --data-binary "@shakespeare_6.0.json"
4.查询数据
API: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
GET /shakespeare/_search?from=0&size=5
{
"query": {
"term": {
"play_name": "Henry IV"
}
}
}
5.排序
默认是按_score desc排序, 可自己修改指定字段
GET /drpro_gmp/_search
{
"query": {
"match_all": {}
},
"sort": {
"create_time": {
"order": "asc"
}
}
}
可以多字段排序
GET /drpro_gmp/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"create_time": {
"order": "asc"
}
},
{
"age": {
"order": "desc"
}
}
]
}
注意: 字段名区别大小写, 查询值忽略大小写
默认分词
POST _analyze
{
"analyzer": "standard",
"text": "There-is & a DOG<br/> in house"
}
IK分词
POST _analyze
{
"analyzer":"ik_max_word",
"text":"测试ik分词器"
}
GET /shakespeare/_search
等同于
GET /shakespeare/_search
{
"query": {
"match_all": {}
}
}
term: 匹配一个值, 且不分词
get /drpro_gmp/_search
{
"query": {
"term": {
"NUM": "第一条"
}
}
}
terms: 匹配多个值, 且不分词
get /drpro_gmp/_search
{
"query": {
"terms": {
"NUM": ["第一条", "第十条"]
}
}
}
range: 范围查询, 支持gt, gte, lt, lte, 可以1个单边条件或是2个闭合条件
get /shakespeare/_search
{
"query": {
"range": {
"line_id": {
"gte": 1,
"lt": 3
}
}
}
}
match: 查询全文本字段, 会分词后查询
GET /galaxylegend/_search
{
"query": {
"match": {
"text_entry": "银河"
}
}
}
match: 查询数字,日期,布尔值和不分析的文本字段, 会精确查询
GET /galaxylegend/_search
{
"query": {
"match": {
"line_id" : 100
}
}
}
类似精确查询的有
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
term和match区别
"term": {
"CONTEXT": "质量保证"
}
和
"match": {
"CONTEXT": "质量保证"
}
term会查文字"质量保证", 而match会查 质量保证/质量/保证 3个分词任意
区别在于
wildcard:
GET /drpro_gmp/_search
{
"query": {
"wildcard": {
"CONTEXT": {
"value": "生产*"
}
}
}
}
bool: 合并多个条件, 包含3种字句
must: 多个查询条件的完全匹配,相当于 and。
must_not: 多个查询条件的相反匹配,相当于 not。
should: 至少有一个查询条件匹配, 相当于 or。
get /drpro_gmp/_search
{
"query": {
"bool": {
"must": {
"term": {
"NUM": "第一条"
}
},
"must_not": {
"match": {
"SECT_TEXT": "附录1"
}
},
"should": [
{
"term": {
"SECTION_TEXT": "范围"
}
},
{
"match": {
"SECT_TEXT": "附录6"
}
}
]
}
}
}
参考: https://elastic.blog.csdn.net/article/details/111412721
https://zhuanlan.zhihu.com/p/24832190
multi_match: 对多字段做match查询, 任意字段符合or符合皆可
GET /drpro_gmp/_search
{
"query": {
"multi_match": {
"fields": ["CONTEXT", "SECTION_TEXT"],
"query": "生产质量"
}
}
}
best_fields: 默认方法, 多个字段中,返回评分最高的
POST blogs/_search
{
"query": {
"multi_match": {
"type": "best_fields",
"query": "Quick pets",
"fields": [
"title",
"body"
],
"tie_breaker": 0.2
}
}
}
most_fields: 匹配多个字段,返回的综合评分(非最高分)
GET /titles/_search
{
"query": {
"multi_match": {
"query": "barking dogs",
"type": "most_fields",
"fields": [
"title^10",
"title.std"
]
}
}
}
cross_fields: 跨字段匹配——待查询内容在多个字段中都显示。
boost: 权重系数, 默认1.0, 可以在有多个条件时, 调整系数, 以修改分值
在fields字段里, 可以用^提高boost, 例如"fields" : [ “title^1.5”, “body” ], 标识title的boost是1.5
get /drpro_gmp/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"SECTION_TEXT": {
"value" : "范围",
"boost": 3.0
}
}
},
{
"match": {
"SECT_TEXT": "附录6"
}
}
]
}
}
}
tie_breaker: 用于best_fields/most_fields场合, 最佳匹配的分数boost系数, 加上其他字段分数tie_breaker用于降权, 一般取值0.1~0.4, 如果0则只计算best_fields/most_fields的分数
minimum_should_match: 最低匹配比例, 可以按分词或字句来, 例如
POST _analyze
{
"analyzer": "ik_max_word",
"text": "企业应当配备足够的、符合要求的人员、厂房、设施和设备"
}
结果为14个分词, 此时如果设置minimum_should_match: 75%, 则14*0.75=10.5, 需满足10个词匹配才作为结果
get /drpro_gmp/_search
{
"query": {
"match": {
"CONTEXT": {
"query": "企业应当配备足够的、符合要求的人员、厂房、设施和设备",
"minimum_should_match": "75%"
}
}
}
}
operator: match字句默认为or, 意思为分词后的任意字符串匹配皆可
fuzziness: 模糊查询, 默认为AUTO
fuzzy_transpositions: 互换模糊, 即ab->ba是允许的
prefix_length: 前缀匹配大小, 默认是0, 英文作用比较明显
进入bin
cmd
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.13.2/elasticsearch-analysis-ik-7.13.2.zip
看到控制台内容
-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.13.2/elasticsearch-analysis-ik-7.13.2.zip
[=================================================] 100%??
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: plugin requires additional permissions @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.net.SocketPermission * connect,resolve
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.
Continue with installation? [y/N]y
-> Installed analysis-ik
-> Please restart Elasticsearch to activate any plugins installed
重启es
进入kibana控制台, 执行
POST _analyze
{
"analyzer":"ik_max_word",
"text":"测试ik分词器"
}
结果正常即可
自定义扩展字典
config/analysis-ik下, 打开IKAnalyzer.cfg.xml, 设置<entry key="ext_dict">mydict.dic</entry>
然后新建mydict.dic文件, 录入自己的词库
原始文件: 银英传.txt
转化成NDJSON格式
创建索引
PUT /galaxylegend
{
"settings": {
"number_of_shards": 1,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"properties": {
"type": {
"type": "keyword"
},
"line_id": {
"type": "integer"
},
"text_entry": {
"type": "text"
}
}
}
}
导入数据
curl -s -H “Content-Type: application/x-ndjson” -XPOST localhost:9200/galaxylegend/_bulk --data-binary “@yyzout.json”
修改默认分词器
PUT /galaxylegend
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
PUT /drpro_gmp
{
"settings": {
"number_of_shards": 1,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"properties": {
"num": {"type": "text"},
"section_id": {"type": "text"},
"sect_id": {"type": "text"},
"type": {"type": "keyword"}
}
}
}
查询数据
get /drpro_gmp/_search
{
"query": {
"match": {
"CONTEXT": {
"query": "企业应当配备足够的、符合要求的人员、厂房、设施和设备",
"minimum_should_match": "75%"
}
}
},
"sort": [
{
"_score": {
"order": "desc"
}
}
]
}
有2种用法,
低级API, 在Java类中书写类似js的promise的代码实现, 兼容性好, 只要会写json
高级API, 在Java类中书写类似criteria的代码实现, 仅兼容6以上, 但版本差异很大, 6和7很多不一样的, 兼容性差, 但未来趋势是这种
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.13.3</version>
</dependency>
原生json方法, 一般不建议使用, 只用于能写json语句, 但过于复杂不好封装的情况下
分2种情况, 异步调用和同步调用, 经试验同步效果好
同步代码:
Request request = new Request("get", "/galaxylegend/_search");
request.setJsonEntity("{\"query\": {\"match\": {\"text_entry\": \"银河\"}}}");
try {
Response response=client.performRequest(request);
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(EntityUtils.toString(response.getEntity()));
} catch (IOException e) {
e.printStackTrace();
}
异步代码:
Request request = new Request("get", "/galaxylegend/_search");
request.setJsonEntity("{\"query\": {\"match\": {\"text_entry\": \"银河\"}}}");
Cancellable cancellable = client.performRequestAsync(request, new ResponseListener() {
@Override
public void onSuccess(Response response) {
System.out.println(response);
}
@Override
public void onFailure(Exception exception) {
exception.printStackTrace();
}
});
本质上是一样的, 用request指定endpoint, 然后发送json脚本执行
包装类方法, 使用一套类完成, 每种操作用的request类不一样, 类似criteria包装
常用类有
等等
如果不指定id, 则会自动生成_id的值, 格式为 abEXmnoBCuBq7w9XOv82
使用IndexRequest, 单次请求插入一条
public void insertData(String index, String id, Map<String, Object> data) {
IndexRequest indexRequest = new IndexRequest(index);
indexRequest.source(data);
//如果不指定id, 则会自动生成_id的值, 格式为abEXmnoBCuBq7w9XOv82
if(id!=null) {
indexRequest.id(id);
}
IndexResponse response = null;
try {
response = client.index(indexRequest, EsConfig.COMMON_OPTIONS);
System.out.println(response);
if(response.getResult().name().equalsIgnoreCase("created")){
System.out.println("创建成功");
}else {
System.out.println("创建失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用BulkRequest, 用集合加入IndexRequest, 单次请求插入一批
public void insertDatas(String index, List<Map<String, Object>> datas) {
BulkRequest request =new BulkRequest(index);
for(int j=0;j<datas.size();j++){
Map<String,Object> item = datas.get(j);
IndexRequest indexRequest = new IndexRequest(index);
if(item.containsKey("id")) {
String id=(String) item.get("id");
indexRequest.id(id);
item.remove("id");
}
indexRequest.source(item);
request.add(indexRequest);
}
try {
BulkResponse bulk = client.bulk(request, EsConfig.COMMON_OPTIONS);
if(bulk.status().getStatus() == 200){
System.out.println("创建成功");
}else {
System.out.println("创建失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用DeleteRequest, 通过id删除
public void deleteData(String index, String id) {
DeleteRequest deleteRequest = new DeleteRequest (index);
deleteRequest.id(id);
DeleteResponse response = null;
try {
response = client.delete(deleteRequest, EsConfig.COMMON_OPTIONS);
System.out.println(response);
if(response.getResult().name().equalsIgnoreCase("deleted")){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用GetRequest, 通过id查询
public void queryById(String index, String id) {
GetRequest request = new GetRequest(index, id);
try {
GetResponse response = client.get(request, EsConfig.COMMON_OPTIONS);
Map<String, Object> sourceAsMap = response.getSourceAsMap();
System.out.println("source: "+sourceAsMap);
} catch (IOException e) {
e.printStackTrace();
}
}
使用SearchRequest, 可以同时查询多个索引
SearchRequest request = new SearchRequest();
request.indices(index);
需要查询参数设置对象, 为SearchSourceBuilder类型
SearchSourceBuilder ssb = new SearchSourceBuilder();
request.source(ssb);
参数设置对象的内容
1.设置分页和排序
ssb.from(0).size(20).sort("line_id", SortOrder.DESC);
2.设置返回字段的包含与排除
第一个[]是包含的字段, 第二个是排除
ssb.fetchSource(new String[]{"line_id", "text_entry"}, new String[]{});
3.设置高亮字段
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("field_1"); //高亮字段1
// highlightBuilder.field("field_2"); //高亮字段2, 还可以继续加
// highlightBuilder.requireFieldMatch(false); //如果要多个字段高亮,这项要为false
highlightBuilder.preTags("<span style=\"color:red\">"); //高亮设置
highlightBuilder.postTags("</span>");
// 下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
highlightBuilder.fragmentSize(800000); //最大高亮分片数(非必要)
highlightBuilder.numOfFragments(0); //从第一个分片获取高亮片段(非必要)
ssb.highlighter(highlightBuilder);
结果高亮
SearchHit[] searchHits = response.getHits().getHits();
for (SearchHit hit : searchHits) {
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//结果高亮, 如果不写最后的map里, 高亮标记会丢失
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields.size() > 0 && highlightFields != null) {
Iterator<String> it = highlightFields.keySet().iterator();
while (it.hasNext()) {
String s = it.next();
HighlightField highlight = highlightFields.get(s);
Text[] fragments = highlight.fragments();
String fragmentString = fragments[0].string();
sourceAsMap.put(s, fragmentString);
}
}
list.add(sourceAsMap);
}
4.设置查询条件
查询所有
ssb.query(QueryBuilders.matchAllQuery());
关键字查询
ssb.query(QueryBuilders.termQuery("fieldname", "北京天安门"));
ssb.query(QueryBuilders.matchQuery("fieldname", "北京天安门"));
QueryBuilders可以创建各类查询字句
QueryBuilders.termQuery("CONTEXT", "质量"); //精确查询单个词
QueryBuilders.termsQuery("CONTEXT", "质量", "产品"); //精确查询多个词
QueryBuilders.matchQuery("CONTEXT", "质量"); //分词查询
QueryBuilders.matchQuery("CONTEXT", "质量").minimumShouldMatch("75"); //分词查询, 且在分词总量达到75%
QueryBuilders.existsQuery("SECT_ID"); //查询是否字段不为空
QueryBuilders.rangeQuery("line_id").gt(1).lte(10); //范围查询
QueryBuilders.wildcardQuery("user", "ki*hy"); //匹配查询
QueryBuilders.idsQuery().addIds("c01", "c02", "c03"); //id查询
//组合条件查询
QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("CONTEXT", "质量"))
.mustNot(QueryBuilders.termQuery("NUM", "第一章"))
.should(QueryBuilders.matchQuery("SECT_ID", "总纲"));
//多字段匹配
QueryBuilders.multiMatchQuery("生产质量", new String[] {"CONTEXT", "SECTION_TEXT"})
.type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
.tieBreaker(0.2f);
5.打印语句脚本
System.out.println(ssb.toString());