Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

简易搜索结果排序

  最简单的排序,就是从不同字段中搜索,并为不同的字段的搜索结果分配权重来排序。这样简单的效果直接用SQL语句就可以实现。假如现在我们有article表,其中有id、title、tags、content这几个字段。现在,我们要在这个表中搜索“次碳酸钴”这个关键字。这些字段的权重是:tags大于title大于content,然后我们就可以构造一个这样的SQL语句来搜索。 select * from (
  select id,title,
    (find_in_set('次碳酸钴',tags)!=0)*100+
    (title like '%次碳酸钴%')*10+
    (content like '%次碳酸钴%') as w
  from article
    where visible=1
)temp
where w
order by w desc
  由于计算得出的w无法直接作为where的条件,因此把它作为子查询,结果放入临时表中再查询一次来排序和筛选。但是这个语句还太简陋了,如果搜索“AS”,也会找到所有“JavaScript”的结果,因为“AS”是“JavaScript”的子串(忽略大小写)。所以对于英文单词的搜索我们不能直接用like去模糊搜索。MySQL是支持正则表达式的,但是支持程度和JavaScript的有很大差别。比如匹配单词边界,不是使用“\b”而是使用“[[:<:]]” 和 “[[:>:]]”分别匹配单词的开始和结束,这些东西可以在MySQL的参考手册中找到。但是MySQL很坑爹,对中文的支持不好,中文和英文混合的情况它不能识别出单词边界,这就要手动去匹配单词边界了。由于MySQL的正则表达在一般情况下是不区分大小写的,可以使用“[a-z]”来匹配英文字母。这样就可以用它来匹配整个单词了。虽然英文的匹配可以使用单词边界,但是中文不能。我们没做中文的分词,所以以字分词。这样我们就需要一个判断,如果搜索的是英文就加上单词边界,如果是中文就直接like搜索。这个判断虽然在SQL语句中也能实现,但是那样效率会很低,划不来。所以在程序中判断好再去查询。中文的查询用之前给出的SQL语句就可以了,英文需要用下面这个。 select * from (
  select id,title,
    (find_in_set('AS',tags)!=0)*100+
    (title regexp '(^|[^a-z])AS([^a-z]|$)')*10+
    (content regexp '(^|[^a-z])AS([^a-z]|$)') as w
  from article
    where visible=1
  )temp
where w
order by w desc
  前面给出的几个查询都是判断搜索词的存在性,而没有根据数量或密度来计算。其实统计这些东西的算法很简单,只不过如果用SQL去实现它,在效率上会有很大问题。不过光说不练假把式,我们还是来实现下吧。现在要MySQL中创建一个计算文本密度的函数。 create function GetDensity(n varchar(64),s text) returns float
begin 
  #初始化统计变量和循环变量
  declare i int default 1;
  declare c int default 0;
  #获取两个参数的字符长度
  declare l int default length(n);
  declare m int default length(s);
  #判断查询字符的前后是否为英文
  declare a bool default left(n,1) regexp '[a-z]';
  declare b bool default right(n,1) regexp '[a-z]';
  while i do
    #在字符串中搜索字符并返回位置
    set i=locate(n,s,i);
    if i!=0 then
      if
        #如果是在单词边界上则记录
        !(a&&substr(s,i-1,1) regexp '[a-z]')&&
        !(b&&substr(s,i+l,1) regexp '[a-z]')
      then
        set c=c+1;
      end if;
      set i=i+1;
    end if;
  end while;
  #返回搜索词在文本中的密度
  return c*l/m;
end;
  定义了这个函数之后就可以在查询语句中使用它了。 select * from (
  select id,title,
    (find_in_set('AS',tags)!=0)*100+
    GetDensity('AS',title)*10+
    GetDensity('AS',content) as w
  from article
    where visible=1
  )temp
where w!=0
order by w desc;
  虽然功能上是可以这样实现,但是这个方法的效率是很低的。如果要在比较大的项目中使用就不能直接在SQL中写函数,应该用比较底层的编程语言写个接口。比如用C写个DLL来让SQL的存储过程来调用。不过话又说回来,稍大的项目应该不会有人选择MySQL吧。
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^