ElasticSearch:基于lucene开发的全文检索工具,底层即lucene
核心概念
- 索引 index 文档集合
- 类型 type 所属类型
- 字段 field 不同属性的分类标识
- 映射 mapping 数据使用规则设置
- 文档 document 一个文档 索引的基础信息单元
- 接近实时 nrt 一个接近实时搜索平台
- 集群 cluster 一个或多个节点组织在一起,一个集群有一个唯一名字标识
- 节点 node 集群中的一个服务器
- 分片和复制 shards&replicas 由于单个节点硬件限制而产生。如:数据太大,单台服务器无法解决。分片:将索引划分多份(可指定片数)并每片多份存储不同节点上。 复制:拉取分片拼合完整。
记
- 索引index(拥有什么样的mapping,mapping内可创建多种类型)~数据库
- 类型type(拥有什么属性即域field,类型内包含多个文档)~一张表
- 文档document(根据type所拥有属性可添加属性和对应的值 即添加域field)~表中一条数据
- 域field(拥有什么关键字和值)~表字段
操作
安装启动:elasticsearch
可用界面操作工具:elasticsearch-head-master 安装=npm install 、 启动=grunt server
发送请求的工具(可用可不用):postman 只是一个请求发送的工具
请求方式:
- get:查询
- post:更新
- put:添加
- delete:删除
索引
创建索引
url:地址:端口/索引名称
请求方式:put
请求体:json数据,即索引库的某些属性
{
"mappings" :{
"类型名称":{
"properties": {
"id": {
"type": "long",
"store":true,
"index":"not_analyzed"
},
"title":{
"type": "text",
"store" : true,
"index" :"analyzed",
"analyzer":"standard"
},
"content": {
"type": "text",
"store" :true,
"index" :"analyzed",
"analyzer":"standard"
}
}
}
}
}
设置mapping
url:地址:端口/索引名称/类型名称/_mappings
请求方式:post
请求体:json数据
{
"类型名称":{
"properties": {
属性...
}
}
}
Elasticsearch支持如下类型:
- 字符串: text, keyword(注:5之前的版本里有string类型,5之后不再支持此类型)
- 数字: byte, short, integer, long, float, double
- 布尔型:boolean
- 日期: date
- 复杂类型:如object, nested等
删除索引
url:地址:端口/索引名称
请求方式:delete
请求体:可无
文档
要区分文档id和名称为id的属性两者是不同的
索引库添加文档
url:地址:端口/索引名称/类型名称[/自定义文档id(不指定id会默认生成)]
请求方式:put或post
请求体:json数据
{
"已有某一属性名称":"值"
......
}
删除文档
url:地址:端口/索引名称/类型名称/文档id
请求方式:delete
请求体:可无
修改文档 先删除,后修改 通过文档id修改,保证修改文档id已存在即可修改某一文档
url:地址:端口/索引名称/类型名称/文档id
请求方式:post
请求体:json数据
{
按照要求修改,例如:
"id":123,
"title":"修改后的title"
......
}
查询文档
1–根据id查询
url:地址:端口/索引名称/类型名称/文档id
请求方式:get
请求体:无
返回:结果
2–根据关键词查询
url:地址:端口/索引名称/类型名称/_search
请求方式:post
请求体:json数据
{
"query":{
"term":{
"属性名称":"值" 如:"title":"新"
}
}
}
返回:结果
2–queryString查询 先分词,后查询
分词器
标准分词器
: 英文按照单词分词,中文只能按一个一个字分词 分词效果测试:
url:地址:端口/_analyze?analyzer=standard&pretty-true&text=xxx
请求方式:get
请求体:
es集成ik中文分析器 下载ik分析器压缩包解压缩,放到es安装目录的plugins目录下 es启动会自动扫描该目录然后扫描ik配置文档
- ik_smart 最少切分
- ik_max_word 最细粒度划分
创建中文分词索引库
elasticSearch集群
- 单服务器:复制多个elasticSearch,修改配置文件
#集群名称,保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-1
#必须为本机ip地址
network.host: 127.0.0.1
#服务端口号,同一机器下必须不一样
http.port: 9201
#集群间通信端口号,同一机器下必须不一样
transport.tcp.port: 9301
#设置集群自动发现机器ip集合
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
- 多服务器:配置配置文件,搭建集群
elasticSearch编程
java管理es相关操作
- 创建java工程
- 添加jar
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>5.6.8</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.6.8</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.24</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- 编写方法
索引库、文档
package com.xiaoai.es;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;
import java.net.InetAddress;
import java.security.PublicKey;
public class ElasticSearchClientTest {
//----创建索引库
@Test
public void createIndex() throws Exception{
// 1- 创建string对象配置集群名称
Settings settings = Settings.builder()
.put("cluster.name","my-elasticsearch") //设置es集群的名称
.build();
// 2- 创建客户端client对象
TransportClient client = new PreBuiltTransportClient(settings);
//设置连接节点的ip、端口 多设置防止节点报废
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
// 3- 使用客户端client对象创建索引库
client.admin().indices()
.prepareCreate("index_hello") // 设置创建索引名称
.get();//get()为执行方法,前面为设置条件
// 4- 关闭client
client.close();
}
//---索引库添加mapping
@Test
public void setMappings() throws Exception{
//1-创建setting 配置
Settings settings = Settings.builder().put("cluster.name","my-elasticsearch").build();
//2-创建client 客户端
TransportClient client = new PreBuiltTransportClient(settings);
//设置连接节点的ip、端口 多设置防止节点报废
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
// 3-创建一个mapping信息
XContentBuilder xcBuilder = XContentFactory.jsonBuilder()
.startObject()
.startObject("article")
.startObject("properties")
.startObject("id")
.field("type","long")
.field("store",true)
.endObject()
.startObject("title")
.field("type","text")
.field("store",true)
.field("analyzer","ik_smart")
.endObject()
.startObject("content")
.field("type","text")
.field("store",true)
.field("analyzer","ik_smart")
.endObject()
.endObject()
.endObject()
.endObject();
//4- 使用客户端将mapping信息设置到索引库 也可以使用PutMappingRequest
client.admin().indices()
.preparePutMapping("index_hello")//设置添加索引库的名称
.setType("article") //设置要做映射的type
.setSource(xcBuilder) //设置mappings信息
.get();//get()为执行方法
// 5-关闭client连接
client.close();
}
//=======================================================================
public TransportClient client;
@Before
public void init() throws Exception{ // 初始化client对象,不然每个方法都要写一遍太麻烦
//1-创建setting 配置
Settings settings = Settings.builder().put("cluster.name","my-elasticsearch").build();
//2-创建client 客户端
client = new PreBuiltTransportClient(settings);
//设置连接节点的ip、端口 多设置防止节点报废
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
}
//----创建文档插入数据1:
@Test
public void addDocument() throws Exception{
// 1-创建client对象
// 2-创建文档对象
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.field("id",2)
.field("title","一个title-2")
.field("content","一个content-2")
.endObject();
// 3-文档加入索引库 参数:1=索引库名称 2-类型名称 3-文档id(不设置则会默认生成一个)
// client.prepareIndex("index_hello","article","1").setSource(builder).get();//get()为执行方法
//或者 无参方法
client.prepareIndex()
.setIndex("index_hello") //设置索引库名称
.setType("article") //设置类型
.setId("2") //设置文档id,不设置会默认生成
.setSource(builder) //设置文档信息,可以XContentBuilder对象,也可以是json的字符串
.get();//get()为执行方法
// 4- 关闭client
client.close();
}
/**
* ----创建文档插入数据2: 通过实体类转化为json字符串 利用jackson工具类
* 需要导入jackson的jar包
* <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
* 不管使用XContentBuilder还是json字符串其最后都是要转成json字符串的
* @throws Exception
*/
@Test
public void addDocumentToPojo() throws Exception{
// 1-创建client对象
// 2-创建实体类对象并设置属性
Article article = new Article();
article.setId(3);
article.setTitle("这是一个title-3");
article.setContent("这是一个content-3");
// 3-实体类对象转换为json格式字符串
ObjectMapper objectMapper = new ObjectMapper();
String jsonDocument = objectMapper.writeValueAsString(article);
// 4-json字符串 即文档写入索引库
client.prepareIndex("index_hello","article","3")
.setSource(jsonDocument, XContentType.JSON) //设置json字符串并指定其为一个json数据
.get();//get()为执行方法
//关闭client
client.close();
}
}
实体类
package com.xiaoai.es;
public class Article {
private long id;
private String title;
private String content;
public long getId() {return id;}
public void setId(long id) {this.id = id;}
public String getTitle() { return title;}
public void setTitle(String title) {this.title = title;}
public String getContent() {return content;}
public void setContent(String content) {this.content = content;}
}
搜索
package com.xiaoai.es;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.Map;
public class SearchIndex {
public TransportClient client;
@Before
public void init() throws Exception{
//1-创建setting 配置
Settings settings = Settings.builder().put("cluster.name","my-elasticsearch").build();
//2-创建client 客户端
client = new PreBuiltTransportClient(settings);
//设置连接节点的ip、端口 多设置防止节点报废
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
}
//搜索=======================================================================
/**
* 1-创建client对象
* 2-创建查询对象,可以使用QueryBuilders工具类创建QueryBuilder对象
* 3-client查询
* 4-得查询结果
* 5-可以取结果总记录数
* 6-取查询列表
* 7-关闭client
*/
//----搜索1--根据文档id搜索
@Test
public void searchById() throws Exception{
// 1-client 创建client对象
// 2-QueryBuilders 创建查询对象,可以使用QueryBuilders工具类创建QueryBuilder对象
QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1","2");//查询id为1和2的数据
// 3-SearchResponse client查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
.get();
// 4-SearchHits 得查询结果
SearchHits hits = searchResponse.getHits();
// 5-可以取结果总记录数
System.out.println("查询结果总记录数:"+hits.getTotalHits()); //hits.totalHits也可以
// 6-取查询列表
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()){
SearchHit searchHit = iterator.next();
// 打印文档对象,一json格式输出
System.out.println(searchHit.getSourceAsString());
//取文档属性
Map<String, Object> document = searchHit.getSource();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
System.out.println("--------------------------------分隔");
}
// 7-关闭client
client.close();
}
// 其实查询步骤都差不多,主要是创建Query时不同,可封装方法,传入Query对象就行
private void search(QueryBuilder queryBuilder) throws Exception{
// 3-SearchResponse client设置查询信息并执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
.setFrom(0)//起始行号,可通过setFrom、setSize两个属性来实现分页。
.setSize(5)//每页可显示行数
.get();
// 4-SearchHits 得查询结果
SearchHits hits = searchResponse.getHits();
// 5-可以取结果总记录数
System.out.println("查询结果总记录数:"+hits.getTotalHits()); //hits.totalHits也可以
// 6-取查询列表
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()){
SearchHit searchHit = iterator.next();
// 打印文档对象,一json格式输出
System.out.println(searchHit.getSourceAsString());
//取文档属性
Map<String, Object> document = searchHit.getSource();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
System.out.println("--------------------------------分隔");
}
// 7-关闭client
client.close();
}
//----搜索2--根据Term查询(即关键字查询)
@Test
public void searchByTerm() throws Exception{
//创建具体查询
QueryBuilder queryBuilder = QueryBuilders.termQuery("title","一个");//参数:1=要搜索的字段,即默认域field 2=要搜索的关键字
search(queryBuilder);
}
//----搜索3--QueryString查询(先分词,后查询)
@Test
public void queryStringSearch() throws Exception{
//创建具体查询
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("这是美好的世界")//参数:1=要搜索的字段,即默认域field 2=要搜索的关键字
.defaultField("title");//默认搜索域,不指定则全文搜索
search(queryBuilder);
}
}
小结
- Settings settings = Settings.builder().put(“cluster.name”,“集群名称”).build() 配置集群名称
- TransportClient client = new PreBuiltTransportClient(settings) 创建客户端 addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(“IP地址”),节点端口)) 添加节点地址
- client.admin().indices().prepareCreate(“索引名称”).get() 创建索引
- client.admin().indices().preparePutMapping(“索引名称”).setType(“类型名称”).setSource(XContentBuilder对象即json数据即maping信息).get() 添加mappings
- XContentBuilder 可创建一个json数据
- client.prepareIndex().setIndex(“索引名称”).setType(“类型名称”).setId(“文档id”).setSource(XContentBuilder对象即json数据即文档信息).get() 创建文档、插入数据
- client.prepareIndex(“index_hello”,“article”,“3”).setSource(实体类转json后的string字符串, XContentType.JSON).get() 创建文档、插入数据(通过建立类型实体类,通过实体类对象转json字符串作为文档的信息)
- 查询
- QueryBuilder 创建查询
- QueryBuilders.idsQuery().addIds(id…) 1-通过id查询
- QueryBuilders.termQuery(“域”,“关键字”) 2-通过关键字查询
- QueryBuilders.queryStringQuery(“这是美好的世界”).defaultField(“默认搜索域,不指定则全文检索”) 3-先分词,后查询 - SearchResponse searchResponse = client.prepareSearch(“index_hello”).setTypes(“article”).setQuery(queryBuilder对象).get() 设置查询条件
- SearchHits hits = searchResponse对象.getHits() 执行查询
- .getTotalHits() 查询总记录
- .iterator() 获取查询结果
- 客户端对象.close()关闭客户端
高亮显示
//设置查询高亮显示
private void search(QueryBuilder queryBuilder,String highlightField) throws Exception{
HighlightBuilder highlightBuilder = new HighlightBuilder();
//高亮显示的字段
highlightBuilder.field(highlightField);//那个域要高亮显示
highlightBuilder.preTags("<em>"); //高亮前缀
highlightBuilder.postTags("<em>");//高楼后缀
// 3-SearchResponse client查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
.setFrom(0)//数据行从0开始
.setSize(5)//每页可显示行数
.highlighter(highlightBuilder) //设置高亮显示
.get();
// 4-SearchHits 得查询结果
SearchHits hits = searchResponse.getHits();
// 5-可以取结果总记录数
System.out.println("查询结果总记录数:"+hits.getTotalHits()); //hits.totalHits也可以
// 6-取查询列表
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()){
SearchHit searchHit = iterator.next();
// 打印文档对象,一json格式输出
System.out.println(searchHit.getSourceAsString());
//取文档属性
Map<String, Object> document = searchHit.getSource();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
System.out.println("\n*********高亮结果");
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
System.out.println(highlightFields);
//取高亮结果
HighlightField field = highlightFields.get(highlightField);
Text[] fragments = field.getFragments();
if (fragments!=null){
String title = fragments[0].toString();
System.out.println(title);
}
System.out.println();
System.out.println("--------------------------------分隔");
}
// 7-关闭client
client.close();
}
//高亮显示
@Test
public void queryStringSearch_gl() throws Exception{
//创建具体查询
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("这是美好的世界")//参数:1=要搜索的字段,即默认域field 2=要搜索的关键字
.defaultField("title");//默认搜索域,不指定则全文搜索
search(queryBuilder,"title");
}
spring data elasticSearch
Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。
相关操作
- 引入jar
- 配置application.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd
">
<!--elastic客户对象的配置-->
<elasticsearch:transport-client id="esClient" cluster-name="my-elasticsearch" cluster-nodes="127.0.0.1:9301,127.0.0.1:9302,127.0.0.1:9303"/>
<!--配置包扫描器,扫描dao的接口-->
<elasticsearch:repositories base-package="com.xiaoai.es.repositories"/>
<!---->
<bean id="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
<constructor-arg name="client" ref="esClient"/>
</bean>
</beans>
- 创建实体类及映射
@Document(indexName="索引名称",type="类型", shards=分片数量, replicas=备份数量)
public class Item {
@Id //标识id
private Long id;
@Field(type=属性类型(如:FieldType.Text),store=true或false(是否保存),analyzer="属性分词器(如:ik_max_word)")
private String title; //标题
其他属性及映射....
属性对应set和get方法.......
}
- 创建接口 继承ElasticsearchRepository<待操作实体类类型,主键id类型>
- 引入接口类对象 引入ElasticsearchTemplate对象;
- ElasticsearchTemplate对象.createIndex(映射实体类class) 创建索引
- 接口类对象.save(实体类对象) 创建文档即插入文档信息 根据实体类id是否存在 存在=修改 不存在=新增
- 接口类对象.deleteById(id) 根据id删除文档 .deleteAll()删除全部文档
- 接口类对象.findAll 查询所有 .findById()根据id查询