elasticsearch
0
elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容
elasticsearch底层是基于lucene来实现的。
什么是elasticsearch?
一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能
什么是elastic stack(ELK)?
是以elasticsearch为核心的技术栈,包括beats、Logstash、kibana、elasticsearch
什么是Lucene?
是Apache的开源搜索引擎类库,提供了搜索引擎的核心API
注意:这里只是一个笔记,我是菜鸟,有啥不对的地方提出来,一起学习
1. 安装
1. Windows版
下载es
https://www.elastic.co/cn/downloads/past-releases#elasticsearch
可以选择下载的版本,这里建议7.6.1,高了可能就要jdk1.8以上了

解压
es的bin目录下点elasticsearch.bat进行启动或命令行启动


安装中文分词工具
安装配置中文分词工具(根据不同版本下载不同的分词插件)【elasticsearch默认对中文不友好,所以要安装中文分词器ik】
下载网址:https://github.com/medcl/elasticsearch-analysis-ik/releases?page=8

解压将文件名改为analysis-ik

安装好后 重启es会看到 加载了 ik分词了

在浏览器中输入localhost:9200检测是否运行成功(出现如下的界面则说明elasticsearch运行成功)

分词:
- ik_smart为最少切分
- ik_max_word为最细粒度划分
安装可视化图形工具Kibana
kibana的版本和elasticsearch的版本必须一致
下载网址:https://www.elastic.co/cn/downloads/past-releases#kibana

到\bin目录,双击kibana.bat(前提是保证elasticsearch服务已经启动)


添加用户名和密码
- 找到config目录下的elasticsearch.yml添加以下信息
xpack.security.enabled: true
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true

2. 重启elasticsearch服务
3. 设置elasticsearch密码
到bin目录下输入cmd进入命令窗口,并执行以下命令
elasticsearch-setup-passwords interactive
- 然后就会让你一个个输入密码,都设置一样即可
5.找到 Kibana 目录下的config中的kibana.yml文件添加以下
elasticsearch.username: "elastic"
elasticsearch.password: "123456"

不设置的话kibana连不上elasticsearch就启动不了
- 再启动就需要输入密码了
- es的账号是
elastic - 密码是你设置的
这里输入的是es的账号密码而不是kibana的账号密码
输入elastic,和密码才能登录

2. docker版
https://blog.youkuaiyun.com/abst122/article/details/125508650#14eskibana_5673
1 依赖
elasticsearch对版本比较严谨,版本对不上就报一些奇奇怪怪的错误
官方版本对照:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#preface.versions
1.1 maven
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--elasticsearch-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
1.2 gradle
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client'
2 配置类
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.example.demo.security.config")
public class ElasticRestClientConfig extends AbstractElasticsearchConfiguration {
@Value("${spring.elasticsearch.rest.uris}")
private String url;
@Value("${spring.elasticsearch.rest.username}")
private String username;
@Value("${spring.elasticsearch.rest.password}")
private String password;
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
url = url.replace("http://", "");
String[] urlArr = url.split(",");
HttpHost[] httpPostArr = new HttpHost[urlArr.length];
for (int i = 0; i < urlArr.length; i++) {
HttpHost httpHost = new HttpHost(urlArr[i].split(":")[0].trim(),
Integer.parseInt(urlArr[i].split(":")[1].trim()), "http");
httpPostArr[i] = httpHost;
}
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
RestClientBuilder builder = RestClient.builder(httpPostArr)
// 异步httpclient配置
.setHttpClientConfigCallback(httpClientBuilder -> {
// 账号密码登录
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
// httpclient连接数配置 未知情况暂时不设置
// httpClientBuilder.setMaxConnTotal(50);
// httpClientBuilder.setMaxConnPerRoute(20);
// httpclient保活策略
httpClientBuilder.setKeepAliveStrategy(((response, context) -> Duration.ofMinutes(5).toMillis()));
return httpClientBuilder;
});
return new RestHighLevelClient(builder);
}
}
3 注解说明
3.1 @Document
@Document:在类级别应用,以指示该类是映射到数据库的候选对象。最重要的属性是:
- indexName:用于存储此实体的索引的名称。它可以包含SpEL模板表达式,例如 “log-#{T(java.time.LocalDate).now().toString()}”
- type:映射类型。如果未设置,则使用小写的类的简单名称。(从版本4.0开始不推荐使用)
- shards:索引的分片数。
- replicas:索引的副本数。
- refreshIntervall:索引的刷新间隔。用于索引创建。默认值为“ 1s”。
- indexStoreType:索引的索引存储类型。用于索引创建。默认值为“ fs”。
- createIndex:标记是否在存储库引导中创建索引。默认值为true。请参见使用相应的映射自动创建索引
- versionType:版本管理的配置。默认值为EXTERNAL。
3.2 @Id
@Id:在字段级别应用,以标记用于标识目的的字段。
3.3 @Transient
@Transient:默认情况下,存储或检索文档时,所有字段都映射到文档,此注释不包括该字段。
3.4 @PersistenceConstructor
@PersistenceConstructor:标记从数据库实例化对象时要使用的给定构造函数,甚至是受保护的程序包。构造函数参数按名称映射到检索到的Document中的键值。
3.5 @Field
@Field:在字段级别应用并定义字段的属性,大多数属性映射到各自的Elasticsearch映射定义(以下列表不完整,请查看注释Javadoc以获得完整参考):
- name:字段名称,它将在Elasticsearch文档中表示,如果未设置,则使用Java字段名称。
- type:字段类型,可以是Text(可分词), Keyword(不可分词), Long, Integer, Short, Byte, Double, Float, Half_Float,Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range,Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator,Flattened, Search_As_You_Type。请参阅Elasticsearch映射类型
- format和日期类型的pattern定义。必须为日期类型定义format
- store:标记是否将原始字段值存储在Elasticsearch中,默认值为false。
- analyzer,searchAnalyzer,normalizer用于指定自定义分析和正规化。
text:
- 会分词,然后进行索引
- 支持模糊、精确查询
- 不支持聚合
- 分词器默认standard ,对于中文来说就是按字分词
- 支持fields属性,可以在fields中添加keyword子类型,以实现精确检索
keyword:
- 不进行分词,直接索引
- 支持模糊、精确查询
- 支持聚合
- 支持按字数建立索引,以便节约索引空间
- 看下text分词规律。
3.6 @GeoPointField
将字段标记为geo_point数据类型。
3.7 @MultiField
映射配置为@MultiField 的,会映射为多字段,并使用对应的分析器。
所谓多字段,是说同一个字段同时建立多种索引,比如 text 和 keyword。注意,不能用 FieldType.Auto
3.8 @Mapping
注意:这里可以不需要mapping文件,定义mapping文件后实体类上字段的注解会失效
//设置mapping
@Mapping(mappingPath = "elasticsearch_mapping.json")//设置mapping
3.9 @Setting
//设置setting
@Setting(settingPath = "elasticsearch/settings.json")
4 创建映射(实体类)
@Data
@AllArgsConstructor
@NoArgsConstructor
// @Document是SpringDataES框架标记实体类的注解
// indexName指定的是索引名称,运行时items索引不存在,SpringDataES会自动创建这个索引
@Document(indexName = Constants.ES_HOTEL_INDEX, indexStoreType = Constants.ES_COMMODITY_INDEX_TYPE)
public class Hotel implements Serializable {
@Id// SpringData标记当前属性为ES主键的注解
private Long id;
/**
* 酒店名称
*/
@Field(type = FieldType.Text,
analyzer = "ik_max_word",
searchAnalyzer = "ik_max_word")// SpringData标记title属性的支持分词的类似和相关分词器
private String title;
/**
* 分类
*/
@Field(type = FieldType.Keyword)// Keyword是不需要分词的字符串类型
private String category;
/**
* 品牌
*/
@Field(type = FieldType.Keyword)
private String brand;
/**
* 价格
*/
@Field(type = FieldType.Double)
private BigDecimal price;
// 设置index=false 今后所有不会称为查询条件的列都照此配置
// 不索引,不代表不保存数据,数据本身仍然是保存在ES的
@Field(type = FieldType.Keyword,index = false)
private String imgPath;
/**
* 定位
*/
@GeoPointField
private GeoPoint localhost;
}
5 构建queryBuilder条件查询
1. 等值查询
查询name=如家:
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("title", "如家"));
2. 范围查询
查询价格大于等于99.9,并且小于等于120:
BoolQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price")
.gte(99.9)// gt大于,不包含边界值
.lte(120);// lt小于,不包含边界值
3. 模糊查询
查询姓名中包含有如家
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.wildcardQuery("title", "*如家*"));
这个我试了没起作用,不知啥原因
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.should(QueryBuilders.matchPhraseQuery("name", name));
queryBuilder.should(QueryBuilders.matchPhraseQuery("brand.name", name));
queryBuilder.should(QueryBuilders.matchPhraseQuery("unitWeight", name));
queryBuilder.should(QueryBuilders.multiMatchQuery(name,"specificationUnit","unitName"));
queryBuilder.minimumShouldMatch(1);
4. 多字段(multifield)查询
实体类
/**
* 商品名称
*/
@MultiField(mainField = @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word"),
otherFields = @InnerField(suffix = "inner", type = FieldType.Text, analyzer = "pinyin"))
private String name;
模糊查询名称和分类
if (StrUtil.isNotBlank(indistinct)) {
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(indistinct, "memberName", "customerCategory.name");
queryBuilder.must(multiMatchQueryBuilder);
}
5. 多条件查询
查询名称为:如家,并且价格在100.5-520之间:
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("title", "如家"))
.must(QueryBuilders.rangeQuery("price")
.gte(100.5)
.lte(520));
6. 集合查询
查询城市在北京、上海、杭州,并且价格在100.5-520之间,查询名称为如家
List<String> list = Arrays.asList("北京", "上海", "杭州");
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("title", "如家"))
.must(QueryBuilders.termsQuery("cityName", list))
.must(QueryBuilders.rangeQuery("price")
.gte(100.5)
.lte(520));
7. should查询
查询姓名包含如家或者是城市是北京,should相当于或者or
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.should(QueryBuilders.wildcardQuery("title", "*如家*"))
.should(QueryBuilders.termQuery("cityName", "北京"));
8. should和must配合查询
查询品牌为如家,名称包含如家或地址为北京的记录,**minimumShouldMatch(1)**表示最少要匹配到一个should条件
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("brand", "如家"))
.should(QueryBuilders.wildcardQuery("title", "*如家*"))
.should(QueryBuilders.termQuery("cityName", "北京"))
.minimumShouldMatch(1);
9. 是否存在查询
查询name有值,tag不存在值
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.existsQuery("name"))
.mustNot(QueryBuilders.existsQuery("aaa"));
10. 分页查询
类似jpa:https://blog.youkuaiyun.com/abst122/article/details/126423932#Spring_Data_Jpa_1514
Pageable pageable = PageRequest.of(request.getCurrent()-1, request.getPageSize());
Page<Hotel> page = searchHotelRepository.findAll(pageable);
11 地理位置查询
地理位置查询https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/
@Override
public Page<Hotel> findPage(double latitude, double longitude, String distance, Pageable pageable) {
// 实现了SearchQuery接口,用于组装QueryBuilder和SortBuilder以及Pageable等
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withPageable(pageable);
// 间接实现了QueryBuilder接口
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
// 以某点为中心,搜索指定范围
GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("localhost");
distanceQueryBuilder.point(latitude, longitude);
// 定义查询单位:公里
distanceQueryBuilder.distance(distance, DistanceUnit.KILOMETERS);
boolQueryBuilder.filter(distanceQueryBuilder);
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
// 按距离升序
GeoDistanceSortBuilder distanceSortBuilder =
new GeoDistanceSortBuilder("localhost", latitude, longitude);
distanceSortBuilder.unit(DistanceUnit.KILOMETERS);
distanceSortBuilder.order(SortOrder.ASC);
nativeSearchQueryBuilder.withSort(distanceSortBuilder);
return searchHotelRepository.search(nativeSearchQueryBuilder.build());
}
@Override
public PageData<HotelResponse> findLocalHostPage(HotelRequest request) {
Pageable pageable = PageRequest.of(request.getCurrent()-1, request.getPageSize());
Page<Hotel> page = findPage(request.getLatitude(), request.getLongitude(), request.getDistance(), pageable);
return new PageData<>(page.getTotalElements(),page.getContent().stream().map(s -> {
HotelResponse response = new HotelResponse();
BeanUtil.copyProperties(s, response);
double lon = s.getLocalhost().getLon();
double lat = s.getLocalhost().getLat();
response.setLatitude(lat);
response.setLongitude(lon);
// 计算两点之间的距离
double distance = GeoDistance.ARC.calculate(request.getLatitude(), request.getLongitude(), lat, lon, DistanceUnit.KILOMETERS);// GeoDistance.ARC和GeoDistance.PLANE,前者比后者计算起来要慢,但精确度要比后者高
response.setDistance(distance);
return response;
}).collect(Collectors.toList()));
}
6 通过RestHighLevelClient查询
使用RestHighLevelClient基本流程:
- 创建 SearchRequest搜索请求
- 创建 SearchRequest 搜索请求,如果不带参数,表示查询所有索引
- 创建 SearchSourceBuilder条件构造
- 创建 SearchSourceBuilder条件构造,构建搜索的条件。
- 添加大部分查询参数到 SearchSourceBuilder,还可以接收 QueryBuilders构建的查询参数。
- 将 SearchSourceBuilder 添加到 SearchRequest中
- 执行查询
- 解析查询结果
模糊查询
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//Fuzzy 查找
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name", "张").fuzziness(Fuzziness.ONE);
searchSourceBuilder.query(fuzzyQueryBuilder);
高亮查询
public void testHighlight() throws IOException {
//1.创建 SearchRequest搜索请求,并指定要查询的索引
SearchRequest searchRequest = new SearchRequest("db_idx4");
//2.创建 SearchSourceBuilder条件构造。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//Term 查找
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("address", "王者");
searchSourceBuilder.query(termQueryBuilder);
//自定义高亮 查找
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("</font>");
highlightBuilder.field("address");
highlightBuilder.requireFieldMatch(false); //多字段时,需要设置为false
highlightBuilder.field("desc");
searchSourceBuilder.highlighter(highlightBuilder);
//3.将 SearchSourceBuilder 添加到 SearchRequest中
searchRequest.source(searchSourceBuilder);
//4.执行查询
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//5.解析查询结果
System.out.println("花费的时长:" + searchResponse.getTook());
SearchHits hits = searchResponse.getHits();
System.out.println("符合条件的总文档数量:" + hits.getTotalHits().value);
hits.forEach(p -> {
System.out.println("文档原生信息:" + p.getSourceAsString());
System.out.println("高亮信息:" + p.getHighlightFields());
});
}
地理位置查询
- 方式一:
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
SearchRequest request = new SearchRequest("hotel");
queryBuilder.must(QueryBuilders.geoDistanceQuery("location")
// 在3km之内
.distance("3", DistanceUnit.KILOMETERS)
// 以那个点为中心
.point(31.256224D, 121.462311D)
.geoDistance(GeoDistance.ARC)
// 一个查询的名字,可选
.queryName("optional_name"));
sourceBuilder.query(queryBuilder);
request.source(sourceBuilder);
// 排序
searchSourceBuilder.sort(
// 不同的类型使用不同的SortBuilder
new GeoDistanceSortBuilder("location", 31.256224D, 121.462311D)
.order(SortOrder.DESC)
.unit(DistanceUnit.METERS)
.geoDistance(GeoDistance.ARC));
- 方式二:
public PageResult search(RequestParams params) {
try {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
buildBasicQuery(params, request);
// 2.2.分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
// 2.3.排序
String location = params.getLocation();
if (location != null && !location.equals("")) {
request.source().sort(SortBuilders
.geoDistanceSort("location", new GeoPoint(location))
.order(SortOrder.ASC)
.unit(DistanceUnit.KILOMETERS)
);
}
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void buildBasicQuery(RequestParams params, SearchRequest request) {
// 1.构建BooleanQuery
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 关键字搜索
String key = params.getKey();
if (key == null || "".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
boolQuery.must(QueryBuilders.matchQuery("all", key));
}
// 城市条件
if (params.getCity() != null && !params.getCity().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
// 品牌条件
if (params.getBrand() != null && !params.getBrand().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
// 星级条件
if (params.getStarName() != null && !params.getStarName().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 价格
if (params.getMinPrice() != null && params.getMaxPrice() != null) {
boolQuery.filter(QueryBuilders
.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice())
);
}
// 2.算分控制
FunctionScoreQueryBuilder functionScoreQuery =
QueryBuilders.functionScoreQuery(
// 原始查询,相关性算分的查询
boolQuery,
// function score的数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
// 其中的一个function score 元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
// 过滤条件
QueryBuilders.termQuery("isAD", true),
// 算分函数
ScoreFunctionBuilders.weightFactorFunction(10)
)
});
request.source().query(functionScoreQuery);
}
1万+

被折叠的 条评论
为什么被折叠?



