赞
踩
1、ES 7.x 及之前版本,选择 Java 8
2、ES 8.x 及之后版本,选择 Java 17 或者 Java 18,建议 Java 17,因为对应版本的 Logstash 不支持 Java 18
3、Java 9、Java 10、Java 12 和 Java 13 均为短期版本,不推荐使用
本文使用的是7.17,下载完后直接解压即可
下载地址:
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.6-linux-x86_64.tar.gz
es因为安全原因是不能使用root用户启动,所以如果需要新建用户使用以下命令新建即可:
1、groupadd centos #创建es组
2、useradd centos -g es -p 密码 #-g指定用户 -p设置密码
3、chown centos:centos -R elasticsearch-7.17.3/ #给予centos用户es文件夹下的权限
es因为安全问题不支持使用root启动的,所以应使用上面新建的用户去启动,需要先切换到创建的用户 su centos
使用 ./bin/elasticsearch -d
启动。 说明: -d 是启动到后台
查看日志: tail -f ./logs/elasticsearch.log
启动后可在命令行输入 curl http://127.0.0.1:9200
或在浏览器访问 http://ip:9200/
(需要关闭防火墙或开放端口) 如出现下面内容说明启动成功。
可以使用如下命令进行简单测试:
查看索引: curl -X GET "http://127.0.0.1:9200/_cat/indices?v
使用put请求创建索引: curl -X PUT http://127.0.0.1:9200/myindex
使用delete请求删除索引: curl -X DELETE http://127.0.0.1:9200/myindex
查看索引请求的返回信息如下,status字段返回状态说明
Green为最佳状态。Yellow即数据和集群可用,但是集群的备份有的是坏的。Red即数据和集群都不可用
导入依赖:文中有使用其它依赖较多,就不贴出来了 fastjson、lang3、lombok、mybatis-plus、druid、jakarta
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
创建实体类,实体类仅供参考
import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.DateFormat; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor @Document(indexName = "myindex", shards = 5, replicas = 1)// myindex 必须全小写 public class GoodsInfo implements Serializable { /** * @Id 主键id * String 分两种:text 可分词,不参加聚合、keyword,不可分词,数据会根据完整匹配,可参与聚合 * ik_max_word :会对文本进行最细粒度的拆分, * ik_smart:会对文本进行最粗粒度的拆分 * 索引时使用ik_max_word、搜索时使用ik_smart * 分词器:analyzer 1、插入文档时,将text类型字段做分词,然后插入倒排索引。2、在查询时,先对text类型输入做分词,再去倒排索引搜索 * 分词器:如果想要“索引”和“查询”, 使用不同的分词器,那么 只需要在字段上 使用 search_analyzer。这样,索引只看 analyzer,查询就看 search_analyzer。 * 此外,如果没有定义,就看有没有 analyzer,再没有就去使用 ES 预设。 */ @Id private String goodId; @Field(type = FieldType.Keyword) private String userId; @Field(type = FieldType.Keyword) private String classId; @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart") private String goodTitle; @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart") private String goodIntro; @Field(type = FieldType.Keyword) private String goodImg; @Field(type = FieldType.Keyword) private String goodAddress; @Field(type = FieldType.Double) private Double price; @Field(type = FieldType.Integer) private int sts; @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8") private String time; }
(想找 稍微复杂 的查询可以跳过这点。)
创建DiscussPostRepository(其实就是个mapper),继承spring提供的ElasticsearchRepository<T, ID>
import com.example.elasticsearch.entity.GoodsInfo; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; /** * PagingAndSortingRepository 可以使用 Page<T> findAll(Pageable pageable); 分页 * ElasticsearchRepository<T,ID> T为实体类,ID为实体类的id,对应id的类型的包装类 id需为主键 * T只能针对单个对象进行创建,实例化填充object也不行, * GoodsInfo只是一个实体类,并不影响后面的使用,所以将GoodsInfo改成自己的也是可以的,String为实体类里面的id */ @Repository public interface DiscussPostRepository extends ElasticsearchRepository<GoodsInfo,String> { //做数据预热 @Select("select * from goods_info order by time desc") List<GoodsInfo> qryAllGoods(); }
简单的curd父类已经提供了,我们直接使用就行
都是些简单的代码就不贴了,在service中使用mapper是一样的。
需要先构建一个高级客户端RestHighLevelClient
import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class ElasticSearchConfig { //请求地址 http://ip @Value("${elasticsearch.url}") private String url; //索引 @Value("${spring.elasticsearch.index}") private String index; //请求端口 9200 @Value("${elasticsearch.port}") private int port; @Bean public RestHighLevelClient restHighLevelClient() { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( //http://127.0.0.1:9200 new HttpHost(url,port) ) ); return client; } }
service:
public interface GoodsInfoElasticService {
void queryBuild(String keyword,String beginTime,String endTime, int page, int size);
}
serviceImpl:
public class GoodsInfoElasticServiceImpl implements GoodsInfoElasticService { @Autowired RestHighLevelClient client; /** * QueryBuilders. * 精确查询:term、terms 范围查询:range 模糊查询:wildcardQuery * term :单值查询 类似 = * terms:多值查询,类似in * range:范围查询 类似 between and * wildcardQuery:模糊查询 like * * 匹配查询:match、multiMatch * match:单字段匹配一个值 根据分词后的结果去查询,如果分词后的结果有命中一个,就会返回,模糊查询 * multiMatch:多字段匹配同一个值 * * 复合查询 bool :must、must_not、should 、filter * must:必须满足条件 * must_not:必须不满足条件 * should:应该满足条件 * filter:过滤,关闭评分,提高查询效率 * * 以下聚合查询 * 统计:max、min、sum、avg、count * stats:一并获取max、min、sum、count、AVG统计 * extendedStats:追加方差、标准差等统计指标 * ardinality:去重 * 分组:单字段分组、多字段分组、筛选后分组 */ /** * 1、SearchRequest 构建查询请求 * 2、SearchSourceBuilder 构建请求体(查询条件) * QueryBuilders 查询条件 --> termQuery()、termsQuery()、rangeQuery() 范围查询 、wildcardQuery()通配符查询(支持* 匹配任何字符序列,包括空 避免*开始,会检索大量内容造成效率缓慢)、idsQuery()只根据id查询 * 、fuzzyQuery()模糊查询、前缀匹配查询 prefixQuery("name","hello"); * AggregateBuilders 聚合条件 --> min、max、sum、range、terms、stats、extendedStats */ @Override public void queryBuild(String keyword, String beginTime, String endTime, int page, int size) { try { SearchRequest request = new SearchRequest(index); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); //组建模糊查询 keyword:例 hello world 会拆分存储,加keyword不进行分词搜索 if (StringUtils.isNotBlank(keyword)) { boolQueryBuilder.must(QueryBuilders.wildcardQuery("goodTitle.keyword", "*" + keyword + "*")); //多字段匹配 operator: or 表示 只要有一个词在文档中出现则就符合条件,and表示每个词都在文档中出现则才符合条件 // boolQueryBuilder.must(QueryBuilders.multiMatchQuery("数据", "goodTitle","goodIntro").operator(Operator.OR)); } //组建时间范围查询 if (StringUtils.isNotBlank(beginTime) && StringUtils.isNotBlank(endTime)) { beginTime = StringUtils.join(beginTime, "000000"); endTime = StringUtils.join(endTime, "999999"); boolQueryBuilder.filter(QueryBuilders.rangeQuery("time.keyword").gte(beginTime).lte(endTime)); } searchSourceBuilder.query(boolQueryBuilder); searchSourceBuilder.from((page - 1) * size); searchSourceBuilder.size(size); //fetchSource(string[],string[]) 第一个数组表明需要哪些字段,第二个是不需要哪些字段 类似 select id,name,age searchSourceBuilder.fetchSource(new String[]{"goodTitle", "goodId", "userId", "classId", "goodIntro"}, new String[]{}); request.source(searchSourceBuilder); SearchResponse response = client.restHighLevelClient().search(request, RequestOptions.DEFAULT); //搜索结果 SearchHits hits = response.getHits(); // 匹配到的总记录数 long totalHits = hits.getTotalHits().value; // 得到匹配度高的文档 SearchHit[] searchHits = hits.getHits(); List<GoodsInfo> goodsInfos = new ArrayList<>(); if (null != searchHits && searchHits.length > 0) { for (SearchHit searchHit : searchHits) { String sourceString = searchHit.getSourceAsString(); GoodsInfo goodsInfo = JSONObject.parseObject(sourceString, GoodsInfo.class); goodsInfos.add(goodsInfo); } } for (int i = 0; i < goodsInfos.size(); i++) { System.out.println(goodsInfos.get(i)); } } catch (Exception e) { e.printStackTrace(); } } }
增加如下依赖:
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.0.1</version>
</dependency>
创建客户端连接工具类
import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.rest_client.RestClientTransport; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Arrays; /** * @author Liner * todo ES连接配置工具 * @date 2021/12/9 21:36 */ @Component public class ElasticSearchConfig { @Value("${elasticsearch.url}") private String url; @Value("${spring.elasticsearch.index}") private String index; @Value("${elasticsearch.port}") private int port; public static ElasticsearchClient client; //http集群 public ElasticsearchClient elasticsearchClient(){ // final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); // credentialsProvider.setCredentials( // AuthScope.ANY, new UsernamePasswordCredentials(account, passWord));//设置账号密码 RestClientBuilder builder = RestClient.builder(new HttpHost(url,port)); // RestClientBuilder builder = RestClient.builder(httpHosts) // .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); // Create the low-level client RestClient restClient = builder.build(); // Create the transport with a Jackson mapper ElasticsearchTransport transport = new RestClientTransport( restClient, new JacksonJsonpMapper()); // And create the API client client = new ElasticsearchClient(transport);//获取连接 return client; } }
import co.elastic.clients.elasticsearch.core.*; import co.elastic.clients.elasticsearch.core.ExistsRequest; import co.elastic.clients.elasticsearch.indices.*; import com.alibaba.fastjson.JSONObject; import com.example.elasticsearch.commons.ElasticSearchConfig; import com.example.elasticsearch.commons.ElasticSearchConfigF; import com.example.elasticsearch.entity.GoodsInfo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Component @Slf4j public class ElasticSearchUtil { @Autowired ElasticSearchConfig client; /** * 创建索引 * * @param index 索引名 * @param type 分片 * @param val * @return */ public String createIndex(String index, String type, String val) { if (existsIndex(index)){ throw new RuntimeException("索引已经存在"); } CreateIndexResponse createResponse = null; try { createResponse = client.elasticsearchClient().indices().create( new CreateIndexRequest.Builder() .index(index) // .aliases("foo", // new Alias.Builder().isWriteIndex(true).build() // ) .build() ); log.info(String.valueOf(createResponse)); return index; } catch (Exception e) { throw new RuntimeException("创建索引失败,索引名:" + index + " 信息:" + createResponse); } } //DELETE /book // 删除索引 public boolean deleteIndex(String index){ if (!existsIndex(index)) { throw new RuntimeException("索引不存在"); } try { //创建删除索引请求 DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest.Builder().index(index).build(); //执行 return client.elasticsearchClient().indices().delete(deleteIndexRequest).acknowledged(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("索引删除失败"); } } /** * 判断索引是否存在 * * @param index 索引 * @return */ public boolean existsIndex(String index) { boolean exists = false; try { exists = client.elasticsearchClient().indices().exists(new co.elastic.clients.elasticsearch.indices.ExistsRequest.Builder().index(index).build()).value(); //exists = client.elasticsearchClient().indices().existsIndexTemplate(new ExistsIndexTemplateRequest.Builder().name(index).build()).value(); } catch (IOException e) { e.printStackTrace(); } return exists; } /** * 查询id数据是否存在,id为主键 * * @param index 索引 * @param id 主键id * @return */ public boolean existsDocumentById(String index, String id) { if (!existsIndex(index)) { log.info("索引不存在:" + index); return false; } boolean exists = false; try { exists = client.elasticsearchClient().exists(new ExistsRequest.Builder().index(index).id(id).build()).value(); return exists; } catch (Exception e) { e.printStackTrace(); } return exists; } /** * 根据主键id删除文档 * * @param index * @param id */ public void deleteDocumentById(String index, String id) { if (!existsIndex(index)) { log.info("索引不存在:" + index); throw new RuntimeException("索引不存在:" + index); } try { if (existsDocumentById(index, id)) { DeleteResponse delete = client.elasticsearchClient().delete(new DeleteRequest.Builder().index(index).id(id).build()); } } catch (Exception e) { e.printStackTrace(); } } /** * 根据id获取文档 * * @throws IOException */ public <T> T getDocumentById(String index, String id, Class<T> clazz) throws IOException { // get /index/_doc/1 GetRequest getRequest = new GetRequest.Builder().index(index).id(id).build(); GetResponse<T> bookGetResponse = client.elasticsearchClient().get(getRequest, clazz); T result = bookGetResponse.source(); return result; } /** * 根据id更新文档 */ public <T> void updateDocument(String index, String id, Class<T> tClass) throws IOException { UpdateResponse<T> personUpdateResponse = client.elasticsearchClient().update( new UpdateRequest.Builder<T, Object>() .index(index) .id(id) .doc(tClass) .build() , tClass); // 执行结果 System.out.println(personUpdateResponse.result()); } /** * 新增数据,如果索引下存在主键id数据则会替换 * * @param index 索引 * @param id 主键id * @param tClass 实体类class * @param <T> * @throws IOException */ public <T> String createDocument(String index, String id, T tClass){ IndexResponse indexResponse; try { // 创建添加文档的请求 IndexRequest<T> indexRequest = new IndexRequest.Builder<T>().index(index).document(tClass).id(id).build(); // 执行 indexResponse = client.elasticsearchClient().index(indexRequest); }catch (Exception e){ throw new RuntimeException("查询失败"); } return indexResponse.id(); } /** * 批量新增数据,暂时用不了,需要指定主键id,如果有指定的class可以循环遍历新增 * @param index 索引 * @param id 主键id * @param tClass 实体类class * @param <T> */ public <T> void createDocumentAll(String index, String id, List<T> tClass){ try { BulkRequest.Builder br = new BulkRequest.Builder(); for (T product : tClass) { br.operations(op -> op .index(idx -> idx .index(index) .id(id) .document(product) ) ); } }catch (Exception e){ e.printStackTrace(); } } }
es官方提供的暂时没有写出复杂的查询构建。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。