一般使用 Lucene 来做全文搜索时,都会碰到这样一个问题,什么时候创建、更新或者删除索引。
假设是发帖子吧,如果一发贴就即时写入索引,好处是索引及时,但这会引起索引库被锁的问题,因为同一个时间可能很多人都在发帖,更新和删除都是同样的问题存在。
还有另外一个方法是后台定时将新增的帖子写入索引库,这种可以避免索引库被锁的问题,不过刚发的帖子就没法被索引到(这个不是大问题,一般都可以接受),但是对一些帖子的修改和删除,处理起来就比较麻烦,你不知道哪些帖子被改了,或者被删了。
OSChina 现在的做法是引入一个 LuceneTask 类,代表要执行的一次索引任务,这个任务可以是添加、修改或者删除,每次新增帖子、修改帖子或者删除帖子时,只是对应的往数据库的一个表里写入一条 LuceneTask 数据即可。而后台通过 crontab 每隔一分钟从 LuceneTask 表中检索出待处理的索引任务记录,处理完毕后将该记录的状态改为已完成。
LuceneTask 类的代码如下:
package net.oschina.search; import java.util.Date; import my.db.QueryHelper; import my.search.SearchEnabled; import net.oschina.beans.Article; import net.oschina.beans.Blog; import net.oschina.beans.News; import net.oschina.beans.Pojo; import net.oschina.beans.Project; /** * Lucene索引更新任务 * @author Winter Lau * @date 2010-1-4 下午04:57:47 */ public class LuceneTask extends Pojo { public final static transient byte OPT_ADD = 0x01; public final static transient byte OPT_UPDATE = 0x02; public final static transient byte OPT_DELETE = 0x04; /** * 返回对应的对象资料 * @return */ public SearchEnabled object(){ switch(obj_type) { case TYPE_PROJECT: return (Project)Project.INSTANCE.Get(obj_id); case TYPE_NEWS: return (News)News.INSTANCE.Get(obj_id); case TYPE_THREAD: return net.oschina.beans.Thread.Read(obj_id); case TYPE_ARTICLE: return (Article)Article.INSTANCE.Get(obj_id); case TYPE_BLOG: return (Blog)Blog.INSTANCE.Get(obj_id); } return null; } public void afterBuild(){ String sql = "UPDATE osc_lucene_tasks SET status=1,handle_time=NOW() WHERE id=?"; QueryHelper.update(sql, getId()); } public static void add(long obj_id, byte obj_type) { new LuceneTask(obj_id, obj_type, OPT_ADD).Save(); } public static void update(long obj_id, byte obj_type) { new LuceneTask(obj_id, obj_type, OPT_UPDATE).Save(); } public static void delete(long obj_id, byte obj_type) { new LuceneTask(obj_id, obj_type, OPT_DELETE).Save(); } public LuceneTask() { } public LuceneTask(long obj_id, byte obj_type, byte opt) { this.obj_id = obj_id; this.obj_type = obj_type; this.opt = opt; this.status = 0; } private long obj_id; private byte obj_type; private byte opt; private byte status; private Date create_time; private Date handle_time; public long getObj_id() { return obj_id; } public void setObj_id(long obj_id) { this.obj_id = obj_id; } public byte getObj_type() { return obj_type; } public void setObj_type(byte obj_type) { this.obj_type = obj_type; } public byte getOpt() { return opt; } public void setOpt(byte opt) { this.opt = opt; } public byte getStatus() { return status; } public void setStatus(byte status) { this.status = status; } public Date getCreate_time() { return create_time; } public void setCreate_time(Date create_time) { this.create_time = create_time; } public Date getHandle_time() { return handle_time; } public void setHandle_time(Date handle_time) { this.handle_time = handle_time; } }其中 obj_id 代表帖子的编号,obj_type 代表帖子的类型,可以是帖子,可以是新闻,也可以是软件。
这个结构其实挺适合 Lucene 实现索引的各类项目,所以贴出来分享一下。
Austin Liu 刘恒辉
Project Manager and Software Designer E-Mail:lzhdim@163.com Blog:https://lzhdim.cnblogs.com 欢迎收藏和转载此博客中的博文,但是请注明出处,给笔者一个与大家交流的空间。谢谢大家。 |