评论

通过正则进行更复杂的模糊查询

很多文章介绍模糊查询只能适应目标词完全包含搜索词的情况,特点是“绝对准确”,如果搜索词中包含一些杂质就无法得出结果了,因为这种方式没有任何容错可能。本文介绍了可容错的正则匹配方法...

  var search='用户输入内容'
  var search_len=0
  const patt = /([^\w\s`!@#$%^&*()_+~\-=[\]\;',.\/{}|:"<>?·!@#¥%……&*()——+【】、;’,。{}|:”《》?“‘]|\d+.?\d+ ?(ml|L|kg|g)?)/ig
  ①search='('+search.match(patt)
  ②.map(function (s{
    search_len++
    return s += '.*'
  ③}).join('|')+'){'+Math.max(1,search_len-2)+',}'
  return await db.collection('goods').where(
    db.command.or([{
        title: db.RegExp({
          regexp: search
        })
      },
      //{
        //其他需搜索字段: db.RegExp({
          //regexp: search,
        //})
      //},
    ])).get()


通过一个表达式,初步处理用户输入内容,结构为:/([^排除的内容]|若干数字和指定字母的连续组合)/全局忽略大小写

(1)大部分符号和空格是无用的,直接过滤掉,此举可以提升用户体验,即当用户输入了一些杂乱内容时,可以有效地将其去除。但是并没有什么卵用,因为正常使用小程序的用户不会输入莫名其妙的字符。

(2)统一排除所有字母和数字,然后指定一些常见的组合作为保留,这些需保留的组合可以根据需要更改。本例为饮料商品数据库,常见的情况是(规格+单位)的形式,比如1.5L,300ml,500g,2kg等,因此这几种情况不能拆分为单字。

(3)过滤完成后,string.match()方法会将余下的内容按字分割成数组(即若干个待查询的元素)并返回(类似于string.split('')的效果),并尽量保留连续的内容(比如500ml应是一整体),将内容输出进入下一阶段处理。


将余下的每个内容进行处理,并记录元素总数

(1)每个元素后接.* ,表示此文字后接零或多个任意字符,以提高容错率。具体原因如下:

最终应用到集合进行搜索的表达式为 (元素1.*|元素2.*|元素3.*){n,},其含义为,搜索词中需至少连续出现n个括号中可选的文字。比如,我们要查找【康师傅冰红茶500ml大瓶】这个标题的商品,如果每个元素不做处理进行匹配,可能出现以下结果:
(搜索词中没有“冰”字,所以无法满足连续5个字的条件)
稍加修改:
(这样,由于“傅”字后可接任意多个未指定的文字,所以虽然没有搜索“冰红”两个字,它们也可以被匹配到了)


(2)循环记录过滤后元素总数

做一些最后的处理

(1)通过Array.join()等方法处理结果,使之最终得到(元素1.*|元素2.*|元素3.*){n,}的格式

(2){n,}表达式表示规则需要出现至少n次,为了提高一些容错率,让这个n比元素总数小2,且至少为1。可以根据实际情况修改其值,或动态调整。例:

Math.max(1, Math.ceil(元素数*0.6)),这个0.6是控制差值的变量,按需要更改。



整个流程大概可以更加优化,无奈技术有限,目前已经是能力内最佳,如果有更好的方法或细节优化请提出。

以下是内容为商品的集合的一些测试结果。通常匹配表达式需要根据实际的集合内容进行修改,照搬不太适合。本集合是千个记录的小集合,耗时仅供参考(本地调试)....:



参考文档:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/match

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

最后一次编辑于  2020-11-12  
点赞 2
收藏
评论
登录 后发表内容