06_es分布式搜索引擎2

一、DSL查询文档

1.DSL 查询分类

①查询所有:match_all

②全文检索:利用分词器对用户输入的内容分词,倒排索引去匹配

match_query

multi_match_query

③精确查询:根据精确词条查找数据,查找的是keyword,数值,日期,boolean类型字段

ids,range,term

④地理geo查询:根据经纬度查询

geo_distance

geo_bounding_box

⑤复合查询:将各种条件组合起来,合并查询条件

bool

function_score

总结:查询DSL的基本语法是什么?

GET /索引库名/_search

{ "query": { "查询类型": { "FIELD": "TEXT"}}}

2. 全文检索

全文检索查询,会对用户输入内容进行分词。用于搜索框搜索

①match查询:对用户输入的内容分词,然后倒排索引库查询。一个字段

查询三钻的酒店

复制代码
GET /hotel/_search
{
  "query": {
    "match": {
      "starName": "三钻"
    }
  }
}

②multi_match:多个字段查询。参与的字段越多,查询性能越差。

查询品牌,酒店名,商业圈有"外滩如家"

复制代码
GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "外滩如家",
      "fields": ["brand","name","business"]
    }
  }
}

3. 精确查询

查询的keyword,不进行分词的字段

①term:根据词条准确值查询

②range:范围查询(价格)

①term:

查询品牌是"7天酒店"

复制代码
GET /hotel/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "7天酒店"
      }
    }
  }
}

②range:

查询200-250酒店

复制代码
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 200,
        "lte": 250
      }
    }
  }
}

总结:精确查询常见的有哪些?

  • term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
  • range查询:根据数值范围查询,可以是数值、日期的范围

4. 地理查询

场景:

查询附近的酒店,附近的人,打车附近的出租车

①矩形范围内:geo_bounding_box

②以指定中心点为半径:

查询这个点15公里范围内的酒店

复制代码
GET /hotel/_search
{
  "query": {
    "geo_distance":{
      "distance":"15km",
      "location":"31.282444,121.479385"
    }
  }
}

5. 相关性算分:竞价排名

①fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名

②词条频率越高,得分越高,排名越靠前

③elasticsearch中的相关性打分算法是什么?

  • TF-IDF:在elasticsearch5.0之前,会随着词频增加而越来越大
  • BM25:在elasticsearch5.0之后,会随着词频增加而增大,但增长曲线会趋于水平

6. 修改相关性算分:竞价排名

使用 function score query,可以修改文档的相关性算分(query score),根据新得到的算分排序。

①原始条件查询,搜索文档并根据相关性打分(query score)

②过滤条件:符合条件的文档才重新算分

③算分函数:

算分函数,算分函数的结果称为function score ,将来会与query score运算,得到新算分,常见的算分函数有:

  • weight:给一个常量值,作为函数结果(function score)
  • field_value_factor:用文档中的某个字段值作为函数结果
  • random_score:随机生成一个值,作为函数结果
  • script_score:自定义计算公式,公式结果作为函数结果

④加权模式,定义function score与query score的运算方式,包括:

  • multiply:两者相乘。默认就是这个
  • replace:用function score 替换 query score
  • 其它:sum、avg、max、min

案例:搜索外滩的酒店,"如家"品牌给公司充钱了,让他的排名靠前一些。

分析:

①文档为品牌是"如家"的

②算分函数是weight

③加权模式是求和sum

复制代码
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "all": "外滩"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "brand": "如家"
            }
          },
          "weight": 2
        }
      ],
      "boost_mode": "sum"
    }
  }
}

7. 复合查询Boolean Query

布尔查询是一个或多个查询子句的组合。子句组合方式:

must:"与",必须匹配每个子查询

should:"或"选择性匹配子查询

must_not:必须不匹配,不参与算分,类似"非"

filter:必须匹配,不算分。

案例1:查询上海的酒店,品牌是皇冠假日或华美达。价格不低于500,评分是大于45分的

案例2:搜索名字包含"如家",价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。

复制代码
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "如家"
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "price": {
              "gte": 400
            }
          }
        }
      ],
      "filter": [
        {
          "geo_distance": {
            "distance": "10km",
            "location": {
              "lat": 31.21,
              "lon": 121.5
            }
          }
        }
      ]
      
    }
  }
}

二、搜索结果处理

1. 排序

es支持对搜索结果排序,默认是根据相关度算分(_score)排序。可以排序的字段:keyword类型,数值类型,地理坐标类型,日期类型。

排序语法

地理坐标排序语法

案例1:对酒店数据按照用户评价降序排序,评价相同的按照价格升序排序

复制代码
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score":"desc"
    },
    {
      "price": "asc"
    }
  ]
}

案例2:实现对酒店数据按照到你的位置坐标的距离升序排序

获取经纬度的方式:https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/

复制代码
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.220393,
          "lon": 121.544427
        },
        "order": "asc",
        "unit": "km"
      }
    }
  ]
}

2. 分页

es的搜索结果默认是top10条。

es通过修改from,size参数控制返回的分页结果

深度分页问题

ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from = 990,size =10的数据:

①首先在每个数据分片上都排序并查询前1000条文档。

②然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档

③最后从这1000条中,选取从990开始的10条文档

如果搜索页数过深,或者结果集(from + size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000

总结

from + size:

  • 优点:支持随机翻页
  • 缺点:深度分页问题,默认查询上限(from + size)是10000
  • 场景:百度、京东、谷歌、淘宝这样的随机翻页搜索

after search:

  • 优点:没有查询上限(单次查询的size不超过10000)
  • 缺点:只能向后逐页查询,不支持随机翻页
  • 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页

3. 高亮

搜索关键字突出显示。

原理:

①搜索关键字标记出来

②页面加css样式

案例:如家酒店高亮

三、RestClient查询文档

1. 快速入门

①请求DSL的组织

RestAPI中其中构建DSL是通过HighLevelRestClient中的resource()来实现的,其中包含了查询、排序、分页、高亮等所有功能

RestAPI中其中构建查询条件的核心部分是由一个名为QueryBuilders的工具类提供的,其中包含了各种查询方法

②解析结果response

③查询全部酒店的完整代码

java 复制代码
@Test
void testMatchAll() throws IOException {
   // 1.准备查询请求,参数是索引库名
   SearchRequest request = new SearchRequest("hotel");
   // 2.组织DSL参数
   request.source().query(QueryBuilders.matchAllQuery());
   // 3.发送请求,得到响应
   SearchResponse response = client.search(request, RequestOptions.DEFAULT);
   // 4.解析结果
   SearchHits searchHits = response.getHits();
   // 4.1 获取查询的条数
   long total = searchHits.getTotalHits().value;
   // 4.2 获取查询的集合
   SearchHit[] hits = searchHits.getHits();
   // 4.3 遍历
   List<HotelDoc>hotelDocList = new ArrayList<>();
   for (SearchHit hit : hits) {
      // 转换为Json
      String json = hit.getSourceAsString();
      // 转换为java对象
      HotelDoc hotelDoc = JSONObject.parseObject(json, HotelDoc.class);
      // 保存在集合中
      hotelDocList.add(hotelDoc);
   }
   System.out.println(hotelDocList);
}

查询的基本步骤是:

  • 创建SearchRequest对象
  • 准备Request.source(),也就是DSL。
    • QueryBuilders来构建查询条件
    • 传入Request.source() 的 query() 方法
  • 发送请求,得到结果
  • 解析结果(参考JSON结果,从外到内,逐层解析)

2. 构建查询条件,只要记住一个类:QueryBuilders

①全文检索查询(分词,模糊查询)

单字段:QueryBuilders.matchQuery(字段名,值)

多字段:QueryBuilders.multiMatchQuery(值, 字段1,字段2);

演示:酒店名字带有"如家"的有哪些?

java 复制代码
request.source().query(QueryBuilders.termQuery("name","如家"));

②精确查询,不分词

精确查询常见的有term查询和range查询

③复合查询boolean query

查询品牌为如家,价格在200元内的酒店

java 复制代码
// 创建bool查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 添加must条件
boolQuery.must(QueryBuilders.termQuery("brand","如家"));
// 添加filter条件
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(200));
request.source().query(boolQuery);

3. 分页和排序

演示:查询名为"如家"的酒店,查询结果进行价格降序,每页显示3条

java 复制代码
// 页码
int page = 1,size=3;
// 2.组织DSL
// 2.1 查询
request.source().query(QueryBuilders.termQuery("name","如家"));
// 2.2 分页 从from序号数size个
request.source().from((page-1)*size).size(size);
// 2.3 价格排序
request.source().sort("price", SortOrder.DESC);

4. 高亮

根据name搜索高亮

代码

java 复制代码
@Test
void testHight() throws IOException{
   // 1.请求request
   SearchRequest request = new SearchRequest("hotel");
   // 2. 组织DSL
   request.source().query(QueryBuilders.matchQuery("all","如家"));
   request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
   // 3.发送请求,得到响应
   SearchResponse response = client.search(request, RequestOptions.DEFAULT);
   // 4.分析结果
   SearchHits searchHits = response.getHits();
   // 5.解析
   SearchHit[] hitss = searchHits.getHits();
   // 6.遍历
   for (SearchHit hit : hitss) {
      // 转换为json
      String json = hit.getSourceAsString();
      // 得到对象
      HotelDoc hotelDoc = JSONObject.parseObject(json, HotelDoc.class);
      // 获取高亮结果
      Map<String, HighlightField> highlightFields = hit.getHighlightFields();
      // 根据字段获取
      HighlightField highlightField = highlightFields.get("name");
      // 获取高亮值
       String name = highlightField.getFragments()[0].string();
       // 覆盖结果
      hotelDoc.setName(name);
      System.out.println(name);

   }

}

四、黑马旅游案例

java 复制代码
@Override
	public PageResult search(RequestParams params) throws IOException {
		// 1.得到请求参数
		String key = params.getKey();
		Integer page = params.getPage();
		Integer size = params.getSize();
		String sortBy = params.getSortBy();
		String brand = params.getBrand();
		String starName = params.getStarName();
		String city = params.getCity();
		Integer minPrice = params.getMinPrice();
		Integer maxPrice = params.getMaxPrice();
		String location = params.getLocation();

		// 2.创建搜索请求
		SearchRequest request = new SearchRequest(HotelConstants.HOTEL_INDEX);
		// 3.编写DSL 组合查询boolQuery
		BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
		// 3.1.1 输入框关键字must
		if (key != null && !"".equals(key)) { //输入框不为空,模糊查询
			boolQuery.must(QueryBuilders.matchQuery("all", key));
		} else {//输入框为空,查询全部
			boolQuery.must(QueryBuilders.matchAllQuery());
		}
		// 3.1.2 城市--filter
		if (city != null && !"".equals(city)) {
			boolQuery.filter(QueryBuilders.termQuery("city", city));
		}
		// 3.1.3 品牌--filter
		if (brand != null && !"".equals(brand)) {
			boolQuery.filter(QueryBuilders.termQuery("brand", brand));
		}
		// 3.1.4 星级--filter
		if (starName != null && !"".equals(starName)) {
			boolQuery.filter(QueryBuilders.termQuery("starName", starName));
		}
		// 3.1.5 价格--filter
		if (minPrice != null && maxPrice != null) {
			boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));
		}
		// 3.1.6 查询条件
		// 3.1.7 =====广告推荐算分查询(查询条件,算分条件)
		 FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(boolQuery,
				 new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
				 		new FunctionScoreQueryBuilder.FilterFunctionBuilder(
				 				QueryBuilders.termQuery("isAD",true),// 算分条件
							    ScoreFunctionBuilders.weightFactorFunction(10) // 算分比例
					    )
				 });


		request.source().query(functionScoreQuery);
		// 3.2 页码
		request.source().from((page - 1) * size).size(size);
		// 3.3 排序
		if (location != null && !"".equals(location)) {
			request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location))
					.order(SortOrder.ASC)
					.unit(DistanceUnit.KILOMETERS));
		}
		if (!SortConstants.DEFAULT.equals(sortBy)) {
			request.source().sort(sortBy, SortOrder.DESC);
		}

		//4. 发送请求得到响应
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 5.解析响应
		PageResult pageResult = handleResponse(response);
		return pageResult;
	}
相关推荐
慧新软件1 分钟前
Google算法更新后,外贸SEO优化策略如何调整
大数据
xushichang123_37 分钟前
如何观看 2026 亚马逊云科技中国峰会全场主题演讲与技术分论坛内容?
大数据·人工智能
倒流时光三十年1 小时前
第六阶段 57 · ES|QL 与 SQL API(用 SQL 查 ES)
数据库·sql·elasticsearch
码云骑士1 小时前
104-实战论文搜索引擎-ArXiv爬取-Milvus存储-RAG问答-Gradio前端
前端·python·搜索引擎·milvus
招财小梗1 小时前
AI矩阵获客,品牌连锁落地有啥方案?
大数据·人工智能·矩阵
黎子越1 小时前
大模型基础名词概念
大数据
zandy10111 小时前
主流 AI Agent搜索Skill推荐及高阶选型指南
大数据·人工智能
跨境卫士苏苏2 小时前
2026年做跨境电商,TikTok美区半托管这3个品类正在严查,第2个很多新手还在铺货
大数据·人工智能·跨境电商·营销策略
GIS数据转换器2 小时前
村镇无人机物流配送与跨域监测一体化平台
大数据·运维·人工智能·科技·无人机
知识燃料2 小时前
企业技术决策者落地 AI Agent 体系,AWS 中国峰会有哪些战略与工程内容可看?
大数据·人工智能