在django中加入搜索引擎

上周蛋疼给做的一个东西加了个搜索引擎,虽然整体来说项目数据也不会很大,但是出于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

你可能感兴趣的:(在django中加入搜索引擎)