谷粒商城十四检索服务

2023-10-23 17:59

本文主要是介绍谷粒商城十四检索服务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

搭建页面环境

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atlinxi.gulimall</groupId><artifactId>gulimall-search</artifactId><version>0.0.1-SNAPSHOT</version><name>gulimall-search</name><description>elasticsearch检索服务</description><properties><java.version>1.8</java.version><elasticsearch.version>7.4.2</elasticsearch.version><spring-cloud.version>2020.0.4</spring-cloud.version></properties><dependencies><dependency><groupId>com.atlinxi.gulimall</groupId><artifactId>gulimall-common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.4.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
<!--        引入热启动--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency>
<!--        <dependency>-->
<!--            <groupId>com.alibaba</groupId>-->
<!--            <artifactId>fastjson</artifactId>-->
<!--            <version>1.2.79</version>-->
<!--        </dependency>--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--        由于SpringCloud Feign高版本不使用Ribbon而是使用spring-cloud-loadbalancer,所以需要引用spring-cloud-loadbalancer或者降版本--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

关闭thymeleaf缓存

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.application.name=gulimall-search
server.port=13000spring.thymeleaf.cache=false

替换index.html中的内容,并将静态资源全部复制到nginx中

href="替换为href="/static/search/src="替换为src="/static/search/

修改windows本地hosts文件

192.168.56.10	gulimall.com
192.168.56.10	search.gulimall.com

修改gulimall.conf

server {listen       80;server_name  gulimall.com *.gulimall.com;#charset koi8-r;#access_log  /var/log/nginx/log/host.access.log  main;location /static {root   /usr/share/nginx/html;}location / {proxy_set_header Host $host;proxy_pass http://gulimall;}#error_page  404              /404.html;# redirect server error pages to the static page /50x.html#error_page   500 502 503 504  /50x.html;location = /50x.html {root   /usr/share/nginx/html;}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {#    proxy_pass   http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {#    root           html;#    fastcgi_pass   127.0.0.1:9000;#    fastcgi_index  index.php;#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;#    include        fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {#    deny  all;#}
}

在gateway模块添加路由至末尾

- id: gulimall_search_routeuri: lb://gulimall-searchpredicates:- Host=search.gulimall.com

product模块的index.html

//  href="/static/#" 去掉
<a href="/static/#" ><img src="/static/index/img/img_09.png" onclick="search()" /></a>window.location.href="/static/http://search.gulimall.com/search.html?keyword="+keyword;
// 将上面的改为
window.location.href="http://search.gulimall.com/list.html?keyword="+keyword;

此时,就可以访问search.gulimall.com

商城检索-检索条件分析

在这里插入图片描述

nginx

gulimall.conf

server {listen       80;server_name  gulimall.com *.gulimall.com;#charset koi8-r;#access_log  /var/log/nginx/log/host.access.log  main;location /static {root   /usr/share/nginx/html;}location / {proxy_set_header Host $host;proxy_pass http://gulimall;}#error_page  404              /404.html;# redirect server error pages to the static page /50x.html#error_page   500 502 503 504  /50x.html;location = /50x.html {root   /usr/share/nginx/html;}

es dsl语句

之前的es索引应该是product,我们在做映射的时候,brandImg、brandName等字段我们只是来看一下,不做聚合和查询,所以当时这些字段的index、doc_values都为false,我们就得做索引的数据迁移(es不能直接修改映射)。

# 更新映射
# 映射更新之后,之前的数据映射还是不会改变的
# 所以我们建一个新的索引用来迁移
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","analyzer": "ik_smart"},"spuId": {"type": "keyword"}}}}# 将product的数据迁移到gulimall_product
POST _reindex
{"source": {"index": "product"},"dest": {"index": "gulimall_product"}
}# 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析# 如果是嵌入式的属性,查询,聚合,分析都应该用嵌入式的
# dsl完整查询语句
{"query": {"bool": {"must": [{"match": {"skuTitle": "华为"}}],"filter": [{"term": {"catalogId": "225"}},{"terms": {"brandId": ["5","6","7"]}},{"nested": {"path": "attrs","query": {"bool": {"must": [{"term": {"attrs.attrId": {"value": "1"}}},{"terms": {"attrs.attrValue": ["CET-AL00","balabala"]}}]}}}},{"term": {"hasStock": {"value": "false"}}},{"range": {"skuPrice": {"gte": 0,"lte": 6000}}}]}},"sort": [{"skuPrice": {"order": "desc"}}],"from": 0,"size": 2,"highlight": {"fields": {"skuTitle": {}},"pre_tags": "<b style='color:yellow'>","post_tags": "</b>"},"aggs": {"brand_agg": {"terms": {"field": "brandId","size": 100},"aggs": {"brand_name_agg": {"terms": {"field": "brandName","size": 10}},"brand_img_agg": {"terms": {"field": "brandImg","size": 10}}}},"catalog_agg": {"terms": {"field": "catalogId","size": 10},"aggs": {"catalog_name_agg": {"terms": {"field": "catalogName","size": 10}}}},"attr_agg": {"nested": {"path": "attrs"},"aggs": {"attr_id": {"terms": {"field": "attrs.attrId","size": 10},"aggs": {"attr_name_agg": {"terms": {"field": "attrs.attrName","size": 10}},"attr_value_agg": {"terms": {"field": "attrs.attrValue","size": 10}}}}}}}
}

修改commons R类

/*** Copyright (c) 2016-2019 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package com.atlinxi.common.utils;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.http.HttpStatus;import java.util.HashMap;
import java.util.Map;/*** 返回数据** @author Mark sunlightcs@gmail.com*** 老师是用泛型的方式封装的data,* 在feign接口中返回泛型类时,由于java的泛型机制,在实例化之前无法得到具体的类型 ,* 因此,虽然服务提供方返回的是具体实例的数据,但是在客户端decode时,无法转化为具体的类。** 上面的话看不太懂,翻译成人话就是,feign在被远程调用返回结果的时候,泛型是null** 因为R继承了HashMap,我们写的所有私有属性都没用,只能存键值对,具体原因未知,** public class R<T> extends HashMap<String, Object> {* 	private static final long serialVersionUID = 1L;** 	private T data;** 	public T getData() {* 		return this.data;*        }*** 	public void setData(T data) {* 		this.data = data;*    }*/
public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R setData(Object data){put("data",data);return this;}public <T> T getData(TypeReference<T> typeReference) {Object data = get("data");	//默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}//利用fastjson进行反序列化public <T> T getData(String key,TypeReference<T> typeReference) {Object data = get(key);	//默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}public R() {put("code", 0);put("msg", "success");}public static R error() {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");}public static R error(String msg) {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}public R put(String key, Object value) {super.put(key, value);return this;}public int getCode(){return (Integer) this.get("code");}
}

所有实体类、常量等

package com.atlinxi.gulimall.search.constant;public class EsConstant {// 这儿写错了,常量名应该都是大写public static final String Product_INDEX = "gulimall_product"; // sku数据在es中的索引public static final Integer Product_PAGESIZE = 2; // 前期为了方便测试,分页只两个
}
// 查询条件实体类
package com.atlinxi.gulimall.search.vo;import lombok.Data;import java.util.List;/*** 封装页面所有可能传递过来的查询条件** catalog3Id=225&keyword=小米&sort=saleCount_asc&hasStock=0/1&brandId=1&brandId=2* &attrs=1_5寸:8寸&attrs=2_16G:8G*/
@Data
public class SearchParam {private String keyword; // 页面传递过来的全文匹配关键字private Long catalog3Id; // 页面传递过来的三级分类id/*** sort=saleCount_asc/desc* sort=skuPrice_asc/desc* sort=hotScore_asc/desc*/private String sort; // 排序条件/*** 好多的过滤条件* hasStock(是否有货)、skuPrice(区间)、brandId、catalog3Id、attrs** hasStock 0/1* skuPrice 1_500/_500/500_* brandId=1* attrs=1_其他:安卓&attrs=2_5寸:6寸(_前面代表属性,后面代表值,多个值之间用:分割)*/private Integer hasStock; // 是否只显示有货 0(无库存) 1(有库存)private String skuPrice; // 价格区间查询private List<Long> brandId; // 按照品牌进行查询,可以多选private List<String> attrs; // 按照属性进行筛选private Integer pageNum = 1; // 页码private String _queryString; // url原生的所有查询条件}
// 返回结果实体类
package com.atlinxi.gulimall.search.vo;import com.atlinxi.common.to.es.SkuEsModel;
import lombok.Data;import java.util.ArrayList;
import java.util.List;@Data
public class SearchResult {// 查询到的所有商品信息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 = new ArrayList<>();private List<Long> attrIds = new ArrayList<>();@Datapublic static class NavVo{private String navName;private String navValue;private String link;}@Datapublic static class BrandVo{private Long brandId;private String brandName;private String brandImg;}@Datapublic static class AttrVo{private Long attrId;private String attrName;private List<String> attrValue;}@Datapublic static class CatalogVo{private Long catalogId;private String catalogName;}
}package com.atlinxi.gulimall.search.vo;import lombok.Data;@Data
public class AttrResponseVo {// 所属分类名字private String catelogName;// 所属分组名字private String groupName;// 三级分类路径private Long[] catelogPath;/*** 属性id*/private Long attrId;/*** 属性名*/private String attrName;/*** 是否需要检索[0-不需要,1-需要]*/private Integer searchType;/*** 属性图标*/private String icon;/*** 可选值列表[用逗号分隔]*/private String valueSelect;/*** 属性类型[0-销售属性,1-基本属性,2-既是销售属性又是基本属性]*/private Integer attrType;/*** 启用状态[0 - 禁用,1 - 启用]*/private Long enable;/*** 所属分类*/private Long catelogId;/*** 快速展示【是否展示在介绍上;0-否 1-是】,在sku中仍然可以调整*/private Integer showDesc;private Long attrGroupId;
}package com.atlinxi.gulimall.search.vo;import lombok.Data;@Data
public class BrandVo {private Long brandId;private String brandName;
}

feign远程调用

package com.atlinxi.gulimall.search.feign;import com.atlinxi.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;import java.util.List;@FeignClient("gulimall-product")
public interface ProductFeignService {// 在写feign接口的时候,请求路径必须是完整的,函数命可以与原服务的函数名不一样@RequestMapping("/product/attr/info/{attrId}")R attrInfo(@PathVariable("attrId") Long attrId);@GetMapping("/product/brand//infos")R BrandsInfo(@RequestParam("brandIds") List<Long> brandIds);
}/**** @param attrId* @return*/// search远程调用的时候耗时太长,我们把返回的结果放进缓存@Cacheable(value = "attr",key = "'attrInfo:' + #root.args[0]")@Overridepublic AttrResVo getAttrInfo(Long attrId) {AttrResVo attrResVo = new AttrResVo();AttrEntity attrEntity = this.getById(attrId);BeanUtils.copyProperties(attrEntity,attrResVo);if (attrEntity.getAttrType()==ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){// 1.设置分组信息AttrAttrgroupRelationEntity attrgroupRelation = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));if (attrgroupRelation!=null){attrResVo.setAttrGroupId(attrgroupRelation.getAttrGroupId());AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupRelation.getAttrGroupId());if (attrGroupEntity!=null){attrResVo.setGroupName(attrGroupEntity.getAttrGroupName());}}}// 2. 设置分类信息Long catelogId = attrEntity.getCatelogId();Long[] catelogPath = categoryService.findCatelogPath(catelogId);CategoryEntity categoryEntity = categoryDao.selectById(catelogId);if (categoryEntity!=null){attrResVo.setCatelogPath(catelogPath);attrResVo.setCatelogName(categoryEntity.getName());}return attrResVo;}

controller

package com.atlinxi.gulimall.search.controller;import com.atlinxi.gulimall.search.service.MallSearchService;
import com.atlinxi.gulimall.search.vo.SearchParam;
import com.atlinxi.gulimall.search.vo.SearchResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;import javax.servlet.http.HttpServletRequest;@Controller
public class SearchController {@AutowiredMallSearchService mallSearchService;/*** springMVC 自动将页面提交过来的所有请求查询参数封装成指定的对象* @param param* @return*/@GetMapping("/list.html")public String listPage(SearchParam param, Model model, HttpServletRequest request){String queryString = request.getQueryString();param.set_queryString(queryString);// 1. 根据传递来的页面的查询参数,去es中检索商品SearchResult result = mallSearchService.search(param);model.addAttribute("result",result);// 我们整合了thymeleaf,所以不用谢templates和.htmlreturn "list";}
}

启动类

package com.atlinxi.gulimall.search;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients(basePackages = "com.atlinxi.gulimall.search.feign")
public class GulimallSearchApplication {public static void main(String[] args) {SpringApplication.run(GulimallSearchApplication.class, args);}}

service

package com.atlinxi.gulimall.search.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atlinxi.common.to.es.SkuEsModel;
import com.atlinxi.common.utils.R;
import com.atlinxi.gulimall.search.config.GulimallElasticSearchConfig;
import com.atlinxi.gulimall.search.constant.EsConstant;
import com.atlinxi.gulimall.search.feign.ProductFeignService;
import com.atlinxi.gulimall.search.service.MallSearchService;
import com.atlinxi.gulimall.search.vo.AttrResponseVo;
import com.atlinxi.gulimall.search.vo.BrandVo;
import com.atlinxi.gulimall.search.vo.SearchParam;
import com.atlinxi.gulimall.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;@Service
public class MallSearchServiceImpl implements MallSearchService {@Autowiredprivate RestHighLevelClient client;@Autowiredprivate ProductFeignService productFeignService;// 去es中进行检索@Overridepublic SearchResult search(SearchParam param) {// 1. 动态构建出查询需要的DSL语句SearchResult result = null;// 1. 准备检索请求SearchRequest searchRequest = buildSearchRequest(param);;try {// 2. 执行检索请求SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);// 3. 分析响应数据封装成我们需要的格式result = buildSearchResult(response, param);} catch (IOException e) {e.printStackTrace();}return result;}/*** 构建结果数据** @param response* @return*/private SearchResult buildSearchResult(SearchResponse response, SearchParam param) {SearchResult result = new SearchResult();// 1. 返回的所有查询到的商品SearchHits hits = response.getHits();List<SkuEsModel> esModels = new ArrayList<>();if (hits.getHits() != null && hits.getHits().length > 0) {for (SearchHit hit : hits.getHits()) {String sourceAsString = hit.getSourceAsString();SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);if (!StringUtils.isEmpty(param.getKeyword())) {HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");String s = skuTitle.getFragments()[0].toString();esModel.setSkuTitle(s);}esModels.add(esModel);}}result.setProducts(esModels);// 2. 当前所有商品涉及到的所有属性信息List<SearchResult.AttrVo> attrVos = new ArrayList<>();ParsedNested attr_agg = response.getAggregations().get("attr_agg");ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {SearchResult.AttrVo attrVo = new SearchResult.AttrVo();// 得到属性的idlong attrId = bucket.getKeyAsNumber().longValue();// 得到属性的名字String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();// 得到属性的所有制值List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> {String keyAsString = ((Terms.Bucket) item).getKeyAsString();return keyAsString;}).collect(Collectors.toList());attrVo.setAttrId(attrId);attrVo.setAttrName(attrName);attrVo.setAttrValue(attrValues);attrVos.add(attrVo);}result.setAttrs(attrVos);// 3. 当前所有商品涉及到的所有品牌信息List<SearchResult.BrandVo> brandVos = new ArrayList<>();ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");for (Terms.Bucket bucket : brand_agg.getBuckets()) {SearchResult.BrandVo brandVo = new SearchResult.BrandVo();// 得到品牌的idlong brandId = bucket.getKeyAsNumber().longValue();// 得到品牌的名字String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();// 得到品牌的图片String brandImg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();brandVo.setBrandId(brandId);brandVo.setBrandName(brandName);brandVo.setBrandImg(brandImg);brandVos.add(brandVo);}result.setBrands(brandVos);// 4. 当前所有商品涉及到的所有分类信息ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();List<? extends Terms.Bucket> buckets = catalog_agg.getBuckets();for (Terms.Bucket bucket : buckets) {SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();// 得到分类idString keyAsString = bucket.getKeyAsString();catalogVo.setCatalogId(Long.parseLong(keyAsString));// 得到分类名ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();catalogVo.setCatalogName(catalog_name);catalogVos.add(catalogVo);}result.setCatalogs(catalogVos);// 5. 分页信息 - 页码result.setPageNum(param.getPageNum());// 分页信息 - 总记录数long total = hits.getTotalHits().value;result.setTotal(total);// 分页信息 - 总页码 - 计算得到int totalPages = (int) total % EsConstant.Product_PAGESIZE == 0 ? (int) total / EsConstant.Product_PAGESIZE : (int) total / EsConstant.Product_PAGESIZE + 1;result.setTotalPages(totalPages);List<Integer> pageNavs = new ArrayList<>();for (int i = 1; i <= totalPages; i++) {pageNavs.add(i);}result.setPageNavs(pageNavs);// 6. 构建面包屑导航功能// 面包屑导航只限于属性,不包括分类和keyword(检索条件)// 因为我们如果去掉分类或者检索条件的话,属性则无意义if (param.getAttrs() != null && param.getAttrs().size() > 0) {List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {SearchResult.NavVo navVo = new SearchResult.NavVo();// 1. 分析每个attrs传过来的查询参数值// attrs=1_其他:安卓&attrs=2_5寸:6寸String[] s = attr.split("_");navVo.setNavValue(s[1]);R r = productFeignService.attrInfo(Long.parseLong(s[0]));result.getAttrIds().add(Long.parseLong(s[0]));if (r.getCode() == 0) {AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {});navVo.setNavName(data.getAttrName());} else {navVo.setNavName(s[0]);}// 2. 取消了面包屑以后,我们要跳转到哪个地方,将请求地址的url置空// 拿到所有的查询条件,去掉当前属性String replace = replaceQueryString(param, attr, "attrs");navVo.setLink("http://search.gulimall.com/list.html?" + replace);return navVo;}).collect(Collectors.toList());// todo 分类,不需要导航取消result.setNavs(collect);}// 品牌,分类if (param.getBrandId() != null && param.getBrandId().size() > 0) {List<SearchResult.NavVo> navs = result.getNavs();SearchResult.NavVo navVo = new SearchResult.NavVo();navVo.setNavName("品牌");R r = productFeignService.BrandsInfo(param.getBrandId());if (r.getCode() == 0) {List<BrandVo> brand = r.getData("brand", new TypeReference<List<BrandVo>>() {});StringBuffer buffer = new StringBuffer();String replace = "";for (BrandVo brandVo : brand) {buffer.append(brandVo.getBrandName() + ";");replace = replaceQueryString(param, brandVo.getBrandId() + "", "brandId");}navVo.setNavValue(buffer.toString());navVo.setLink(replace);}navs.add(navVo);}return result;}private String replaceQueryString(SearchParam param, String value, String key) {String encode = null;try {// 中文需要编码encode = URLEncoder.encode(value, "UTF-8");encode = encode.replace("+", "%20"); // 浏览器对空格编码和java不一样,浏览器是%20,java是+} catch (UnsupportedEncodingException e) {e.printStackTrace();}String replace = param.get_queryString().replace("&" + key + "=" + encode, "");return replace;}/*** 准备检索请求* <p>* 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析** @return*/private SearchRequest buildSearchRequest(SearchParam param) {SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); // 构建DSL语句/*** 查询:模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)*/// 1. 构建bool queryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 1.1 must 模糊匹配if (!StringUtils.isEmpty(param.getKeyword())) {boolQuery.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));}// 1.2 bool filter 按照三级分类id查询if (param.getCatalog3Id() != null) {boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));}// 1.2 bool filter 按照品牌id查询if (param.getBrandId() != null && param.getBrandId().size() > 0) {boolQuery.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));}// 1.2 bool filter 按照所有指定的属性进行查询if (param.getAttrs() != null && param.getAttrs().size() > 0) {for (String attrStr : param.getAttrs()) {// attrs=1_5寸:8寸&attrs=2_16G:8GBoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();// attrs=1_5寸:8寸&String[] s = attrStr.split("_");String attrId = s[0]; // 检索的属性idString[] attrValue = s[1].split(":"); // 这个属性检索用的值nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", attrValue));// param3 聚合的这些结果以什么方式参与评分// 在这儿我们先不让它参与评分// 每一个必须都得生成一个nested查询NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);boolQuery.filter(nestedQuery);}}// 1.2 bool filter 按照是否有库存进行查询// 字段是用0,1代表,es是用bool值代表if (param.getHasStock() != null) {boolQuery.filter(QueryBuilders.termsQuery("hasStock", param.getHasStock() == 1));}// 1.2 bool filter 按照价格区间 skuPrice 1_500/_500/500_if (!StringUtils.isEmpty(param.getSkuPrice())) {/***         {*           "range":{*             "skuPrice":{*               "gte":0,*               "lte":6000*             }*           }*         }*/RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");String[] s = param.getSkuPrice().split("_");if (s.length == 2) {// 区间rangeQuery.gte(s[0]).lt(s[1]);} else if (s.length == 1) {if (param.getSkuPrice().startsWith("_")) {rangeQuery.lte(s[0]);}if (param.getSkuPrice().endsWith("_")) {rangeQuery.gte(s[0]);}}boolQuery.filter(rangeQuery);}sourceBuilder.query(boolQuery);/*** 排序,分页,高亮,*/// 2.1 排序if (!StringUtils.isEmpty(param.getSort())) {String sort = param.getSort();// sort=saleCount_asc/descString[] s = sort.split("_");SortOrder order = s[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;sourceBuilder.sort(s[0], order);}// 2.2 分页// pageNum:1 from:0 size:2// pageNum:2 from:2 size:2// from = (pageNum-1)*sizesourceBuilder.from((param.getPageNum() - 1) * EsConstant.Product_PAGESIZE);sourceBuilder.size(EsConstant.Product_PAGESIZE);// 2.3 高亮if (!StringUtils.isEmpty(param.getKeyword())) {HighlightBuilder builder = new HighlightBuilder();builder.field("skuTitle");builder.preTags("<b style='color:red'>");builder.postTags("</b>");sourceBuilder.highlighter(builder);}/*** 聚合分析*/// 1. 品牌聚合TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");brand_agg.field("brandId").size(50);// 品牌聚合的子聚合brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));sourceBuilder.aggregation(brand_agg);// 2. 分类聚合TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").field("catalogId").size(2);catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));sourceBuilder.aggregation(catalog_agg);// 3. 属性聚合NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");// 聚合出当前所有的attrIdTermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");// 聚合分析出当前attr_id对应的名字attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));// 聚合分析出当前attr_id对应的所有可能的属性值attrValueattr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));attr_agg.subAggregation(attr_id_agg);sourceBuilder.aggregation(attr_agg);String s = sourceBuilder.toString();System.out.println("构建的DSL" + s);SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.Product_INDEX}, sourceBuilder);return searchRequest;}
}

刘怡婷知道当小孩最大的好处,就是没有人会认真看待她的话。她大可吹牛、食言,甚至说谎。也是大人反射性的自我保护,因为小孩最初说的往往是雪亮真言,大人只好安慰自己,小孩子懂什么。挫折之下,小孩从说实话的孩子进化为可以选择说实话的孩子,在话语的民主中,小孩才长成大人。

房思琪的初恋乐园
林奕含

这篇关于谷粒商城十四检索服务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/269528

相关文章

运营版开源代码 多语言跨境商城 跨境电商平台

默认中英双语 后台带翻译接口 支持133种语言自动翻译 支持多商户联盟 一键部署版本 伪静态+后台登陆后缀 源码下载:https://download.csdn.net/download/m0_66047725/89722389 更多资源下载:关注我。

十四、我们应当怎样做需求分析:子用例与扩展用例

用例模型作为UML中4+1视图中非常重要的一员,非常集中地体现了面向对象的分析与设计思想。用例模型将现实世界中连续的一个一个业务流程,按照场景划分到了一个一个的用例中。由于场景的出现,使得用例中的业务流程存在着高度的内聚性,从而成为了日后各种对象的雏形。同时,在用例分析中,又将那些存在于各个用例中的,相同或相近的业务操作提取出来,形成一个一个的子用例或扩展用例,又体现了面向对象设计中的复用性。现在

计算机毕业设计选题推荐-域名管理系统-域名商城-域名竞拍系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python项目 安卓项目 微信小程序项目 文章目录 一、前言二、开发环境三、系统界面展示四、代码参考五、论文参考六、系统视频结语

基于开源链动 2 + 1 模式、AI 智能名片与 S2B2C 商城小程序的用户忠诚度计划

摘要:本文深入探讨了在商业环境中执行用户忠诚度计划的创新途径。通过整合开源链动 2 + 1 模式、AI 智能名片以及 S2B2C 商城小程序等先进元素,从提供福利、解决问题和创造赚钱机会三个核心方面展开详细阐述。研究表明,这些新技术和新模式的有机结合,能够为企业打造更具吸引力和影响力的用户忠诚度计划,从而实现商业效益的最大化与可持续发展。 一、引言 在当今竞争激烈且市场环境快速变化的时代,

【C++STL(十四)】一个哈希桶简单模拟实现unordered_map/set

目录 前言 一、改造哈希桶 改造结点类 改造主体  模板参数改造  迭代器(重点) 改造完整哈希桶 unordered_map模拟实现 unordered_set模拟实现 前言 前面的文章都有说unordered_map/set的底层结构就是哈希表,严格来说是哈希桶,那么接下来我们就尝试使用同一个哈希桶来模拟实现一下。整体的逻辑和一棵红黑树封装map/set类似,所以

链动2+1模式AI智能名片S2B2C商城小程序源码在社群商业价值构建中的应用探索

摘要:在数字经济浪潮的推动下,社群作为商业生态的核心组成部分,其商业价值正以前所未有的速度增长。本文深入探讨了如何通过“链动2+1模式AI智能名片S2B2C商城小程序源码”这一前沿技术工具,深度挖掘并优化社群的商业价值。通过详细分析该模式的技术特点、应用策略及其实施路径,本文旨在为社群经济的繁荣发展提供一套系统化、可操作的解决方案。 引言 随着互联网技术的飞速发展,社群已经成为连接消费者、

部署黑马商城至云服务器

1阿里云ECS 1.1获取云服务器 想要将项目部署到云服务器,首先我们需要有一个云服务器,本次我们用的是阿里云ECS云服务器  我们登录进入主页,点击左上角的产品 依次按照上图点击,我们可以利用学生认证,然后领取免费的服务器时长 拥有了服务器后,我们点击管理控制台 我们创建一个实例服务器,依次填写好相关信息,然后我们会获取到一个公网ip,这个公网ip就是以后别人访问的主机ip服务

Flink实例(115):自定义时间和窗口的操作符(十四)窗口操作符(四)触发器(Triggers) (二)

Flink 中窗口是很重要的一个功能,而窗口又经常配合触发器一起使用。 Flink 自带的触发器大概有: CountTrigger: 指定条数触发ContinuousEventTimeTrigger:指定事件时间触发ContinuousProcessingTimeTrigger:指定处理时间触发ProcessingTimeTrigger: 默认触发器,窗口结束触发EventTime

Spark学习之路 (十四)SparkCore的调优之资源调优JVM的GC垃圾收集器

《2021年最新版大数据面试题全面开启更新》 欢迎关注github《大数据成神之路》 目录 一、概述 二、垃圾收集器(garbage collector (GC)) 是什么? 三、为什么需要GC? 四、为什么需要多种GC? 五、对象存活的判断 六、垃圾回收算法 6.1 标记 -清除算法 6.2 复制算法 6.3 标记-整理算法 6.4 分代收集算法 七、垃圾收集器 7.1 Serial收集器

【硬刚ES】ES基础(十四)Elasticsearch聚合分析简介

本文是对《【硬刚大数据之学习路线篇】从零到大数据专家的学习指南(全面升级版)》的ES部分补充。