Skip to content

音乐搜索的极致(续)

12530 PC客户端音乐搜索项目一期的总结和思考。

SlideShare 上的 pdf:

PPT 的文字内容:

  1. 音乐搜索的极致 唐福林 tangfulin@gmail.com http://blog.fulin.org
  2. 目录  项目简介  需求描述  搜索实现  查询示例  持续改进
  3. 项目简介 (1/3)  中国移动  12530  咪咕  Miniportal  搜索  Out source : edadao
  4. 项目简介 (2/3)  时间: 2009 年 9 月 12 日到 10 月 22 日  地点:成都,郫县,犀浦,移动音乐基 地  参与人员:  需求提供: wangquanli@12530,   zhengchangsong@12530  开发人员: mike,tangfulin,xww,wanghui  特别贡献: dave
  5. 项目简介 (3/3)  部署情况:  位置:移动音乐基地,西区枢纽机房  机器:  建索引: 227.98  搜索: 227.221  Dual Core AMD Opteron(tm) Processor 8218 2.6G * 8  8G mem  Red Hat Enterprise Linux Server release 5.3 (Tikanga)  Linux 2.6.18­128.el5PAE #1 SMP  i686 athlon  GNU/Linux  索引大小: 3 个索引目录共 1.2G  流量:  机器负载情况:
  6. 需求描述  搜索字段:歌手,歌曲,专辑,歌词  搜索方式:  精确匹配  前缀匹配  分词匹配  模糊匹配  拼音全量匹配  拼音首字母匹配  拼音同音匹配  拼音纠错匹配  关键词提示: • 搜索框下拉提示 • 纠错提示
  7. 需求-精确匹配  规则:精确匹配或过滤所有特殊字符后精 确匹配  单个字段:  歌手:阿唬 , 80 前后  歌曲:爱情 BT 大讲堂  专辑: Alive!  多个字段联合:  歌手名+歌曲名:刘德华 今天  歌手名+专辑名:许茹芸 爱 . 旅行 . 一公里
  8. 需求-前缀匹配  规则:过滤所有特殊字符后前缀匹配  单个字段:  歌手:刘德,张学  歌曲:  专辑:
  9. 需求-分词匹配  规则:过滤所有特殊字符后分词匹配  单个字段:  歌手:德华,杰伦,学友  歌曲:  专辑:  歌词:  多个字段联合:先 Must ,再 Should  歌手名+歌曲名+专辑名:  歌手名+专辑名:
  10. 需求-模糊匹配  规则:一定模糊度的词匹配(注:很 慢)  单个字段:  歌手:刘大华  歌曲: beautful, califonia  专辑:
  11. 需求-拼音全量匹配  规则:用户输入拼音匹配  单个字段:  歌手: liudehua, zhangxueyou  歌曲:  专辑:
  12. 需求-拼音首字母匹配  规则:用户输入拼音首字母匹配  单个字段:  歌手: ldh, zxy  歌曲:  专辑:
  13. 需求-拼音同音匹配  规则:用户拼音输入法,输入错误的同 音字匹配  单个字段:  歌手:柳的话,两用器  歌曲:  专辑:
  14. 需求-拼音纠错匹配  规则:用户拼音输入法输入错误的字, 或直接输入错误的拼音  n l,h­f,z zh,c ch,s sh,an ang,en eng,in ing  单个字段:  歌手:牛德华, niudehua  歌曲:  专辑:
  15. 搜索实现  建索引策略  冗余字段  中文将拼音,首字母也建进索引里  搜索 Query 策略  弃用多次查询的策略  采用多个 Query 拼装成一个 BooleanQuery ,设置不同的权值, 一次查询的策略
  16. 搜索实现:建索引策略 (1/2)  歌手: singer_name  singer_name_save: 保存字段, trim 后,原封不动  singer_name_filtered:  过滤字段,过滤所有的特殊字符,转小写  singer_name_analyzed:  分词字段  singer_name_notanalyzed:  不分词字段,前缀匹配使用  singer_name_full:  拼音全量字段  singer_name_first:  拼音首字母字段  歌曲: song_name   专辑: album_name
  17. 搜索实现:建索引策略 (2/2)  关键词: keyword  所有的歌手名,歌曲名,专辑名, 歌手+歌曲,歌手+专辑,都视为 关键词  单独一个索引文件  供下拉提示和搜索无结果时的纠错 提示使用  也提供拼音全量,拼音首字母,拼 音纠错等功能
  18. 搜索实现:搜索策略 (1/6)  策略列表: 1. 精确匹配:歌手,歌曲,专辑,不分词字段,去掉前后多余空格,精确匹配 2. 过滤后的精确匹配:歌手,歌曲,专辑,过滤字段,去掉所有特殊字符,英文转成小 写,精确匹配 3. 拼音全量匹配:歌手,歌曲,专辑,拼音全量字段,去掉所有非英文字符,英文转成小 写,精确匹配 4. 同音纠错匹配:歌手,歌曲,专辑,拼音全量字段,只对含中文的搜索词使用,中文转 拼音,英文转小写,去掉所有特殊字符,精确匹配 5. 拼音首字母匹配:歌手,拼音首字母字段,中文转拼音首字母,英文转小写,去掉所有 特殊字符,精确匹配 6. 前缀匹配:歌手,歌曲,专辑,不分词字段,去掉前后多余空格,英文转小写,前缀匹 配 7. 分词 Must 匹配:歌手,歌曲,专辑,(歌词),分词字段,分词,词之间使用 Must 连 接,分词匹配
  19. 搜索实现:搜索策略 (2/6)  策略列表(续): 1. 分词 Should 匹配:歌手,歌曲,专辑,(歌词),分词字段,分词,词之间使用 Should 连接,分词匹配 2. 合并分词 (must) 匹配:歌手+歌曲+专辑 分词字段,分词,(当前使用 must  连 接),分词匹配 3. 合并分词 (should) 匹配:歌手+歌曲+专辑 分词字段,分词,(当前使用 Should  连 接),分词匹配 4. 拼音纠错匹配查询(忽略掉鼻音等) : 歌手,歌曲,专辑,分词字段,去掉前后多余空 格,英文转小写 . 5. 中文模糊匹配 ,中文时模糊度: 0.65: 歌手,歌曲,专辑,分词字段,去掉前后多余空 格,英文转小写 . 6. 英文模糊匹配,英文模糊度: 0.85: 歌手,歌曲,专辑,分词字段,去掉前后多余空 格,英文转小写 .
  20. 搜索实现:搜索策略 (3/6)  精度选择  只搜索精确匹配结果  精确匹配,过滤后精确匹配,前缀匹配  拼音全量,首字母  分词全部命中  只搜索模糊匹配结果  分词部分命中  同音纠错,拼音纠错  模糊匹配  去掉精确匹配的结果  搜索全部结果
  21. 搜索实现:搜索策略 (4/6)  字段选择  只搜索歌手  只搜索歌曲  只搜索专辑  只搜索歌词  搜索关键词字段  搜索全部字段(暂时不包括关键词 和歌词)
  22. 搜索实现:搜索策略 (5/6)  设置权值  将所有的策略置入一个有序列表中  列表中相邻的两个策略之间权值相差常数倍(当前 设置为 10 )。过大可能会导致 lucene 评分溢出,过 小可能会导致不同策略命中的结果集重叠  调整列表中策略的先后次序以调整结果集中各种命 中的出现顺序  二级权值:在搜索全部的时候,歌手 > 歌曲 > 专 辑,所以需要在同一个策略内部再设置字段权值  中文分词命中的权值设置: Lucene  默认打分策略 中,并没有考虑命中的词的长度。为了优先显示长 的词命中的结果,对分词 Query 中每个词根据长度 设置不同的权值
  23. 搜索实现:搜索策略 (6/6)  索引文件划分  搜索歌曲索引  搜索专辑索引  搜索关键词索引  排序策略  编辑置顶  Lucene  评分  业务量(点击,订阅,播放等)  关键词词频
  24. 搜索示例  歌曲索引,全部字段,精确搜索  搜 刘德华,结果条数: 329  QUERY:(((singer_name_notanslysis: 刘德华 ^9.0  song_name_notanslysis: 刘德华 ^4.0 album_name_notanslysis: 刘德 华 ))^10000.0) (((singer_name_filtered: 刘德华 ^9.0 song_name_filtered: 刘德华 ^4.0 album_name_filtered: 刘德华 ))^1000.0)  (((singer_name_filtered: 刘德华 *^9.0 song_name_filtered: 刘德华 *^4.0  album_name_filtered: 刘德华 *))^100.0) ((((((+singer_name_anslysis: 刘 德华 +singer_name_anslysis: 德华 )^9.0) ((+song_name_anslysis: 刘德 华 +song_name_anslysis: 德华 )^4.0) (+album_name_anslysis: 刘德华 +album_name_anslysis: 德华 ))))^10.0) ((((+singer_song_album: 刘德华 +singer_song_album: 德华 ))))
  25. 搜索示例  歌曲索引,全部字段,精确搜索  搜 ldh ,结果条数: 401 (命中刘德华,刘大浩等)  QUERY:(((singer_name_notanslysis:ldh^9.0  song_name_notanslysis:ldh^4.0  album_name_notanslysis:ldh))^1000.00006)  (((singer_name_filtered:ldh^9.0 song_name_filtered:ldh^4.0  album_name_filtered:ldh))^100.00001) (((singer_name_full:ldh^9.0  song_name_full:ldh^4.0 album_name_full:ldh))^10.000001)  (((singer_name_first:ldh))^1.0000001) (((singer_name_filtered:ldh*^9.0  song_name_filtered:ldh*^4.0 album_name_filtered:ldh*))^0.10000001)  ((((((+singer_name_anslysis:ldh)^9.0) ((+song_name_anslysis:ldh)^4.0)  (+album_name_anslysis:ldh))))^0.010000001)  (((((+singer_song_album:ldh))))^0.0010)
  26. 搜索示例  歌曲索引,全部字段,精确搜索  搜 liudehua ,结果条数: 324 (歌名《我不是刘德华》无法命中)  QUERY:(((singer_name_notanslysis:liudehua^9.0  song_name_notanslysis:liudehua^4.0  album_name_notanslysis:liudehua))^1000.00006)  (((singer_name_filtered:liudehua^9.0 song_name_filtered:liudehua^4.0  album_name_filtered:liudehua))^100.00001)  (((singer_name_full:liudehua^9.0 song_name_full:liudehua^4.0  album_name_full:liudehua))^10.000001)  (((singer_name_filtered:liudehua*^9.0 song_name_filtered:liudehua*^4.0  album_name_filtered:liudehua*))^1.0000001)  ((((((+singer_name_anslysis:liudehua)^9.0)  ((+song_name_anslysis:liudehua)^4.0)  (+album_name_anslysis:liudehua))))^0.10000001)  (((((+singer_song_album:liudehua))))^0.010000001)  (((singer_name_first:liudehua))^0.0010)
  27. 搜索示例  歌曲索引,全部字段,模糊搜索  搜 刘德华,结果条数: 44 (命中爱德华,杨德华等)  QUERY: ((((singer_name_notanslysis: 刘德华 ^9.0 song_name_notanslysis: 刘德华 ^4.0  album_name_notanslysis: 刘德华 ))^10000.0) (((singer_name_filtered: 刘德华 ^9.0  song_name_filtered: 刘德华 ^4.0 album_name_filtered: 刘德华 ))^1000.0)  (((singer_name_filtered: 刘德华 *^9.0 song_name_filtered: 刘德华 *^4.0  album_name_filtered: 刘德华 *))^100.0) ((((((+singer_name_anslysis: 刘德华 +singer_name_anslysis: 德华 )^9.0) ((+song_name_anslysis: 刘德华 +song_name_anslysis: 德华 )^4.0) (+album_name_anslysis: 刘德华 +album_name_anslysis: 德华 ))))^10.0)  ((((+singer_song_album: 刘德华 +singer_song_album: 德华 ))))) +(((((singer_song_album: 刘德华 ^9.0 singer_song_album: 德华 ^4.0)))^10000.0) (((((singer_name_anslysis: 刘德华 ^9.0 singer_name_anslysis: 德华 ^4.0)^9.0) ((song_name_anslysis: 刘德华 ^9.0  song_name_anslysis: 德华 ^4.0)^4.0) (album_name_anslysis: 刘德华 ^9.0  album_name_anslysis: 德华 ^4.0)))^1000.0) (((((singer_name_full:liudehua)^9.0)  ((song_name_full:liudehua)^4.0) (album_name_full:liudehua)))^100.0) ((())^10.0)  ((singer_name_anslysis: 刘德华 ~0.65^9.0 song_name_anslysis: 刘德华 ~0.65^4.0  album_name_anslysis: 刘德华 ~0.65)))
  28. 搜索示例  歌曲索引,全部字段,模糊搜索  搜 牛德华,结果条数: 840 (命中刘德华,爱德华,杨德华等)  QUERY: ((((singer_name_notanslysis: 牛德华 ^9.0 song_name_notanslysis: 牛德华 ^4.0  album_name_notanslysis: 牛德华 ))^10000.0) (((singer_name_filtered: 牛德华 ^9.0  song_name_filtered: 牛德华 ^4.0 album_name_filtered: 牛德华 ))^1000.0)  (((singer_name_filtered: 牛德华 *^9.0 song_name_filtered: 牛德华 *^4.0  album_name_filtered: 牛德华 *))^100.0) ((((((+singer_name_anslysis: 牛 +singer_name_anslysis: 德华 )^9.0) ((+song_name_anslysis: 牛 +song_name_anslysis: 德 华 )^4.0) (+album_name_anslysis: 牛 +album_name_anslysis: 德华 ))))^10.0)  ((((+singer_song_album: 牛 +singer_song_album: 德华 ))))) +(((((singer_song_album: 牛 singer_song_album: 德华 ^4.0)))^10000.0) (((((singer_name_anslysis: 牛 singer_name_anslysis: 德华 ^4.0)^9.0) ((song_name_anslysis: 牛 song_name_anslysis: 德华 ^4.0)^4.0) (album_name_anslysis: 牛 album_name_anslysis: 德华 ^4.0)))^1000.0)  (((((singer_name_full:niudehua)^9.0) ((song_name_full:niudehua)^4.0)  (album_name_full:niudehua)))^100.0) ((())^10.0) ((singer_name_anslysis: 牛德华 ~0.65^9.0  song_name_anslysis: 牛德华 ~0.65^4.0 album_name_anslysis: 牛德华 ~0.65)))
  29. 搜索示例  歌曲索引,全部字段,模糊搜索  搜 niudehua ,结果条数: 324  QUERY:­((((singer_name_notanslysis:niudehua^9.0 song_name_notanslysis:niudehua^4.0  album_name_notanslysis:niudehua))^1000.00006) (((singer_name_filtered:niudehua^9.0  song_name_filtered:niudehua^4.0 album_name_filtered:niudehua))^100.00001)  (((singer_name_full:niudehua^9.0 song_name_full:niudehua^4.0  album_name_full:niudehua))^10.000001) (((singer_name_filtered:niudehua*^9.0  song_name_filtered:niudehua*^4.0 album_name_filtered:niudehua*))^1.0000001)  ((((((+singer_name_anslysis:niudehua)^9.0) ((+song_name_anslysis:niudehua)^4.0)  (+album_name_anslysis:niudehua))))^0.10000001)  (((((+singer_song_album:niudehua))))^0.010000001)  (((singer_name_first:niudehua))^0.0010)) +(((((singer_song_album:niudehua^64.0)))^1000.0)  (((((singer_name_anslysis:niudehua^64.0)^9.0) ((song_name_anslysis:niudehua^64.0)^4.0)  (album_name_anslysis:niudehua^64.0)))^100.0) (((((singer_name_full:niudefua  singer_name_full:liudehua)^9.0) ((song_name_full:niudefua song_name_full:liudehua)^4.0)  (album_name_full:niudefua album_name_full:liudehua)))^10.0)  ((singer_name_anslysis:niudehua~0.85^9.0 song_name_anslysis:niudehua~0.85^4.0  album_name_anslysis:niudehua~0.85)))
  30. 搜索示例  歌曲索引,歌手字段,精确搜索  搜 杰伦,结果条数: 270  QUERY:(((singer_name_notanslysis: 杰伦 ))^1000.0) (((singer_name_filtered: 杰伦 ))^100.0)  (((singer_name_filtered: 杰伦 *))^10.0) ((((+singer_name_anslysis: 杰伦 ))))
  31. 搜索示例  歌曲索引,歌曲字段,模糊搜索  搜 li ,结果条数: 117  (命中 你 )  QUERY: ((((song_name_notanslysis:li))^10000.0) (((song_name_filtered:li))^1000.0)  (((song_name_full:li))^100.0) (((song_name_filtered:li*))^10.0)  ((((+song_name_anslysis:li))))) +(((((song_name_anslysis:li^4.0)))^100.0)  ((((song_name_full:ni)))^10.0) ((song_name_anslysis:li~0.85)))
  32. 搜索示例  关键词索引,搜索歌曲  搜 liu ,结果:  流浪 流星 留恋 六月雪 浏阳河 流浪狗 流浪者之歌 留不住你的温柔  QUERY:+keyword_type:2  keyword_word_notanslysis:liu + (((keyword_word_filtered:liu*)^100.0) ((keyword_word_full:liu*)^100.0)  ((keyword_word_first:liu*)^100.0))
  33. 搜索示例  关键词索引,搜索歌手  搜 liu ,结果:  刘德华 刘冠群 刘亦敏 刘韵 刘若英 刘基俊 刘益中 刘芳 刘庆  QUERY:+keyword_type:1  keyword_word_notanslysis:liu + (((keyword_word_filtered:liu*)^100.0) ((keyword_word_full:liu*)^100.0)  ((keyword_word_first:liu*)^100.0))
  34. 搜索示例  关键词索引,搜索全部字段  搜 zhoujie ,结果:(有歌手+歌曲的命中)  周杰伦 周杰磊 周杰伦我求求你了 周杰伦传递祝福 周杰伦春节祝福 周杰伦情人节祝福  周杰伦 蒲公英的约定  周杰伦 最长的电影  周杰伦 阳光宅男  周杰伦 甜甜的  QUERY:+(keyword_type:1 keyword_type:2 keyword_type:3)  keyword_word_notanslysis:zhoujie +(((keyword_word_filtered:zhoujie*)^100.0)  ((keyword_word_full:zhoujie*)^100.0) ((keyword_word_first:zhoujie*)^100.0))
  35. 搜索示例  关键词索引,搜索全部字段  搜 柳的话,结果:(拼音同音命中)  刘德华 留得华  刘德华 冰雨  刘德华 中国人  刘德华 幸福这么远那么甜  刘德华 百分百好戏  刘德华 笑着哭  刘德华 谢谢你的爱  刘德华 爱你一万年  刘德华 情义俩心坚  QUERY:+(keyword_type:1 keyword_type:2 keyword_type:3)  keyword_word_notanslysis: 柳的话 +(((keyword_word_filtered: 柳的话 *)^100.0)  (((keyword_word_full:liudehua*)^100.0) ((keyword_word_full:liudihua*)^100.0)))
  36. 持续改进  性能调优: resin ,内存, cache  汉字转拼音:多音字,特殊符号  拼音纠错:另一种思路,标准化 vs 排列组合  关键词: Trie 树,按词频排序,加入歌词数据  业务要求以频繁更新的业务量作为排序依据  标签搜索:新需求  搜索关键词,保持先后顺序  自定义打分算法的尝试  Lucene  升级到 2.9.1 , bug 1974 , explain  显示 0 或者 NaN  显示时最佳片段截取, html 实体截断问题  标红,按策略的山寨标红与 Lucene 自带标红的优劣比较及取舍
  37. 性能调优 • 目标:单台机器,百万数量级的索引, 1000 个并发 下, 99% 0.5 秒内返回 • 优势: • 索引更新不是很频繁,可以忽略不计 • 服务器性能不错, 8cpu , 8G 内存 • 劣势: • 并发大,返回时间 0.5 秒要求太苛刻 • 一期代码有很多不合理的地方 • 项目时间紧张 • 使用了 resin 作为容器,有太多不可控因素
  38. 汉字转拼音 • Pinyin4j  的词库 • 自己整理的多音字表 • 当前将所有多音字的组合都建在索引里 • 莫文蔚: mowenwei, mowenyu  • 优点:保证能查到 • 缺点:输入 mowenyu  能查到莫文蔚,而且一个歌 名中如果有好几个多音字,排列组合的数量比较 可观 • 拼音首字母,而不是拼音声母 • 张学友: zxy ,不是 zhxy
  39. 拼音纠错 • 规则: • n l,h f,z zh,c ch,s sh,an ang,en eng,in ing (on ong) • 当前实现: • 将用户输入的搜索词中的每个出现,依次替换成对应的纠 错,联合成一个 Should Query • 如: liudehua: niudehua, liudefua • 另一种思路:标准化 • 规定规则中的替换只能单向: n >l,z >zh 等 • 在索引中增加一个标准化拼音字段,如曾经最美,该字段 存储的值为 chengjingzhuimei • 用户输入关键词,也经过同样的标准化后,在该字段进行 查询
  40. 关键词 • 关键词当前使用 Lucene  的索引前缀查询的方式实现 • 也包括拼音,首字母,纠错等 Query • 本来还有模糊查询的 Query ,但后来发现太影响查询 速度了,于是就暂时去掉了 • 当前没有把歌词数据建到关键词索引中去。如果把歌 词建进去,这个索引就太大了,必须要进行分拆 • 考虑使用 Trie 树: • 多棵树,拼音,首字母,中文需要各自建树 • 模糊查询的问题
  41. 排序 vs 更新 • 业务需求希望能以歌曲的业务量(播放,下载等量) 作为排序的一个依据 • 意味着需要频繁的更新索引,而且为了更新这样一个 数字字段,需要将整个文档删除重新添加,不划算 • 打算重载 Lucene  的 Collection  类,自己实现排序字 段值的加载,不从索引里面读取 • 问题: 2.4  与 2.9  在这个地方的实现上有很大的不 同,没法无缝切换
  42. 标签搜索 • 固定维度的标签,编辑填写,非用户产生内容 • 如:奥运,免费,铃声,开心,悲伤等 • 关键是产品设计,非技术实现 • 参考: google  泡泡挑歌
  43. 保持搜索关键词的顺序 • 延后实现的一个需求 • 只命中跟用户输入的多个关键词之间的顺序一致的结果 • 如: • 用户输入 “谢谢 爱”命中“谢谢你的爱”,但输入 “爱 谢谢”不命中 • 用户输入 “眼睛 背叛 心”命中“你的眼睛背叛了 你的心”,但输入“心 背叛 眼睛”不命中 • 实现: • 分词命中,无法保留顺序信息 • 模糊查询,效率太差 • ???
  44. 打分算法  需求: • 产品人员对 Lucene 打分算法的不理解,要求单纯的以某一个 依据来进行排序,如命中的词的个数 • 拼多个 Query 查询的副作用:多个 Query 的评分累加得到的 最后得分,会导致各个 Query 的命中结果重叠(想法:把 累加改成取最大值?) • 一个想法:同样的分词命中,命中较长的词的结果排前面  教训: • 没有金刚钻,别揽瓷器活!不要轻易的去改 Lucene 的评分算法
  45. Lucene 版本的选择 • 首选 2.9.0 • Bug 1974 : https://issues.apache.org/jira/browse/LUCENE 1974 • 换成了 2.4.1  • 为了性能及长远打算,还是希望换回 2.9.1 • Explain  函数调用返回评分 0  或 NaN
  46. 摘要截取 • Miniportal  空间有限,歌曲,专辑,甚至歌手名都可 能需要截断 • 截断时需要考虑标红问题 • 截断时需要考虑 html  实体的问题
  47. 标红 • Lucene  标红的优点与不足 • 优点:正统,可升级 • 缺点:不能满足需求,前缀标红,拼音标红等 • 山寨标红 • 按照每种 Query 进行相应的标红,最后合并 •
  48. 更多讨论 http://blog.fulin.org

成都:一路向西

成都。

要回北京了。最后一个周末,心情很不好,看看熊猫卡上剩下没去的地方,决定去雪山。

一个人,即使不能幸福,也要快乐。

早上起床晚了,吃了午饭才从郫县出发。郫县到金沙,金沙到大邑,大邑到双河,也就是西岭镇,没想到离雪山还是7公里,而且因为修路,没有公交车上去了,只有打黑车。
不巧的是,出来没注意,身上没有足够的现金了,而西岭镇是如此的荒凉,以至于根本没有任何的金融机构,更别说取款机了。打了个车,往回走到某个大一点的镇上取了钱,再继续往雪山进发。
在离雪山(售票处)还有1公里的地方,修路。打的车是类似qq那种很小的车,是奔奔还是乐驰没仔细看,肯定是过不去了,还好旁边有轻卡正好要进山,搭上个便车。
下午4点,终于到了售票处,这时我作了一个极其错误的决定,那就是下了车。用熊猫卡换了票,看着轻卡决尘而去(比喻句,因为山里没有尘,有的都是泥),正想抬腿往里面走,售票员轻轻的说了一句:这里离第一个景点还有2公里。faint,干嘛在离景点还有2公里的地方设这么一个卖票点?
刚走了没几步,才发现下雨了。山中的雨很细,阵阵凉风吹过,雨似乎也变成一阵一阵的了。路的两边都是拔地而起的山,郁郁葱葱的树丛也固定不住太过陡峭的山坡,到处都是滑坡的痕迹。山与山中间的空隙中,一条小河蜿蜒流淌,路就是沿着河修的。偶尔还可以看到依山伴水而建的山庄,在初现的夜色中迷茫一片,没有灯光,也没有人声。
继续向前,因为没有别的选择。终于到了第一个景点,同时也是停车场。买了把雨伞,跟小卖部的人拉了几句家常,这才明白,从这里开始再往上,就是山路了。从这里算起,这条路有12公里,路的尽头,叫阴阳界,照字面意思理解,大概就是雪线了吧。淡季,下着雨的傍晚,自然人很少。附近三三两两的人,大都是准备今晚在这里过夜,明天一早起来爬山的。

西岭雪山
西岭雪山

一个人旅行,最大的好处就是可以随心,而不用考虑任何其他的因素。比如说,想半夜爬山,爬就是了。所以我就开始爬山。
一路上雨继续下,时大时小。天越来越黑,一半是真的天黑了,一般是山里的水汽太大。路上几乎看不到人影,寥寥的几个,也都是下山的。山路依然是沿着河修建的,不过时而在河左边,时而又转到右边。过河的桥,有木桥,也有吊桥。更令人欣喜的是,桥头大都挂着这样的标语:野猴出没地,严禁游客喂食,挑逗野猴!太有才了。

吊桥
吊桥

不知道有多久没有看见过这样清澈的水了,也不知道有多久没有看到过这样奔流的河了。记忆中老家屋后那条河的源头,似乎就是这个样子的。那些一起下河洗澡的日子,一起抓螃蟹的哥们,都越来越远了。
爬了2公里到第一个接待站,天已经完全黑下来了。接待站里有2个工作人员,可以过夜,还有饭吃。下一个接待站还有2公里,犹豫了2秒钟,我就放弃了继续往山爬的念头。掏出手机才发现,没有信号。接待站是自己发电,有电视信号,可是没有手机信号。弱弱的问了一句,确认也没有有线电话,更不用说网络了。也就是说,完全的与世隔绝了。有多久没有与世隔绝了?想想,自从2004年拥有了第一部手机之后,就很少很少了吧。
不出意料,床单被子上都是一股潮湿冰冷发霉的味道,在这样一年有大半年会下雨的地方,木屋下面还是一个小瀑布的地方,还能有什么要求呢?不过幸好有电热毯,忍忍还能睡着。如果没有,那我宁愿坐一晚上了。

入睡前还在想,明早上起来继续往上爬。可是早上醒来,忽然就意兴阑珊了。深深的吸了好几口冰凉的空气,冲着山顶的方向挥挥手,转身返回。

7点半起床开始走,8点半走回到停车场。又很幸运的搭了个便车,一路来到昨天黑车过不去的那个修路的口。那便车到这里就不走了,可是这个时候这个地方,上哪里找黑车去?没有办法,走吧。据说是7公里,到西岭镇的公交车站,走的快一点,不到2个小时也就到了。

早晨的风景比傍晚更漂亮,一路上峰回路转,水声伴随着鸟鸣。一抬头,半山腰上水汽凝结成烟一般的白雾,远一点的山大都只露出一个个山顶,水墨画的意境,也就如此了罢。

回成都的路上,顺便去了一下安仁的刘文彩大地主的刘氏庄园,买了一斤地主家的牛皮糖。
回到成都市内,又去了一下金沙博物馆,感受了一下传说中的4D电影《梦回金沙》。

谁说的人非要快乐不可
好像快乐由得人选择
找不到的那个人来不来呢
我会是谁的谁是我的

一个人去旅行,可以快乐,却逃不开寂寞。

音乐搜索的极致

12530 PC客户端 咪咕 (页面最下方有一个很不显眼的下载链接) 搜索 原本计划是今天上线内测,20号正是随资源库后台一起上线,其实昨晚就已经替换掉了正式服务器上原来的接口。正因为昨晚悄无声息的上线,原本已经下班走到 家门口的我们,又被电话叫回公司,来解决一个刚刚发现的bug。

音乐搜索,第一期还没有特别做歌词的搜索,只对歌手名,歌曲名,专辑名做优化,加上数据量本身就很小(一共才不到100万首歌),只好在查询上做文章。我们当前一共设置了十层查询 Query:

1。精确匹配:歌手,歌曲,专辑,不分词字段,去掉前后多余空格,精确匹配
2。过滤后的精确匹配:歌手,歌曲,专辑,过滤字段,去掉所有特殊字符,英文转成小写,精确匹配
3。拼音全量匹配:歌手,歌曲,专辑,拼音全量字段,去掉所有非英文字符,英文转成小写,精确匹配
4。同音纠错匹配:歌手,歌曲,专辑,拼音全量字段,只对含中文的搜索词使用,中文转拼音,英文转小写,去掉所有特殊字符,精确匹配
5。拼音首字母匹配:歌手,拼音首字母字段,中文转拼音首字母,英文转小写,去掉所有特殊字符,精确匹配
6。前缀匹配:歌手,歌曲,专辑,不分词字段,去掉前后多余空格,英文转小写,前缀匹配
7。分词Must匹配:歌手,歌曲,专辑,(歌词),分词字段,分词,词之间使用Must连接,分词匹配
8。分词Should匹配:歌手,歌曲,专辑,(歌词),分词字段,分词,词之间使用Should连接,分词匹配
9。合并分词匹配:歌手+歌曲+专辑 分词字段,分词,(当前使用 Should 连接),分词匹配
10。模糊匹配:歌手,歌曲,专辑,分词字段,去掉前后多余空格,英文转小写,模糊匹配, 包含中文时模糊度:0.65 全英文模糊度:0.85

其中模糊匹配还分了两级:

a 拼音纠错
b 模糊查询,包括中文模糊和英文模糊(模糊度不一样)

当前拼音模糊是使用组合的办法来实现的:

1。建索引的时候,拼音全量字段里建的是字段的准确拼音,包括多音字的组合
2。搜索的时候,将用户输入的关键词转成拼音,在拼音全量字段里搜
3。模糊的时候,将用户输入关键词转成的拼音,按照模糊规则:n-l 互换,zh-z, ch-c, sh-s 互换,an-ang, en-eng, in-ing, on-ong 互换,每次只换一个(当前只支持模糊度为1的拼音模糊查询),如果有多个可以替换的点,则返回的结果为一个数组组合,然后使用 精确匹配在拼音全量字段进行查询

还有一种做法:

首先定义个所谓的拼音标准化过程:
n->l,zh->z, ch->c, sh->s ,an->ang, en->eng, in->ing, on->ong 不是互换,而是单向替换。
将一个拼音串的所有可替换点都替换后,得到的一个串,称为标准化串。
1。建索引的时候,歌曲名,歌手名,专辑名各新增一个标准化串字段,按”,”分词(多音字),存储字段的拼音标准化串
2。搜索的时候,将用户输入的关键词转成拼音,在拼音全量里面搜索
3。模糊的时候,将用户输入关键词的拼音再转成标准化串,在标准化串字段里面搜索

优点:不同那么复杂的组合逻辑
缺点:无法控制模糊度

拼一个很大的 Query 去 Lucene 里面查询最大的问题就是,排序很难控制。不停的查看 Lucene Explain 出来的打分细节,再微调 Query 之间的 boost 权值,再查看打分细节,再微调。特别是分词命中这个 Query ,Lucene 分词命中的默认打分规则,总觉得不太满意。自己做了一个 Similarity 的子类来算分,可毕竟不是专业的,考虑的不够全面,解决了一个问题,副作用带来更多的问题。最后,还是不得不放弃这个方向的尝试。

拼一个大 Query 的一个意外收获就是,发现 Lucene 2.9.0 的一个 bug:LUCENE-1974,提到官方 JIRA 后,很快被确认,并修复了,并且我提交的 TestCase 也被将接纳到 Lucene 的测试用例集合中。可惜 2.9.1 出来前,我们还是不得不将项目切换回 2.4.1 , 以避免这个 bug。

现在使用的 IK 分词器,总觉得行为有些奇怪,又没有什么地方可以设置的。天龙八部,最多分词分出来“天”,“八”是我们不想要的,最长分词,又分不出“天龙”,真是郁闷。

因为 Query 太复杂,Lucene 自带的标红效果不是很令人满意,所以标红的部分也是完全自己做的。仿照 Query 的模式,定义了一系列的规则,如全量命中,拼音命中,分词命中等,记录下每种规则匹配到的区段,最后做一次归并就可以了。

关键词提示,即用户在搜索框里输入的同时,下拉一个提示列表。现在的做法是建了一个单独的关键词索引,用户输入的时候,使用前缀去匹配。中文的 Trie 树比英文复杂不少,所以最开始没有选它。但现在发现关键词索引太大了,Lucene 更新太慢,才后悔最初的选型失误。关键词里面需要保存词频的信息,搜索量的信息,所以以后的更新肯定也会不少。明天继续想办法解决这个问题。

做完这个项目以后,大约所有的搜索功能,都不会让我觉得害怕了吧。

原创,转载请著名出处:唐福林 博客雨 http://blog.fulin.org/2009/10/acme_of_music_search.html

关于音乐搜索

音乐搜索属于垂直搜索的一种,但它又有着自己独特的一些需求。

首先,几乎所有的音乐搜索都实现了用户输入时的关键词提示功能。但在网上搜索相关的技术文章,大多是讲如何用 Js 实现前台表现层的功能,少有的几篇关于后台技术实现的文章,也都太过简单。标准的办法是使用 Trie 树,但太过晦涩,不够直观。我们打算直接使用 Lucene 的前缀查询来实现,并且计划在项目上线后写一个比较详细的说明。

其次,很多的音乐搜索都提供了拼音查询的功能。比如说用户输入 “liudehua”,关键词提示里会给出 “刘德华”,但即使用户不理会提示,直接点击提交,在服务器端,还是可以查询到关于 “刘德华” 的条目。甚至,用户输入拼音首字母 “ldh”,都可以匹配到 “刘德华”。这主要是考虑到使用音乐搜索的用户群的特点(低龄?懒惰?互联网初级用户?),以及某些艺人的名字确实比较难拼写吧。技术上其实很简单,建索 引的时候,将歌曲名,歌手名等都转成拼音一并进行索引就可以了。唯一一点需要注意的地方在于,多音字的处理。

再次,有些搜索引擎,像 qq music,提供了同音字纠错的功能,可以在用户输入“周洁论”的时候,命中关于“周杰伦”的结果。有了上一步的拼音索引,这一步也很容易实现了。再多做 一步,考虑到南北方的口音差别,很多人 en 与 eng,zh 与 z,n 与 l 不分,在搜索过程中进行一些简单的替换,拼音模糊纠错功能也就水到渠成了。

最后,汉字的模糊搜索。我们常用的一个例子就是,用户输入“刘大华”,能否命中“刘德华”?技术上肯定是可以的,lucene 本身就提供这样的查询,只是在产品设计上,是否有代替用户思考的嫌疑呢?这就需要产品人员去仔细思量了。

前面说的是功能,后面说说排序。

最基础的排序当然是按文档匹配度,也就是 lucene 的 score 来排了。但是有时候编辑推荐的歌曲是一定要排前面的,这个比较好实现。可是点击率比较高的歌曲也要靠前排,这个就有点麻烦了,因为牵涉到频繁的字段更新,以及 boost 值的微调。

最麻烦的是上面说的那一堆的特殊处理。比如用户输入了一个词,精确匹配肯定应该排最前面了,没有精确匹配中文的,拼音全量匹配也可以,分词匹配,或 者部分匹配的结果次之,再接下来应该是前缀搜索,同音字纠错,模糊搜索的匹配条目。最开始的想法一直是多次搜索,可是在多次搜索里,一是无法控制所谓的精 确匹配;二是多次搜索打包的结果用于排序的时候,很麻烦;三则,多次搜索,本身的逻辑就非常复杂。不过今天学会一招,如果不考虑性能损耗,可以说是屠龙刀 级别的必杀技:打包多个 Query 对象,一次搜索!排序的问题,当然使用 Query.setBoost 解决了。至于精确匹配,冗余一个字段,不分词就行。

搭建好了 Hudson,写了一个看起来蛮复杂的 build.xml ,然后每天看着它自动的编译,测试,发布,还是有点成就感的。

开始写测试用例。一边写也一边在思考,搜索引擎项目该如何进行功能正确性的测试,又如何进行搜索结果好坏的评价呢?

生活琐碎

离职倒计时,三,二,一。

回想在手机之家的这半年,有两件事情是值得感谢的:一是转到了自己喜欢的方向:搜索,二是认识了一帮很好的同事。这大概都是在新浪,腾讯之类的大公 司里无法做到的吧。在大公司的流水线上,很少可以任由自己选择喜欢的方向,独当一面的历练,就更难得了。至于同事,很多时候就只是同事。新浪还好一些,因 为环境比较宽松,大家还可以有工作之余的生活时间,而腾讯,早晚的班车,按天安排的任务,让人觉得就像在一台上满发条的大机器中一样,被胁裹着往前,没有 了个人的存在。

Brightcove (http://www.brightcove.com/en/) 那种典型的外企风格,对纯粹做技术的人来说,诱惑力不是一般的大,是非常的大。开放式的办公室,开放式的开发团队,“找最强的人,做最好的产品”的理念, 说英语的机会,去美国的可能,都是非常吸引人的。可惜的是暂时没有开发工程师的职位,如果去,只能先做一段时间的测试工程师,明年再转。

大道信通那边还是一直在催,如果不出意外,周六可能就去成都了。十一直接从成都去广州。港澳通行证要 14 号才能拿到,而14号的时候,我已经不在北京了。

刚买的 T43,在经过e世界店家往 cpu 上涂了一层散热硅胶之后,仍然坚持“死机到底”。打电话过去,店家说可能是内存的问题,把两条512的换成一条1G的试试。厌倦了那些“可能”,“试 试”,直接要求换一台,顺便换成高分屏的。习惯了 1400*1050,再用 1024*800还真别扭。

妈妈和浩洋去了东莞。浩洋开始上幼儿园,据说只有第一天哭了一会,后面就不哭了。爸爸一个人在家,无聊得开始折腾猪圈了。

唐星星下周回东安。旭林搬到了清华的宿舍。唐芳和肖甜都忙。lily在遥远的广州。一个人下楼吃饭,站在桔红的路灯下,雨后的路上车来车往,忽然觉得有点孤单。

当前几个主要的Lucene中文分词器的比较

1. 基本介绍:

paoding :Lucene中文分词“庖丁解牛” Paoding Analysis
imdict :imdict智能词典所采用的智能中文分词程序
mmseg4j : 用 Chih-Hao Tsai 的 MMSeg 算法 实现的中文分词器
ik :采用了特有的“正向迭代最细粒度切分算法“,多子处理器分析模式

2. 开发者及开发活跃度:

paodingqieqie.wang, google code 上最后一次代码提交:2008-06-12,svn 版本号 132
imdictXiaoPingGao, 进入了 lucene contribute,lucene trunk 中 contrib/analyzers/smartcn/ 最后一次提交:2009-07-24,
mmseg4jchenlb2008,google code 中 2009-08-03 (昨天),版本号 57,log为:mmseg4j-1.7 创建分支
iklinliangyi2005,google code 中 2009-07-31,版本号 41

3. 用户自定义词库:

paoding :支持不限制个数的用户自定义词库,纯文本格式,一行一词,使用后台线程检测词库的更新,自动编译更新过的词库到二进制版本,并加载
imdict :暂时不支持用户自定义词库。但 原版 ICTCLAS 支持。支持用户自定义 stop words
mmseg4j :自带sogou词库,支持名为 wordsxxx.dic, utf8文本格式的用户自定义词库,一行一词。不支持自动检测。 -Dmmseg.dic.path
ik : 支持api级的用户词库加载,和配置级的词库文件指定,无 BOM 的 UTF-8 编码,\r\n 分割。不支持自动检测。

4. 速度(基于官方介绍,非自己测试)

paoding :在PIII 1G内存个人机器上,1秒 可准确分词 100万 汉字
imdict483.64 (字节/秒),259517(汉字/秒)
mmseg4j : complex 1200kb/s左右, simple 1900kb/s左右
ik :具有50万字/秒的高速处理能力

5. 算法和代码复杂度

paoding :svn src 目录一共1.3M,6个properties文件,48个java文件,6895 行。使用不用的 Knife 切不同类型的流,不算很复杂。
imdict :词库 6.7M(这个词库是必须的),src 目录 152k,20个java文件,2399行。使用 ICTCLAS HHMM隐马尔科夫模型,“利用大量语料库的训练来统计汉语词汇的词频和跳转概率,从而根据这些统计结果对整个汉语句子计算最似然(likelihood)的切分”
mmseg4j : svn src 目录一共 132k,23个java文件,2089行。MMSeg 算法 ,有点复杂。
ik : svn src 目录一共6.6M(词典文件也在里面),22个java文件,4217行。多子处理器分析,跟paoding类似,歧义分析算法还没有弄明白。

6. 文档

paoding :几乎无。代码里有一些注释,但因为实现比较复杂,读代码还是有一些难度的。
imdict : 几乎无。 ICTCLAS 也没有详细的文档,HHMM隐马尔科夫模型的数学性太强,不太好理解。
mmseg4jMMSeg 算法 是英文的,但原理比较简单。实现也比较清晰。
ik : 有一个pdf使用手册,里面有使用示例和配置说明。

7. 其它

paoding :引入隐喻,设计比较合理。search 1.0 版本就用的这个。主要优势在于原生支持词库更新检测。主要劣势为作者已经不更新甚至不维护了。
imdict :进入了 lucene trunk,原版 ictclas 在各种评测中都有不错的表现,有坚实的理论基础,不是个人山寨。缺点为暂时不支持用户词库。
mmseg4j : 在complex基础上实现了最多分词(max-word),但是还不成熟,还有很多需要改进的地方。
ik :  针对Lucene全文检索优化的查询分析器IKQueryParser

8. 结论

个人觉得,可以在 mmseg4j 和 paoding 中选一个。关于这两个分词效果的对比,可以参考:

http://blog.chenlb.com/2009/04/mmseg4j-max-word-segment-compare-with-paoding-in-effect.html

或者自己再包装一下,将 paoding 的词库更新检测做一个单独的模块实现,然后就可以在所有基于词库的分词算法之间无缝切换了。

ps,对不同的 field 使用不同的分词器是一个可以考虑的方法。比如 tag 字段,就应该使用一个最简单的分词器,按空格分词就可以了。

关于行业相关的站内搜索分词的一些想法

最近在给一个类似 京东新蛋 的3C类电子商城提供站内搜索。基本功能都是现成的,但部署上去以后,发现搜索结果比论坛,cms的搜索结果差远了。仔细的看了几个搜索记录的 debug log,才弄明白为什么:

所有的中文分词组件(paodingimdict, mmseg4j, ik 等等等等), 都是为了应付日常使用的语言而设计的,特别是 imdict “基于自然语言处理领域的隐马尔科夫模型”,当前的实现还不支持用户自定义词库。其他几个支持用户自定义词库,paoding 甚至起了一个线程来扫描词库的变化,以达到动态更新词库的目的。但词库的维护还是需要使用者来做。mmseg4j 自带了一个 sogou 细胞词库,也都是日常生活用词。不论是基于语料库训练概率还是基于精巧的词库匹配算法,当需要分词的文档中,大部分词都是“未登录词”(行业相关性较强的时候就是这样,比如搜公司名,品牌名,甚至产品型号,配置等等),机器都是无可奈何的。

行业相关的词库因为用的少,关注的人也少,能使用的资源也就少的多了。在没有现成可用的词库的前提下,怎么样以最小的代价得到一个可以接受的搜索精度呢?想了一下,暂时只想到这么几点,欢迎大家补充:

  • 根据行业特点修改分词算法的一些细节处理:比如大部分分词器实现,都会把 “A1600” 这种字母数字混合出现分解成 “A” 和 “1600” 两个词。在日常生活中,这样处理是有道理的。但在 3C 类的电子商城网站中,用户输入 “A1600” 进行搜索,与输入 “A   1600” 的意图肯定是不一样的:前者是想搜摩托罗拉的明手机,后者嘛,就很难揣测了。
  • 站内搜索,一般来说是在一个比较小的有限集合中进行搜索,所以搜索结果集合也是比较小的,甚至经常没有完全命中用户所有的关键词的情况出现。提供给用户一些可能相关的内容总要比显示“对不起,没有搜到任何结果”要好。所以文档中 “摩托罗拉” 除了要分出一个 “摩托罗拉” 以外,“摩托” 也是一个可以接受的索引词,以防用户输入一个莫名其妙的 “摩托” 的时候,搜不到任何结果(我们这里是卖3C类产品的,不卖摩托车,也不卖摩托艇,谢谢!)。—— mmseg4j 的 max-word 算法。
  • 方便的用户添加新词功能:这里的用户,既可以是网站管理员,也可以是最终用户,甚至是写一个程序去baidu,google或其他的什么数据源抓取过来的跟行业相关的词汇。新词经过清理,排重,验证,确认等预处理环节后,添加到词库里面去。如何吸引用户来为网站做有价值的贡献,永远是一个值得探讨的问题。
  • 方便的重建索引功能:在词库的量变快要引起质变的时候,应该重建一下索引。在敏捷开发如日中天的现在,搜索功能也是在不断完善的。索引源数据是不变的,但索引,应该跟着搜索功能的完善而完善,搜索结果,也应该随之得到提升。

未完,待续。。。

Lucene 2.9 上实现 GroupBy 功能

Lucene 2.9 上,终于在 Searchable 接口中支持 search(weight, filter, collector) 了,而不是像 2.4 中那样,只在 IndexSearcher 中支持,而在 MultiSearcher 中,需要自己手工的添加 Collector。 把原来的非常丑陋的 HitCollector 换成了一般丑陋的 Collector ,本来觉得还挺高兴的,但仔细一看代码前的注释:“This API is experimental and might change in incompatible ways in the next release.” 无语了。

虽然 API 还是实验性的,但看起来已经到达了可用的阶段,所以 IMobile Search 2.0 中,还是使用 Lucene 2.9 的 API ,将原来临时性质的 groupby 功能实现重构了一下,大致思路是这样的:

  • 使用 Collector 实现
  • 实现一个 抽象类 o.a.l.search.Collector  的子类
  • 在子类中维护一个 o.a.l.search.TopFieldCollector 实例来做具体的收集动作
  • 在子类 collect(int doc) 调用中,对字段值进行 groupby 聚合,丢弃重复的记录

关键点在于如何对字段值进行 groupby 聚合。上一个版本的实现里,在 collect(int doc, float score) 函数调用里调用了 reader.document(doc),而这种做法是 Lucene 强烈反对的,注释里面的原话是这样说的:

Note: This is called in an inner search loop. For good search performance,
implementations of this method should not call {@link Searcher#doc(int)} or
{@link org.apache.lucene.index.IndexReader#document(int)} on every hit.
Doing so can slow searches by an order of magnitude or more.

那么就需要一个地方缓存着所有文档中的这个字段的值,及对应的文档 id 。 因为 Lucene 每次更新后文档 id 都会重新排列,所以这个缓存还必须在每次更新后重新生成。

最开始受原来新浪爱问搜索的做法的影响,考虑可以在更新完成后,将这个字段的所有的值及对应id dump 到硬盘上,在 Search 端 reopen 索引的时候,顺便载入。但后来觉得这样太麻烦,需要改好几个文件,需要重启好几个进程,为什么不直接在 Search 端从索引中读取内容更新缓存呢?当然,读取缓存肯定不是用 reader.document() 去遍历,而是照着 o.a.l.search.DuplicateFilter 里面的做法,使用 TermEnum 遍历这个字段的所有索引值。(前提是:这个字段的 index 是 NOT_ANALYZED 的。数据库中允许 groupby 的字段也应该为数字,或短小的字符串,不是么?)

核心代码看起来是这个样子的:

[code=java]
Term startTerm = new Term(fieldName);
TermEnum te = reader.terms(startTerm);
if(te != null)
{
Term currTerm = te.term();

while((currTerm != null) && (currTerm.field() == startTerm.field()))
{
if(te.docFreq() > 0)
{
TermDocs td = reader.termDocs(currTerm);

while(td.next())
{
fcache[td.doc()] = currTerm.text();
log.debug("add " + td.doc() + " : " + currTerm.text());
}
}
if(!te.next())
{
break;
}
currTerm=te.term();
}
}
[/code]

beta技术沙龙 邀请

http://club.blogbeta.com/82.html

beta技术沙龙·大型网站的lucene搜索实战

时间:7月26日14点30分开始
地点:奇遇花园咖啡馆 http://storygarden.me/cafe/map

主题:大型网站的lucene搜索实战
演讲简介:本次活动介绍基于Lucene的站内搜索的实践,后台技术层面的一些想法与实践,包括缩短更新周期,简化重建索引流程,支持大数据量频繁更新的索引,以及在性能和可用性方面作的努力。
主讲人:唐福林 (http://blog.fulin.org https://twitter.com/tangfl)
主讲人简介:从高中的 NOIP 到大学的ACM,是编程竞赛的参与者,也是算法的爱好者,以追求程序或系统的性能的极致为乐。 目前就职于手机之家,负责基于lucene的站内搜索的开发及持续改进。

参与人员:最大的前提是技术出身,第二个要求是写blog。积极参加现场沙龙聊天;如有可能对话题通过blog保持跟进关注。http://club.blogbeta.com/ 网上报名或直接现身沙龙,参与免费,点单另付。
关于沙龙:blogbeta技术沙龙由一群技术人员发起并参与,旨在以技术的视角看待社会、互联网和未来,以务实精神深化交流,促进创新。

update: 讲稿的最终版本下载:

http://files.getdropbox.com/u/648709/%E5%9F%BA%E4%BA%8Elucene%E7%9A%84%E7%AB%99%E5%86%85%E6%90%9C%E7%B4%A2-beta.pdf

https://docs.google.com/fileview?id=0By5e0-mxw7O0NWM5MzhhZTctZGEwOS00NzkzLTliMGYtY2U0ZjBlMDJhZjcy&hl=zh_CN

LinkedIn Voldemort 简单介绍

LinkedIn 发布了一个 key-value 存储(发布?)系统 Voldemort,详细介绍在
http://project-voldemort.com/blog/2009/06/building-a-1-tb-data-cycle-at-linkedin-with-hadoop-and-project-voldemort/

抽空看了一下,写了个 ppt 简单介绍: