一、ElasticSearch简介
Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
二、ElasticSearch编程操作
确保已经安装ElasticSearch,创建一个maven工程。
首先实现创建索引和映射。
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lxt</groupId>
<artifactId>es-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<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>
</project>
- ElasticSearchClientTest
public class ElasticSearchClientTest {
TransportClient client;
@Before
public void init() throws Exception {
//1.配置
Settings settings = Settings.builder().put("cluster.name", "my-application").build();
//2.客户端
client = new PreBuiltTransportClient(settings);
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.0.111"),9300));
}
@Test
public void createIndex() throws Exception{
//3.使用api创建索引
client.admin().indices().prepareCreate("index_hello").get();
//4.关闭client
client.close();
}
@Test
public void setMappings() throws Exception{
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.startObject("article")
.startObject("properties")
.startObject("id")
.field("type","integer").field("store","yes")
.endObject()
.startObject("title")
.field("type","string").field("store","yes").field("analyzer","ik_smart")
.endObject()
.startObject("content")
.field("type","string").field("store","yes").field("analyzer","ik_smart")
.endObject()
.endObject()
.endObject()
.endObject();
//3.使用api创建映射
client.admin().indices().preparePutMapping("index_hello").setType("article").setSource(builder).get();
//4.关闭client
client.close();
}
}
接下来构建document对象,添加jackson依赖。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.1</version>
</dependency>
- ElasticSearchClientTest
/**
* 使用XContentBuilder构建Document对象
* @throws Exception
*/
@Test
public void testAddDocument() throws Exception{
//创建一个client对象
//创建一个文档对象
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.field("id",2l)
.field("title","北方入秋速度明显加快 多地降温幅度最多可达10度22222")
.field("content","阿联酋一架客机在纽约机场被隔离 10名乘客病倒")
.endObject();
//把文档对象添加到索引库
client.prepareIndex()
//设置索引名称
.setIndex("index_hello")
//设置type
.setType("article")
//设置文档的id,如果不设置的话自动地生成一个id
.setId("2")
//设置文档信息
.setSource(builder)
//执行操作
.get();
//关闭客户端
client.close();
}
@Test
public void testAddDocument2() throws Exception{
//创建一个Article对象
Article article = new Article();
//设置对象的属性
article.setId(3);
article.setTitle("MH370坠毁在柬埔寨密林?中国一公司调十颗卫星去拍摄");
article.setContent("警惕荒唐的死亡游戏!俄15岁少年输掉游戏后用电锯自杀");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(article);
//把文档对象添加到索引库
client.prepareIndex()
//设置索引名称
.setIndex("index_hello")
//设置type
.setType("article")
//设置文档的id,如果不设置的话自动地生成一个id
.setId("3")
//设置文档信息
.setSource(json, XContentType.JSON)
//执行操作
.get();
//关闭客户端
client.close();
}
@Test
public void testAddDocument3() throws Exception{
for (int i = 4; i < 100; i++) {
//创建一个Article对象
Article article = new Article();
//设置对象的属性
article.setId(i);
article.setTitle("女护士路遇昏迷男子跪地抢救:救人是职责更是本能" + i);
article.setContent("江西变质营养餐事件已致24人就医 多名官员被调查" + i);
//把article对象转化成json格式的字符串
ObjectMapper objectMapper = new ObjectMapper();
String jsonDocument = objectMapper.writeValueAsString(article);
System.out.println(jsonDocument);
//使用client对象把文档写入索引库
client.prepareIndex("index_hello","article",i + "")
.setSource(jsonDocument,XContentType.JSON)
.get();
}
//关闭客户端
client.close();
}
- Article
public class Article {
private Integer id;
private String title;
private String content;
...
}
操作查询API。
- IndexSearch
public class IndexSearch {
TransportClient client;
@Before
public void init() throws Exception {
//1.配置
Settings settings = Settings.builder().put("cluster.name", "my-application").build();
//2.客户端
client = new PreBuiltTransportClient(settings);
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.0.111"),9300));
}
private void search(QueryBuilder queryBuilder) {
//2.执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
.get();
//3.处理结果
SearchHits searchHits = searchResponse.getHits();
System.out.println("总行数: " + searchHits.getTotalHits());
Iterator<SearchHit> it = searchHits.iterator();
while (it.hasNext()) {
SearchHit searchHit = it.next();
//source->document的json输出
System.out.println(searchHit.getSourceAsString());
System.out.println("----文档属性----");
Map<String, Object> document = searchHit.getSource();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
}
}
/**
* 1.term查询
* @throws Exception
*/
@Test
public void testQueryByTeam() throws Exception {
//1.构建queryBuilder
QueryBuilder queryBuilder = QueryBuilders.termQuery("title","女护士");
search(queryBuilder);
}
/**
* 2.queryStringQuery查询
* @throws Exception
*/
@Test
public void testQueryByQueryString() throws Exception {
//1.构建queryBuilder
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
//2.执行查询
search(queryBuilder);
}
/**
* 3.matchQuery查询
* @throws Exception
*/
@Test
public void testQueryByMatchQuery() throws Exception {
//1.构建queryBuilder
QueryBuilder queryBuilder = QueryBuilders.matchQuery("title","美丽的女护士");
//2.执行查询
search(queryBuilder);
}
/**
* 4.按照id查询
* @throws Exception
*/
@Test
public void testSearchById() throws Exception {
//1.构建queryBuilder
QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1","2");
search(queryBuilder);
}
/**
* 5.分页查询
* @throws Exception
*/
@Test
public void testSearchByMatchAll() throws Exception {
//1.构建queryBuilder
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
//2.执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
.setFrom(0)
.setSize(5)
.get();
//3.处理结果
SearchHits searchHits = searchResponse.getHits();
System.out.println("总行数: " + searchHits.getTotalHits());
Iterator<SearchHit> it = searchHits.iterator();
while (it.hasNext()) {
SearchHit searchHit = it.next();
//source->document的json输出
System.out.println(searchHit.getSourceAsString());
System.out.println("----文档属性----");
Map<String, Object> document = searchHit.getSource();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
}
}
}
在每个搜索结果中 高亮 部分文本片段,以便让用户知道为何该文档符合查询条件。在 Elasticsearch 中检索出高亮片段也很容易。
- IndexSearch
/**
* 6.高亮查询
* @throws Exception
*/
@Test
public void testSearchByHighLight() throws Exception {
//1.构建queryBuilder
// QueryBuilder queryBuilder = QueryBuilders.matchQuery("title","北方");
QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery("10","title","content");
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.field("content");
highlightBuilder.preTags("<em>");
highlightBuilder.postTags("</em>");
//2.执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
.highlighter(highlightBuilder)
.get();
//3.处理结果
SearchHits searchHits = searchResponse.getHits();
System.out.println("总行数: " + searchHits.getTotalHits());
Iterator<SearchHit> it = searchHits.iterator();
while (it.hasNext()) {
SearchHit searchHit = it.next();
//source->document的json输出
System.out.println("----文档内容----");
System.out.println(searchHit.getSourceAsString());
System.out.println("----高亮结果----");
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
for(Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
System.out.println(entry.getKey() + ":\t" + Arrays.toString(entry.getValue().getFragments()));
}
}
}
三、Spring Data ElasticSearch
Spring Data是一个用于简化数据库访问的开源框架,Spring Data ElasticSearch是Spring Data家族中的一个引擎。
用Spring Initializr创建工程。
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.16.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lxt</groupId>
<artifactId>es-demo2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es-demo2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.yml
spring:
data:
elasticsearch:
cluster-name: my-application
cluster-nodes: 192.168.0.111:9300
- Article
@Document(indexName = "lxt_blog", type = "article")
public class Article {
@Id
@Field(type = FieldType.Long, store = true)
private Long id;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
private String content;
...
}
- ArticleDao
public interface ArticleDao extends ElasticsearchRepository<Article,Long> {
public List<Article> findByTitleLike(String title);
public List<Article> findByTitle(String title);
public List<Article> findByTitleLikeOrContent(String title, String content);
public List<Article> findByTitleOrContent(String title, String content, Pageable pageable);
}
- EsDemo2ApplicationTests
@RunWith(SpringRunner.class)
@SpringBootTest
public class EsDemo2ApplicationTests {
@Autowired
private ArticleDao dao;
@Autowired
private ElasticsearchTemplate template;
/**
* 创建索引
*/
@Test
public void createIndex() {
template.createIndex(Article.class);
//配置mapping
// template.putMapping(Article.class);
}
/**
* 增加数据
* @throws Exception
*/
@Test
public void addDocument() throws Exception {
for (int i = 1; i <= 20; i++) {
//创建一个Article对象
Article article = new Article();
article.setId(new Long(i));
article.setTitle("女护士路遇昏迷男子跪地抢救:救人是职责更是本能" + i);
article.setContent("这是一个美丽的女护士妹妹" + i);
//把文档写入索引库
dao.save(article);
}
}
/**
* 删除数据
* @throws Exception
*/
@Test
public void deleteDocumentById() throws Exception {
dao.deleteById(3l);
}
/**
* 查询所有
*/
@Test
public void findAll() {
dao.findAll().forEach(System.out :: println);
}
/**
* 查询单个
*/
@Test
public void findById() {
System.out.println(dao.findById(1l));
}
/**
* 查询标题
*/
@Test
public void findByTitle() {
dao.findByTitle("美丽女护士").forEach(System.out :: println); //使用match查询
}
/**
* 查询标题和内容
*/
@Test
public void findByTitleOrContent() {
//加like的话就会分词
dao.findByTitleLikeOrContent("美丽女护士","女护士").forEach(System.out :: println);
}
/**
* 分页查询
*/
@Test
public void findByTitlePage() {
Pageable pageable = PageRequest.of(1,5);
dao.findByTitleOrContent("女护士","女护士",pageable).forEach(System.out :: println);
}
/**
* 本地查询,用原来的方法
* @throws Exception
*/
@Test
public void testNativeSearchQuery() throws Exception {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.queryStringQuery("女护士").defaultField("title"))
.withPageable(PageRequest.of(1,5))
.build();
template.queryForList(query,Article.class).forEach(System.out :: println);
}
}