使用Solr快速实现Django的全文搜索[转]
使用Solr快速实现Django的全文搜索。http://fzuslideblog.appspot.com/2010/03/25/django_solr_search.html原文地址
Django本身并没有提供全文搜索的功能,而自己给Django添加全文搜索的功能选择也有很多,可以用Sphinx,Lucene,Xapian等等来做。这里我们选用基于Lucene的全文搜索服务器Solr来快速的搭建Django的全文搜索应用。
需要用到的开源项目:
Solr:http://lucene.apache.org/solr/ (Apache License)
Django-solr-search: http://code.google.com/p/django-solr-search/( BSD L)
Pymmseg-cpp: http://code.google.com/p/pymmseg-cpp/ (MIT L)
Django-solr-search是用来连接Solr服务器的Django插件,Py mmseg-cpp是Python封装的mmseg中文分词模块。
1.配置环境
首先安装Solr, 下载下来直接解压,然后进入apache-solr-1.4.0/example下面,直接运行:
java -jar start.jar
然后查看http://localhost:8983/solr/,看到欢迎界面,一切ok。
然后把Django-solr-search下载下来,可以选择直接setup,也可以直接把其中 的solango放入需要增加全文搜索的Project下面。
然后修改settings.py,加入solango的app,如下:
INSTALLED_APPS = {
...
'solango',
...
}
添加Solr的路径:
SOLR_ROOT ='path/apache-solr-1.4.0/example/'
SOLR_SCHEMA_PATH = SOLR_ROOT + 'solr/conf/schema.xml'
SOLR_DATA_DIR = SOLR_ROOT + 'solr/data'
SOLR_DEFAULT_OPERATOR = 'or'
最后在项目下运行命令:
让django启动Solr服务器 python manage.py solr --start
查看是否连接成功:
python manage.py shell
>>> import solango
>>> solango.connection.is_available()
True
显示True,环境就搭好了。
2.定义Document模型
先看我们需要全文搜索的model:
class ElectronicComponent(models.Model):
'''
Electronic Components Products
'''
p_name = models.CharField(_('product name'),max_length=200,
help_text=_("Example:可调式电容"))
partno = models.CharField(_('part number'),max_length=200,
help_text=_("Alphanumeric characters only (letters, digits and underscores)"))
dc = models.CharField(_('date code'),max_length=10,default=' ',
help_text=_("digits and '+','-','/'only "))
qty = models.IntegerField(_('quantity'),null=True,blank=True,
help_text=_("digits only"))
mfg = models.CharField(_('manufactory'),max_length=200,default=' ',
help_text=_("manufactory"))
pack = models.CharField(_('packaging'),max_length=20,default=' ',
help_text=_("packaging"))
desp = models.TextField(_('description'),default=' ',
help_text=_("description"))
date_update = models.DateTimeField(_('last update'), default=datetime.datetime.now)
special_attrib = models.ManyToManyField('BasicAttribValue')
cate_ID = models.ForeignKey('ElecompCategory')
u = models.ForeignKey(IcUser)
def __unicode__(self):
return self.partno
根据上面的model,定义全文搜索的Document模型:
from models import ElectronicComponent
import solango
class ElectronicComponentDocument(solango.SearchDocument):
'''
非IC全文搜索模型
'''
p_name = solango.fields.CharField(copy=True)
partno = solango.fields.CharField(copy=True)
mfg = solango.fields.CharField(copy=True)
pack = solango.fields.CharField(copy=True)
desp = solango.fields.CharField(copy=True)
cate_ID = solango.fields.CharField(copy=True)
user = solango.fields.CharField(copy=True)
def transform_user(self, instance):
return instance.u
solango.register(ElectronicComponent, ElectronicComponentDocument)
根据原有的ElectronicComponent生成ElectronicComponentDocument全文搜索模型,后面transform_user函数返回自己想给user返回的值。
3.建立索引
定义好以上的模型后,就可以建立索引了,先查看一下刚才定义的fields:
运行命令:python manage.py solr --fields
########## FIELDS ###########
<field name="mfg" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="url" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="text" type="text" indexed="true" stored="true" omitNorms="false" required="false" multiValued="true"/>
<field name="site_id" type="integer" indexed="true" stored="true" omitNorms="false" required="true" multiValued="false"/>
<field name="desp" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="partno" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="cate_ID" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="user" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="model" type="string" indexed="true" stored="true" omitNorms="false" required="true" multiValued="false"/>
<field name="p_name" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
<field name="id" type="string" indexed="true" stored="true" omitNorms="false" required="true" multiValued="false"/>
<field name="pack" type="string" indexed="true" stored="true" omitNorms="false" required="false" multiValued="false"/>
######## COPY FIELDS ########
<copyField source="mfg" dest="text"/>
<copyField source="desp" dest="text"/>
<copyField source="partno" dest="text"/>
<copyField source="cate_ID" dest="text"/>
<copyField source="user" dest="text"/>
<copyField source="p_name" dest="text"/>
<copyField source="pack" dest="text"/>
以上就是刚才模型中定义的Fields,如果以前已经有旧的索引存在了,就先运行:
python manage.py solr –-flush
把旧索引清空。然后根据我们定义的Document模型同步Schema.xml:
python manage.py solr --schema
最后重新启动Solr就可建立索引:
python manage.py solr --reindex
4.查询与分词
能够简便的实现facets查询,这也是选择Solr来做全文搜索服务器原因之一。
对刚才建立的索引,做一个测试:
In : from solango import connection
In : from solango.solr.query import Query
In : q = Query({'facet.field': 'cate_ID'}, q='ad')
In : r = connection.select(q)
In : r.count
Out: 25
In : facet_dict = {}
In : for facet in r.facets:
...: facet_dict = []
...: for value in facet.values:
...: if value.count > 0:
...: facet_dict.append({'name': value.name, 'value': value.value, 'count': value.count})
...:
...:
In : facet_dict
Out:
{u'cate_ID': [{'count': 16,
'name': u'/u6307/u793a/u706f',
'value': u'/u6307/u793a/u706f'},
{'count': 4,
'name': u'/u5176/u4ed6/u4e94/u91d1/u3001/u5de5/u5177',
'value': u'/u5176/u4ed6/u4e94/u91d1/u3001/u5de5/u5177'},
{'count': 3,
'name': u'/u5355/u7247/u673aMcu',
'value': u'/u5355/u7247/u673aMCU'},
{'count': 1,
'name': u'/u5e72/u7c27/u7ba1',
'value': u'/u5e72/u7c27/u7ba1'},
{'count': 1,
'name': u'/u793a/u6ce2/u5668',
'value': u'/u793a/u6ce2/u5668'}],
u'model': [{'count': 25,
'name': u'Electroniccomponent',
'value': u'product__electroniccomponent'}]}
In : for one in facet_dict['cate_ID']:
print one['name'], one['count']
....:
....:
指示灯 16
其他五金、工具 4
单片机Mcu 3
干簧管 1
示波器 1
上面给facet增加一个新的field:cate_ID(分类 ),以“ad”作为关键字来查询,可以看到共检索出25条数据,同时也得到了这些数据位于5种不同分类及分别的数量。
利用facet进行查询,对于分类搜索提供了一个行之有效的解决方案。
定义更多种类的Query来实现搜索,基本上就跟Django没有关系,完全看对Solr的操作了。
至于分词,可以独立出来看,而且可以选择的方式方法很多,这里拿Pymmseg举个例子:
>>> from pymmseg import mmseg
>>> mmseg.dict_load_defaults()
>>> text = '电子电路电阻电容'
>>> algor = mmseg.Algorithm(text)
>>> for tok in algor:
... print '%s [%d..%d]' % (tok.text, tok.start, tok.end)
...
电子电路
电阻
电容
5.模板显示与排序
对于模板的显示,Django-solr-search也提供了简洁的方式,在定义Document模型的之后加入对应的模板,来看个官方的例子。
在定义模型的时候指定Document对应的模板:
class EntryDocument(solango.SearchDocument):
... Fields ...
class Media:
template = "coltrane/entry_document.html"
... Transforms ...
solango.register(Entry, EntryDocument)
模板:
<div class="searchdocument">
<h3>Entry: <a href="{{document.fields.url.value }}" >{{ document.fields.title.value }}</a> </h3>
<p>
{{ document.highlight|safe }}
</p>
<ul class="sublinks">
<li>at {{ document.fields.date.value|date:"N j Y" }}</li>
<li class="last"><a href="{{document.fields.url.value }}">permalink</a></li>
</ul>
</div>
在其他的页面中使用render_html方法, 引用嵌入定义的Document模板
{% for doc in paginator.results.documents %}
{{doc.render_html|safe}}
{% endfor %}
当然,你也可以像以前一样使用普通的方式渲染模板。
后台代码:
def solr_search(request):
'''
用solr进行全文搜索
'''
keywords = request.GET['q']
q = Query({'facet.field': 'cate_ID'},q=keywords)
r = solr_conn.select(q)
facet_dict = {}
for facet in r.facets:
facet_dict = []
for value in facet.values:
if value.count > 0:
facet_dict.append({'name': value.name, 'value': value.value, 'count': value.count})
return render_to_response('product/productlist_solr.html',
{'documents': r.documents,
'cate_ID_dict': facet_dict['cate_ID'],
'keyword': keywords},
context_instance=RequestContext(request))
模板中引用:
{% for cate_ID in cate_ID_dict %}
<td><a href="/product/category/{{cate_ID.name}}.html">{{cate_ID.name}}({{cate_ID.count}})</a></td>
{% endfor %}
{% autoescape off %}
{% for one in documents %}
<tr class="{% cycle row1,row2 %}">
<td><input type="checkbox" name="checkone" value="{{one.id}}" /></td>
<td align="left" class="iclist"><a href="/productmenu/{{one.id}}.html"><spanclass="partno">{{one.partno}}</span></a></td>
<td>{{one.mfg}}</td>
<td>{{one.desp}}</td>
<td class="red"><a href="/inquire/ask_attach/?partno={{one.id}}">{% trans 'Inquiry' %}</a></td>
</tr>
{% endfor %}
{% endautoescape %}
显示效果:
http://farm3.static.flickr.com/2792/4461259733_c696dab350_o.jpg
排序其实都是通过Solr来控制的,在settings.py中加入下面代码来控制排序:
SEARCH_SORT_PARAMS = {
"score desc": "Relevance",
"date desc" : "Date" # Added date
}
6.索引的更新
索引的更新问题对于一个应用来说也至关重要。这里提供了两种基本的更新方法。
ImmediateIndexer:
这个是默认的即时更新,不需要任何设置。但是lucene的索引创建速度并不快,因此这种方法只适合在数据量比较小,数据写入和删除操作并不多的情况下使用。
DBQueuedIndexer
这个方法是通过在数据库中创建一张表,记录下来数据操作的队列,然后在指定的时间针对这个队列表里的数据进行更新操作。
需要在settings.py加入:
SEARCH_INDEXER = "solango.indexing.DBQueuedIndexer"
定时运行:
python manage.py solr –index-queued
回过头来再看一下Django使用Solr搜索的原理,并不复杂,就是一个完整的爬虫类:
通过 urllib2对Solr服务器的url进行访问,再读取Solr返回的xml文本,对文本解析,封装,最后渲染模板。
以上就是快速的给Django应用加入全文搜索的一个方法,主要还是Django-solr-search的使用。而这个插件应用背景也比较有趣,最初做给是给《华盛顿时报》的网站使用的,这家报纸从创建之初竟然连续20年从未实现过盈利。
官方文档地址:http://www.screeley.com/djangosolr/index.html
除了 Django-solr-search之外,还有很多第三方的全文搜索插件可以使用,例如Haystack 等等,有兴趣也不妨学习一下。
版权声明:本文为博主原创文章,未经博主允许不得转载。
页:
[1]