我们知道ES的操作方式有两种,一种是http风格的,一种是tcp风格的,http风格的我们可以通过发送请求去进行索引的增删改查等操作、tcp风格的处理方式则需要我们编码去调用实现,由于ES本身是基于Lucene,而Lucene又是Java的开源搜索引擎,所以我们用Java语言去实现ES的调用。
创建一个带有Maven的Java项目,添加Jar包并引入Maven的坐标
<!--指定编译来源为jdk1.9-->
<properties>
<maven.compiler.source>1.9</maven.compiler.source>
<maven.compiler.target>1.9</maven.compiler.target>
</properties>
<!--引入的es和es客户端的版本-->
<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>
<!--引入的json序列化版本-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
指定一个cluster集群,也就是我们上篇blog创建的elasticsearch-tml集群:为了方便我们每个方法使用client,把client放到公共的before里:
@Before
public void init() throws Exception {
//创建一个Settings对象
Settings settings = Settings.builder()
.put("cluster.name", "elasticsearch-tml")
.build();
//创建一个TransPortClient对象,注意连接的端口是9300开头的
client = new PreBuiltTransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9301))
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9302))
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9303));
}
这里解释下before的用法:
@BeforeClass – 表示在类中的任意public static void方法执行之前执行
@AfterClass – 表示在类中的任意public static void方法执行之后执行
@Before – 表示在任意使用@Test注解标注的public void方法执行之前执行
@After – 表示在任意使用@Test注解标注的public void方法执行之后执行
@Test – 使用该注解标注的public void方法会表示为一个测试方法
创建索引库的时候,因为有了before的方法,所以创建客户端对象就不需要做了,直接创建一个索引
@Test
public void createIndex() throws Exception {
client.admin().indices().prepareCreate("index_hello")
//执行操作
.get();
//关闭client对象
client.close();
}
创建完索引可以直接设置Mappings,例如我们想创建如下结构的Mappings:
{
"article":{
"properties":{
"id":{
"type":"long",
"store":true
},
"title":{
"type":"text",
"store":true,
"index":true,
"analyzer":"ik_smart"
},
"content":{
"type":"text",
"store":true,
"index":true,
"analyzer":"ik_smart"
}
}
}
}
那么需要使用XContentBuilder去构造一个Json对象:
@Test
public void setMappings() throws Exception {
XContentBuilder builder = 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();
//使用client把mapping信息设置到索引库中
client.admin().indices()
//设置要做映射的索引
.preparePutMapping("index_hello")
//设置要做映射的type
.setType("article")
//mapping信息,可以是XContentBuilder对象可以是json格式的字符串
.setSource(builder)
//执行操作
.get();
//关闭链接
client.close();
}
添加文档同样很简单,首先构造一个文档对象,然后定位到索引和type的位置,设置好文档id,直接塞进去就好了:文档对象以type为基本单位:
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;
}
}
构造好的对象用于添加属性内容以及转为Json:
@Test
public void testAddDocument2() throws Exception {
//创建一个Article对象
Article article = new Article();
//设置对象的属性
article.setId(2l);
article.setTitle("tml第二个测试");
article.setContent("tml第二个测试内容");
//把article对象转换成json格式的字符串。
ObjectMapper objectMapper = new ObjectMapper();
String jsonDocument = objectMapper.writeValueAsString(article);
System.out.println(jsonDocument);
//使用client对象把文档写入索引库
client.prepareIndex("index_hello","article", "2")
.setSource(jsonDocument, XContentType.JSON)
.get();
//关闭客户端
client.close();
}
可以从head里查看文档的创建效果:
我们批量创建几个文档,便于下边文档搜索时验证:
使用ES客户端搜索同样我们验证之前的几种搜索方式:id查询、term查询以及query查询,同时再尝试下分页查询和查询的高亮显示。为了方便起见,我们把构造查询对象的公共代码提出来:
private void search(QueryBuilder queryBuilder) throws Exception {
//执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
//设置分页信息
.setFrom(0)
//每页显示的行数
.setSize(5)
.get();
//取查询结果
SearchHits searchHits = searchResponse.getHits();
//取查询结果的总记录数
System.out.println("查询结果总记录数:" + searchHits.getTotalHits());
//查询结果列表
Iterator<SearchHit> iterator = searchHits.iterator();
while(iterator.hasNext()) {
SearchHit searchHit = iterator.next();
//打印文档对象,以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"));
}
//关闭client
client.close();
}
依据ID查询时,只需要给查询对象添加对应的id列表:
@Test
public void testSearchById() throws Exception {
//创建一个client对象
//创建一个查询对象
QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("2", "8");
search(queryBuilder);
}
依据terms查询,因为我们使用了ik分词,所以应该能得到较好的分词效果:
@Test
public void testQueryByTerm() throws Exception {
//创建一个QueryBuilder对象
//参数1:要搜索的字段
//参数2:要搜索的关键词
QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "变形金刚");
//执行查询
search(queryBuilder);
}
带分析的查询方式,输入一个查询语句,先分割再查询:
@Test
public void testQueryStringQuery() throws Exception {
//创建一个QueryBuilder对象
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("tml是很帅的变形金刚")
.defaultField("title");
//执行查询
search(queryBuilder);
}
可以看到6个document都被搜索出来了,但因为分页设置了显示5条,只显示5个记录。
我们调整分页显示为分页显示10条记录,就可以看到上述搜索能被全部显示出来:
private void search(QueryBuilder queryBuilder) throws Exception {
//执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
//设置分页信息
.setFrom(0)
//每页显示的行数
.setSize(10)
.get();
//取查询结果
SearchHits searchHits = searchResponse.getHits();
//取查询结果的总记录数
System.out.println("查询结果总记录数:" + searchHits.getTotalHits());
//查询结果列表
Iterator<SearchHit> iterator = searchHits.iterator();
while(iterator.hasNext()) {
SearchHit searchHit = iterator.next();
//打印文档对象,以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"));
}
//关闭client
client.close();
}
再次执行查询:
@Test
public void testQueryStringQuery() throws Exception {
//创建一个QueryBuilder对象
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("tml是很帅的变形金刚")
.defaultField("title");
//执行查询
search(queryBuilder);
}
高亮显示的时候,可以重新设置下querybuilder:
private void search(QueryBuilder queryBuilder, String highlightField) throws Exception {
HighlightBuilder highlightBuilder = new HighlightBuilder();
//高亮显示的字段
highlightBuilder.field(highlightField);
highlightBuilder.preTags("");
highlightBuilder.postTags("");
//执行查询
SearchResponse searchResponse = client.prepareSearch("index_hello")
.setTypes("article")
.setQuery(queryBuilder)
//设置分页信息
.setFrom(0)
//每页显示的行数
.setSize(5)
//设置高亮信息
.highlighter(highlightBuilder)
.get();
//取查询结果
SearchHits searchHits = searchResponse.getHits();
//取查询结果的总记录数
System.out.println("查询结果总记录数:" + searchHits.getTotalHits());
//查询结果列表
Iterator<SearchHit> iterator = searchHits.iterator();
while(iterator.hasNext()) {
SearchHit searchHit = iterator.next();
//打印文档对象,以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"));
System.out.println("************高亮结果");
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
System.out.println(highlightFields);
//取title高亮显示的结果
HighlightField field = highlightFields.get(highlightField);
Text[] fragments = field.getFragments();
if (fragments != null) {
String title = fragments[0].toString();
System.out.println(title);
}
}
//关闭client
client.close();
}
然后再进行高亮查询:
@Test
public void testQueryStringQueryHeight() throws Exception {
//创建一个QueryBuilder对象
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("女护士")
.defaultField("title");
//执行查询
search(queryBuilder, "title");
}