山坡网的用户抱怨“为什么搜索‘二鬼子李富贵’找不到‘二鬼子汉奸李富贵’?我用百度搜都能找到。”

当时我就滴汗了,用户说的有道理,应该要能搜索到。

之前的方案很简单,用户输入的字串会在数据库里做正则表达式匹配,以便用“二鬼子”能搜到“二鬼子汉奸李富贵”。事实证明,我想当然了,即便是这么简单的一个书名搜索,也不能马虎。

那就来分析一下怎么做吧,即便不是专业做搜索的,思路上也可以先YY一下。按照本能,先把问题大而化小。

1. 先把搜索字符串进行中文分词

2. 用词组在数据库里做 or 包含匹配。

3. 搜索出来的结果按与搜索条件相关度排序。

看起来也不难(玩笑话,每一条水都很深),一条一条来解决。

1. 中文分词。

我找了一下,免费的不多,选择了盘古分词的Go语言Port。从Github看到,代码是四个月以前的了,字典文件有些老。所以我在盘古网站上下载了最新的字典,测试了一下Go的代码,运行结果良好。

这个Port可能蛮久没有更新过,代码的结构不能直接go get,需要自己下载src里面的segment文件夹出来使用。

我把新的字典文件全都放到了revel app的conf文件夹里,如下图所示。

然后在Controller.Init方法里加上初始化代码。

revel.OnAppStart(func() {
   segHandler = segment.NewSegment()
   err := segHandler.Init(path.Join(revel.ConfPaths[0], "dicts"))
   if err != nil {
     glog.Fatalln("Failed to init segment handler", err)
   }

然后在需要的地方就可以开始用它分词了。

//对搜索的字符串进行分词
searchKey := "(" + key + "|"

segs := segHandler.DoSegment(key)
for cur := segs.Front(); cur != nil; cur = cur.Next() {
  word := cur.Value.(*dict.WordInfo)
  if word.Word != "的" {
    searchKey += word.Word + "|"
  }
}

searchKey = strings.TrimRight(searchKey, "|") + ")"

searchResults, pageSum, err := d.findBookBy(M{"$or": []M{
  M{"title": M{"$regex": searchKey}},
  M{"author": M{"$regex": key}},
  M{"category": M{"$regex": key}}}}, "-score", pageNum, numPerPage)
if err != nil {
  return nil, pageSum, err
}

思路是把搜索条件分成词组,再组合成正则表达式,比如“二鬼子李富贵”变成“(二|鬼子|李富贵)”,然后使用mongodb的正则查询。

这里我把“的”字去掉了,因为中文里面“的”字用的太多了,基本没有查询价值。

2. 搜索出来的结果按与搜索条件相关度排序。

搜索引擎里,这部分是技术含量最大的。我这边只是牛刀小试,所以方案简单很多,把搜索出来的书籍标题与搜索条件比对相似度。

正好,字符串比对相似度的库我之前Port过一个,叫做simhash(当时为什么port我都忘了,哈,工具箱里东西多还是有好处的!)。算法具体就不多说了,免得跑题。看用法吧。

needle := "Reading bytes into structs using reflection"
hayStack := "Golang - mapping an variable length array to a struct"likeness := GetLikenessValue(needle, hayStack)
fmt.Println("Likeness:", likeness)
就一个函数,输入两个字符串,输出一个从0到1的浮点数,代表相似百分比。
为了方便计算,我在SearchResult结构中加入了一个新的字段,OriginalQueryString,存储原始搜索条件,之后实现一下Sort接口。

type SearchResult struct {
  Id                bson.ObjectId "_id"
  Title             string
  OriginQueryString string //原始的搜索条件,用于排序
}

type SearchResults []SearchResult

func (srs SearchResults) Len() int {
  return len(srs)
}

func (srs SearchResults) Less(i, j int) bool {
  likenessI := simhash.GetLikenessValue(srs[i].Title, srs[i].OriginQueryString)
  likenessJ := simhash.GetLikenessValue(srs[j].Title, srs[j].OriginQueryString)

return likenessI < likenessJ
}

func (srs SearchResults) Swap(i, j int) {
  srs[i], srs[j] = srs[j], srs[i]
}

就可以在搜索出来之后按照相关性排序了。

//为searchResult的OriginQueryString赋值,以便按照搜索相关性排序
for i, _ := range searchResults {
  searchResults[i].OriginQueryString = key
}

sort.Sort(sort.Reverse(SearchResults(searchResults)))

我的实现到这里就完成了。

但其实有一部分很重要的东西我取巧了。由于使用模糊搜索,结果集的大小是无法预料的,全部取的话随时可能把内存用完。分批的话怎么保证相关性排序的准确性呢?好问题,这里是非常关键又很难做的部分,我取巧的方式是把书籍按评分排序,然后取前20个出来,仅仅在这20本书中做相似度排序。这并不是完美的方案,仅仅只是够用。

后期如果有时间,可以用mongodb的游标做一个即省内存又靠谱的实现。

转载于:https://www.cnblogs.com/AllenDang/p/3335396.html

Go语言实战 - 我需要站内搜索相关推荐

  1. 站内搜索应用的方案设计的分析和总结

    http://www.poluoluo.com/jzxy/200907/63759.html 我为银杏泰克站内搜索服务商做产品顾问期间,经手了十几个站点的站内搜索应用的方案设计,略作一些分析和总结. ...

  2. ajax+lucene pdf,基于Ajax/Lucene的站内搜索技术研究

    摘要: 站内搜索引擎是找出网站重要信息的必要工具,高效的站内搜索将有助于提升网站的价值,发挥网站应有的作用.虽然现在一些网络巨头已开始研究并应用这类工具,但整个互联网行业中,受制于技术的门槛,真正的站 ...

  3. -gMIS持续优化更新, +InSiteSearch站内搜索

    2019独角兽企业重金招聘Python工程师标准>>> -gMIS 部署和应用的场景越来越多,最近在考虑为所有gMIS承载管理的数据库系统增加一个站内搜索功能, +InSiteSea ...

  4. 一个ASP站内搜索的实例源代码

    假如你拥有一个庞大的网站,比如(www.ehpos.com),内容又多,那么来访者往往很难找到自己所需要的东东,这时候你就需要一个站内搜索来帮助来访者更快的找到索要的资料了!现在你就可以用asp轻易的 ...

  5. 一步步开发自己的博客 .NET版(5、Lucenne.Net 和 必应站内搜索)

    前言 这次开发的博客主要功能或特点:     第一:可以兼容各终端,特别是手机端.     第二:到时会用到大量html5,炫啊.     第三:导入博客园的精华文章,并做分类.(不要封我)     ...

  6. Lucene.net站内搜索—5、搜索引擎第一版实现

    目录 Lucene.net站内搜索-1.SEO优化 Lucene.net站内搜索-2.Lucene.Net简介和分词 Lucene.net站内搜索-3.最简单搜索引擎代码 Lucene.net站内搜索 ...

  7. 使用Google Custom Search打造站内搜索

    链接: Google AJAX 搜索 API 参考: http://www.google.com/cse/docs/cref.html?hl=zh-CN http://www.google.com/c ...

  8. 站内搜索--3--之Lucene.Net使用

    上一篇 站内搜索---2----之Log4Net使用 Lucene.Net是由Java版本的Lucene移植过来的,所有的类.方法都几乎和Lucene一模一样. Lucene.Net只是一个全文检索开 ...

  9. 站内搜索——Lucene +盘古分词

    为了方便的学习站内搜索,下面我来演示一个MVC项目. 1.首先在项目中[添加引入]三个程序集和[Dict]文件夹,并新建一个[分词内容存放目录] Lucene.Net.dll.PanGu.dll.Pa ...

最新文章

  1. jquery笔记___返回值问题
  2. ActiveReports 报表应用教程 (1)-Hello ActiveReports
  3. 微分先行PID控制算法用C语言实现!
  4. python实时监控_使用Python监控Linux系统
  5. linux内核启动后门,Linux下编写隐蔽的自启动回连后门
  6. Deepin v20系统关机或重启的时候提示unattended upgrades shutdown的解决办法
  7. 20165203 《网络对抗技术》week1 Kali的安装与配置
  8. 压缩包安装mySQL 与 Qt中使用mySQL
  9. java tostring方法_Java虚拟机如执行方法调用的(二)?
  10. Topcoder SRM 655 DIV1 250 CountryGroupHard
  11. python xlrd模块_Python中xlrd模块解析
  12. 敢不敢做一个复杂的人
  13. 乐行学院Redis5学习教程 第一章redis5的安装
  14. python str和repr的区别_python的str()和repr()的区别
  15. Mac book笔记本输入法错乱
  16. C++学习 11.18.19
  17. error怎么开机 fan_电脑开机提示CPU Fan Error是什么意思?如何解决?
  18. openwrt 下编译ipk
  19. 我于今夜猝死,年仅22岁;程序猿们请代替家人照顾好自己
  20. 练手写的python 淘宝购物车秒杀器 源码,成品链接,效果图

热门文章

  1. 具有完全权限的管理员”的功能介绍
  2. python点云拼接
  3. 关于qmail的笔记
  4. Linux——文件或目录的权限管理、网络管理、进程管理、服务管理的相关命令
  5. php curl调用第三方接口小样
  6. KaTex 编写示例 公式 数学公式
  7. FM(Factorization Machine)因式分解机 与 TensorFlow实现 详解
  8. iPhone14到手先做啥,捷客特教你避雷手机配件“坑”
  9. 学习python第一天
  10. 使用javac编译单个Java文件