1 Bulk批量操作
1.1 脚本操作
#批量操作
POST _bulk
{"delete":{"_index":"person1","_id":"5"}} #删除5号
{"create":{"_index":"person1","_id":"8"}} #新增8号(下边是新增的数据)
{"name":"八号","age":18,"address":"北京"}
{"update":{"_index":"person1","_id":"2"}} #更新2号 name为2号
{"doc":{"name":"2号"}}
2 导入数据库数据
2.1 分析&创建索引
# 需要根据数据库字段信息,定义出来es的索引和映射配置,且手动执行一下
PUT goods
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_smart"
},
"price": {
"type": "double"
},
"createTime": {
"type": "date"
},
"categoryName": {
"type": "keyword"
},
"brandName": {
"type": "keyword"
},
"spec": {
"type": "object"
},
"saleNum": {
"type": "integer"
},
"stock": {
"type": "integer"
}
}
}
}
2.2 代码实现
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "goods") //索引的名
public class Good {
@Id
private Double id;
@Field(type = FieldType.Text ,analyzer = "ik_smart")
private String title;
private Double price;
private Double stock;
private Double saleNum;
private Date createTime;
@Field(analyzer = "keyword")
private String categoryName;
@Field(analyzer = "keyword")
private String brandName;
private LinkedHashMap spec;
public void setSpec(String spec) {
LinkedHashMap parse = JSON.parseObject(spec, LinkedHashMap.class);
this.spec = parse;
}
}
@SpringBootTest
class EsBulkTests {
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Autowired
private GoodMapper goodMapper;
//批量添加
@Test
public void bulkAddTest(){
//从数据库中获取所有的数据zho
List<Good> list = goodMapper.findAll();
//把数据批量添加到es中
restTemplate.save(list);
}
//批量删除
@Test
public void bulkDeleteTest(){
//指定删除条件
CriteriaQuery query = new CriteriaQuery(Criteria.where("id").notIn("-1"));
//根据条件进行批量删除
restTemplate.delete(query,Good.class);
}
}
3 ElasticSearch查询
3.1 matchAll
脚本
# matchAll: 查询所有
# 默认情况下,es一次展示10条数据,通过from和size来控制分页
# 查询结果详解
GET goods/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 100
}
API
/**
* 查询所有
* 1. matchAll
* 2. 将查询结果封装为Goods对象,装载到List中
* 3. 分页。默认显示10条
*/
@Test
public void matchAllQueryTest2() {
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
Query query = new NativeSearchQueryBuilder().withQuery(queryBuilder)
.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC)) //查询结果按price进行排序
.withPageable(Pageable.ofSize(10).withPage(0)) //进行分页操作size指定大小,page 指定页码
.build();
SearchHits<Good> hits = restTemplate.search(query, Good.class);
System.out.println(hits);
long totalHits = hits.getTotalHits();
System.out.println("查询到的总数:" + totalHits);
//把查询到的结果,转换为List集合
List<Good> list = hits.get().map(hit -> hit.getContent()).collect(Collectors.toList());
for (Good good : list) {
System.out.println(good);
}
}
public void executeQuery(QueryBuilder queryBuilder) {
Query query = new NativeSearchQueryBuilder().withQuery(queryBuilder)
.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC)) //查询结果按price进行排序
.withPageable(Pageable.ofSize(10).withPage(0)) //进行分页操作size指定大小,page 指定页码
.build();
SearchHits<Good> hits = restTemplate.search(query, Good.class);
long totalHits = hits.getTotalHits();
System.out.println("查询到的总数:" + totalHits);
//把查询到的结果,转换为List集合
List<Good> list = hits.get().map(hit -> hit.getContent()).collect(Collectors.toList());
for (Good good : list) { //打印每条信息
System.out.println(good);
}
}
3.2 termQuery
注意:因为term是“完整词条查询”,所以,这种查询适合keyword 、numeric、date
脚本
GET 索引名称/_search
{
"query": {
"term": {
"字段名称": {
"value": "查询条件"
}
}
}
}
API
//2.termQuery 词条等值查询
@Test
public void termQueryTest() {
//如果指定的列不分词则,则要求,该列必须和“华为手机”一模一样
//如果指定的列分词,则要求词条中有和“华为手机”一摸一样的词条
QueryBuilder queryBuilder = QueryBuilders.termsQuery("title", "华为");
executeQuery(queryBuilder);
}
3.3 matchQuery
注意:match查询会对查询条件进行分词,然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR)
脚本
# 语法一
GET 索引名称/_search
{
"query": {
"match": {
"字段名称": "查询条件"
}
}
}
# 语法二
GET 索引名称/_search
{
"query": {
"match": {
"字段名称": {
"query": "查询条件",
"operator": "操作(or或者and)"
}
}
}
}
API
//3.matchQuery先分词,然后等值查询. 只能查询已经分词的列
@Test
public void matchQueryTest() {
//先对华为手机进行分词,然后根据分词后的的词条查询执行的列
//operator指分别查询寻后的操作 or表示去并集,and 表示取并集
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "华为手机").operator(Operator.OR);
executeQuery(queryBuilder);
} //求并集
3.4 模糊查询
3.4.1 wildcard查询
wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
# wildcard 查询。查询条件分词,模糊查询
GET goods/_search
{
"query": {
"wildcard": {
"title": {
"value": "华*"
}
}
}
}
API
//1.wildcard对词条进行模糊查询
@Test
public void wildcardQueryTest() {
// 如果指定的列, 不分词, 则要求, 该列的值, 必须满足条件. *代表任意多个任意字符, ?代表一个任意字符
// 判断指定的列, 分词了, 则要求, "词条"中, 必须满足条件. 有就获取, 没有就不要
QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("title", "华*");
executeQuery(queryBuilder);
}
3.4.2 regexp正则查询
正则查询取决于正则表达式的效率
GET goods/_search
{
"query": {
"regexp": {
"title": "\\w+(.)*"
}
}
}
API
//2.正则查询
@Test
public void regexQueryTest() {
// 如果指定的列, 不分词, 则要求, 该列的值, 必须满足正则表达式
// 判断指定的列, 分词了, 则要求, "词条"中, 必须满足正则表达式. 有就获取, 没有就不要
QueryBuilder queryBuilder = QueryBuilders.regexpQuery("title", "\\w+(.)*");
executeQuery(queryBuilder);
}
3.4.3 prefix前缀查询
对keyword类型支持比较好,其他类型数据不太适合
# 前缀查询 对keyword类型支持比较好
GET goods/_search
{
"query": {
"prefix": {
"brandName": {
"value": "三"
}
}
}
}
API
//3.前缀查询
@Test
public void prefixQueryTest() {
// prefixQuery: 前缀匹配. 其实就是wildcardQuery关键字后边加*
// 如果指定的列, 不分词, 则要求, 该列的值, 必须满足正则表达式
// 判断指定的列, 分词了, 则要求, "词条"中, 必须满足正则表达式. 有就获取, 没有就不要
QueryBuilder queryBuilder = QueryBuilders.prefixQuery("brandName", "三");
executeQuery(queryBuilder);
}
3.5 范围&排序查询
脚本
# 范围查询
GET goods/_search
{
"query": {
"range": {
"price": { # 按照价格price查询
"gte": 2000, # 大于等于2000,gt:大于
"lte": 3000 # 小于等于3000,lt:小于
}
}
},
"sort": [ # 排序,可选
{
"price": {
"order": "desc" # 按照价格,降序排列
}
}
]
}
API
//1.范围&排序查询
@Test
public void rangedQueryTest() {
QueryBuilder queryBuilder = QueryBuilders.rangeQuery("price").gte(1000).lte(5000);
executeQuery(queryBuilder);
}
3.6 QueryString查询
3.6.1 概述
QueryString是多条件查询。会对查询条件进行分词,然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR)。
QueryString可以指定多个查询字段
QueryString的查询分两种"query_string"和"simple_query_string"
3.6.2 query_string
GET goods/_search
{
"query": {
"query_string": {
"fields": ["title","categoryName","brandName"],
"query": "华为 AND 手机"
}
}
}
# 注意:
# query可以书写多个词条,这些词条也会被默认分词,词条之间,默认“OR”
# "华为 AND 手机":
# 检查一整条数据中的"title","categoryName","brandName"字段,"华为"和"手机"词条同时存在。
# 注意:"华为"和"手机"词条可以在不同字段中,但整条数据中必须全部存在
# "华为 OR 手机":
# 检查一整条数据中的"title","categoryName","brandName"字段,任意字段,只要包含"华为"或"手机"中的任何词条,都满足要求
3.6.3 simple_query_string
GET goods/_search
{
"query": {
"simple_query_string": {
"fields": ["title","categoryName","brandName"],
"query": "华为 AND 手机"
}
}
}
# 注意:
# query可以书写多个词条,这些词条也会被默认分词,词条之间,使用OR
# simple_query_string不支持"AND"连接符。如:"华为 AND 手机",查询时会将 “华为”、"and"、“手机”分别进行查询
3.6.4 default_operator
GET goods/_search
{
"query": {
"query_string": {
"fields": ["title","brandName","categoryName"],
"query": "华为手机",
"default_operator": "AND"
}
}
}
# 注意:
# 1.default_operator: 默认为"OR"
# 2.default_operator一般不和query中的关键字同时使用
# "default_operator": "AND"
# 求满足词条要求的交集
# "query": "华为 手机","default_operator": "OR"
# 求满足词条要求的并集
3.6.5 API
QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机")
.field("titl //1.queryString查询(多条件查询)
@Test
public void queryStringTest() {
//查询: 一条数据中,title/categoryName/brandName三列中,必须同时包含"华为"和"手机"
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("华为 AND 手机").field("title").field("categoryName").field("bandName");
executeQuery(queryBuilder);
}e").field("categoryName").field("brandName")
.defaultOperator(Operator.AND);
//2.
@Test
public void simpleQueryStringQuery(){
//查询: match查询title, match查询categoryName, match查询brandName, 求并集
//注意: 使用该种查询的时候,要保证"查询的所有列"都是"分词列"
QueryBuilder queryBuilder = QueryBuilders.simpleQueryStringQuery("华为手机").field("title").field("categoryName").field("bandName").defaultOperator(Operator.AND);
executeQuery(queryBuilder);
}
simple_query_string:有default_operator连接符的脚本
GET goods/_search
{
"query": {
"simple_query_string": {
"fields": ["title","brandName","categoryName"],
"query": "华为手机 "
, "default_operator": "OR"
}
}
}
注意:query中的or and 是查询时 匹配条件是否同时出现----or 出现一个即可,and 两个条件同时出现
default_operator的or and 是对结果进行 并集(or)、交集(and)
3.7 布尔查询
boolQuery:对多个查询条件连接。连接方式:
- must(and):条件必须成立,条件可以有多个
- must_not(not):条件必须不成立,条件可以有多个
- should(or):条件可以成立,条件可以有多个
- filter:条件必须成立,性能比must高。不会计算得分,条件可以有多个。(得分: 即条件匹配度,匹配度越高,得分越高)
脚本
GET goods/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"brandName": {
"value": "华为"
}
}
}
],
"filter":[
{
"term": {
"title": "手机"
}
},
{
"range":{
"price": {
"gte": 2000,
"lte": 3000
}
}
}
]
}
}
}
#filter 单独使用 filter可以是单个条件,也可多个条件(数组形式)
GET goods/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"brandName": {
"value": "华为"
}
}
}
]
}
}
}
API
//布尔查询(即多条条件一并成立)
@Test
public void testBoolQuery() {
//多条件查询
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("brandName","华为"))
.filter(QueryBuilders.matchQuery("title","手机"))
.filter(QueryBuilders.rangeQuery("price").gte(2000).lte(3000));
executeQuery(queryBuilder);
//Query query = new NativeSearchQueryBuilder()
// .withQuery(QueryBuilders.termQuery("brandName","华为"))
// .withFilter(QueryBuilders.matchQuery("title","手机"))
// .withFilter(QueryBuilders.rangeQuery("price").gte(2000).lte(3000))
// .build();
}
3.8 聚合查询
3.8.1 指标聚合
相当于MySQL的聚合函数。max、min、avg、sum等
脚本
# 指标聚合-聚合函数
GET goods/_search
{
"query": { // 基础查询
"match": {
"title": "手机"
}
},
"aggs": { // 在基础查询的基础上,开始聚合
"max_price": { // 结果所存储的字段名,名称自定义
"max": { // 聚合类型
"field": "price" // 聚合依据(也就是按照price列进行聚合)
}
}
}
}
API
@Autowired
private ElasticsearchRestTemplate restTemplate;
/**
* 聚合函数-指标聚合
*/
@Test
public void maxTest() {
//准备查询条件
QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "手机");
//编译查询条件
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(AggregationBuilders.max("price_max").field("price")).build();//对brandName进行分组统计
//执行查询条件
SearchHits<Good> hits = restTemplate.search(query, Good.class);
long totalHits = hits.getTotalHits();
System.out.println("查询到的总数:"+totalHits);
Aggregations aggregations = hits.getAggregations();
Max price_max = aggregations.get("price_max");
double price_maxValue = price_max.getValue();
System.out.println(price_maxValue);
}
3.8.2 指标聚合
相当于MySQL的 group by 分组查询。
注意:不要对text类型的数据进行分组,会失败。
脚本
# 桶聚合-分组查询
GET goods/_search
{
"query": {
"match": {
"title": "手机"
}
},
"aggs": {
"goods_brands": {
"terms": {
"field": "brandName",
"size": 100 // 查询结果数量(默认分页显示,只显示前10条)
}
}
}
}
API
/**
* 分组查询-桶聚合
*/
@Test
public void GroupByTest() {
//准备查询条件
QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "手机");
//编译查询条件
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(AggregationBuilders.terms("brands").field("brandName")).build();
//执行查询条件
SearchHits<Good> hits = restTemplate.search(query, Good.class);
long totalHits = hits.getTotalHits();
System.out.println("查询到的总数:"+totalHits);
Aggregations aggregations = hits.getAggregations();
Terms brands = aggregations.get("brands");
List<? extends Terms.Bucket> buckets = brands.getBuckets();
for (Terms.Bucket bucket : buckets) {
String keyAsString = bucket.getKeyAsString();
long docCount = bucket.getDocCount();
System.out.println(keyAsString+"....."+docCount);
}
}
}
3.9 高亮查询
所谓高亮查询,其实就是把查询结果中的"关键词"的前后,加上前后缀。
脚本
# 对title查询,对查询到的结果中的"电视",加上前后缀
GET goods/_search
{
"query": {
"match": {
"title": "电视"
}
},
"highlight": {
"fields": {
"title": {
"pre_tags": "<font color='red'>", // 前缀
"post_tags": "</font>" // 后缀
}
}
}
}
# 注意:
# 如果不加前后缀,默认前缀时<em>,后缀</em>
# 查询结果并不是在原字段上加前后缀,而是会有一个新的列,叫做highlight,高亮处理后的文本在那里边
API
@Autowired
private ElasticsearchRestTemplate restTemplate;
/**
* 高亮查询
*/
@Test
public void highlightTest() {
//1.准备查询条件(title包含电视的)
QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "电视");
//2.编译查询条件
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(queryBuilder).withHighlightBuilder(new HighlightBuilder().field("title").preTags("。。前面。。").postTags("..后面..")).withPageable(Pageable.ofSize(10).withPage(0)).build();
//3.执行查询
SearchHits<Good> hits = restTemplate.search(query, Good.class);
long totalHits = hits.getTotalHits();
System.out.println("查询到的总数:" + totalHits);
//4.获取高亮
List<Good> list = new ArrayList<>();
for (SearchHit<Good> hit : hits) {
Good good = hit.getContent();
List<String> title_list = hit.getHighlightField("title");
String titleStr = "";
for (String s : title_list) {
titleStr += s;
}
good.setTitle(titleStr);
list.add(good);
}
System.out.println(list);
}
}
3.10 重建索引&索引别名
3.10.1 查询别名
#查询别名 默认别名无法查看,默认别名同索引名
GET goods/_alias/
#结果
{
"goods" : {
"aliases" : { }
}
}
3.10.2 添加别名
POST 索引名/_alias/别名
注意:
- 索引可以有多个别名
- 多个索引可以指向同一个别名,查询时查询的是并集,然不能做添加。强烈不建议让多个索引指向同一个别名
- 删除索引后,别名自动删除。
- 当别名没有被索引引用时,自动删除
- 当索引有别名后,操作别名和操作索引没区别。但不能执行删除索引库操作。
3.10.3 删除别名
POST _aliases
{
"actions": [
{
"remove": { "index": "student_index_v1", "alias": "student_index_v11" }
}
]
}
3.10.4 修改别名
POST /_aliases
{
"actions": [
{ "remove": { "index": "索引名", "alias": "旧别名" }},
{ "add": { "index": "索引名", "alias": "新别名" }}
]
}
3.10.5 拷贝数据
# 拷贝数据
POST _reindex
{
"source": {
"index": "原索引"
},
"dest": {
"index": "目标索引"
}
}
3.10.6 API
/**
* 获取所有的别名
* @throws IOException
*/
@Test
public void getAliases() throws IOException {
IndicesClient indicesClient = client.indices();
GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
GetAliasesResponse response = indicesClient.getAlias(getAliasesRequest,RequestOptions.DEFAULT);
Map<String, Set<AliasMetaData>> aliases = response.getAliases();
for (String s : aliases.keySet()) {
System.out.println(s+":"+aliases.get(s));
}
}
/**
* 获取指定索引的别名
* @throws IOException
*/
@Test
public void getAliasesByIndex() throws IOException {
IndicesClient indicesClient = client.indices();
GetIndexRequest getIndexRequest = new GetIndexRequest("student_index_v1");
GetIndexResponse response = indicesClient.get(getIndexRequest, RequestOptions.DEFAULT);
Map<String, List<AliasMetaData>> aliases = response.getAliases();
for (String s : aliases.keySet()) {
System.out.println(s+":"+aliases.get(s));
}
}
/**
* 给索引添加别名
* @throws IOException
*/
@Test
public void addAliasesByIndex() throws IOException {
IndicesClient indicesClient = client.indices();
IndicesAliasesRequest request = new IndicesAliasesRequest();
IndicesAliasesRequest.AliasActions alias = IndicesAliasesRequest.AliasActions.add();
alias.index("student_index_v1"); //索引
alias.alias("student_index_v12"); //别名
request.addAliasAction(alias);
AcknowledgedResponse response = indicesClient.updateAliases(request, RequestOptions.DEFAULT);
System.out.println(response.isAcknowledged());
}
/**
* 删除索引的别名
* @throws IOException
*/
@Test
public void removeAliasesByIndex() throws IOException {
IndicesClient indicesClient = client.indices();
IndicesAliasesRequest request = new IndicesAliasesRequest();
IndicesAliasesRequest.AliasActions alias = IndicesAliasesRequest.AliasActions.remove();
alias.index("student_index_v1"); //索引
alias.alias("student_index_v12"); //别名
request.addAliasAction(alias);
AcknowledgedResponse response = indicesClient.updateAliases(request, RequestOptions.DEFAULT);
System.out.println(response.isAcknowledged());
}
/**
* 数据迁移
* @throws IOException
*/
@Test
public void sourceToDest() throws IOException {
IndicesClient indicesClient = client.indices();
//1.获取操作文档的对象
ReindexRequest reindexRequest = new ReindexRequest();
reindexRequest.setSourceIndices("student_index_v1");
reindexRequest.setDestIndex("student_index_v2");
//添加数据,获取结果
BulkByScrollResponse response = client.reindex(reindexRequest, RequestOptions.DEFAULT);
//打印响应结果
System.out.println(response.getTotal());
}