我们使用上次介绍ElasticSearch远程扩展词典时创建的springboot项目es_remote_dict来讲述06.ElasticSearch在Java中的使用,首先在pom.xml中引入以下依赖
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>6.8.0</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>6.8.0</version> </dependency> <dependency> <groupId>org.elasticsearch.plugin</groupId> <artifactId>transport-netty4-client</artifactId> <version>6.8.0</version> </dependency>
@Test public void testCreateESClient() throws UnknownHostException { PreBuiltTransportClient preBuiltTransportClient = new PreBuiltTransportClient(Settings.EMPTY); preBuiltTransportClient.addTransportAddress(new TransportAddress( InetAddress.getByName("192.168.8.101"),9300)); }
/** * @Author Christy * @Date 2021/4/29 11:19 **/ public class TestIndexTypeAndMapping { private TransportClient transportClient; @Before public void before() throws UnknownHostException { // 创建客户端 this.transportClient = new PreBuiltTransportClient(Settings.EMPTY); // 设置ES服务地址和端口 transportClient.addTransportAddresses(new TransportAddress(InetAddress.getByName("192.168.8.101"),9300)); } @After public void after(){ transportClient.close(); } // 创建索引 @Test public void createIndex(){ // 创建一个索引,前提是需要创建的索引不存在 CreateIndexResponse createIndexResponse = transportClient.admin().indices().prepareCreate("tide").get(); // 获取信息 boolean acknowledged = createIndexResponse.isAcknowledged(); System.out.println(acknowledged); } }
运行createIndex()
,我们可以看到索引创建成功
然后我们去kibana执行命令GET /_cat/indices?v
查询一下索引是否存在
// 删除索引 @Test public void deleteIndex(){ AcknowledgedResponse tide = transportClient.admin().indices().prepareDelete("tide").get(); System.out.println("delete index tide is:" + tide.isAcknowledged()); }
再去kibana中查询看索引是否还在
// 创建索引 类型 映射 @Test public void createIndexTypeMapping() throws ExecutionException, InterruptedException { // 创建一个索引请求对象 CreateIndexRequest tide = new CreateIndexRequest("tide"); // 索引设置类型 /** * 参数一:type名字 * 参数二:映射的JSON字符串 * 参数三:映射格式类型 */ tide.mapping("user", "{\"properties\":{\"name\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_max_word\"},\"age\":{\"type\":\"integer\"},\"bir\":{\"type\":\"date\"},\"introduce\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_max_word\"},\"address\":{\"type\":\"keyword\"}}}", XContentType.JSON); // 创建索引 CreateIndexResponse createIndexResponse = transportClient.admin().indices().create(tide).get(); System.out.println("create index type and mapping is: " + createIndexResponse.isAcknowledged()); }
执行结果显示成功
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency>
/** * @Author Christy * @Date 2021/4/29 12:40 **/ @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class User { private String id; private String name; private Integer age; private Date bir; private String introduce; private String address; }
注意:User实体类中的属性必须跟ES中type的字段对应
/** * 文档的基本操作 * @author Christy * @date 2021/4/29 12:44 * @return */ public class TestDocumentBase { private TransportClient transportClient; @Before public void init() throws UnknownHostException { // 创建客户端 this.transportClient = new PreBuiltTransportClient(Settings.EMPTY); // 设置es服务地址 transportClient.addTransportAddresses(new TransportAddress(InetAddress.getByName("192.168.8.101"),9300)); } @After public void after(){ transportClient.close(); } }
/** * 添加一个文档自动生成一个id * @author Christy * @date 2021/4/29 12:48 * @return */ public void createDocAutoId(){ // 构建一个文档 User user = new User(null, "孙悟空", 685, new Date(), "花果山水帘洞美猴王齐天大圣孙悟空", "花果山"); // 转为JSON String userJSON = JSONObject.toJSONStringWithDateFormat(user, "yyyy-MM-dd"); IndexResponse indexResponse = transportClient.prepareIndex("tide", "user").setSource(userJSON, XContentType.JSON).get(); System.out.println("孙悟空出生了:" + indexResponse.status()); }
/** * 添加一个文档指定一个id * @author Christy * @date 2021/4/29 12:53 * @return */ public void createDocOptionId(){ // 构建一个文档 User user = new User("1", "猪八戒", 965, new Date(), "天蓬元帅猪刚鬣", "高老庄"); // 转为JSON String userJSON = JSONObject.toJSONStringWithDateFormat(user, "yyyy-MM-dd"); IndexResponse indexResponse = transportClient.prepareIndex("tide", "user",user.getId()).setSource(userJSON, XContentType.JSON).get(); System.out.println("猪八戒投胎了:" + indexResponse.status()); }
我们根据id的生成方式不同添加了两条数据,现在我们去kibana中查看一下
/** * 更新一条文档 * @author Christy * @date 2021/4/29 13:41 * @return */ @Test public void updateDoc(){ User user = new User(); user.setIntroduce("天蓬元帅猪八戒掌管10万天河水军,威风凛凛!"); String userJSON = JSONObject.toJSONString(user); UpdateResponse updateResponse = transportClient.prepareUpdate("tide", "user", "1").setDoc(userJSON, XContentType.JSON).get(); System.out.println("猪八戒简介更新了:" + updateResponse.status()); }
/** * 删除一条文档 * @author Christy * @date 2021/4/29 13:57 * @return */ @Test public void deleteOne(){ DeleteResponse deleteResponse = transportClient.prepareDelete("tide", "user", "roAjHHkBRz-Sn-2fB1gg").get(); System.out.println("孙悟空被六耳猕猴打死了:" + deleteResponse.status()); }
我们再去kibana中查看一下数据,发现孙悟空真的被六耳猕猴打死了o(╥﹏╥)o
/** * 查询一条文档 * @author Christy * @date 2021/4/29 14:08 * @param * @return void */ @Test public void queryOne() throws ParseException { GetResponse getResponse = transportClient.prepareGet("tide", "user", "1").get(); System.out.println("提取猪八戒的档案成功:" + getResponse.getSourceAsString()); Map<String, Object> sourceAsMap = getResponse.getSourceAsMap(); User bajie = new User(); bajie.setId(String.valueOf(sourceAsMap.get("id"))); bajie.setName(String.valueOf(sourceAsMap.get("name"))); bajie.setAge(Integer.valueOf(String.valueOf(sourceAsMap.get("age")))); bajie.setBir(new SimpleDateFormat("yyyy-MM-dd").parse(String.valueOf(sourceAsMap.get("bir")))); bajie.setIntroduce(String.valueOf(sourceAsMap.get("introduce"))); bajie.setAddress(String.valueOf(sourceAsMap.get("address"))); System.out.println("八戒你无处遁形了:" + bajie); }
/** * 查询所有符合条件的记录 * @author Christy * @date 2021/4/29 14:34 * @param * @return void */ @Test public void queryTerm(){ TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("introduce", "最美"); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(termQueryBuilder) .get(); System.out.println("符合条件的记录数:" + searchResponse.getHits().getTotalHits()); System.out.println("符合条件的记录中的最大的分数:" + searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit: hits) { System.out.println(hit.getSourceAsString()); } }
/** * 批量操作 * @author Christy * @date 2021/4/29 14:50 * @param * @return void */ @Test public void bulk(){ User user = new User("10", "女娲", 100000, new Date(), "我是万物之主,整个宇宙都是我的", "九重天"); // 添加 IndexRequest indexRequest = new IndexRequest("christy", "user", user.getId()) .source(JSONObject.toJSONStringWithDateFormat(user,"yyyy-MM-dd"), XContentType.JSON); // 删除 我们将Elastic、TOM和Jerry删除掉 DeleteRequest deleteRequest01 = new DeleteRequest("christy", "user", "qYAtG3kBRz-Sn-2fMFjj"); DeleteRequest deleteRequest02 = new DeleteRequest("christy", "user", "qIAtG3kBRz-Sn-2fMFjj"); DeleteRequest deleteRequest03 = new DeleteRequest("christy", "user", "q4AtG3kBRz-Sn-2fMFjj"); // 更新 User user01 = new User(); user01.setIntroduce("我是一个干饭人,干饭人之歌说的就是我"); UpdateRequest updateRequest = new UpdateRequest("christy", "user", "CszCGHkB5KgTrUTeLyE_").doc(JSONObject.toJSONString(user01), XContentType.JSON); BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk(); BulkResponse bulkItemResponses = bulkRequestBuilder.add(indexRequest) .add(deleteRequest01) .add(deleteRequest02) .add(deleteRequest03) .add(updateRequest) .get(); BulkItemResponse[] items = bulkItemResponses.getItems(); for (BulkItemResponse item : items) { System.out.println(item.status()); } }
我们再去kibana查询一下结果,本来我们有12条数据,删除三条,新增一条,目前总数应该在10条,而且我们修改了干饭人的简介
@Test public void testQuery(){ MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(matchAllQueryBuilder) .get(); System.out.println("符合查询条件的文档总条数:" + searchResponse.getHits().getTotalHits()); System.out.println("符合查询条件的文档最大得分数:" + searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("-------->" + hit.getSourceAsString()); } }
@Test public void testQuery(){ MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(matchAllQueryBuilder) .setFrom(0) // 起始条数,默认从0开始 (当前页-1)*size .setSize(5) // 设置每页展示的条数 .get(); System.out.println("符合查询条件的文档总条数:" + searchResponse.getHits().getTotalHits()); System.out.println("符合查询条件的文档最大得分数:" + searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("-------->" + hit.getSourceAsString()); } }
@Test public void testQuery(){ MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(matchAllQueryBuilder) .setFrom(0) // 起始条数,默认从0开始 (当前页-1)*size .setSize(5) // 设置每页展示的条数 .addSort("age", SortOrder.DESC) // 按年龄排序 .get(); System.out.println("符合查询条件的文档总条数:" + searchResponse.getHits().getTotalHits()); System.out.println("符合查询条件的文档最大得分数:" + searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("-------->" + hit.getSourceAsString()); } }
上面的输出结果中最大的分数为NaN,是因为我们自定义了排序规则导致原先系统的按分数排序失效
@Test public void testQuery(){ // 查询所有 MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(matchAllQueryBuilder) .setFrom(0) // 起始条数,默认从0开始 (当前页-1)*size .setSize(10) // 设置每页展示的条数 .addSort("age", SortOrder.DESC) // 按年龄排序 // 指定返回除bir以外的所有字段 .setSource(SearchSourceBuilder.searchSource().fetchSource("*", "bir")) .get(); System.out.println("符合查询条件的文档总条数:" + searchResponse.getHits().getTotalHits()); System.out.println("符合查询条件的文档最大得分数:" + searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("-------->" + hit.getSourceAsString()); } query(matchAllQueryBuilder); }
通过以上例子我们发现除了查询条件不一样,其余的代码可以公用,我们先来封装一下代码
public void query(QueryBuilder queryBuilder){ SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(queryBuilder) .setFrom(0) // 起始条数,默认从0开始 (当前页-1)*size .setSize(10) // 设置每页展示的条数 .addSort("age", SortOrder.DESC) // 按年龄排序 .get(); System.out.println("符合查询条件的文档总条数:" + searchResponse.getHits().getTotalHits()); System.out.println("符合查询条件的文档最大得分数:" + searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("-------->" + hit.getSourceAsString()); } }
构造一个term查询的queryBuilder
@Test public void testQuery(){ // 查询所有 MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); // term查询 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("introduce", "最美"); query(termQueryBuilder); }
// range查询 RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gte(0).lte(20); query(rangeQueryBuilder);
// wildcardQuery 通配符查询 ?一个 * 0到多个 WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("introduce", "最*"); query(wildcardQueryBuilder);
//prefixQuery 前缀查询 PrefixQueryBuilder prefixQueryBuilder = QueryBuilders.prefixQuery("introduce", "最"); query(prefixQueryBuilder);
// ids查询 我们把女娲和郝建查询出来 IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds("10").addIds("C8zCGHkB5KgTrUTeLyE_"); query(idsQueryBuilder);
// fuzzy查询 0-2不允许模糊 3-5 允许模糊一个 >5 最多允许模糊两个 这里只演示使用方法不再演示错误的做法 FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("introduce", "普拉多"); query(fuzzyQueryBuilder);
// bool查询 must must not should // introduce 中包含最的 BoolQueryBuilder mustQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("introduce", "最")); query(mustQueryBuilder); // introduce 中不包含最的 BoolQueryBuilder mustNotQueryBuilder = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("introduce", "最")); query(mustNotQueryBuilder); // introduce中包含最或者年龄大于等于10岁的 BoolQueryBuilder shouldQueryBuilder = QueryBuilders.boolQuery().should(QueryBuilders.termQuery("introduce", "最")) .should(QueryBuilders.rangeQuery("age").gte(10)); query(shouldQueryBuilder);
我们先回忆一下高亮的查询语法和查询结果,然后按照高亮的语法和结果来写这个高亮查询
GET /christy/user/_search { "query": { "term": { "introduce": { "value": "干饭" } } }, "highlight": { "require_field_match": "false", "fields": { "*":{} }, "pre_tags": ["<span style='color:red;'>"], "post_tags": ["</span>"] } }
那么我们的Java的代码应该像下面这这样
@Test public void highLightQuery(){ HighlightBuilder highlightBuilder = new HighlightBuilder() .field("*") .requireFieldMatch(false) .preTags("<span style='color:red'>") .postTags("</span>"); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setQuery(QueryBuilders.multiMatchQuery("干饭", "name", "introduce")) .highlighter(highlightBuilder) // 高亮查询 .get(); List<User> userList = new ArrayList<>(); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { User user = new User(); Map<String, Object> sourceAsMap = hit.getSourceAsMap(); user.setId(hit.getId()); user.setName(sourceAsMap.get("name").toString()); user.setAge(Integer.valueOf(sourceAsMap.get("age").toString())); // user.setBir(new SimpleDateFormat("yyyy-MM-dd").parse(sourceAsMap.get("bir").toString()); user.setIntroduce(sourceAsMap.get("introduce").toString()); user.setAddress(sourceAsMap.get("address").toString()); // 获取高亮字段 Map<String, HighlightField> highlightFields = hit.getHighlightFields(); if(highlightFields.containsKey("name")){ String highLightName = highlightFields.get("name").fragments()[0].toString(); user.setName(highLightName); } if(highlightFields.containsKey("introduce")){ String highLightIntroduce = highlightFields.get("introduce").fragments()[0].toString(); user.setIntroduce(highLightIntroduce); } userList.add(user); } // 遍历输出替换高亮后的结果 userList.forEach(user -> System.out.println(user)); }
/** * 过滤查询 主要用在查询执行之前对大量数据进行筛选 * postFilter 用来过滤 * @author Christy * @date 2021/4/29 18:43 * @return */ @Test public void filterQuery(){ RangeQueryBuilder ageRangeQueryBuilder = QueryBuilders.rangeQuery("age").gte(0).lte(10); SearchResponse searchResponse = transportClient.prepareSearch("christy") .setTypes("user") .setPostFilter(ageRangeQueryBuilder) //过滤查询 .setQuery(QueryBuilders.matchAllQuery())//查询 .get(); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsString()); } }