上周蛋疼给做的一个东西加了个搜索引擎,虽然整体来说项目数据也不会很大,但是出于DBA的职业嘛,不想用like或者mysql的fulltext(myisam表的原因),我的想法是能不让DB来实现的就不让DB来实现。所以就开始找python相关的。下面来说下。
首先找了个中文分词词库,jieba,https://github.com/fxsjy/jieba
用法很简单:
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import jieba
s = jieba.cut('网易杭州研究院')
print ', '.join(s)
结果如下:
Building Trie...
loading model from cache
loading model cost 0.877662181854 seconds.
Trie has been built succesfully.
网易, 杭州, 研究院
总体来说,效果还是可以的,初期用用就够了。
接下来说说whoosh,http://packages.python.org/Whoosh/
whoosh的话,没有深入研究过,主要是根据正则表达式来匹配,纯python实现,架构参考了Lucene。但是对于中文支持不好的,所以加入了jieba。
最后来说django-haystack,这是重点了。http://haystacksearch.org/
ps:安装haystack的时候,最好别用pip的,pip下载下来的是1.2+版本的,因为文档里用的HAYSTACK_CONNECTIONS是针对2.0+版本的,所以从github里clone新版本比较好。https://github.com/toastdriven/django-haystack
heystack的好处就是接口统一,支持Solr, Elasticsearch, Whoosh and Xapian之类的搜索引擎,而且不用换代码去适应的。
目前我用的是django-heystack+whoosh,效果其实也就一般,后续会考虑切换成solr,等我弄好了再总结solr。
首先在/Library/Python/2.7/site-packages/haystack/backends下增加个文件,为了让whoosh支持中文分词的。
ChineseAnalyzer.py
import jieba
from whoosh.analysis import RegexAnalyzer
from whoosh.analysis import Tokenizer,Token
class ChineseTokenizer(Tokenizer):
def __call__(self, value, positions=False, chars=False,
keeporiginal=False, removestops=True,
start_pos=0, start_char=0, mode='', **kwargs):
#assert isinstance(value, text_type), "%r is not unicode" % value
t = Token(positions, chars, removestops=removestops, mode=mode,
**kwargs)
seglist=jieba.cut(value,cut_all=True)
for w in seglist:
t.original = t.text = w
t.boost = 1.0
if positions:
t.pos=start_pos+value.find(w)
if chars:
.startchar=start_char+value.find(w)
t.endchar=start_char+value.find(w)+len(w)
yield t
def ChineseAnalyzer():
return ChineseTokenizer()
然后修改whoosh_backend.py为whoosh_cn_backend.py:
在import中加:
from ChineseAnalyzer import ChineseAnalyzer
将其中的schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer(), field_boost=field_class.boost)句修改为:
schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost)
然后新建个django来试试的.例子url:http://django-haystack.readthedocs.org/en/latest/tutorial.html
settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
# Added.
**'haystack',**
# Then your usual apps...
'blog',
]
<!-- lang: python -->
import os
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
},
}
models.py:
from django.db import models
from django.contrib.auth.models import User
class Note(models.Model):
user = models.ForeignKey(User)
pub_date = models.DateTimeField()
title = models.CharField(max_length=200)
body = models.TextField()
def __unicode__(self):
return self.title
然后针对note写search_indexes.py:
import datetime
from haystack import indexes
from blog.models import Note
class NoteIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
author = indexes.CharField(model_attr='user')
pub_date = indexes.DateTimeField(model_attr='pub_date')
def get_model(self):
return Note
def index_queryset(self):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
这里补充说明下,我在实际应用中index_queryset中一般写的是:
return self.get_model().objects.all()
接着在/templates/search/indexes/blog下新建note_text.txt:
{{ object.title }}
{{ object.user.get_full_name }}
{{ object.body }}
注意的是,文件名要以models中的一个class名字开头。
最后在/templates/search/新建个search.html:
<h2>Search</h2>
<form method="get" action=".">
<table>
{{ form.as_table }}
<tr>
<td> </td>
<td>
<input type="submit" value="Search">
</td>
</tr>
</table>
{% if query %}
<h3>Results</h3>
{% for result in page.object_list %}
<p>
<a href="{{ result.object.get_absolute_url }}">{{ result.object.title }}</a>
</p>
{% empty %}
<p>No results found.</p>
{% endfor %}
{% if page.has_previous or page.has_next %}
<div>
{% if page.has_previous %}<a href="?q={{ query }}&page={{ page.previous_page_number }}">{% endif %}« Previous{% if page.has_previous %}</a>{% endif %}
|
{% if page.has_next %}<a href="?q={{ query }}&page={{ page.next_page_number }}">{% endif %}Next »{% if page.has_next %}</a>{% endif %}
</div>
{% endif %}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
</form>
完了后,修改urls.py:
加入一句:
url(r'^search/', include('haystack.urls')),
最后重建索引,
python manage.py rebuild_index
这样的话,就算大功告成了。输入http://localhost:8000/search/就可以搜索了。
search.html什么之类的都可以根据自己需求修改,本篇用的是官方文档的例子。
写的比较乱,估计错误很多的,改天改成solr后好好重新整理下的。
另外,参考如下:
http://blog.csdn.net/Java2King/article/details/5308326