目录
1、添加依赖(一般都用这个方法进行opensearch SDK的添加)
介绍
智能开放搜索OpenSearch是基于阿里巴巴自主研发的大规模分布式搜索引擎搭建的一站式商用智能搜索平台,目前为包括淘宝、天猫、菜鸟在内的阿里集团核心搜索业务提供中台服务支持。经过多年的行业搜索经验沉淀、双11大促流量冲击,智能开放搜索OpenSearch打磨出一套高性能、高时效、高可用、强稳定搜索全家桶服务,包括行业算法版、高性能检索版、向量检索版、召回引擎版四类商品版本,以满足各行各业的搜索需求。OpenSearch以平台服务化的形式,将专业搜索技术简单化、低门槛化和低成本化,让搜索不再成为客户的业务瓶颈,以低成本实现产品搜索功能并快速迭代。
上面是官网的介绍,其实就是一个阿里版es搜索引擎
本章只介绍对通用行业版的简单使用,对数据的导入以及搜索查询,修改和增加相同(会根据主键进行覆盖),删除只需要在推送的json中将Command.ADD.toString()换成Command.DELETE.toString()即可。
快速入门
应用结构创建
这里是官方文档地址快速入门 (aliyun.com)
将os(opensearch以下简称os) 看作一个数据库,那么创建应用结构就是创建数据表;
主键只有两种类型,int和字符串(LITERAL)
初始入门讲的很详细,我就讲一个小坑。
1、应用结构创建完毕后,需要使用线下变更才能进行改变,选择手动发布,因为变更后数据不会迁移到新的应用结构,所以需要重新导入数据
接口文档
使用os+java+mybatis-plus之前我们先看一下单独的os+java是怎么进行数据的导入和查询的
还是先放上官方文档最近发布的使用教程 (aliyun.com)
1、添加依赖(一般都用这个方法进行opensearch SDK的添加)
使用最新稳定版,官方文档使用的不是最新版所以在进行开发的时候出现了方法不
存在的报错。
2.1、推送数据
package org.example;
import com.aliyun.opensearch.DocumentClient;
import com.aliyun.opensearch.OpenSearchClient;
import com.aliyun.opensearch.sdk.dependencies.com.google.common.collect.Maps;
import com.aliyun.opensearch.sdk.dependencies.org.json.JSONArray;
import com.aliyun.opensearch.sdk.dependencies.org.json.JSONObject;
import com.aliyun.opensearch.sdk.generated.OpenSearch;
import com.aliyun.opensearch.sdk.generated.commons.OpenSearchClientException;
import com.aliyun.opensearch.sdk.generated.commons.OpenSearchException;
import com.aliyun.opensearch.sdk.generated.commons.OpenSearchResult;
import com.aliyun.opensearch.sdk.generated.document.Command;
import com.aliyun.opensearch.sdk.generated.document.DocumentConstants;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Random;
public class os {
private static String appName = "替换为opensearch应用名";
private static String tableName = "替换opensearch应用表名";
private static String accesskey = "您的阿里云的Access Key ID";
private static String secret = "阿里云 Access Key ID 对应的 Access Key Secret";
private static String host = "这里的host需要根据访问应用基本信息页中提供的API入口来确定";
public static void main(String[] args) {
//-------------数据推送示例代码-----------------
int id = 1111;
//定义Map对象存储上传文档数据,
Map<String, Object> doc = Maps.newLinkedHashMap();
//按照 (字段名,传入值)
doc.put("id", id);
doc.put("name","张三");
//os进行数据添加时会根据主键进行覆盖,所以新增和修改都是Command.ADD.toString()
JSONObject json = new JSONObject();
//删除就是将第二个参数改成Command.DELETE.toString()
json.put(DocumentConstants.DOC_KEY_CMD, Command.ADD.toString());
json.put(DocumentConstants.DOC_KEY_FIELDS, doc);
//定义Map对象存储上传文档数据,此为文档2
Map<String, Object> doc2 = Maps.newLinkedHashMap();
int id2 =2222;
doc2.put("id", id2);
doc2.put("name","李四");
//新增及更新都设为ADD,不支持update,当已存在相同主键值文档时做更新,否则新增,此处作为新增
JSONObject json2 = new JSONObject();
json2.put(DocumentConstants.DOC_KEY_CMD, Command.ADD.toString());
json2.put(DocumentConstants.DOC_KEY_FIELDS, doc2);
JSONArray docsJsonArr = new JSONArray();
docsJsonArr.put(json);//新增文档1
docsJsonArr.put(json2);//新增文档2
String docsJson = docsJsonArr.toString();
//创建并构造OpenSearch对象
OpenSearch openSearch = new OpenSearch(accesskey, secret, host);
//创建OpenSearchClient对象,并以OpenSearch对象作为构造参数
OpenSearchClient serviceClient = new OpenSearchClient(openSearch);
//定义DocumentClient对象添加json格式doc数据批量提交
DocumentClient documentClient = new DocumentClient(serviceClient);
try {
//执行推送操作
OpenSearchResult osr = documentClient.push(docsJson, appName, tableName);
//判断数据是否推送成功,主要通过判断2处,第一处判断用户方推送是否成功,第二处是应用控制台中有无报错日志
//用户方推送成功后,也有可能在应用端执行失败,此错误会直接在应用控制台错误日志中生成,比如字段内容转换失败
if (osr.getResult().equalsIgnoreCase("true")) {
System.out.println("用户方推送无报错!\n以下为getTraceInfo推送请求Id:" + osr.getTraceInfo().getRequestId());
} else {
System.out.println("用户方推送报错!" + osr.getTraceInfo());
}
} catch (OpenSearchException e) {
e.printStackTrace();
} catch (OpenSearchClientException e) {
e.printStackTrace();
}
}
}
重点在于进行推送数据时,需要按照(字段 ,值)的方式进行添加
2.2、查看数据是否推送成功
可以直接在os控制台查看实例,如下我导入成功了6297条数据
2.3控制台测试搜索
控制提供了简便的搜索测试,可用来测试自己数据导入是否完整,也可以测试应用结构中设置的索引和其他设置
3、搜索
想要特定的查询方法,需要自己去应结构中设置精/粗排表达式,我这里直接使用默认的。
package org.example;
import com.aliyun.opensearch.OpenSearchClient;
import com.aliyun.opensearch.SearcherClient;
import com.aliyun.opensearch.sdk.dependencies.com.google.common.collect.Lists;
import com.aliyun.opensearch.sdk.dependencies.org.json.JSONObject;
import com.aliyun.opensearch.sdk.generated.OpenSearch;
import com.aliyun.opensearch.sdk.generated.commons.OpenSearchClientException;
import com.aliyun.opensearch.sdk.generated.commons.OpenSearchException;
import com.aliyun.opensearch.sdk.generated.search.*;
import com.aliyun.opensearch.sdk.generated.search.general.SearchResult;
import com.aliyun.opensearch.search.SearchParamsBuilder;
import com.aliyun.opensearch.search.SearchResultDebug;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class search {
private static String appName = "替换为opensearch应用名";
private static String accesskey = "替换accesskey";
private static String secret = "替换secret";
private static String host = "替换应用的API访问地址";
public static void main(String[] args) {
//创建并构造OpenSearch对象
OpenSearch openSearch = new OpenSearch(accesskey, secret, host);
//创建OpenSearchClient对象,并以OpenSearch对象作为构造参数
OpenSearchClient serviceClient = new OpenSearchClient(openSearch);
//创建SearcherClient对象,并以OpenSearchClient对象作为构造参数
SearcherClient searcherClient = new SearcherClient(serviceClient);
//定义Config对象,用于设定config子句参数,分页或数据返回格式,指定应用名等等
Config config = new Config(Lists.newArrayList(appName));
config.setStart(0);
config.setHits(5);
//设置返回格式为FULLJSON,目前支持返回 XML,JSON,FULLJSON 等格式
config.setSearchFormat(SearchFormat.FULLJSON);
// 设置搜索结果返回应用中哪些字段
config.setFetchFields(Lists.newArrayList("id", "name"));
// 创建参数对象
SearchParams searchParams = new SearchParams(config);
//这里可以用 name:“酒” 的格式进行查询 也可以直接将要查询的字符串放入,他会使用default进行查询
searchParams.setQuery("酒");
// 设置sort条件
Sort sorter = new Sort();
sorter.addToSortFields(new SortField("id", Order.DECREASE)); //设置id字段降序
sorter.addToSortFields(new SortField("RANK", Order.INCREASE)); //若id相同则以RANK升序
//添加Sort对象参数
searchParams.setSort(sorter);
// 设置粗精排表达式,此处设置为默认
Rank rank = new Rank();
rank.setFirstRankName("default");
rank.setSecondRankName("default");
rank.setReRankSize(5);//设置参与精排文档个数
//添加Aggregate对象参数
searchParams.setRank(rank);
try {
// 执行返回查询结果。用户需按code和message,进行异常情况判断。code对应的错误信息查看——错误码文档。
SearchResult searchResult = searcherClient.execute(searchParams);
String result = searchResult.getResult();
JSONObject obj = new JSONObject(result);
// 输出查询结果
System.out.println(obj.toString());
//个别用户可能需要debug请求地址信息
SearchResultDebug searchdebugrst = searcherClient.executeDebug(searchParams);
//输出上次查询请求串信息
System.out.println(searchdebugrst.getRequestUrl());
} catch (OpenSearchException e) {
e.printStackTrace();
} catch (OpenSearchClientException e) {
e.printStackTrace();
}
}
}
更多的筛选条件可以看官网文档搜索Demo (aliyun.com)
进阶
上面已经知道如何进行数据的推送和搜索了,现在可以整合项目了
难点在于,按照字段进行导入时还需要拿到实体类中的属性名转换成下划线拼接格式,如下
前置说明
在开始前说明下应用结构,省的大家看不懂源码。
因为作者也是第一使用,在设置应用结构的时候直接将所有的字段都上传了进去(实际上只需要将展示给用户看的字段和id上传即可),而且os应用结构快速导入只支持阿里自己的云数据库所以我手搓了88个字段
响应json结构
实体类
源码中的实体类CommodityInfo,CommodityInfoDTO,CommodityInfoVO
分别是实体类,接收类,返回类,
package com.huashunmall.commodity.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.huashunmall.common.base.BaseEntity;
import com.huashunmall.common.utils.DateUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* @author yusiyi generate
* @since2021-12-28 16:46:30
* @description 商品信息表
**/
@Data
@TableName("commodity_info")
public class CommodityInfo implements Serializable {
private static final long serialVersionUID = 1L;
// 数据库字段是:id 实体类属性是:id 注释是:主键编号 创建时间是 :2021-12-28 16:46:30
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
// 数据库字段是:dealer_id 实体类属性是:dealerId 注释是:商家id 创建时间是 :2021-12-28 16:46:30
private String dealerId;
private String dealerVendorNo;
// 数据库字段是:dealer_name 实体类属性是:dealerName 注释是:商家名称 创建时间是 :2021-12-28 16:46:30
private String dealerName;
// 数据库字段是:commodity_type_id 实体类属性是:commodityTypeId 注释是:产品三级类型 创建时间是 :2021-12-28 16:46:30
@TableField(value = "commodity_type_id", fill = FieldFill.UPDATE)
private String commodityTypeId;
// 数据库字段是:name 实体类属性是:name 注释是:产品名称 创建时间是 :2021-12-28 16:46:30
private String name;
// 数据库字段是:price 实体类属性是:price 注释是:价格 创建时间是 :2021-12-28 16:46:30
private Double price;
// 数据库字段是:is_delivery_pay 实体类属性是:isDeliveryPay 注释是:是否支持货到付款(0 不支持,1支持) 创建时间是 :2021-12-28 16:46:30
private Integer isDeliveryPay;
// 数据库字段是:adjunct 实体类属性是:adjunct 注释是:主图 创建时间是 :2021-12-28 16:46:30
private String adjunct;
// 数据库字段是:title 实体类属性是:title 注释是:副标题 创建时间是 :2021-12-28 16:46:30
private String title;
// 数据库字段是:cose_price 实体类属性是:cosePrice 注释是:进货价,成本价 创建时间是 :2021-12-28 16:46:30
private Double cosePrice;
// 数据库字段是:market_price 实体类属性是:marketPrice 注释是:市场价 创建时间是 :2021-12-28 16:46:30
private Double marketPrice;
// 数据库字段是:inventory_warning_amount 实体类属性是:inventoryWarningAmount 注释是:库存预警数量 创建时间是 :2021-12-28 16:46:30
private Integer inventoryWarningAmount;
// 数据库字段是:business_no 实体类属性是:businessNo 注释是:商家货号 创建时间是 :2021-12-28 16:46:30
private String businessNo;
// 数据库字段是:commodity_description 实体类属性是:commodityDescription 注释是:产品描述 创建时间是 :2021-12-28 16:46:30
private String commodityDescription;
// 数据库字段是:commodity_brand_value 实体类属性是:commodityBrandValue 注释是:产品品牌 创建时间是 :2021-12-28 16:46:30
private String commodityBrandValue;
// 数据库字段是:commodity_quality 实体类属性是:commodityQuality 注释是:产品质量 创建时间是 :2021-12-28 16:46:30
private String commodityQuality;
// 数据库字段是:is_appreciation_invoice 实体类属性是:isAppreciationInvoice 注释是:是否开增值税发票(0是,1否) 创建时间是 :2021-12-28 16:46:30
private Integer isAppreciationInvoice;
// 数据库字段是:hit 实体类属性是:hit 注释是:浏览量 创建时间是 :2021-12-28 16:46:30
private Integer hit;
// 数据库字段是:status 实体类属性是:status 注释是:状态(1未上架,2已上架,3已下架) 创建时间是 :2021-12-28 16:46:30
private Integer status;
// 数据库字段是:sellcount 实体类属性是:sellcount 注释是:销售数量 创建时间是 :2021-12-28 16:46:30
private Integer sellcount;
private Integer attrSellcount;
// 数据库字段是:stock 实体类属性是:stock 注释是:库存 创建时间是 :2021-12-28 16:46:30
private Integer stock;
// 数据库字段是:image 实体类属性是:image 注释是:套图 创建时间是 :2021-12-28 16:46:30
private String image;
// 数据库字段是:freight_stacking 实体类属性是:freightStacking 注释是:1不累加,2累加 创建时间是 :2021-12-28 16:46:30
private String freightStacking;
// 数据库字段是:freight_type 实体类属性是:freightType 注释是:运费类型(1固定运费,2运费模板,3配送费) 创建时间是 :2021-12-28 16:46:30
private Integer freightType;
// 数据库字段是:fixed_freight 实体类属性是:fixedFreight 注释是:运费 创建时间是 :2021-12-28 16:46:30
private Double fixedFreight;
// 数据库字段是:freight_model 实体类属性是:freightModel 注释是:运费模版名称 创建时间是 :2021-12-28 16:46:30
private String freightModel;
// 数据库字段是:commodity_type_id1 实体类属性是:commodityTypeId1 注释是:产品一级类型 创建时间是 :2021-12-28 16:46:30
private String commodityTypeId1;
// 数据库字段是:commodity_type_id2 实体类属性是:commodityTypeId2 注释是:产品二级类型 创建时间是 :2021-12-28 16:46:30
private String commodityTypeId2;
// 数据库字段是:comment_num 实体类属性是:commentNum 注释是:评价数量 创建时间是 :2021-12-28 16:46:30
private Integer commentNum;
// 数据库字段是:good_comment_num 实体类属性是:goodCommentNum 注释是:好评数量 创建时间是 :2021-12-28 16:46:30
private Integer goodCommentNum;
// 数据库字段是:medium_comment_num 实体类属性是:mediumCommentNum 注释是:中评数量 创建时间是 :2021-12-28 16:46:30
private Integer mediumCommentNum;
// 数据库字段是:negative_comment_num 实体类属性是:negativeCommentNum 注释是:差评数量 创建时间是 :2021-12-28 16:46:30
private Integer negativeCommentNum;
// 数据库字段是:add_comment_num 实体类属性是:addCommentNum 注释是:追评数量 创建时间是 :2021-12-28 16:46:30
private Integer addCommentNum;
// 数据库字段是:image_comment_num 实体类属性是:imageCommentNum 注释是:有图评价数量 创建时间是 :2021-12-28 16:46:30
private Integer imageCommentNum;
// 数据库字段是:common_score 实体类属性是:commonScore 注释是:评价分数 创建时间是 :2021-12-28 16:46:30
private Integer commonScore;
// 数据库字段是:approve_state 实体类属性是:approveState 注释是:审批状态(0待审核,1通过,2不通过) 创建时间是 :2021-12-28 16:46:30
private Integer approveState;
// 数据库字段是:approve_user 实体类属性是:approveUser 注释是:审批人 创建时间是 :2021-12-28 16:46:30
private String approveUser;
// 数据库字段是:approve_date 实体类属性是:approveDate 注释是:审批时间 创建时间是 :2021-12-28 16:46:30
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date approveDate;
// 数据库字段是:approve_remark 实体类属性是:approveRemark 注释是:审批备注 创建时间是 :2021-12-28 16:46:30
private String approveRemark;
// 数据库字段是:racking_date 实体类属性是:rackingDate 注释是:上架时间 创建时间是 :2021-12-28 16:46:30
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date rackingDate;
// 数据库字段是:sold_out_date 实体类属性是:soldOutDate 注释是:下架时间 创建时间是 :2021-12-28 16:46:30
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date soldOutDate;
// 数据库字段是:freight_model_id 实体类属性是:freightModelId 注释是:运费模板id 创建时间是 :2021-12-28 16:46:30
private String freightModelId;
// 数据库字段是:purchase_need_know 实体类属性是:purchaseNeedKnow 注释是:购买需知 创建时间是 :2021-12-28 16:46:30
private String purchaseNeedKnow;
// 数据库字段是:commodity_no 实体类属性是:commodityNo 注释是:产品编号 创建时间是 :2021-12-28 16:46:30
private String commodityNo;
// 数据库字段是:is_quota 实体类属性是:isQuota 注释是:是否限购(1不限购,2限购) 创建时间是 :2021-12-28 16:46:30
private String isQuota;
// 数据库字段是:quota_num 实体类属性是:quotaNum 注释是:限购数量 创建时间是 :2021-12-28 16:46:30
private Integer quotaNum;
// 数据库字段是:is_virtual 实体类属性是:isVirtual 注释是:商品类型(0实物商品,1到店消费,2即时消费,3优惠买单) 创建时间是 :2021-12-28 16:46:30
private Integer isVirtual;
// 数据库字段是:start_buy_num 实体类属性是:startBuyNum 注释是:起始购买数量 创建时间是 :2021-12-28 16:46:30
private Integer startBuyNum;
// 数据库字段是:order_by 实体类属性是:orderBy 注释是:排序 创建时间是 :2021-12-28 16:46:30
private Integer orderBy;
// 数据库字段是:run_errands_fee 实体类属性是:runErrandsFee 注释是:跑腿费 创建时间是 :2021-12-28 16:46:30
private Integer runErrandsFee;
// 数据库字段是:is_self 实体类属性是:isSelf 注释是:发货方式(0快递,1仅自提,2支持自提和快递) 创建时间是 :2021-12-28 16:46:30
private Integer isSelf;
// 数据库字段是:dealer_type_name 实体类属性是:dealerTypeName 注释是:店铺类型 创建时间是 :2021-12-28 16:46:30
private String dealerTypeName;
// 数据库字段是:deliver_goods_address 实体类属性是:deliverGoodsAddress 注释是:发货地址 创建时间是 :2021-12-28 16:46:30
private String deliverGoodsAddress;
// 数据库字段是:deliver_goods_date 实体类属性是:deliverGoodsDate 注释是:发货时间 创建时间是 :2021-12-28 16:46:30
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date deliverGoodsDate;
// 数据库字段是:commodity_brand_id 实体类属性是:commodityBrandId 注释是:品牌 创建时间是 :2021-12-28 16:46:30
private String commodityBrandId;
// 数据库字段是:commodity_comprehensive 实体类属性是:commodityComprehensive 注释是:综合排序 创建时间是 :2021-12-28 16:46:30
private String commodityComprehensive;
// 数据库字段是:favourite_counts 实体类属性是:favouriteCounts 注释是:收藏次数 创建时间是 :2021-12-28 16:46:30
private Integer favouriteCounts;
// 数据库字段是:stock_state 实体类属性是:stockState 注释是:1正常,2已售罄 创建时间是 :2021-12-28 16:46:30
private Integer stockState;
// 数据库字段是:stock_in 实体类属性是:stockIn 注释是:入库数量 创建时间是 :2021-12-28 16:46:30
private Integer stockIn;
// 数据库字段是:stock_out 实体类属性是:stockOut 注释是:出库数量 创建时间是 :2021-12-28 16:46:30
private Integer stockOut;
// 数据库字段是:commodity_label 实体类属性是:commodityLabel 注释是:商品标签 创建时间是 :2021-12-28 16:46:30
private String commodityLabel;
// 数据库字段是:video_url 实体类属性是:videoUrl 注释是:视频 创建时间是 :2021-12-28 16:46:30
private String videoUrl;
// 数据库字段是:core_sum 实体类属性是:coreSum 注释是:合计评分(三个评分相加在合计) 创建时间是 :2021-12-28 16:46:30
private Integer coreSum;
// 数据库字段是:commodity_spec_json 实体类属性是:commoditySpecJson 注释是:产品规格json 创建时间是 :2021-12-28 16:46:30
private String commoditySpecJson;
// 数据库字段是:dealer_commodity_type_id1 实体类属性是:dealerCommodityTypeId1 注释是:商家产品分类一级 创建时间是 :2021-12-28 16:46:30
private String dealerCommodityTypeId1;
// 数据库字段是:dealer_commodity_type_id2 实体类属性是:dealerCommodityTypeId2 注释是:商家产品分类二级 创建时间是 :2021-12-28 16:46:30
private String dealerCommodityTypeId2;
// 数据库字段是:quota_begin_date 实体类属性是:quotaBeginDate 注释是:限购开始时间 创建时间是 :2021-12-28 16:46:30
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date quotaBeginDate;
// 数据库字段是:quota_end_date 实体类属性是:quotaEndDate 注释是:限购结束时间 创建时间是 :2021-12-28 16:46:30
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date quotaEndDate;
// 数据库字段是:search_key 实体类属性是:searchKey 注释是:搜索关键字 创建时间是 :2021-12-28 16:46:30
private String searchKey;
// 数据库字段是:spec_state 实体类属性是:specState 注释是:1无规格,2有规格 创建时间是 :2021-12-28 16:46:30
private Integer specState;
// 数据库字段是:supplier 实体类属性是:supplier 注释是:供应商 创建时间是 :2021-12-28 16:46:30
private String supplier;
// 数据库字段是:weight 实体类属性是:weight 注释是:重量(kg) 创建时间是 :2021-12-28 16:46:30
private Double weight;
// 数据库字段是:end_day 实体类属性是:endDay 注释是:保质期,天 创建时间是 :2022-07-30 21:12:09
private Integer endDay;
// 数据库字段是:commodity_address 实体类属性是:commodityAddress 注释是:产地 创建时间是 :2022-07-30 21:12:09
private String commodityAddress;
// 数据库字段是:express_type 实体类属性是:expressType 注释是:储运条件 创建时间是 :2022-07-30 21:12:09
private String expressType;
// 数据库字段是:yanshou 实体类属性是:yanshou 注释是:验收标准 创建时间是 :2022-07-30 21:12:09
private String yanshou;
// 数据库字段是:unit_id 实体类属性是:unitId 注释是:存储单位id 创建时间是 :2022-07-30 21:12:09
private String unitId;
// 数据库字段是:unit_name 实体类属性是:unitName 注释是:存储单位名称 创建时间是 :2022-07-30 21:12:09
private String unitName;
// 指导价来源
private String priceSource;
// 数据库字段是:province_id 实体类属性是:provinceId 注释是:省份id 创建时间是 :2022-05-10 18:17:00
// private String provinceId;
// 数据库字段是:province_name 实体类属性是:provinceName 注释是:省份名称 创建时间是 :2022-05-10 18:17:00
// private String provinceName;
// 数据库字段是:city_id 实体类属性是:cityId 注释是:城市id 创建时间是 :2022-05-10 18:17:00
// private String cityId;
// 数据库字段是:city_name 实体类属性是:cityName 注释是:城市名称 创建时间是 :2022-05-10 18:17:00
// private String cityName;
// 数据库字段是:region_id 实体类属性是:regionId 注释是:县区id 创建时间是 :2022-05-10 18:17:00
// private String regionId;
// 数据库字段是:region_name 实体类属性是:regionName 注释是:县区名称 创建时间是 :2022-05-10 18:17:00
// private String regionName;
private String approveUser2;
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
private Date approveDate2;
private String approveRemark2;
private int purchaseFreightType;
private Double purchaseFreight;
private String purchaseFreightModelId;
private String purchaseFreightModelName;
}
开始
全量导入
那么我们结合mybatis-plus进行数据全量导入
首先我们知道使用os的目的在于利用它强大的搜索引擎然后展示数据,所以从mybatis-plus拿数据时直接设置条件,先拿到数据量,然后使用线程池分批导入(一般数据量比较大,使用线程池会更快)
在写之前我们先创建个工具方法用于将实体类按照格式封装进Map中然后将其放入JSONObject中
(因为我是将所有字段都传入os应用结构中,所以我这里只能写个遍历+反射转换,正常只需要传入需要设置索引的字段)
工具方法1
注意这里传入的是我对应的实体类
/**
* 封装实体类到doc并转换成json格式
*
* @param entity 需要导入的实体类
* @return
* @throws IllegalAccessException
*/
public JSONObject getJson(CommodityInfo entity) throws IllegalAccessException {
//定义Map对象存储上传文档数据,此为文档
Map<String, Object> doc = Maps.newLinkedHashMap();
Field[] fields = entity.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
String key = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name);
//判断反射类型
Class<?> fieldType = field.getType();
//是Date类型且不为空
if (Date.class.equals(fieldType) && ObjectUtil.isNotEmpty(field.get(entity))) {
Date date = (Date) field.get(entity);
long time = date.getTime();
doc.put(key, time);
} else {
doc.put(key, field.get(entity));
}
}
//新增及更新都设为ADD,不支持update,当已存在相同主键值文档时做更新,否则新增,此处作为新增
JSONObject json = new JSONObject();
json.put(DocumentConstants.DOC_KEY_CMD, Command.ADD.toString());
json.put(DocumentConstants.DOC_KEY_FIELDS, doc);
return json;
}
然后将push到os的源码提取出来封装
工具方法2
/**
* 推送至open search
*正常推送没有返回值,所以判断下方便确定是否导入成功返回给前端
* @param docsJson json格式文档
* @param appName
* @param tableName
*/
public Boolean push(String docsJson, String appName, String tableName, DocumentClient documentClient) {
try {
//执行推送操作
OpenSearchResult osr = documentClient.push(docsJson, appName, tableName);
//判断数据是否推送成功,主要通过判断2处,第一处判断用户方推送是否成功,第二处是应用控制台中有无报错日志
//用户方推送成功后,也有可能在应用端执行失败,此错误会直接在应用控制台错误日志中生成,比如字段内容转换失败
if (osr.getResult().equalsIgnoreCase("true")) {
System.out.println("用户方推送无报错!\n以下为getTraceInfo推送请求Id:" + osr.getTraceInfo().getRequestId());
} else {
System.out.println("用户方推送报错!" + osr.getTraceInfo());
return false;
}
} catch (OpenSearchException e) {
e.printStackTrace();
} catch (OpenSearchClientException e) {
e.printStackTrace();
}
return true;
}
全量导入代码
private static String appName = "替换为opensearch应用名";
private static String tableName = "应用结构中的表明";
private static String accesskey = "替换accesskey";
private static String secret = "替换secret";
private static String host = "替换应用的API访问地址";
//每次导入的数量
private Integer size = 50;
/**
* 全量导入
*
* @return
* @throws IllegalAccessException
*/
@Override
public Boolean importAll() throws IllegalAccessException {
//创建并构造OpenSearch对象
OpenSearch openSearch = new OpenSearch(accesskey, secret, host);
//创建OpenSearchClient对象,并以OpenSearch对象作为构造参数
OpenSearchClient serviceClient = new OpenSearchClient(openSearch);
//定义DocumentClient对象添加json格式doc数据批量提交
DocumentClient documentClient = new DocumentClient(serviceClient);
//设置mybatisplus的筛选条件
QueryWrapper<CommodityInfo> queryWrapper = new QueryWrapper<>();
//逻辑删除,获取未被展示的数据
queryWrapper.eq("is_delete",Integer.valueOf(1));
//审批状态,获取审批通过的数据
queryWrapper.eq("approve_state",Integer.valueOf(1));
//获取满足条件的总数
Integer count = baseMapper.selectCount(queryWrapper);
//每次导入size条需要导入num次
int num = (count / size) + 1;
//线程计数器
CountDownLatch countDownLatch = new CountDownLatch(num);
//导入计数器(用于判断是否全部导入成功)
AtomicInteger counter = new AtomicInteger(0);
//计时器
long startTime = System.currentTimeMillis();
for (int i = 1; i < num + 1; i++) {
JSONArray docsJsonArr = new JSONArray();
//获取分页集合
Page<CommodityInfo> page = new Page<>(i, size);
Page<CommodityInfo> commodityInfoPage = baseMapper.selectPage(page, queryWrapper);
List<CommodityInfo> commodityInfos = commodityInfoPage.getRecords();
//遍历列表
for (CommodityInfo commodityInfo : commodityInfos) {
//使用工具方法1转换成json文档
com.aliyun.opensearch.sdk.dependencies.org.json.JSONObject json = getJson(commodityInfo);
docsJsonArr.put(json);
}
String docsJson = docsJsonArr.toString();
//导入时使用子线程进行导入
ProductTreadPool.poolExecutor.execute(() -> {
//推送第i页数据
Boolean flag = push(docsJson, appName, tableName, documentClient);
if (flag) {
//导入成功,计数器+1
counter.incrementAndGet();
}
//每推送完一次计数器-1,不需要判断是否导入成功,只用于阻塞线程
countDownLatch.countDown();
});
}
try {
//将线程阻塞到此处,直到程序计数器清零
countDownLatch.await();
long endTime = System.currentTimeMillis();
//判断计数器是否等于num确定是否全部导入成功
if (counter.get() < num) {
System.out.println("导入失败,运行时间:" + (endTime - startTime) + "ms");
return false;
}
System.out.println("导入完成,运行时间:" + (endTime - startTime) + "ms");
return true;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
上面这些都是写在service中的,返回一个boolean给前端。
导入成功去控制台看是否有错误信息和文档数(一般错误信息就是字段类型不匹配)
增量导入 就不做演示了,就是上传单个数据很简单
搜索
难点:返回值是json格式的字符串需要提取出来转换,然后放入返回用的列表
private static String appName = "替换为opensearch应用名";
private static String accesskey = "替换accesskey";
private static String secret = "替换secret";
private static String host = "替换应用的API访问地址";
/**
* 搜索
*
* @param searchValue 传入搜索的字符串
* @param page 分页条件
*/
public Page search(String searchValue, Page page) {
OpenSearch openSearch = new OpenSearch(accesskey, secret, host);
//创建OpenSearchClient对象,并以OpenSearch对象作为构造参数
OpenSearchClient serviceClient = new OpenSearchClient(openSearch);
//创建SearcherClient对象,并以OpenSearchClient对象作为构造参数
SearcherClient searcherClient = new SearcherClient(serviceClient);
//定义Config对象,用于设定config子句参数,用于分页或设置数据返回格式
Config config = new Config(Lists.newArrayList(appName));
int current = (int) page.getCurrent();
int pageSize = (int) page.getSize();
config.setStart((current - 1) * pageSize);
config.setHits((int) page.getSize());
//设置返回格式为json,目前只支持返回xml和json格式,暂不支持返回fulljson类型
config.setSearchFormat(SearchFormat.JSON);
// 设置搜索结果返回应用中哪些字段
config.setFetchFields(Lists.newArrayList(
"id",
"dealer_id",//商家id
"dealer_name",//商家名称
"name",//产品名称
"price",//价格
"adjunct",//产品图片
"title",//副标题
"hit",//浏览量
"sellcount"//销售量
));
// 创建参数对象
SearchParams searchParams = new SearchParams(config);
// 设置sort条件
Sort sorter = new Sort();
//设置升降序条件,按顺序升降序
sorter.addToSortFields(new SortField("sellcount", Order.DECREASE)); //销售量降序
sorter.addToSortFields(new SortField("attr_sellcount", Order.DECREASE));//附加销量
sorter.addToSortFields(new SortField("favourite_counts", Order.DECREASE));//收藏数
sorter.addToSortFields(new SortField("price", Order.DECREASE));//价格
sorter.addToSortFields(new SortField("comment_num", Order.DECREASE));//评价数
sorter.addToSortFields(new SortField("racking_date", Order.DECREASE));//上架时间
sorter.addToSortFields(new SortField("RANK", Order.INCREASE)); //相同则以RANK升序
//添加Sort对象参数
searchParams.setSort(sorter);
//放入查询值
searchParams.setQuery(searchValue);
// 执行返回查询结果
SearchResult searchResult;
try {
searchResult = searcherClient.execute(searchParams);
String result = searchResult.getResult();
System.out.println("数据:" + result);
//将数据按照结构取出
JSONObject obj = new JSONObject(result);
JSONObject res = obj.getJSONObject("result");//取出消息主体
int total = res.getInt("total");//满足查询条件的总数
JSONArray array = res.getJSONArray("items");//取出数据主题(是个集合)
List<CommodityInfoVO> dataList = new ArrayList<>();//用于接收返回对象
//opensearch的JSONObject不支持增强for循环所以实际开发选其他的,我懒得改了
for (int i = 0; i < array.length(); i++) {
JSONObject item = array.getJSONObject(i);
CommodityInfoVO commodityInfoVO = new CommodityInfoVO();
commodityInfoVO.setId(item.getString("id"));
commodityInfoVO.setDealerId(item.getString("dealer_id"));
commodityInfoVO.setDealerName(item.getString("dealer_name"));
commodityInfoVO.setName(item.getString("name"));
commodityInfoVO.setPrice(Double.parseDouble(item.getString("price")));
commodityInfoVO.setAdjunct(item.getString("adjunct"));
commodityInfoVO.setTitle(item.getString("title"));
commodityInfoVO.setHit(Integer.parseInt(item.getString("hit")));
commodityInfoVO.setSellcount(Integer.parseInt(item.getString("sellcount")));
dataList.add(commodityInfoVO);
}
//返回总页数和列表
page.setTotal(total);
page.setRecords(dataList);
for (CommodityInfoVO commodityInfoVO : dataList) {
System.out.println("展示对象名称" + commodityInfoVO.getName());
}
} catch (OpenSearchException e) {
e.printStackTrace();
} catch (OpenSearchClientException e) {
e.printStackTrace();
}
return page;
}
测试一下,参数值如下
控制台输出
一点坑
在控制台进行数据清空操作时,他是复制一个线下应用进行数据删除操作,创建完成后替换线上应用。
普通版本text类型不能超过32个,时间类型不能超过4个。
导入时对应的时间类型需要转换成Long类型再进行上传。
在进行应用结构设置的时候尽量一次确定好,因为os进行更改设置需要进行初始化,时间很长。