ElasticSearch入门
1. 概念
1.1 什么是ElasticSearch?
ElasticSearch简称ES,es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。ES也使用Java开发并使用Lucene作为其核心来实现所有引擎和搜索的共鞥你,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文检索变得简单。
1.2 ElasticSearch对比Solr
- Solr利用Zookeeper进行分布式管理,而LasticSearch自身带有分布式协调管理功能
- Solr支持更多格式的数据,而Elasticsearch只支持json格式
- Solr提供的功能更多,而Elasticsearch更注重核心功能,高级功能由第三方插件提供
- Solr在传统的搜索应用中表现好于Elasticsearch,而在处理实时数据时效率明显低于ElasticSearch
1.3 和关系数据库对比
-
index : 相当于关系型数据库中的 database (库级别的)
-- mappings:相当于关系型数据库的schema
-
type: 相当于关系型数据库中的表的概念
-
document:相当于关系型数据中的表中的每一行数据
-
field: 相当于关系型数据中的表中的每一个字段
1.4 Elasticsearch 其他相关概念
-
Near Realtime(NRT) 几乎实时
Elasticsearch是一个几乎实时的搜索平台。意思是,从索引一个文档到这个文档可被搜索只需要一点点的延迟,这个时间一般为毫秒级。就是快!
-
Cluster 集群
群集是一个或多个节点(服务器)的集合, 这些节点共同保存整个数据,并在所有节点上提供联合索引和搜索功能。一个集群由一个唯一集群ID确定,并指定一个集群名(默认为“elasticsearch”)。该集群名非常重要,因为节点可以通过这个集群名加入群集,一个节点只能是群集的一部分。
确保在不同的环境中不要使用相同的群集名称,否则可能会导致连接错误的群集节点。例如,你可以使用logging-dev、logging-stage、logging-prod分别为开发、阶段产品、生产集群做记录。
最后总结一句话:elasticserach做集群很牛逼。集群是它的一大特色。
-
Node****节点(集群的组成部分)
节点是单个服务器实例,它是群集的一部分,可以存储数据,并参与群集的索引和搜索功能。就像一个集群,节点的名称默认为一个随机的通用唯一标识符(UUID),确定在启动时分配给该节点。如果不希望默认,可以定义任何节点名。这个名字对管理很重要,目的是要确定你的网络服务器对应于你的ElasticSearch群集节点。
我们可以通过群集名配置节点以连接特定的群集。默认情况下,每个节点设置加入名为“elasticSearch”的集 群。 这意味着如果你启动多个节点在网络上,假设他们能发现彼此都会自动形成和加入一个名 为“elasticsearch”的集群。
在单个群集中,您可以拥有尽可能多的节点。此外,如果“elasticsearch”在同一个网络中,没有其他节点正在运行,从单个节点的默认情况下会形成一个新的单节点名为"elasticsearch"的集群。
**4. 分片和复制 shards&replicas
索引可以存储大量的数据,这些数据可能超过单个节点的硬件限制。例如,十亿个文件占用磁盘空间1TB的单指标可能不适合对单个节点的磁盘或可能太慢服务仅从单个节点的搜索请求。
为了解决这一问题,Elasticsearch提供细分你的指标分成多个块称为分片的能力。当你创建一个索引,你可以简单地定义你想要的分片数量。每个分片本身是一个全功能的、独立的“指数”,可以托管在集群中的任何节点。
Shards分片的重要性主要体现在以下两个特征:
-
分片允许您水平拆分或缩放内容的大小
-
分片允许你分配和并行操作的碎片(可能在多个节点上)从而提高性能/吞吐量
这个机制中的碎片是分布式的以及其文件汇总到搜索请求是完全由ElasticSearch管理,对用户来说是透明的。
在同一个集群网络或云环境上,故障是任何时候都会出现的,拥有一个故障转移机制以防分片和结点因为某些 原因离线或消失是非常有用的,并且被强烈推荐。为此,Elasticsearch允许你创建一个或多个拷贝,你的索引 分片进入所谓的副本或称作复制品的分片,简称Replicas。
Replicas的重要性主要体现在以下两个特征:
-
副本为分片或节点失败提供了高可用性。为此,需要注意的是,一个副本的分片不会分配在同一个节点作为原始的或主分片,副本是从主分片那里复制过来的。
-
副本允许用户扩展你的搜索量或吞吐量,因为搜索可以在所有副本上并行执行。
2. Java原生操作ElasticSearch
2.1 插入
2.1.1 创建Maven工程导入jar包
<!-- 必导依赖-->
<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>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
2.1.2 测试类
public class ElasticSearchDemo {
/**
* 创建索引库
*/
@Test
public void test01() throws IOException {
// 创建连接ES的操作对象
// Settings.EMPTY: 表示当前不配置集群
PreBuiltTransportClient builtTransportClient = new PreBuiltTransportClient(Settings.EMPTY);
// 连接ES服务器
TransportClient transportClient=null;
try {
transportClient = builtTransportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
// 准备一个文档信息 {"id":1,"title":"xxx","content":"xxx"}
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject() // {
.field("id",1) // "id":1
.field("title","什么是ElasticSearch") // "title":"elastic"
.field("content","ElasticSearch简称ES,es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。ES也使用Java开发并使用Lucene作为其核心来实现所有引擎和搜索的共鞥你,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文检索变得简单。") // "content":"xxx"
.endObject(); // }
// 创建索引
builtTransportClient.prepareIndex("blog1","article","1").setSource(builder).get();
} catch (UnknownHostException e) {
e.printStackTrace();
} finally {
assert transportClient != null;
transportClient.close();
}
}
}
2.1.3 效果
2.2 查询
2.2.1 查询所有
public class ElasticSearchQuery {
@Test
public void test01() {
// 获取Es连接对象 自定义工具类
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
// 构建查询的请求对象
//QueryBuilders.matchAllQuery() 查询全部
//QueryBuilders.queryStringQuery("ElasticSearch") 字符串查询
//QueryBuilders.wildcardQuery("content","*索*"); // 模糊查询
SearchRequestBuilder searchRequestBuilder = transportClient.prepareSearch("blog1").setTypes("article").setQuery(QueryBuilders.matchAllQuery());
// 执行查询
SearchResponse searchResponse = searchRequestBuilder.get();
SearchHits hits = searchResponse.getHits();
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()) {
SearchHit hit = iterator.next();
System.out.println(hit.getSourceAsString());
}
// 关闭资源
ElasticSearchUtil.close();
}
}
高亮查询
/**
* 高亮查询
*/
@Test
public void test05() {
// 获取Es连接对象
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
// 构建查询的请求对象
SearchRequestBuilder requestBuilder = transportClient
.prepareSearch("blog2")
.setTypes("article")
.setQuery(QueryBuilders.termQuery("title","搜索"));// 词条查询
// 设置分页查询
requestBuilder.setFrom(0);
requestBuilder.setSize(15);
// 设置排序 根据id倒序查询
requestBuilder.addSort("id", SortOrder.DESC);
// 设置高亮对象
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.preTags("<font style='color:red'>");
highlightBuilder.postTags("</font>");
requestBuilder.highlighter(highlightBuilder);
// 执行查询
SearchResponse searchResponse = requestBuilder.get();
SearchHits hits = searchResponse.getHits();
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()) {
SearchHit hit = iterator.next();
Text[] titles = hit.getHighlightFields().get("title").getFragments();
for (Text text: titles) {
System.out.println(text);
}
}
// 关闭资源
ElasticSearchUtil.close();
}
2.2.2 分词查询
es默认是按照单个字符进行分词查询的,所以如果不安装分词器的话,查询不出词组,这里我们安装的是IK分词器
例如:我是程序员
分词后的效果为:我、是、程、序、员
而我们需要的分词效果是:我、是、程序、程序员
2.2.2.1 IK分词器的安装
-
下载地址:
-
解压,将解压后的文件夹拷贝到下,并重命名文件夹为ik
2.2.2.1 IK分词器测试
IK提供了两个分词算法ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word为最细粒度划分
-
最小切分:在浏览器地址栏输入地址
http://127.0.0.1:9200/_analyze?analyzer=ik_smart&pretty=true&text=我是程序员
-
最细切分:在浏览器地址栏输入地址
http://127.0.0.1:9200/_analyze?analyzer=ik_max_word&pretty=true&text=我是程序员
-
在ES没有自己手动创建索引的映射mapping情况下,默认创建索引的情况下无法使用IK分词器!!!
2.2.3 创建索引
/**
* 创建映射信息
*/
@Test
public void test01() throws IOException, ExecutionException, InterruptedException {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
XContentBuilder builder = XContentFactory.jsonBuilder();
/*builder.startObject() // {
.field("id",1) // "id":1
.field("title","什么是ElasticSearch") // "title":"elastic"
.field("content","ElasticSearch简称ES,es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。ES也使用Java开发并使用Lucene作为其核心来实现所有引擎和搜索的共鞥你,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文检索变得简单。") // "content":"xxx"
.endObject(); // }*/
// 创建索引库 加入映射信息 {
// "mappings":{
// "article":{
// "properties": {
// "id": {"type": long}
// "title": {"type": string}
// "title": {"type": string}
// }
// }
// }
// }
builder.startObject()
.startObject("article")
.startObject("properties")
.startObject("id")
.field("type","integer") //id 类型
.field("store","yes") //是否存储
.endObject()
.startObject("title")
.field("type","string") //id 类型
.field("store","yes") //是否存储
.field("analyzer","ik_max_word") //分词
.endObject()
.startObject("content")
.field("type","string") //id 类型
.field("store","yes") //是否存储
.field("analyzer","ik_max_word")
.endObject()
.endObject()
.endObject()
.endObject();
// 获取mappings映射对象
PutMappingRequest mappingRequest = Requests.putMappingRequest("blog2").type("article").source(builder);
// 将映射信息加入到索引库
transportClient.admin().indices().putMapping(mappingRequest).get();
//CreateIndexResponse indexResponse = transportClient.admin().indices().prepareCreate("blog2").get();
// 删除索引库
//transportClient.admin().indices().prepareDelete("blog2").get();
//indexResponse.addCustomFields(builder);
ElasticSearchUtil.close();
}
/**
* 创建文档信息 通过XContentBuilder构建
*/
@Test
public void test02() throws IOException, ExecutionException, InterruptedException {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject()
.field("1",1)
.field("title","ElasticSearch是一个基于Lucene的搜索服务器")
.field("content","它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是
" +
"用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能
" +
"够达到实时搜索,稳定,可靠,快速,安装使用方便。
")
.endObject();
// 将文档信息加入指定的索引库
transportClient.prepareIndex("blog2","article","3").setSource(builder).get();
ElasticSearchUtil.close();
}
/**
* 创建文档信息 使用Json转换实体
*/
@Test
public void test03() throws IOException, ExecutionException, InterruptedException {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
// 初始化实体类
Article article = new Article();
article.setId(4);
article.setTitle("ElasticSearch是一个基于Lucene的搜索服务器");
article.setContent("它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是" +
"用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能" +
"够达到实时搜索,稳定,可靠,快速,安装使用方便。\n");
// 创建jackson
ObjectMapper objectMapper = new ObjectMapper();
String articleStr = objectMapper.writeValueAsString(article);
// 将文档信息加入指定的索引库
transportClient.prepareIndex("blog2","article",article.getId().toString()).setSource(articleStr).get();
ElasticSearchUtil.close();
}
2.3 修改文档
/**
* 修改文档信息 使用Json转换实体
*/
@Test
public void test04() throws IOException, ExecutionException, InterruptedException {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
// 初始化实体类
Article article = new Article();
article.setId(4);
article.setTitle("ElasticSearch是一个基于Lucene的搜索服务器44");
article.setContent("44它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是" +
"用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能" +
"够达到实时搜索,稳定,可靠,快速,安装使用方便。\n");
// 创建jackson
ObjectMapper objectMapper = new ObjectMapper();
String articleStr = objectMapper.writeValueAsString(article);
// 修改文档
transportClient.prepareUpdate("blog2","article",article.getId().toString()).setDoc(articleStr).get();
ElasticSearchUtil.close();
}
/**
* 修改文档信息 使用Json转换实体
*/
@Test
public void test05() throws IOException, ExecutionException, InterruptedException {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
// 初始化实体类
Article article = new Article();
article.setId(4);
article.setTitle("ElasticSearch是一个基于Lucene的搜索服务器44");
article.setContent("44它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是" +
"用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能" +
"够达到实时搜索,稳定,可靠,快速,安装使用方便。\n");
// 创建jackson
ObjectMapper objectMapper = new ObjectMapper();
String articleStr = objectMapper.writeValueAsString(article);
//创建一个修改对象
UpdateRequest updateRequest = new UpdateRequest("blog2","article",article.getId().toString());
updateRequest.doc(articleStr, XContentType.JSON);
// 修改文档
transportClient.update(updateRequest);
ElasticSearchUtil.close();
}
3.4 删除文档
/**
* 删除文档 DeleteRequest
*/
@Test
public void test06() throws IOException, ExecutionException, InterruptedException {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
DeleteRequest updateRequest = new DeleteRequest("blog2","article","4");
// 修改文档
transportClient.delete(updateRequest);
ElasticSearchUtil.close();
}
/**
* 删除文档
*/
@Test
public void test07() {
TransportClient transportClient = ElasticSearchUtil.getTransportClient();
// 删除文档
transportClient.prepareDelete("blog1","article","1");
ElasticSearchUtil.close();
}
3. SpringData ElasticSearch 操作ES
Spring Data ElasticSearch 基于Spring Data API 简化ElasticSearch操作,将原生操作es的客户端API进行封装.Spring Data为ES项目提供集成搜索引擎.Spring Data ElasticSearch POJO的关键功能区域为中心的模型与ES交互文档和轻松编写一个存储库数据访问层
3.1 导入坐标
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependencies>
<!--spring web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring整合es-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</dependencies>
3.2 添加application.yml
3.3 创建实体类
/**
其中,注解解释如下:
@Document(indexName="blob3",type="article"):
indexName:索引的名称(必填项)
type:索引的类型
@Id:唯一标识
@Field(index=true,analyzer="ik_smart",store=true,searchAnalyzer="ik_smart",type = FieldType.text)
index:是否设置分词
analyzer:存储时使用的分词器
searchAnalyze:搜索时使用的分词器
store:是否存储
type: 数据类型
注: 一旦添加了@Filed注解,所有的默认值都不再生效。此外,如果添加了@Filed注解,那么type字段必须指定。
所以一般不用添加。
*/
@Document(indexName = "blog3",type = "article")
public class Article {
@Id
//@Field(store = true,type = FieldType.Integer)
private Integer id;
//@Field(index = true,analyzer = "ik_smart",store = true,type = FieldType.text)
private String title;
//@Field(index = true,analyzer = "ik_smart",store = true,type = FieldType.text)
private String content;
getter...
}
3.4 创建dao接口
public interface ArticleDao extends ElasticsearchRepository<Article,Integer> {
// 按照dao层方法命名查询
Page<Article> findByTitle(String str, Pageable pageable);
}
3.5 创建service
@Service
public class ArticleService {
@Autowired
private ArticleDao articleDao;
/**
* 保存一条数据
* @param article
*/
public void save(Article article) {
articleDao.save(article);
}
/**
* 修改数据
* @param article
*/
public void update(Article article) {
// 修改其实就是一种覆盖
articleDao.save(article);
}
/**
* 根据Id修改数据
* @param id
*/
public void deleteById(Integer id) {
articleDao.deleteById(id);
}
/**
* 删除所有
*/
public void deleteAll() {
articleDao.deleteAll();
}
/**
* 根据对象删除
*/
public void delete(Article article) {
articleDao.delete(article);
}
/**
* 查询
*/
public List<Article> findAll() {
Iterable<Article> all = articleDao.findAll();
ArrayList<Article> articles = Lists.newArrayList(all);
return articles;
}
public Page<Article> findAll(String str, Pageable pageable) {
return articleDao.findByTitle(str,pageable);
}
}
3.6 创建测试类
/**
* 入门案例
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = EsApplication.class)
public class SpringDataEsTest {
@Autowired
private ArticleService service;
/**
* 增
*/
@Test
public void test01() {
Article article = new Article();
article.setId(1);
article.setTitle("spring data 操作 ElasticSearch");
article.setContent("它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是" +
" 用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能" +
" +
" 够达到实时搜索,稳定,可靠,快速,安装使用方便。");
service.save(article);
}
/**
* 删除 其他两种略
*/
@Test
public void test02() {
service.deleteById(1);
}
/**
* 修改
*/
@Test
public void test03() {
Article article = new Article();
article.setId(1);
article.setTitle("spring data 操作 ElasticSearch update");
article.setContent("它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是" +
" 用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能" +
" +
" 够达到实时搜索,稳定,可靠,快速,安装使用方便。");
service.update(article);
}
/**
* 查
*/
@Test
public void test04() {
List<Article> all = service.findAll();
for (Article article : all) {
System.out.println(article.toString());
}
}
/**
* 词条查询
*/
@Test
public void test06() {
String str = "update";
Pageable pageable = PageRequest.of(0,10);
Page<Article> all = service.findAll(str,pageable);
List<Article> content = all.getContent();
System.out.println(content);
System.out.println("总条数:" + all.getTotalElements());
}