July 23rd, 2008 by 张磊
关于ultrasphinx的在windows上的安装和简单修改,请看这篇文章:深入探索:Windows+Rails+中文全文检索。
在上一篇文章里,我们初步可以让sphinx、ultrasphinx以及libmmseg正常地工作了。但是这些还不够,因为在实际的使用中,rails的AR之间会有很多关联。比如用户搜索一本图书时,可能希望也搜索它的作者,但如果图书和作者放在两个表呢?我们需要在ultrasphinx中处理这些关联。我在学习时能找到的资料都是英文,今天写这篇文章,希望能对英文不好的同学们有些帮助。
在Rails项目的ActiveRecord定义中,我们通过is_indexed函数告诉ultrasphinx这个Model需要进行索引的字段信息,最简单的写法:
class Book< ActiveRecord::Base
is_indexed :fields =>[:title]
end
这里就表示将title属性交给ultrasphinx去做索引。但如果是一些关联的对象,该怎么做呢?如果使用acts_as_ferret,可以比较灵活,因为索引是绑定在Model的。而ultrasphix的思路是把索引和model独立开来,在特定的时候,使用SQL语句从数据库中获得数据,然后异步生成索引。
那么,如果要加入关联对象的字段,该怎么办呢?上面已经说到,信息是从数据库里得到,然后异步进行索引的。也就是说我们需要进行连接查询,然后获得相应的字段——大体思路就是这样,ultrasphinx提供了一些办法尽量避免让你编写复杂的SQL。
返回刚才的例子,假设每本图书都属于一个出版社,这是一对多的关系。出版社用Press来表示,表示名称的属性是name。我们需要让用户可以一起检索到出版社的名字,ultrasphinx中提供了一个”:include”来帮我们做到:
class Press < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :press
is_indexed :fields=>[:title], :include=>[{:association_name=>'press' , :field=>'name' , :as=>'press_name }]
end
保存后重新运行”rake ultrasphinx:configure”,将在RAILS_ROOT/config/ultrasphinx/目录生成一个新的development.conf。如果有兴趣可以打开看看其中的SQL生成成了什么样子,不过还是建议你别看了,继续看文章吧。之后运行”rake ultrasphinx:index”,就会重新生成索引,将出版社信息也增加进去。可以再运行”rake ultrasphinx:daemon:start”启动伺服器,测试的办法请看这篇文章,不再赘述。
上面交代了belongs_to的时候的处理方式,如果是has_many呢?ultrasphinx为提供了另一个”:concatenate”。假如每本书(Book)都有一些描述信息(Info.content),我们想把这些信息也一起加入到索引中。
class Info < ActiveRecord::Base
belongs_to :book
end
class Book < ActiveRecord::Base
belongs_to :press
has_many :infos
is_indexed :fields => [:title], :include=>[{:association_name=>'press' , :field=>'name' , :as=>'press_name }] ,
:concatenate => [{:association_name=>'infos' , :field=>'content' , :as=>'content'}]
end
重新生成一次配置文件,并重建索引,此时”content”描述已经可以被搜索了,多条content之间用空格分隔,不会影响分词。
一对一的情况类似一对多,还有多对多怎么办呢?因为这个过于复杂,在英文文档里有一句:If the associations weren‘t just has_many and belongs_to, you would need to use the :association_sql key to set up a custom JOIN. Ultrasphinx is not an object-relational mapper, and the association generation is intended to stay minimal—don‘t be afraid of :association_sql.大意是:如果模型之间的关联不是has_many或belongs_to,就需要使用:association_sql来指定一个JOIN的语句。Ultrasphinx不是一个ORM,也不打算在通过关联生成SQL上面花多大力气——不要害怕使用:association_sql。
我承认我意识到要自己写:association_sql的时候还是害怕了。不过自己尝试地写了一下,发现还是比较简单。现在假设一个我要在图书的索引中,加入图书作者的信息(Author.name)。图书和作者的对应关系是典型的多对多:一个作者可能有多本书,一本书可能有多个作者。我们还是用:concatenate:
class Author < ActiveRecord::Base
has_and_belongs_to_many :books
end
class Book < ActiveRecord::Base
belongs_to :press
has_many :infos
is_indexed :fields => [:title], :include=>[{:association_name=>'press' , :field=>'name' , :as=>'press_name }] ,
:concatenate => [
{:association_name=>'infos' , :field=>'content' , :as=>'content'},
{:association_name=>'authors' , :field=>'name' , :as => 'authors' ,
:association_sql => 'JOIN authors ON '+
'authors.id in (select authors_books.author_id from authors_books'+
' where authors_books.book_id=books.id)'
}
]
end
应该还容易理解,不能理解也没事,到时做的时候,直接抄下来改两笔就行。保存之后再按照上面步骤重新生成一次索引,现在”作者”也可以被搜索了。
需要注意的是,不要盲目地把所有东西都接到一个model理然后去建索引,虽然sphinx的效率高到”150万条记录一两分钟就索引完毕,2-4GB以内的文本检索速度不到0.1秒钟“,但过多的JOIN对性能的损耗是很严重的,创建索引速度的瓶颈会转到数据库上,尤其是在大表的连接查询上。如果在建索引时遇到效率的问题,可以到Development.conf中查看一下生成的SQL语句,用explain看看是否需要在相应字段增加索引。
OK,如果还有啥使用中的问题,可以和我联系,我也正在学习。zhanglei909#gmail.com
July 22nd, 2008 by 张磊
萝卜丝不停跑出好成绩,但对于“只和自己比赛”的刘翔,我觉得这还不是什么威胁。2004年的奥运之前,刘翔虽然已经崭露头角,但还是没人看好他。此时中国代表团内部或是刘翔师徒之间,肯定已经知道刘翔有了夺冠的实力。奥运会上跑出12秒91也必然不会是刘翔训练时的最好成绩。
后来孙海平和刘翔转战南北,很多时候都在用孙子兵法。能13秒晋级的绝对不跑进12秒,让外人根本不知道刘翔到底有多高的水平。可是前些日子的受伤,似乎打乱了爷俩的计划;今天更有人辟谣,这一看完了,刘翔怕是真的不行了。
不过,谁知道这次受伤是不是障眼法呢?关于奥运关于祖国,总有那么多雾一样的事情。
July 21st, 2008 by 张磊
先说点题外话,以后大家买独立服务器的时候,还是买linux的吧。如今这么活跃的开源社区为linux贡献了好多柴火,而相应的软件在windows实在发展得缓慢。要是我当初买了linux服务器,以前和现在都能少许多烦恼,今天的“深入探索”,根本就用不着。
事情是这样的,我手头的一个项目里,打算用rmmseg+ferret+acts_as_ferret搭配起来做全文检索。一直也相安无事,直到我今天决定放到生产环境试试看。这一试不要紧,发现DRB服务器启动不起来,提示的是”fork() funtion is unimplemented on this machine”。windows上哪里有fork啊,我顿时欲哭无泪。如果不用DRB的话,就也不能用acts_as_ferret了(原因请看robbin的这个帖子)。
恩,这时我并没有灰心,因为我在前几天看到了另一个解决方案,用的是sphinx,狮身人面。其中有windows版本的安装:
在Windows上安装
在Windows上为Sphinx打补丁、编译、连接libmmseg要比在Linux上做这些事情麻烦得多,而且大多数Windows上的开发人员都没有自己编译开源软件的习惯,幸好李沫南已经做了一个安装包:
http://www.coreseek.com/ft/csft_setup_2.5.1.exe
执行这个安装包即可安装Coreseek的Windows版,假设将Coreseek安装在D:\CsFullText25
将D:\CsFullText25\bin加入到环境变量PATH中,以便以后在命令行能够找到Sphinx提供的各种工具。
安装Ultrasphinx
Sphinx在Linux和Windows上都已经安装好了,我们可以通过一个Rails程序来做一下测试。
假设我们原先有一个Rails应用thought_log
cd thought_log
ruby script/plugin install -x svn://rubyforge.org/var/svn/fauna/ultrasphinx/trunk
若这个Rails应用尚未提交到SVN中,或者使用其他版本管理工具,则使用
ruby script/plugin install svn://rubyforge.org/var/svn/fauna/ultrasphinx/trunk
注意,执行这条命令前需要先安装好SVN for Windows(不是TortoiseSVN)。
当我按部就班执行到红色的那一步,命令行里提示我该svn不存在。我直接打开,也是404错误。这个最重要的rails插件,在我要安装的时候,居然消失了。
百般无奈,只好放一放,出去打个电话。电话回来,突然有了灵感,尝试用”gem install ultrasphinx”,居然安装成功了,不过是作为gem。没关系,我跑到gems目录下,找到ultrasphinx的文件夹,整个复制到rails项目的vendor下面,同时把文件夹改名为ultrasphinx。
果然,gem安装的ultrasphinx就是那个消失的rails插件,不晓得为啥从插件变身gem了。但是探索还没有结束:
为了在Windows上正常使用Ultrasphinx,需要为Ultrasphinx打一点补丁:
修改vendor/plugins/ultrasphinx/tasks/ultrasphinx.rake
在文件最后加入:
def os_path path
if RUBY_PLATFORM =~ /mswin32/
path.gsub!(‘/’, ‘\\’)
end
path
end
然后将其中的:
“searchd –config ‘#{Ultrasphinx::CONF_PATH}’”
改为:
“searchd –config \”#{os_path(Ultrasphinx::CONF_PATH)}\”"
这样修改的原因是类似以下的写法:
searchd –config ‘D:/WORK/thought_log/config/ultrasphinx/development.conf’
在Windows上是无法正常运行的,必须改为:
searchd –config “D:\WORK\thought_log\config\ultrasphinx\development.conf”
除了例子中对searchd的调用,在ultrasphinx.rake文件中所有执行命令行调用的地方都需要做这样的修改。
看出来了吧,需要这么打补丁,是因为人家ultrasphinx根本就是为linux做的。想在windows上跑起来,要做的远远不止这些。
经过我的不断尝试,发现在ultraphinx.rake中,所有涉及到system调用的,全部需要重写。使用”kill -9 #{XXXpid}”的地方,我都改成了”task /f /im searchd.exe”,这样可以确保在使用”rake ultrasphinx:daemon:stop”时能杀掉伺服的进程。但是另一个函数”ultrasphinx_daemon_running?”是通过linux的ps命令查看进程是否存活,我不知道怎么修改,就干脆屏蔽了它,不知道会不会带来别的后果。
在经历大量修改之后,运行”rake ultrasphinx:index”终于可以正常地生成索引了,速度那叫一个快。

运行”rake ultrasphinx:daemon:start”也可以正常启动伺服器,Oh Yeah!

sphinx+ultrasphinx+libmmseg终于可以正常工作了。我把自己修改之后的ultrasphinx在GoogleCode开了一个project,如果大家需要,可以在http://code.google.com/p/ultrasphinx-win32/获得,配合这篇文章,相信,可以让你少走很多弯路。
做完了这些,可以让rails使用sphinx强大的全文检索了。但我们在实际使用中往往会遇到对象间关联的情况,经过我的探索,发现ultrasphinx插件可以经过简单配置就可以支持对象间的关联。当然,在遇到多对多的情况下,还是需要写一些sql作为辅助的。见:如何用Ultrasphinx插件处理对象间关联。
July 21st, 2008 by 张磊

红色魔术贴
在淘宝寻找帆布鞋,第一眼就喜欢上了匡威这款红色的魔术贴。和子宁一合计,一人买了一双。
很久以前,从家里到外面读高中,我是个自卑的孩子。那时穿的衣服大多都是白色米黄色,当然,更多时候是家里买什么我穿什么。后来和子宁交往,开始穿黑色的衣服。黑色也是需要搭配的,全身都是黑色会看起来像是铁板。
如今倒不再拘束穿什么颜色了,因为穿什么都一样,我还是那个微笑。工作之后才了解,为啥智勇会经常去vancl。心情大好,穿点鲜艳的颜色。要是我爸在,又该说我哗众取宠了。
July 20th, 2008 by 张磊
晚上坐在兰州拉面吃一份牛肉炒饭,看到新闻联播里说北京又开始单双号了,还划出了奥运专用车道。
新闻里说,如果是急救的救护车,可以使用奥运专用车道,这充分体现了人文奥运的精神。
没有奥运车道的时候,我们就不人文了?

外国友人
Photo From DBAnotes
July 19th, 2008 by 张磊
子宁在戴尔的网站上买了台戴尔的台式机,没想到在付款上面颇多周折。上个月我打算换本本时也曾考虑过戴尔,那时已经看到戴尔支持支付宝付款。虽然后来选择去淘宝买小黑,但一直都对戴尔印象不错。
我一直觉得,如果在一个网站买东西能让我都觉得困难,他家肯定会很有问题。今天就真遇到问题了。和戴尔的销售代表联系,人家说用支付宝付款需要是“企业帐户”——简直是开玩笑,我觉得她肯定不知道支付宝企业帐户是用来干啥的。在线直接支付的话,又超过了银行卡支付限额。最后没办法,子宁只好打算去拿着现金去银行,办理对公汇款。
让顾客大热天揣着大把现金去银行汇款,对戴尔来说,是一件可耻的事情。就是去中关村配机器,也是可以刷卡的吧。可以在线订购却不能在线付款,这是瘸了腿的电子商务。对戴尔来说,支付宝是个很好的解决方案,只是他们的销售代表根本都不熟悉支付宝,该如何是好?
看到邮件发来的付款方式里面,居然都没有支付宝:
支付方式
1. 现金银行支付
-
· 带上本人身份证
· 到就近的任一银行对公柜台办理(周一至周五8:30-16:00)
· 若交款单有“款项来源“或“备注“或“汇款用途“一栏,请填上您的报价单号: XXXXXXXX
· 付完款请第一时间与我联系告知付款信息,如果方便可将付款底单传真至XXXXXXXX
2. 信用支付卡 (戴尔公司推荐使用)
-
· 戴尔接受工行牡丹卡,建行龙卡,中行长城卡,招商银行卡以及Master、Visa等国内及国际信用卡
· 请告知我相关信用卡资料,我们即可处理您的付款
(为保护信用卡信息安全,请勿通过书面方式传送信用卡信息)
3. 网上银行支付
· 直接登录个人网上银行界面进行转账并第一时间致电告知我付款信息
July 18th, 2008 by 张磊
最近动手练习做点东西,但在数据库的设计上心里又开始嘀咕。不是以前没做过,只是越来越小心。在我还没学过数据库概论的时候,也就是能达到个第二范式的标准,往往还会加点冗余。
后来学了数据库概论,才知道原来自己之前在蒙头探索的东西,早就成了成熟的理论。更往上的范式我没看懂,但第三范式确实看起来干干净净。只是在实际的设计中,究竟有多少是按照第三范式来做的呢?多个表之间复杂的关系,会不会成为性能的杀手?我自己没主意,也没经验——以前在buyren时,我连接查询和缓存都没用过,好像也不慢
主要的疑惑来自两篇文章,一篇是robbin的关系模型和对象模型方面的文章,我以前也曾写过。
JavaEye使用了Rails的ActiveRecord ORM,表设计符合三大范式,所有页面都是动态页面,要对数据库发送大量查询,很多Web页面至少要向数据库发送50条以上的SQL语句。 ……
……
最后,我的结论就是对象模型和关系模型在数据库存储上不存在阻抗不匹配,面向对象的程序设计和面向数据库的程序设计应该是一致的,而不应该是对立和冲突的,请不要把面向对象和面向数据库对立起来,不是他们对立,而是你不了解什么才是真正良好的设计。
另一篇则是Fenng谈Twitter的
数据库方面的经验
尽可能的索引(Fenng补充:不要过度索引). 因为 RoR 应用的特殊性, 索引是在代码中向 DB 提交的. 另外一个值得议题的是, 反范式. 严格遵守范式是要吃苦头的.建立可行的测试方法,明确的知道你的SQL都在用什么方式运行.(另外,我有个疑问是 rails 不支持 2 阶段提交的吧?)
后来Fenng问,使用范式有什么目的?我马上就愣了。去找资料、搜索,从来都只看到说啥是三范式,没从根本上说三范式能起到啥作用。我从下午一直疑惑到现在。写这篇blog,如果牛人能看到,可否为我解答一下?
July 17th, 2008 by 张磊
晚上从游船下来,四周很安静,能听到很远地方的鸟声,以及淙淙的水声。坐在桌前,居然觉得小黑的风扇怎么这么大声,才意识到,实在是周围太安静了。

湖边

湖上

晚霞(能看到不?)

国旗
多普达130W像素的镜头,也只能拍这种效果的照片了。
July 16th, 2008 by 张磊
本来不想写东西,不过看到了这条新闻,于是从游船回来还是忍不住写写。
支付观察第一集:购物返现
支付观察第二集:信用卡还款
在我能了解到的这些电子支付行业的公司里,除了支付宝,快钱这家我是很欣赏的。虽然我一向很少用快钱的服务,但还是很欣赏他的策略。毕竟,支付宝虽然的确服务了淘宝三年,但支付宝最初的用户都是从淘宝获得的,而且现在也还源源不断地从淘宝获得更多用户。淘宝和支付宝,有点像盛大和唐骏
“2004年,陈天桥只想到了,他要让唐骏托一下盛大,没想到,盛大更托了一下唐骏。”
而对块钱来说,颇有点白手起家的感觉,自己去寻找用户,然后把他们拉进来。我之所以说欣赏快钱,就是觉得快钱对策略的选择很合理(我不敢说很好)。支付宝今天都没能让成为当当上可以用的支付手段,而快钱至少在两年前就已经做到了,还和当当合办过一些活动,比如通过快钱支付直减10元或是送礼券啥的。
而快钱之后推出的交水电费及公共事业费、信用卡还款以及购物返现,也都走在了电子支付公司的前列。我善意地猜测支付宝一定也在做类似的服务出来,但为啥落后了?其实还是策略上的差异。支付宝拥抱B2C其实不是从今年开始,也有了很长的历史,如今有了30万的商户;但一直没能进驻卓越当当这种级别的公司,我们可以认为是支付宝没去重视。
B2C和C2C有多大的距离?淘宝的钻石、皇冠卖家难道不能被称为B?支付宝为淘宝服务的3年,其实也是自身不断积累和发展的三年,而如今的电子商务,垂直B2C又有点星星之火的味道。多年以前,人们“上网”是件新鲜而时髦的事;如今,企业“上网”变得越来越时髦。我相信所有的电子支付公司,都看到了这一点。
其实这个行业比以前说的视频网站面临着更大的政策风险,但我依然相信,会有Game Over的一天。到那时说起支付,大家想到的就是支付宝,别的公司再努力,也不过是帮忙撑大这市场。
July 16th, 2008 by 张磊
公司组织Outting,到千岛湖,昨晚在和房途的兄弟们K歌的时候才接到电话说我也可以参加(原来一直是没份儿的)。昨晚一边唱歌一边一瓶一瓶地喝啤酒,最后摇摇晃晃,都不记得喝了几瓶了。
今天早上集合,在车上过了将近3小时,终于到了千岛湖。似乎已经好久都没有坐过这么长时间的汽车(吃饭时同事们还在讨论Fenng会不会晕车)。午餐尚可,在一个叫鱼味馆的地方。之后到酒店,据说是准五星级,感觉很不错,依山而建,还有个小阳台,出去就是一片大湖,和隐隐的远山。

酒店的一个个小房间

湖泊和远山,阳台向外望

房间内景

随身T61
路上一直穿着黑色的衬衣和裤子,挥汗如雨,到酒店终于换上了清凉的短袖和沙滩裤。 Continue reading ‘The lake of a thousand islands’