openscoring_openmove使用说明

openscoring_openmove使用说明文章目录什么是OpenSearchOpenSearch和ElasticSearch对比opensearchElasticSearch对比数据准备OpenSearch的创建我们对OpenSearc

什么是OpenSearch

开放搜索(OpenSearch)是一款结构化数据搜索托管服务,为移动应用开发者和网站站长提供简单、高效、稳定、低成本和可扩展的搜索解决方案。

OpenSearch基于阿里巴巴自主研发的大规模分布式搜索引擎平台,该平台承载了阿里巴巴全部主要搜索业务,包括淘宝、天猫、一淘、1688、ICBU、神马搜索等业务。OpenSearch以平台服务化的形式,将专业搜索技术简单化、低门槛化和低成本化,让搜索引擎技术不再成为客户的业务瓶颈,以低成本实现产品搜索功能并快速迭代。
使用OpenSearch搭建搜索服务,您只需:

  1. 创建搜索应用
  2. 编辑您的应用结构
  3. 上传数据
  4. 从您的网站或应用程序提交搜索请求
    简单、高效、低成本和可扩展。但要是后期用好,还需慢慢调试。

Open Search 和 Elastic Search对比

open search

优点:

  1. 支持用户上传数据或同步云数据,实时性有保障。(可以节省1-2台服务器)

  2. 应用结构、排序相关性自由定制,搜索服务更个性化。可以自定义粗排精排算法,但是LCU 和查询命中的文档、召回的文档、formula的复杂度、查询的复杂度等都有关系。

  3. 基于阿里巴巴在搜索领域的积累,提供查询分析功能,对用户查询词进行纠错、词权重分析、停用词过滤,让搜索服务更智能。可以自定义分词器,下拉提示等。可以很方便的设置召回结果的粗排精排,并且内置了一些对应的函数。

    下拉提示例子,连衣裙 这个query,可以通过如下方式查询得到:
    中文前缀:连,连衣;
    全拼前缀:l, li, lian, lianyi, lianyiqun, …
    简拼前缀:l, ly, lyq;
    汉字加拼音: 连yi, 连衣qun;
    并且下拉提示可以进行人工干预如推荐名单和黑名单。

  4. 可视化的界面、丰富的模板,不用精通代码也能快速创建自己的搜索应用。

  5. 一张OpenSearch表可以支持多个rds及TDDL(mysql)来源表(如分库分表的场景),并且还有一些字段处理插件,这个挺适合咱们的博客场景
    在这里插入图片描述

  6. 提供A/Btest功能,方便进行优化(快速迭代算法)
    在这里插入图片描述
    缺点

  7. open search和es的命中文档数差了一个数量级 通用分词 和es中ik_smark对比

  8. 子账号没有权限

  9. 分词不能使用or进行query

  10. 目前主辅表,仅支持 N:1 或 1:1 的关系,不支持 1:N(即多表数据关联关系中,多的一方只能是主表,且主表只能有1个)。可以进行表拆分或者合并进行应对。但是改动可能较大。

  11. 主辅表需通过应用表外键与附表主键进行数据关联,且表外键只能关联辅表主键。

  12. 最多只支持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 查询语句限制

  1. open search直接使用默认的粗排精排算法
    在这里插入图片描述
  2. 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).添加表
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    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 代码

  1. 添加maven
<dependency>
  <groupId>com.aliyun.opensearch</groupId>
  <artifactId>aliyun-sdk-opensearch</artifactId>
  <version>3.2.0</version>
</dependency>
  1. 部分代码
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;
    }

遇到的问题

  1. 表关联问题
  2. 性能问题 (filter 字段建立索引)
  3. 子账号没有权限导入数据
  4. 目前redis中的数据不能进行关联同步
  5. query分词之后只能and进行搜索

引用:官方文档

备注:

  1. LCU是用来衡量搜索应用计算能力的单位,一个LCU代表搜索集群中10millicores的计算能力,计算资源估算方法:LCU个数=QPS*compute_cost,millicores是CPU资源的单位,即一个核的1/1000, compute_cost是单次查询计算消耗的LCU

今天的文章openscoring_openmove使用说明分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/74834.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注