文章目录
什么是OpenSearch
开放搜索(OpenSearch)是一款结构化数据搜索托管服务,为移动应用开发者和网站站长提供简单、高效、稳定、低成本和可扩展的搜索解决方案。
OpenSearch基于阿里巴巴自主研发的大规模分布式搜索引擎平台,该平台承载了阿里巴巴全部主要搜索业务,包括淘宝、天猫、一淘、1688、ICBU、神马搜索等业务。OpenSearch以平台服务化的形式,将专业搜索技术简单化、低门槛化和低成本化,让搜索引擎技术不再成为客户的业务瓶颈,以低成本实现产品搜索功能并快速迭代。
使用OpenSearch搭建搜索服务,您只需:
- 创建搜索应用
- 编辑您的应用结构
- 上传数据
- 从您的网站或应用程序提交搜索请求
简单、高效、低成本和可扩展。但要是后期用好,还需慢慢调试。
Open Search 和 Elastic Search对比
open search
优点:
-
支持用户上传数据或同步云数据,实时性有保障。(可以节省1-2台服务器)
-
应用结构、排序相关性自由定制,搜索服务更个性化。可以自定义粗排精排算法,但是LCU 和查询命中的文档、召回的文档、formula的复杂度、查询的复杂度等都有关系。
-
基于阿里巴巴在搜索领域的积累,提供查询分析功能,对用户查询词进行纠错、词权重分析、停用词过滤,让搜索服务更智能。可以自定义分词器,下拉提示等。可以很方便的设置召回结果的粗排精排,并且内置了一些对应的函数。
下拉提示例子,连衣裙 这个query,可以通过如下方式查询得到:
中文前缀:连,连衣;
全拼前缀:l, li, lian, lianyi, lianyiqun, …
简拼前缀:l, ly, lyq;
汉字加拼音: 连yi, 连衣qun;
并且下拉提示可以进行人工干预如推荐名单和黑名单。 -
可视化的界面、丰富的模板,不用精通代码也能快速创建自己的搜索应用。
-
一张OpenSearch表可以支持多个rds及TDDL(mysql)来源表(如分库分表的场景),并且还有一些字段处理插件,这个挺适合咱们的博客场景
-
提供A/Btest功能,方便进行优化(快速迭代算法)
缺点 -
open search和es的命中文档数差了一个数量级 通用分词 和es中ik_smark对比
-
子账号没有权限
-
分词不能使用or进行query
-
目前主辅表,仅支持 N:1 或 1:1 的关系,不支持 1:N(即多表数据关联关系中,多的一方只能是主表,且主表只能有1个)。可以进行表拆分或者合并进行应对。但是改动可能较大。
-
主辅表需通过应用表外键与附表主键进行数据关联,且表外键只能关联辅表主键。
-
最多只支持2层关联。
多表数据关联支持
表a->表b,表b->表c
表a->表d
不支持超过2层多表数据关联
表a->表b,表b->表c,表c->表d
不支持环状多表数据关联
表a->表b,表b->表a
ElasticSearch
1、在查询方面更加灵活
2、需要自己实现etl工作
对比
数据准备
问答数据:文档数257,250,存储容量为815M。
OpenSearch 和 es 查询语句限制
- open search直接使用默认的粗排精排算法
- es和open search 都查询title 和body
LCU 使用情况
目前ask的数据测试query,并发25左右,单次查询均值在5ms以内,平均值LCU在0.3左右。
人工评估结果
es查询评估平均得分3.50,opensearch查询评估平均得分2.76
Open Search 的创建
一共有三种创建方式:
- 通过模板创建应用结构
- 通过上传文档创建应用结构
- 通过数据源创建应用结构
步骤:
1).添加表
2).手动修改创建的应用结构
3). 定义索引结构
1⃣️需放到 query子句中的字段,必须创建为索引(浮点型不支持创建为索引),分词方式详情请参见字段和分词类型。
需放到 filter子句,sort子句,及函数中涉及字段有明确标识,需设置为属性的字段必须创建为属性。
分词字段类型无法配置为属性,例如 TEXT,SHORT_TEXT等都不支持,只支持数值字段类型及不分词字段类型配置为属性,例如 int,int_array,float,float_array,double,double_array,literal,literal_array 等字段类型。
同步数据源
创建成功
数据上传
上面我们是以RDS为例,激活应用后会默认开始导入全量数据,可以在应用管理 – 基本配置 – 索引重建中看到具体进度。
我们对Open Search的使用
API 分类
搜索方式
可以通过http(get、post)形式或Java、PHP sdk方式进行搜索与上传。
目前在用的产品
APP中@ 操作
具体的Java 代码
- 添加maven
<dependency>
<groupId>com.aliyun.opensearch</groupId>
<artifactId>aliyun-sdk-opensearch</artifactId>
<version>3.2.0</version>
</dependency>
- 部分代码
private static SearcherClient searcherClient=null;
static {
//创建并构造OpenSearch对象
OpenSearch openSearch = new OpenSearch(Constants.ACCESSKEY, Constants.SECRET, Constants.HOST);
//创建OpenSearchClient对象,并以OpenSearch对象作为构造参数
OpenSearchClient serviceClient = new OpenSearchClient(openSearch);
//创建SearcherClient对象,并以OpenSearchClient对象作为构造参数
searcherClient = new SearcherClient(serviceClient);
}
/**
* 配置信息
* @return
*/
public Config getConfig(List<String> appNames,int start,int hit,List<String> fields){
//定义Config对象,用于设定config子句参数,指定应用名,分页,数据返回格式等等
Config config = new Config(appNames);
config.setStart(start);
config.setHits(hit);
//设置返回格式为fulljson格式
config.setSearchFormat(SearchFormat.JSON);
// 设置搜索结果返回应用中哪些字段
config.setFetchFields(fields);
return config;
}
/**
* 粗排精排
* @return
*/
public Rank getRank(int size){
if(size>500){
size=500;
}
// 设置精排文档
Rank rank=new Rank();
rank.setReRankSize(size);
return rank;
}
/**
* @param jsonParam 传过来的参数
* {
* "filter":"username=\"Joanna_or_zhouzhou\"",//按照某个字段过滤
* "reRankSize":"500",//参与精排的条数
* "fetchFields":"id,title,nickname",//获取的域/字段
* "pageSize":"20",//每页多少条
* "index":"nickname",//索引
* "page":"0",//第几页 注:0为第一页
* "sort":"-id", //-按照某个字段降序 + 按照某个字段增序 最好不要使用sort 耗资源
* "queryWord":"巴掌大的脚印" , //搜索词
* "summary":[
* {
* "snippet":"1",//片段数量
* "field":"nickname",//指定的生效的字段。此字段必需为可分词的text类型的字段。
* "len":"50",//片段长度
* "ellipsis":"...",//片段链接符
* "element":"em"//飘红标签
* }
* ]
* }
* @return
*/
public ResultVo dataDispose(com.alibaba.fastjson.JSONObject jsonParam){
ResultVo resultVo=new ResultVo();
com.alibaba.fastjson.JSONArray jsonArray=new com.alibaba.fastjson.JSONArray();
List<String> appNames=new ArrayList<>();
String appName=jsonParam.getString("appName");
if(StringUtils.isEmpty(appName)){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0001);
resultVo.setMessage("app应用名为空");
resultVo.setData(jsonArray);
return resultVo;
}else{
appNames.add(appName);
}
if(jsonParam==null || jsonParam.size()<=0){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0002);
resultVo.setMessage("app参数为空");
resultVo.setData(jsonArray);
return resultVo;
}
//搜索词
String queryWord=jsonParam.getString("queryWord");
if(queryWord==null || queryWord.isEmpty()){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0003);
resultVo.setMessage("queryWord为必填项不能为空");
resultVo.setData(jsonArray);
return resultVo;
}else{
queryWord=queryWord.replaceAll("\\\\"," ").replaceAll("\'"," ");
}
int start=0;
int hit=20;
//第几页 注:0为第一页 config=start:20, hit:20, format:xml
String page=jsonParam.getString("page");
//每页多少条
String pageSize=jsonParam.getString("pageSize");
//opensearch start+hit<=5000,超过5000会直接报错无结果。
if(page!=null && NumberUtils.isDigits(page) && Integer.valueOf(page)>=0){
if(pageSize!=null && NumberUtils.isDigits(pageSize) && Integer.valueOf(pageSize)>0){
hit=Integer.valueOf(pageSize);
}
start=Integer.valueOf(page)*hit;
if(start+hit>Constants.MAXSEARCHRESULT){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0004);
resultVo.setMessage("查询总数不能超过5000");
resultVo.setData(jsonArray);
return resultVo;
}
}
List<String> fetchFields=new ArrayList<>();
//获取的域/字段
String fetchFieldStr=jsonParam.getString("fetchFields");
if(fetchFieldStr==null){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0005);
resultVo.setMessage("获取结果域为必填项");
resultVo.setData(jsonArray);
return resultVo;
}else{
fetchFields.addAll(Arrays.asList(fetchFieldStr.split(",")));
}
Config config = getConfig(appNames,start,hit,fetchFields);
SearchParams searchParams = new SearchParams(config);
//参与精排的条数
String reRankSize=jsonParam.getString("reRankSize");
if(reRankSize!=null && NumberUtils.isDigits(reRankSize)){
searchParams.setRank(getRank(Integer.valueOf(reRankSize)));
}else{
searchParams.setRank(getRank(200));
}
//索引
String index=jsonParam.getString("index");
//-按照某个字段降序 + 按照某个字段增序 最好不要使用sort 耗资源
String queryStr="";
if(index==null){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0007);
resultVo.setMessage("索引不能为空");
resultVo.setData(jsonArray);
return resultVo;
}else{
queryStr=index+":"+"'"+queryWord+"'";
}
//按照某个字段过滤
String filter=jsonParam.getString("filter");
if(filter!=null){
if(filter.indexOf("=")>-1){
int eindex = filter.indexOf("=");
String pre = filter.substring(0, eindex);
String next = filter.substring(eindex + 1);
queryStr+=" AND "+pre+":"+next;
}
// //这里针对CSDN_User 做处理 提高查询效率
// if("CSDN_User".equals(appName)){
//
// }else{
// searchParams.setFilter(filter);
// }
}
String sort=jsonParam.getString("sort");
if(sort!=null){
queryStr=queryStr+"&&sort="+sort;
}
log.info("querystr:"+queryStr);
searchParams.setQuery(queryStr);
SearchParamsBuilder paramsBuilder = SearchParamsBuilder.create(searchParams);
try {
JSONArray summary = jsonParam.getJSONArray("summary");
if(summary!=null && summary.size()>0){
for (int i=0;i<summary.size();i++){
com.alibaba.fastjson.JSONObject jsonObject = summary.getJSONObject(i);
String field = jsonObject.getString("field");
int snippet=1;
String snippetStr = jsonObject.getString("snippet");
if(snippetStr!=null && NumberUtils.isDigits(snippetStr)){
snippet=Integer.valueOf(snippetStr);
}
int len=50;
String lenStr = jsonObject.getString("len");
if(lenStr!=null && NumberUtils.isDigits(lenStr)){
len=Integer.valueOf(lenStr);
}
String ellipsis = jsonObject.getString("ellipsis");
String element = jsonObject.getString("element");
paramsBuilder.addSummary(field,len,element,ellipsis,snippet);
}
}
}catch (Exception e){
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0006);
resultVo.setMessage(e.getMessage());
resultVo.setData(jsonArray);
return resultVo;
}
try {
SearchResult searchResult = searcherClient.execute(paramsBuilder);
String result = searchResult.getResult();
JSONObject obj = new JSONObject(result);
Object status = obj.get("status");
if(Constants.OK.equals(status+"")){
JSONObject map = (JSONObject)obj.get("result");
Object items = map.get("items");
resultVo.setData(JSON.parseArray(items.toString()));
resultVo.setCode("200");
resultVo.setMessage("查询成功");
com.aliyun.opensearch.sdk.dependencies.org.json.JSONArray compute_cost = (com.aliyun.opensearch.sdk.dependencies.org.json.JSONArray)map.get("compute_cost");
if(compute_cost!=null && compute_cost.length()>0){
JSONObject jsonObject = compute_cost.getJSONObject(0);
Object lcu = jsonObject.get("value");
log.info("查询成功,消耗LCU:"+lcu);
}
}else{
resultVo.setData(jsonArray);
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE00010);
Object errors = obj.get("errors");
if(errors!=null){
resultVo.setMessage(errors+"");
}else{
resultVo.setMessage("查询失败");
}
log.error("查询失败:"+errors);
return resultVo;
}
} catch (OpenSearchException e) {
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0008);
resultVo.setMessage(e.getMessage());
resultVo.setData(jsonArray);
log.error(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0008+" : "+e.getMessage());
return resultVo;
} catch (OpenSearchClientException e) {
resultVo.setCode(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0009);
resultVo.setMessage(e.getMessage());
resultVo.setData(jsonArray);
log.error(Constants.ERRORHTTPCODE+Constants.PRODUCTCODE+Constants.CODE0009+" : "+e.getMessage());
return resultVo;
}
return resultVo;
}
public ResultVo searchData(com.alibaba.fastjson.JSONObject jsonParam) {
ResultVo resultVo = dataDispose(jsonParam);
return resultVo;
}
遇到的问题
- 表关联问题
- 性能问题 (filter 字段建立索引)
- 子账号没有权限导入数据
- 目前redis中的数据不能进行关联同步
- query分词之后只能and进行搜索
引用:官方文档
备注:
- LCU是用来衡量搜索应用计算能力的单位,一个LCU代表搜索集群中10millicores的计算能力,计算资源估算方法:LCU个数=QPS*compute_cost,millicores是CPU资源的单位,即一个核的1/1000, compute_cost是单次查询计算消耗的LCU
今天的文章openscoring_openmove使用说明分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/74834.html