谷粒商城-高级-52 -检索服务-检索参数模型构建及 DSL 测试
一、检索查询参数模型分析抽取
检索条件分析
1、全文检索:skuTitle-》keyword
2、排序:saleCount(销量)、hotScore(热度分)、skuPrice(价格)
3、过滤:hasStock、skuPrice区间、brandId、catalog3Id、attrs
4、聚合:attrs
完整查询参数
keyword=小米 &sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandId=1&catalog3Id=1&at trs=1_3G:4G:5G&attrs=2_骁龙845&attrs=4_高清屏
查询参数Vo封装:gulimall-search/src/main/java/com/atguigu/gulimall/search/vo/SearchParam.java
package com.atguigu.gulimall.search.vo;
import lombok.Data;
import java.util.List;
/**
* @Description: 封装页面所有可能传递过来的查询条件
*
* @example:
* keyword=小米 &sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandId=1&catalog3Id=1&at trs=1_3G:4G:5G&attrs=2_骁龙845&attrs=4_高清屏
* @author: kaiyi
* @create: 2020-09-03 01:27
*/
@Data
public class SearchParam {
private String keyword; // 页面传递过来的全文匹配关键字
private Long catalog3Id; // 三级分类ID
/**
* 排序条件:sort=price/salecount/hotscore_desc/asc
*/
private String sort;
/**
* 过滤条件:hasStock、skuPrice区间、brandId、catalog3Id、attrs
* hasStock=0/1
* skuPrice=1_500/_500/500_1000
* brandId=1
* attrs=2_5寸:6寸 (2表示属性ID,后边:表示多选)
*/
private Integer hasStock; // 是否只显示有货
private String skuPrice; // 价格区间查询
private List<Long> brandId; // 按照品牌进行查询,可以多选
private List<String> attrs; // 按照属性进行分组
private Integer pageNum; // 页码
/**
* 原生的所有查询条件
*/
private String _queryString;
}
二、检索返回结果模型分析抽取
返回结果参数Vo封装:gulimall-search/src/main/java/com/atguigu/gulimall/search/vo/SearchResponse.java
package com.atguigu.gulimall.search.vo;
import com.atguigu.common.to.es.SkuEsModel;
import lombok.Data;
import java.util.List;
/**
* @author: kaiyi
* @create: 2020-09-03 10:08
*/
public class SearchResponse {
// 查询到的所有商品信息
private List<SkuEsModel> products;
/**
* 当前页码
*/
private Integer pageNum;
/**
* 总记录数
*/
private Long total;
/**
* 总页码
*/
private Integer totalPages;
private List<Integer> pageNavs;
/**
* 当前查询到的结果,所有涉及到的品牌
*/
private List<BrandVo> brands;
/**
* 当前查询到的结果,所有涉及到的所有属性
*/
private List<AttrVo> attrs;
/**
* 当前查询到的结果,所有涉及到的所有分类
*/
private List<CatalogVo> catalogs;
//===========================以上是返回给页面的所有信息============================//
/* 面包屑导航数据 */
private List<NavVo> navs;
@Data
public static class NavVo {
private String navName;
private String navValue;
private String link;
}
@Data
public static class BrandVo{
private Long brandId;
private String brandName;
private String brandImg;
}
@Data
public static class AttrVo{
private Long attrId;
private String attrName;
private String attrValue;
}
@Data
public static class CatalogVo{
private Long catalogId;
private String catalogName;
}
}
三、控制器调用
com/atguigu/gulimall/search/controller/SearchController.java
package com.atguigu.gulimall.search.controller;
import com.atguigu.gulimall.search.service.MallSearchService;
import com.atguigu.gulimall.search.vo.SearchParam;
import org.elasticsearch.action.search.SearchResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author: kaiyi
* @create: 2020-09-03 00:37
*/
@Controller
public class SearchController {
@Autowired
MallSearchService mallSearchService;
/**
* SpringMvc会自动将页面提交过来的所有请求参数封装成我们指定的对象
* @param param
* @return
*/
@GetMapping("/list.html")
public String listPage(SearchParam param, Model model) {
// 1、根据传递过来的页面查询参数,去es中检索商品
SearchResponse result = mallSearchService.search(param);
model.addAttribute("result", result);
return "list";
}
}
四、DSL测试
GET gulimall_product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "华为"
}
}
],
"filter": [
{
"term": {
"catalogId": "225"
}
},
{
"terms": {
"brandId": [
"1",
"2",
"4",
"9"
]
}
},
{
"term": {
"attrs.attrId": 5
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": 5
}
}
},
{
"terms": {
"attrs.attrValue": [
"麒麟800"
]
}
}
]
}
}
}
}
]
}
}
}
nested 嵌套类型问题处理
在做 nested 嵌套类型测试时出现这样的错误:[nested] nested object under path [attrs] is not of nested type
, 原因是这个字段 attrs
不是 nested类型,所以报错了:
GET gulimall_product/_search
{
"query": {
"bool": {
"filter": {
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": 5
}
}
},
{
"terms": {
"attrs.attrValue": [
"麒麟800"
]
}
}
]
}
}
}
}
}
}
}
像这样的:
{
"some_index": {
"mappings": {
"normal_type": {
"properties": {
"nested_type": {
"type": "nested",
"properties": {
"address": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
}
}
}
}
}
}
The "type": "nested"
, line is required for the nested queries to work which have "path":
assigned to nested_type
, like this:
GET /some_index/normal_type/_search
{
"query": {
"nested": {
"query": {
"bool": {}
},
"path": "nested_type"
}
}
}
先看看 gulimall_product 索引字段的属性:
// 查询指定索引的信息(settings,mappings)
GET gulimall_product
结果:
{
"gulimall_product" : {
"aliases" : { },
"mappings" : {
"properties" : {
"attrs" : {
"properties" : {
"attrId" : {
"type" : "long"
},
"attrName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"attrValue" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"brandId" : {
"type" : "long"
},
"brandImg" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"brandName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"catalogId" : {
"type" : "long"
},
"catalogName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"hasStock" : {
"type" : "boolean"
},
"hotScore" : {
"type" : "long"
},
"saleCount" : {
"type" : "long"
},
"skuId" : {
"type" : "long"
},
"skuImg" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"skuPrice" : {
"type" : "float"
},
"skuTitle" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"spuId" : {
"type" : "long"
}
}
},
"settings" : {
"index" : {
"creation_date" : "1598598402124",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "w9qaZkMUTXa9DRbLSdAjCg",
"version" : {
"created" : "7040299"
},
"provided_name" : "gulimall_product"
}
}
}
}
我们需要将 attrs
字段 改为 nested
类型, 那么Elasticsearch怎么修改索引字段类型?
由于ElasticSearch没有像mysql一样可以直接字段数据类型的方法,因此需要通过创建中间索引:data_index_1,备份数据到中间索引:data_index_1,然后删除原索引: data_index,重新创建正确数据类型索引:data_index,再把中间索引:data_index_1的数据备份到新创建索引:data_index。语句通过kibana的 dev_tools/console 执行。
操作步骤如下:
. 创建一个中间索引
. 向中间索引备份源索引的数据(mapping)
. 查询确认数据是否copy过去
. 删除有问题的索引
. 重新创建同名的索引(★字段类型修改正确★)
. 从中间索引还原到源索引的数据
. 删除中间索引
参考修改脚本,kibana执行:
# 1. 创建一个中间索引
#创建索引
PUT demo_metric_1/
# 创建Mapping
POST demo_metric_1/type/_mapping
{
"type": {
"properties": {
"log_time_date": {
"type": "date",
"format": "epoch_millis"
},
.....
}
}
}
# 2. 向中间索引备份源索引的数据
# 重建索引
POST _reindex
{
"source": {
"index": "demo_metric"
},
"dest": {
"index": "demo_metric_1"
}
}
# 3.查询确认数据是否copy过去
GET /demo_metric/type/_search
GET /demo_metric_1/type/_search
# 4.删除有问题的索引
# 删除有问题的索引
DELETE demo_metric
# 5.重新创建同名的索引(★字段类型修改正确★)
#创建索引
PUT demo_metric/
# 创建Mapping
POST demo_metric/type/_mapping
{
"type": {
"properties": {
"log_time_date": {
"type": "date",
"format": "epoch_millis"
},
.....
}
}
}
# 6. 从中间索引还原到源索引的数据
# 重建索引
POST _reindex
{
"source": {
"index": "demo_metric_1"
},
"dest": {
"index": "demo_metric"
}
}
# 7. 删除中间索引
DELETE demo_metric_1
在kibaba的索引管理中,找到我们要修改的索引 Mapping,然后复制,如下图:
或者也可以通过命令获取索引Mapping:
# 1、获取索引的映射
GET gulimall_product/_mapping
mapping
{
"gulimall_product" : {
"mappings" : {
"properties" : {
"attrs" : {
"properties" : {
"attrId" : {
"type" : "long"
},
"attrName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"attrValue" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"brandId" : {
"type" : "long"
},
"brandImg" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"brandName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"catalogId" : {
"type" : "long"
},
"catalogName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"hasStock" : {
"type" : "boolean"
},
"hotScore" : {
"type" : "long"
},
"saleCount" : {
"type" : "long"
},
"skuId" : {
"type" : "long"
},
"skuImg" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"skuPrice" : {
"type" : "float"
},
"skuTitle" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"spuId" : {
"type" : "long"
}
}
}
}
}
本项目操作步骤:
改映射数据,需要用数据迁移,原来的类型不能改动。
# 1、获取索引的映射
GET gulimall_product/_mapping
# 2、创建中间索引(直接使用下边的)
# PUT gulimall_product_1/
# 3、创建索引,并且同时设置索引Mapping
PUT gulimall_product_2
{
"mappings": {
"properties": {
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword"
},
"attrValue": {
"type": "keyword"
}
}
},
"brandId": {
"type": "long"
},
"brandImg": {
"type": "keyword"
},
"brandName": {
"type": "keyword"
},
"catalogId": {
"type": "long"
},
"catalogName": {
"type": "keyword"
},
"hasStock": {
"type": "boolean"
},
"hotScore": {
"type": "long"
},
"saleCount": {
"type": "long"
},
"skuId": {
"type": "long"
},
"skuImg": {
"type": "keyword"
},
"skuPrice": {
"type": "keyword"
},
"skuTitle": {
"type": "text"
},
"spuId": {
"type": "keyword"
}
}
}
}
# 4、查看创建的索引
GET gulimall_product_2
# 5、迁移数据
POST _reindex
{
"source": {
"index": "gulimall_product"
},
"dest": {
"index": "gulimall_product_2"
}
}
# 6、查询数据是否已迁移成功
GET gulimall_product_2/_search
# 7、上边迁移成功后,删除有问题的索引
DELETE gulimall_product
# 8、重新创建同名的索引(★字段类型修改正确★)
PUT gulimall_product
{
"mappings": {
"properties": {
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword"
},
"attrValue": {
"type": "keyword"
}
}
},
"brandId": {
"type": "long"
},
"brandImg": {
"type": "keyword"
},
"brandName": {
"type": "keyword"
},
"catalogId": {
"type": "long"
},
"catalogName": {
"type": "keyword"
},
"hasStock": {
"type": "boolean"
},
"hotScore": {
"type": "long"
},
"saleCount": {
"type": "long"
},
"skuId": {
"type": "long"
},
"skuImg": {
"type": "keyword"
},
"skuPrice": {
"type": "keyword"
},
"skuTitle": {
"type": "text"
},
"spuId": {
"type": "keyword"
}
}
}
}
# 9、从中间索引还原到源索引的数据
POST _reindex
{
"source": {
"index": "gulimall_product_2"
},
"dest": {
"index": "gulimall_product"
}
}
# 10、再次查询数据是否已迁移成功
GET gulimall_product/_search
# 11、上边迁移成功后,删除中间索引
DELETE gulimall_product_2
修改attrs为nested嵌套类型后,查询成功:
相关文章:
Elasticsearch官方文档Boolean queryed
Elasticsearch: nested object under path is not of
Elasticsearch怎么修改索引字段类型?
我用的ElasticSearch7+版本,使用插件postman的时候,PUT提交报错
为者常成,行者常至
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)