×

wwsearch企业微信检索引擎

消耗积分:2 | 格式:zip | 大小:31.72 MB | 2022-06-21

张杰

分享资料个

授权协议 BSD
开发语言 C/C++
操作系统 跨平台
软件类型 开源软件

软件简介

wwsearch简介

wwsearch是企业微信后台自研的全文检索引擎。它为海量用户下的全文快速检索而设计,底层支持可插拔的lsm tree存储引擎。目前覆盖企业微信所有在线检索场景:企业员工通讯录、审批、日报、周报、汇报、企业素材检索,也包括企业邮箱的全文邮件检索。 最大业务场景有300+亿条记录,索引词项万亿+,存储容量几十TB,支撑实时在线用户检索。

功能介绍

  1. 实时数据修改:数据写入即实时可查。对外提供插入、更新、删除、覆盖写等接口,可适应更新频繁场景,也适应于少改或不改场景。
  2. 支持灵活Query:支持词的等值、前缀、模糊匹配。多个Query通过And 、Or进行组合,满足不同场景的检索需求。Query还可以按指定field进行检索。
  3. 后置过滤:支持对检索索引后的结果进行二次过滤,支持等值、数值范围、数组元素查找、字符串模糊等过滤特性。适用于如无法建立高区分度索引的字段过滤、带有业务特定场景的过滤。
  4. 灵活排序:支持按多个field的属性值组合排序,类似order by语义。
  5. 检索功能可扩展:场景需要时,可以扩展各类聚合函数(sum/avg…),也可以支持场景文本打分。

实现剖析

接口说明

具体使用例子参考example/example.cpp。 这里简单对接口字段进行说明。

Index

主要涉及6个接口,分别是:

  1. AddDocuments:仅当文档id不存在时添加;
  2. UpdateDocuments:仅当文档id存在时更新;更新时会保留旧文档存在的未更新field内容;
  3. AddOrUpdateDocuments:若文档id不存在则添加,若存在则更新;
  4. ReplaceDocuments:仅当文档id存在时替换;
  5. DeleteDocuments:仅当文档id存在时删除;
  6. AddDocumentsWithoutRead:文档id不存在则添加,存在则覆盖;

下面以用户常用的AddOrUpdateDocuments为例说明用法。

// wwsearch/index_writer.h
bool AddOrUpdateDocuments(const TableID &table,
                          std::vector &documents,
                          std::string *store_buffer = nullptr,
                          SearchTracer *tracer = nullptr);

// wwsearch/document.h
class DocumentUpdater {
    ...
  Document new_document_;
    ...
};

class Document {
    ... 
  std::vector fields_;
  DocumentID document_id_;
    ...
};

// wwsearch/index_field.h
class IndexField {
    ...
  FieldID field_id_;
  IndexFieldFlag field_flag_;
  kIndexFieldType field_type_;
  uint64_t numeric_value_;
  std::string string_value_;
    ...
}

用户使用涉及主要字段说明:

  1. TableID : bussiness_type(uint8_t) + partition_set(uint64_t)组成,分表;
  2. DocumentID : uint64_t,文档id,文档的唯一标识;
  3. IndexField : 文档列的信息,包括列属性和值。
    • field_id_,field的ID
    • field_flag_,索引标记
      • kTokenizeFieldFlag,是否分词
      • kStoreFieldFlag,是否存储原始数据
      • kDocValueFieldFlag,是否存储列值属性
      • kSuffixBuildFlag,是否后缀展开
      • kInvertIndexFieldFlag,是否建立倒排索引
    • field_type_,值类型
      • kUint32IndexField
      • kUint64IndexField
      • kStringIndexField
    • numeric_value_/ string_value_,字段原始值

Query

主要涉及接口:

// wwsearch/searcher.h
SearchStatus DoQuery(const TableID &table, Query &query, size_t top,
                     std::vector *filter,
                     std::vector *sorter,
                     std::list &docs,
                     uint32_t min_match_filter_num = 0)

用户使用涉及主要字段说明:

  1. TableID : bussiness_type(uint8_t) + partition_set(uint64_t)组成,分表;
  2. Query :构建查询的字段信息,可支持AndQuery和OrQuery的嵌套格式,支持PrefixQuery前缀查询;参考
  3. Filter :过滤器,支持数字/字符串/数组/多字符串条件过滤;
  4. SortCondition :对查询得到的文档输出做排序,支持指定field做排序,目前只支持指定数字的field排序;
  5. min_match_filter_num设置最小匹配的filter数,只要匹配的filter大于此数的文档才能输出。

构建方法

依赖模块说明

依赖模块为:

# wwsearch/deps/
protobuf-2.4.1
snappy-1.0.4
rocksdb-v5.16.6
tokenizer-mmseg

仓库中已提前编译生成依赖库,您也可以根据编译环境重新编译依赖的第三方模块。

构建方法:

需要使用支持c++ 11的编译环境构建

mkdir build
cd build
cmake  ..
make -j32
cp ../deps/tokenizer/etc/wwsearch_* .

编译完成将可以看到:

  1. wwsearch_ut : 单元测试;
  2. wwsearch_example : 简单示例,包括index和query。

接下来可以愉快使用啦,enjoy it!

贡献代码

提交pull request贡献代码前,请参考 Contributing.md 。 wwsearch基于c++11开发,遵循Google C++ Style Guide代码风格,提交代码前需要使用附带的.clang-format格式化代码;

反馈问题

使用中遇到问题,可以有以下途径反馈:

  1. 直接在[issues]提问;

开源协议

wwsearch 开源协议为 Apache License Version 2.0 ,详细的 License 请参考 LICENSE.TXT

 

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'wwsearch企业微信检索引擎',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);