早些时候就在google到Whoosh和xapian的性能对比文章,只是由于文章被墙,今天才翻墙看到。
文章是xapian作者写的。就文章里的对比结果来看,whoosh和xapian的性能差距还是比较明显。索引和搜索的速度有近4倍的差距,在full cache情况下的性能差距更是达到了60倍。
除算法原因外,whoosh的纯python定位也决定了whoosh很难达到其他c/java的搜索引擎库的速度。
当然,whoosh的优势是易用性,在考虑性能的情况下whoosh不是首先。
注:Xapian performance comparision with Whoosh
分类目录归档:编程
使用嵌入式jetty启动axis提供webservice服务
JDK6已经内置了webservice支持,使用JDK6开发webservice是一件很方便的事。很不幸的是,由于IDE的支持axis成为使用最广的java webservice库。而且由于的部分应用使用了不兼容的RPC/encoded模式,使得你还不得不用axis。
通常情况下用axis开发webservice服务端,需要挂在tomcat等web服务器下,以servlet的方式提供服务。但有些时候我会想将接口部分以一个单独应用程序的方式进行发布,另外再多带个web服务器似乎有些笨拙。
使用嵌入式的jetty是一个比较简单的解决方式。其中需要注意的是,一定要设置Context的ResourceBase,不然是无法找到webservice配置文件的。我当初就是想当然的认为jetty会默认在当前路径下查找配置文件,导致一直无法正确发布服务。
protected static void runJetty() throws Exception {
Server server = new Server();
SocketConnector connector = new SocketConnector();
connector.setMaxIdleTime(30000);
connector.setPort(8000);//jetty的端口
server.addConnector(connector);
ServletHolder axisServletholder = new ServletHolder(new AxisServlet());
ServletHolder axisAdminServletholder = new ServletHolder(new AdminServlet());
Context root = new Context(server, "/", Context.SESSIONS);
root.setResourceBase("./web/");//WEB资源目录,./web/WEB-INF/server-config.wsdd
root.addServlet(axisServletholder, "/servlet/AxisServlet");
root.addServlet(axisServletholder, "/ws/*");//设置webservice的发布目录
root.addServlet(axisServletholder, "*.jws");
root.addServlet(axisAdminServletholder, "/servlet/AdminServlet");
server.start();
}
纯python的全文搜索组件Whoosh
haystack 是 django 全文搜索的一个中间件,可以粘合 django 应用和 solr、xapian、whoosh 全文搜索引擎。
solr和xapian是早就知道的,Whoosh就没听过了。简单的了解后感觉这东西还是非常不错的。whoosh是一个纯python实现的全文搜索引擎。对python应用而言,whoosh的纯python实现,使whoosh的集成会容易很多,而且扩展起来也会容易很多。
下面是对Whoosh官方简介的翻译
Whoosh: 高效的纯python全文搜索组件
Whoosh是一个纯python实现的全文搜索组件。Whoosh不但功能完善,还非常的快。
Whoosh的作者是MattChaput,由Side Effects Software公司开发。项目的最初用于Houdini(Side Effects Software公司开发的3D动画软件)的在线帮助系统。Side Effects Software公司将该项目开源。
主要特性
- 敏捷的API(Pythonic API)。
- 纯python实现,无二进制包。程序不会莫名其妙的崩溃。
- 按字段进行索引。
- 索引和搜索都非常的快 — 是目前最快的纯python全文搜索引擎。
- 良好的构架,评分模块/分词模块/存储模块等各个模块都是可插拔的。
- 功能强大的查询语言(通过pyparsing实现功能)。
- 纯python实现的拼写检查(目前唯一的纯python拼写检查实现)
为啥选择Whoosh
- 纯python实现,省了编译二进制包的繁琐过程。
- python代码比java更容易读懂,而且用起来也更方便。(翻者注:这个容易引发口水)
- 在很多时候易用性比单纯的最求速度更重要。
Whoosh从其他的开源搜索引擎中获取了大量的灵感。 基础构架参考Lucene,使用KinoSearch的索引算法,部分评分算法来自Terrier,英文的词语态变化来自Minion.
django的论坛app
在django的资源页面里有个第三方论坛app的比较页面Django Forum Apps Comparison。一眼望过去,可用的app还不真不少,数下了有15个之多。但真正看下来,似乎很难找到一个让人满意的。
我对论坛app的要求是:
- 得要自带一个还比较漂亮的界面模板。
模板的开发工作在django应用的开发中占了很大的比重,甚至可能比写python代码花的时间还多。可惜的是很多的app应用甚至连个最基础的demo模板都没带。 - 最好支持Richeditor和BBCode。
支持BBCode的app还是有几个的。Richeditor的支持基本上属于模板的范畴,参考条目1,似乎还没看到支持Richeditor的论坛(注:我没每个app都看,不能确定,pybb默认模板带了个Markup的编辑器)。 - 支持附件。
国内的论坛应用大多偏娱乐,用户喜欢在自己的帖子里插入图片等东西。虽然可以再单独提供一个上传附件的组件,让用户上传后再在论坛里引用,但用户体验就要差不少了。
既然找不到满意的app,那就只能自己动手做了。目前计划在pybb(注:pybb的许可协议没看太懂,似乎是类似BSD的)的基础上进行开发。目前的开发计划如下:
- 先给项目换个新名字LBForum
- 换个漂亮些的模板(同时增加richeditor)
考虑过不少模板。- phpbb3 目前最流行的开源论坛程序,css和html写得很不错。但似乎有些复杂了,套用起来有些麻烦。
- phpbb2/javaeye/jforum 这几论坛程序都长得差不多,UI应当都是参照phpbb2(javaeye的老大自己说了javaeye的界面就是仿phpbb3)来做的。其实也不能说这几个论坛的模板有什么不好。更多的是不喜欢里面过多的table。
- discuz5 不是太喜欢discuz7的界面。discuz5(springside常用的那款界面)的界面感觉清爽些,但改了些后发现里面的html和css写的实在不怎么样。
- fluxbb 目前打算用她的模板了。界面给人的感觉不错,html和css写得挺好。界面够简单,要套用模板应当不会太困难。
- 增强附件功能
虽然pybb提供了附件的支持,但功能还是比较弱。 - 增加一个公共的个人信息模块
pybb已提供了一个保存用户个人信息的功能,但通常这些信息会是整个工程所共享的。我觉得这些信息还是单独放到一个专有的app里比较好。这个app可以以代码的方式包括的工程里,需要增加个人信息的时候直接修改model代码。
后记
最后还是选择了完全从头进行开发。pybb里面可以用的东西不多,而且他的开发思路和我还是有些分歧。
JdbcTemplate之BeanPropertyRowMapper
Spring的JdbcTemplate无疑极大的简化了JDBC操作。只是查询出的数据都直接放在MAP里。想要直接从数据库里直接查询出对象的朋友不免要有些失望。
这写天本想给JdbcTemplate做个扩展,让JdbcTemplate自动绑定对象。等写得差不多的时候,很不幸的发现自己只是重复造轮子而已。Spring已经自带了比较完善的解决方案,通过BeanPropertyRowMapper自动绑定数据库的列到对象。BeanPropertyRowMapper的绑定规则是,FiledName相同的绑定,如果绑定失败,将尝试将hiVik转换成hi_vik查找数据库的列。
如果需要查询出一个包含YouVO对象的List只需要做如下操作就可以了。
List objList = DbHelper.getJdbcTemplate().query(sql, new Object[]{}, new BeanPropertyRowMapper(YouVO.class));
java的类ROR框架Play!
Play!一个类ROR的java框架。和Grails不同的是,Play!没有用Groovy等脚本技术进行扩展。直接使用java技术,这对java程序员来说要亲切很多,而且推广阻力也相应的会小不少。
最近简单的了解Play!,感觉确实是挺有意思的一个东西。Play!作为ROR“仿制品”,在开发思想方面和ROR还是比较接近。Play!本身提供了不少开发相关的辅助命令。使用命令创建出的新工程直接就可以运行了。相比之下,基于ssh的开发,光是脚手架的搭建就得费不少事。
数据模型方面使用JPA定义数据对象,直接从对象生成数据库。这点和django比较像,这也是比较符合我开发习惯的一个做法。
模板方面和Django类似,支持模板的继承。Django模板的继承给我的体验很好。jsp页面虽然可以使用include实现复用,但对于结构相似的页面依旧需要重复的include。Sitemesh虽然可以实现类似django模板的功能但看到那繁琐的配置我就撤了。
总的来说Play!给我的印象还是挺不错的,希望在日后的工作中可以用到。
其他
Play!的FAQ里有一个解答让我挺印象深刻的。其中有关于为什么使用不符合java规范的play做包名的说明。play!本身就是一个平台,运行在play!上的东西都将是符合play!规范的,所以不必为play这个包名而计较。play!专注于web的敏捷开发,为了适应web敏捷的需要一些无伤大雅的“另类”做法也是未尝不可的。想自己在某些情况下对代码还是有些所谓的“洁癖”。在django开发的时候喜欢在自己的app外面还要另外加一层包,而这种做法在一定程度上是不符合django做法的(django的app名必须唯一,即使上层的包名不同也没用)。
url_helper简化Django的url配置
django的url采用正则表达式进行配置,虽然强大却也广为诟病。反对者们认为django的url配置过于繁琐,且不支持默认的路由功能。
我倒觉得还好,只是如果觉得不爽,为什么不自己小小的hack一下,反正也就几行代码的事。
在这个背景下,我整了这个url_helper,利用url_helper可以简化配置和实现url的默认路由。所谓的url_helper其实就只有url_helper.py一个文件,使用的时候只想要import就可以。
url_helper的具体用法请参考具体的例子:
下面对使用方法做个简单的说明。
url的默认路由
from url_helper import execute, url_
import views
urlpatterns += patterns('',
url(r'^(?P.*)', execute, {'views': views}),
)
在urls.py里增加如下配置,其中views为需要进行路由的views模块。url的规则为 /action/param1/param2/…/ 。
例如:
#/edit/4/
def edit(request, n="id"):
html = """ edit object: %s""" % n
return HttpResponse(html)
在没有指定action的时候默认使用的action为index。
提供函数url_简化url配置
仿照ROR的做法,参数用”:”标识。
例如:
url_(r’/space/:username/:tag/’, views.url_), 对应的django url配置为url(r’^space/(?P<username>[^/]+)/(?P<tag>[^/]+)/$’, views.url_),
#url_(r’/space/:username/:tag/’, views.url_),
#/space/vicalloy/just/
def url_(request, username, tag):
html = """ username: %s
tag: %s""" % (username, tag)
return HttpResponse(html)
url_helper的完整代码
就如前面说的,代码非常少。不过实际应用的话,应当还需要做一些扩展。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from django import http
from django.conf.urls.defaults import url
import re
def execute(request, urls, views):
"""
urls [methodName/]param1/param2/.../
methodName default index
"""
def get_method(views, methodName):
try:
return getattr(views, methodName)
except Exception, e:
return None
method = None
params = [e for e in urls.split("/") if e]
params.reverse()
if params:
method = get_method(views, params.pop())
if not method:
method = get_method(views, 'index')
if not method:
raise http.Http404('The requested admin page does not exist.')
return method(request, *params)
def url_(*args,**dic):
regex = args[0]
if regex[0] == "/":
regex = regex[1:]
regex = '^' + regex
regex = regex + '$'
regex = re.sub(":[^/]+",
lambda matchobj: "(?P<%s>[^/]+)" % matchobj.group(0)[1:],
regex)
return url(regex, *args[1:], **dic)
[Django]增强的创建app的命令
简介
虽然django的admin漂亮的实现了CRUD,不过在有时候admin并不是这么好用。然后开始进行手写CRUD,接着发现自己又陷入了重复操作中。相比而言Ruby自动生成的添加删除功能就好不少,你生成的基础框架是可以扩展的。
为了减少手动书写CRUD的工作量,我写了一个扩展的startapp command。使用这个命令会自动生成 list/new/edit 的操作和html文件(这样修改起来就方便多了)。
使用说明
如需要在其他工程使用该命令,只需要将项目中的django_extensions文件夹复制到其他工程的app目录,并在settings.py里把django_extensions添加到app列表里。该扩展命令为create_app,使用方法和django官方的startapp一致(如:manage.py create_app blog)。
命令演示
为了方便演示,该扩展命令包含了一个演示工程(hidjango)。下面的步骤是windows平台(因为用了几个bat)。
- 运行\hidjango\scripts\create_app.bat。根据提示输入app的名称,如blog。
- 打开\hidjango\settings.py,在app列表里添加 hidjango.blog 。
- 运行\hidjango\scripts\syncdb.bat,初始化数据库。
- 修改\hidjango\urls.py,添加app的映射 (r’^’, include(‘hidjango.blog.urls’)), 。
- 运行\hidjango\scripts\runserver.bat启动开发服务器。
- 在浏览器输入http://127.0.0.1:7000/访问页面,并可以实现完整的添加删除操作。
注:
- command的py代码大部分都是从django-command-extensions(http://code.google.com/p/django-command-extensions)里复制过来的,我只做了少部分的修改。
- 该命令还比较简单,还有很多可以扩展的地方。比如app_name不一定和model_name相同等。
模板文件预览:
/django_extensions/conf/app_template/
|~templates/
| `~{{ app_name }}/
| |-base.html
| |-edit.html
| |-list.html
| `-new.html
|~templatetags/
| `-__init__.py
|-__init__.py
|-admin.py
|-forms.py
|-models.py
|-tests.py
|-urls.py
`-views.py
django-tagging的使用方法
django使用app机制来实现组件的重用,充分的利用已有的app可以极大的简化开发工作。目前django下的app虽然还不够丰富,却也还是有部分不错的。django-tagging就是一个不错的app。
现在tag的应用非常广泛,tag基本上成了各网站的必备项目之一,django-tagging就是一个提供tag功能的app。django-tagging提供的功能非常丰富,使用起来却十分简单。下面我就介绍一些常用的用法,让大家对该app有个基本的了解,更详细的介绍还是老老实实的去看django-tagging的使用说明吧:)。
tagging.fields.TagField
我们先定义一个数据库模型Widget,下面的范例都用Widget来进行说明
class Widget(models.Model):
name = models.CharField(max_length=50)
tags = TagField()
就如上面的代码,只要在数据库模型中增加tags字段就可以为该对象提供tag支持了。tags被映射为CharField,在为对象添加tag时为,英文逗号分割的字符串如:
Widget(name='hello', tags='test,hi,hello')
这样就为新建立的对象添加了test hi hello三个tag了。
获取某个tag下的所有对象的代码如下:
#取出所有属于TAG hi的对象
tag = get_object_or_404(Tag, name='hi')
widgets = TaggedItem.objects.get_by_model(Widget, tag)
如要取出Widget用到的所有tag的代码为:
tags = Widget.tags.all()
提升pystardict对stardict字典文件的加载速度
stardict是linux下使用最广的字段程序,在广大网友的贡献下,stardict的字典文件可是相当的丰富。pystardict是一个读取startdict字典文件的python库。
前些天在邮件列表看到有人提到用pystardict加载stardict的字典文件速度慢的问题。加载字段文件时需要解析字段的索引文件(.idx)取出所有的单词信息。但python未提供指针,处理速度远比不上c。
我尝试用正则表达式对索引解析部分的代码进行重写,经测试,速度应当能提高3/5的样子。感觉依旧不是太理想,不知道是否还有什么别的办法。
我将改动后的代码生成了一个patch发给pystardict的作者,不知道是否会被采用。
下面是idx解析的关键代码(idx的结构确实是非常的简单):
import re
rawstr = r"""([\d\D]+?\x00[\d\D]{8})"""
matchstr = self._file
match_obj = re.findall(rawstr, matchstr)
for e in match_obj:
c = e.find('\x00')
record_tuple = unpack('!%sc1x%sL' % (c, idx_offset_format), e)
word, cords = ''.join(record_tuple[:c]), record_tuple[c:]
self._idx[word] = cords
后记
今天收到原作者的邮件,我提交的patch已经接收了,新的pystardict已经更新过。不过他用的是我早些提交的patch。那个patch里,我unpack的时候没有做跳过\x00的处理,所以要稍微丑点。