表1.1 Lucene版本发布历史
版本 |
发布日期 |
里程碑 |
0.01 |
2000年3月30日 |
在SourceForge网站第一次开源发布 |
0.04 |
2000年4月19日 |
包含基于语法的语汇单元化StandardTokenizer等 |
1.0 |
2000年10月日 |
修复bug,性能优化 |
1.01b |
2001年6月2日 |
在SourceForge网站最后一次发布,修复bug,支持前缀查询 |
1.2 rc1 |
2001年10月2日 |
在Apache Jakarta第一次发布 |
1.2 rc2 |
2001年10月19日 |
发布源代码,修复bug |
1.2 rc3 |
2002年1月27日 |
修复bug |
1.2 rc4 |
2002年2月14日 |
修复bug |
1.2 rc5 |
2002年5月14日 |
新增MultiFieldQueryParser等,修复bug |
1.2 rc6 |
2002年6月13日 |
修改QueryParser支持?通配符 |
1.3 rc1 |
2003年3月24日 |
修改QueryParser支持范围查询,修复bug |
1.3 rc2 |
2003年10月22日 |
新增CachingWrapperFilter和PerFieldAnalyzerWrapper等,修复bug |
1.3 rc3 |
2003年11月25日 |
支持minMergeDocs,修复bug |
1.3 final |
2003年12月26日 |
修复bug |
1.4 rc1 |
2004年3月29日 |
修改.tis文件格式,新增ParallelMultiSearcher等 |
1.4 rc2 |
2004年3月30日 |
修复bug |
1.4 rc3 |
2004年5月11日 |
修复bug |
1.4 final |
2004年7月1日 |
修复bug,更新部分API实现 |
1.4.1 |
2004年8月2日 |
修复bug |
1.4.2 |
2004年10月1月 |
修复bug,优化IndexSearcher |
1.4.3 |
2004年12月7日 |
修复bug |
1.9 rc1 |
2006年2月21日 |
新增MMapDirectory等,修复bug |
1.9.1 final |
2006年2月27日 |
兼容1.4.3之后的版本,修复bug |
1.9.1 |
2006年3月2日 |
修复bug |
2.0.0 |
2006年6月1日 |
修复bug,性能优化,不在兼容1.4.3版本 |
2.1.0 |
2007年2月17日 |
新增FieldSelector等,修复bug,性能优化 |
2.2.0 |
2007年6月19日 |
新增BoostingTermQuery等,修复bug,性能优化 |
2.3.0 |
2008年1月23日 |
新增SpanQueryFilter等,修复bug,性能优化 |
2.3.1 |
2008年2月22日 |
修复bug |
2.3.2 |
2008年5月6日 |
修复bug |
2.4.0 |
2008年10月8日 |
新增QueryAutoStopWordAnalyzer等,修复bug,性能优化 |
2.4.1 |
2009年3月9日 |
修复bug |
2.9.0 |
2009年9月25日 |
新增FieldCacheRangeFilter等,修复bug,性能优化 |
2.9.1 |
2009年11月6日 |
修复bug |
2.9.2 |
2010年2月26日 |
修复bug,性能优化 |
2.9.3 |
2010年6月18日 |
修复bug,性能优化 |
2.9.4 |
2010年12月3日 |
修复bug,性能优化 |
3.0.0 |
2009年11月25日 |
新增AttributeFactory等,修复bug,性能优化 |
3.0.1 |
2010年2月26日 |
修复bug,性能优化 |
3.0.2 |
2010年6月18日 |
修复bug,性能优化 |
3.0.3 |
2010年12月3日 |
修复bug,性能优化 |
3.1.0 |
2011年3月31日 |
新增ReusableAnalyzerBase等,修复bug,性能优化 |
3.2.0 |
2011年6月3日 |
新增TieredMergePolicy等,修复bug,性能优化 |
3.3.0 |
2011年7月1日 |
新增TwoPhaseCommitTool等,修复bug,性能优化 |
3.4.0 |
2011年9月14日 |
新增FixedBitSet等,修复bug,性能优化 |
3.5.0 |
2011年11月27日 |
新增IndexSearcher.searchAfter等,修复bug,性能优化 |
3.6.0 |
2012年4月12日 |
新增FieldValueFilter等,修复bug,性能优化 |
3.6.1 |
2012年7月22日 |
修复bug,性能优化 |
3.6.2 |
2012年12月25日 |
修复bug |
4.0.0-alpha |
2012年7月3日 |
新增RegexpQuery等,修复bug,性能优化 |
4.0.0-beta |
2012年8月13日 |
新增BloomFilteringPostingsFormat等,修复bug,性能优化 |
4.0.0 |
2012年10月12日 |
新增BlockPostingsFormat等,修复bug |
4.1.0 |
2013年1月22日 |
新增AnalyzingSuggester和FuzzySuggester等,性能优化 |
4.2.0 |
4.3.0 |
4.4.0 |
zoie最新版本只支持lucene3.5 lucene3.6+目前不支持,zoie最新版本为zoie-core-3.3.0
spring配置文件:
<bean id="userIndexJob" class="org.summercool.platform.searchengine.indexjob.UserIndexJob"
init-method="init" destroy-method="destroy">
<property name="zoieBatchDelay" value="120000"/>
<property name="zoieBatchSize" value="3000"/>
<property name="userIndexPath" value="WEB-INF/classes/dataIndex/user/"></property>
</bean>
检索部分:
public class UserIndexJob {
private Log log = LogFactory.getLog(UserIndexJob.class);
public static final long MAX_INCREMENT_INDEX_NUMBER = 1300000; // 最大增量索引资源数
public String userIndexPath;
private IdCompator idCompator = new IdCompator();// id比较器
private long _currentVersion = 0L;
@SuppressWarnings("rawtypes")
public ZoieSystem zoieSystem;
//批量大小:即队列中放入多少项方才触发索引
// 内存中的大小
private int zoieBatchSize;
//批量延时:即等待多长时间方才触发索引
// 最大延迟时间(单位:毫秒)
private int zoieBatchDelay;
private float docboost = 1.0f;
private int rows = 20000;
public String lastUpdateTime;
@SuppressWarnings({ "unchecked", "rawtypes" })
public void init(){
try {
System.out.println("UserIndexJob init start");
//索引文件夹
File idxDir = new File(userIndexPath);
if(!idxDir.exists()){
idxDir.mkdir();
}
//数据解析器
ZoieIndexableInterpreter interpreter = new MyUserInfoDataInterpreter();
//设置翻译器
DefaultIndexReaderDecorator decorator = new DefaultIndexReaderDecorator();
PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(new IKAnalyzer());
ZoieConfig zoieConfig = new ZoieConfig();
zoieConfig.setBatchDelay(zoieBatchDelay);
zoieConfig.setBatchSize(zoieBatchSize);
zoieConfig.setAnalyzer(analyzer);//设置分词器
zoieConfig.setSimilarity(new DefaultSimilarity());//设置相似性评分器
zoieConfig.setRtIndexing(true);
zoieSystem = new ZoieSystem(idxDir, interpreter, decorator, zoieConfig);
zoieSystem.start();
zoieSystem.getAdminMBean().flushToDiskIndex();
System.out.println("UserIndexJob init end");
setLastUpdateTime(); // 设置上次更新时间
} catch (Exception e) {
e.printStackTrace();
log.error(e, e);
}
}
@SuppressWarnings("unchecked")
public void doUpdateIndexData(){
try {
System.out.println("执行job start");
// 增量索引,检查资源是否有更新
UserInfoDao userInfoDao = ServiceFactory.getBean(UserInfoDao.class);
String maxUpdateTime = userInfoDao.getUserLastModify();
if (StringUtils.isNotBlank(lastUpdateTime) && lastUpdateTime.equals(maxUpdateTime)) { // 无资源更新
System.out.println("======user index data no update!========");
return;
}
Thread.sleep(1000);
long maxId = 0;
long total = 0;
int start = 0;
if (StringUtils.isNotBlank(lastUpdateTime)){// 增量索引
// 得到增量索引量
long increNum = userInfoDao.getIncrementIndexResNumber(lastUpdateTime, maxUpdateTime);
if (increNum > MAX_INCREMENT_INDEX_NUMBER) { // 转换成全量索引
lastUpdateTime = null; // 全量索引
}
}
if(StringUtils.isBlank(lastUpdateTime)){
maxId = userInfoDao.getMaxID();
}
long begin = System.currentTimeMillis();
while (true) {
List<UserInfo> resList = userInfoDao.findUserIndexInfo(lastUpdateTime,maxUpdateTime,start, rows);
if (resList.size() > 0) { // 索引资源
List<DataEvent<Document>> dataEventList = transform(resList);
if (null != dataEventList && !dataEventList.isEmpty()) {
zoieSystem.consume(dataEventList);
}
}
total += resList.size();
System.out.println("=========user index increment num:" + total);
if (resList.size() < rows) {
break;
}
if (null == lastUpdateTime) { // 全量索引
if (resList.size() > 0) {
Collections.sort(resList, idCompator); // 排序
if (resList.get(0).getUserId() >= maxId) { // 全量索引完成
break;
}
}
}
start = start + rows;
}
long end = System.currentTimeMillis();
lastUpdateTime = maxUpdateTime;
System.out.println("============user index increment total num:" + total
+ ",elasped time " + ((end - begin) / 1000) + " seconds");
} catch (Exception e) {
e.printStackTrace();
log.info(e.getMessage(),e);
}
}
private List<DataEvent<Document>> transform(List<UserInfo> resList) {
List<DataEvent<Document>> dataEventList = new ArrayList<DataEvent<Document>>();
if (resList != null && !resList.isEmpty()) {
for (UserInfo user : resList) {
Document doc = new Document();
doc.setBoost(docboost);
doc.add(new Field("userId", user.getUserId().toString(), Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("headImg", StringUtils.isNotBlank(user.getHeadImg())?user.getHeadImg() : "", Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("nickName", StringUtils.isNotBlank(user.getNickName())?user.getNickName():"", Field.Store.YES,Field.Index.ANALYZED));
doc.add(new Field("loginName", StringUtils.isNotBlank(user.getLoginName())?user.getLoginName():"", Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("checkinCount", String.valueOf(user.getCheckinCount()), Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("favoriteCount", String.valueOf(user.getFavoriteCount()), Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("sex", String.valueOf(user.getSex()), Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("updatedTime", StringUtils.isNotBlank(user.getUpdatedTime())?user.getUpdatedTime():"", Field.Store.YES,Field.Index.NOT_ANALYZED));
dataEventList.add(new DataEvent<Document>(doc, "1.0"));
}
}
return dataEventList;
}
@SuppressWarnings("unchecked")
private void setLastUpdateTime() {
List<ZoieIndexReader<IndexReader>> zoieReaderList = null;
MultiReader multiReader = null;
IndexSearcher indexSearcher = null;
try {
zoieReaderList = zoieSystem.getIndexReaders();
multiReader = new MultiReader(zoieReaderList.toArray(new IndexReader[zoieReaderList.size()]), false);
indexSearcher = new IndexSearcher(multiReader);
indexSearcher.setSimilarity(new AppSimilarity());
Sort sort = new Sort(new SortField("updatedTime",SortField.STRING, true));
TopDocs topDocs = indexSearcher.search(new MatchAllDocsQuery(), 1, sort);
if (topDocs.totalHits == 0) {
System.out.println("======search user index path no results======");
return;
}
Document doc = indexSearcher.doc(topDocs.scoreDocs[0].doc);
lastUpdateTime = DateUtil.formateDate(doc.get("updatedTime"), 1);
System.out.println("lastUpdateTime:"+lastUpdateTime);
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(null !=indexSearcher){
indexSearcher.close();
indexSearcher = null;
}
if (null != multiReader) {
multiReader.close();
multiReader = null;
}
if (null != zoieReaderList && !zoieReaderList.isEmpty()) {
zoieSystem.returnIndexReaders(zoieReaderList);
zoieReaderList = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void destroy(){
zoieSystem.shutdown(); // 将内存索引刷新到磁盘索引中
System.out.println("destroy method");
}
private static class IdCompator implements Comparator<UserInfo> {
public int compare(UserInfo o1, UserInfo o2) {
if (o2.getUserId() > o1.getUserId()) {
return 1;
} else if (o2.getUserId() < o1.getUserId()) {
return -1;
}
return 0;
}
}
public String getZoieVersion() {
return Long.toString(_currentVersion);
}
public String getMinZoieVersion() {
return Long.toString(0L);
}
public String nextZoieVersion() {
return Long.toString(++_currentVersion);
}
public String getUserIndexPath() {
return userIndexPath;
}
public void setUserIndexPath(String userIndexPath) {
if(StringUtils.isNotBlank(userIndexPath)){
this.userIndexPath =SearchEngineCore.getResourcePath(userIndexPath)+ File.separator + "userIndex";
}else{
this.userIndexPath = SearchEngineCore.getIndexpath("VSOYOU_USER_INDEX_PATH")+ File.separator + "userIndex";
}
}
@SuppressWarnings("rawtypes")
public ZoieSystem getZoieSystem() {
return zoieSystem;
}
@SuppressWarnings("rawtypes")
public void setZoieSystem(ZoieSystem zoieSystem) {
this.zoieSystem = zoieSystem;
}
public int getZoieBatchSize() {
return zoieBatchSize;
}
public void setZoieBatchSize(int zoieBatchSize) {
this.zoieBatchSize = zoieBatchSize;
}
public int getZoieBatchDelay() {
return zoieBatchDelay;
}
public void setZoieBatchDelay(int zoieBatchDelay) {
this.zoieBatchDelay = zoieBatchDelay;
}
}
实时搜索部分:
public class UserSearch {
private UserIndexJob userIndexJob;
@SuppressWarnings("unchecked")
public Map<String, Object> seachUser(String searchWord, int page, int pageSize) {
Map<String, Object> map = new HashMap<String, Object>();
map.put(Const.HEADIMG_DOMAIN_KEY,Const.HEADIMG_DOMAIN_VALUE);
List<ZoieIndexReader<IndexReader>> zoieReaderList = null;
MultiReader multiReader = null;
IndexSearcher indexSearcher = null;
TopDocs topDocs = null;
try {
searchWord = SearchUtil.wmlEncode(searchWord);
searchWord = SearchUtil.traditionalToSimple(searchWord).trim();// 繁体转简体
zoieReaderList = userIndexJob.zoieSystem.getIndexReaders();
multiReader = new MultiReader(zoieReaderList.toArray(new IndexReader[zoieReaderList.size()]), false);
indexSearcher = new IndexSearcher(multiReader);
indexSearcher.setSimilarity(new DefaultSimilarity());
BooleanQuery allQuery = new BooleanQuery();
QueryParser parser = new QueryParser(Version.LUCENE_35,"nickName",new IKAnalyzer());
Query query = parser.parse(searchWord);
query.setBoost(100.0f);
allQuery.add(query, BooleanClause.Occur.SHOULD);
QuerySort keywordQuerySort = getKeywordQuerySort(searchWord);
keywordQuerySort.query.setBoost(50.0f);
allQuery.add(keywordQuerySort.query, BooleanClause.Occur.SHOULD);
topDocs = indexSearcher.search(allQuery, page*pageSize, keywordQuerySort.sort);
if(topDocs == null || topDocs.totalHits ==0){
map.put("list", null);
return map;
}
map.put("pageCount", getPageCount(topDocs.totalHits,pageSize));
ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 搜索返回的结果集合
//查询起始记录位置
int begin = (page - 1)*pageSize ;
//查询终止记录位置
int end = Math.min(begin + pageSize, scoreDocs.length);
List<UserInfo> userInfos = addHits2List(indexSearcher,scoreDocs,begin,end);
map.put("list", userInfos);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (null != indexSearcher) {
indexSearcher.close();
indexSearcher = null;
}
if (null != multiReader) {
multiReader.close();
multiReader = null;
}
if (null != zoieReaderList && !zoieReaderList.isEmpty()) {
userIndexJob.zoieSystem.returnIndexReaders(zoieReaderList);
zoieReaderList = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return map;
}
private static List<UserInfo> addHits2List(IndexSearcher indexSearcher,ScoreDoc[] scoreDocs, int begin, int end) {
List<UserInfo> userInfos = new ArrayList<UserInfo>();
try {
for (int i = begin; i < end; i++) {
int docID = scoreDocs[i].doc;
Document doc = indexSearcher.doc(docID);
UserInfo userInfo = new UserInfo();
userInfo.setCheckinCount(Integer.valueOf(doc.get("checkinCount")));
userInfo.setFavoriteCount(Integer.valueOf(doc.get("favoriteCount")));
userInfo.setHeadImg(doc.get("headImg"));
if(StringUtils.isNotBlank(doc.get("nickName"))){
userInfo.setNickName(doc.get("nickName"));
}else{
userInfo.setNickName(doc.get("loginName"));
}
userInfo.setLoginName(doc.get("loginName"));
userInfo.setSex(Integer.valueOf(doc.get("sex")));
userInfo.setUserId(Long.valueOf(doc.get("userId")));
userInfos.add(userInfo);
}
} catch (IOException e) {
e.printStackTrace();
}
return userInfos;
}
private static QuerySort getKeywordQuerySort(String searchWord) {
QuerySort querySort = new QuerySort();
querySort.query = new PrefixQuery(new Term("loginName", searchWord));
//先按记录的得分排序,然后再按记录的签到总数倒序,收藏总数倒序
querySort.sort = new Sort(new SortField[] {
new SortField(null,SortField.SCORE,false),
new SortField("checkinCount", SortField.INT, true),
new SortField("favoriteCount", SortField.INT, true)
});
return querySort;
}
private int getPageCount(int rowCount, int pageSize) {
int pageCount = 1;
if ((rowCount % pageSize) == 0) {
pageCount = rowCount / pageSize;
} else {
pageCount = rowCount / pageSize + 1;
}
if (pageCount == 0) {
pageCount = 1;
}
return pageCount;
}
public UserIndexJob getUserIndexJob() {
return userIndexJob;
}
public void setUserIndexJob(UserIndexJob userIndexJob) {
this.userIndexJob = userIndexJob;
}
}
相关推荐
在信息检索领域,Lucene是一个广泛使用的全文搜索引擎库,其强大的索引能力和高效的搜索性能为开发者提供了强大的支持。然而,随着数据量的不断增长,如何高效地进行增量索引,即只对新增或更新的数据进行索引,而...
本篇文章将详细介绍如何利用Lucene 5和Zoie来构建一个能够实现实时或近实时索引更新的系统。 一、Lucene 5:全文搜索引擎的基石 Lucene是一款开源的Java库,它提供了高性能、可扩展的文本搜索功能。在Lucene 5版本...
它维护了两个索引,一个是热索引,用于处理新数据和实时查询;另一个是冷索引,用于批量查询和历史数据的访问。这种设计兼顾了实时性和稳定性,使得Zoie在处理大量并发请求时依然能保持高效的性能。 尽管Zoie的文档...
**Lucene 概述** Lucene 是一个高性能、全文本搜索库,由 Doug Cutting 创建并在 2001 年...开发者可以利用 Lucene 构建自己的搜索引擎,而 LinkedIn 通过支持 Bobo 和 Zoie 等项目,进一步推动了实时搜索技术的发展。
zoie, 实时搜索/索引系统 什么是 ZoieZoie是用Java编写的实时搜索/索引系统。维基维基在以下位置可用:http://linkedin.jira.com/wiki/display/ZOIE/Home问题问题在以下位置跟踪:http://link
4. **响应式设计**:随着移动设备的普及,CSS媒体查询@media用于根据设备特性调整样式,确保网站在各种屏幕尺寸下都能正常显示。 5. **定位机制**:static、relative、absolute、fixed等定位方式及其应用场景,帮助...
- **Solr**、**Elasticsearch**、**Zoie**、**BoCo**等:基于Lucene开发的搜索引擎解决方案,各有特色。 - **Sphinx**:一款高性能的全文检索引擎,适用于大数据量的实时搜索。 - **Xapian**:一种高度可扩展的全文...
- 搜索索引和检索引擎基于开源项目Lucene构建,而实时索引采用了Zoie技术。 - 数据接入可能利用DataStream进行同步,支持HTTP/REST API和Java SDK等多种服务接口。 - 系统配置管理使用了Zookeeper工具,以实现...
- **海量数据实时搜索**:结合HBase和Solr提供千亿级别的数据实时查询和全文检索。 - **海量数据查询**:基于Hive和Pig提供Web界面的海量数据可视化查询服务。 #### 第五篇:重点产品介绍——海狗系统(ARSC) **...
- **Zoie**:基于Lucene构建,来自LinkedIn,适用于中小规模的实时搜索需求。 - **Sphinx**:自1.10-beta版本起支持实时搜索功能,被广泛应用于craigslist、netlog等网站。 通过以上分析可以看出,实时搜索不仅是一...
- 在实时更新过程中,确保全量和实时数据的一致性至关重要。这可能涉及到软提交(SoftCommit)和硬提交(HardCommit),以及使用像Zoie这样的系统来管理多个索引版本。 8. **数据处理模型**: - 数据模型设计包括将...
3. **实时搜索设计**:如Zoie系统,结合内存和磁盘,提供快速的搜索响应。 4. **Lucene TF/IDF公式**:TF(Term Frequency)衡量词频,IDF(Inverse Document Frequency)衡量词的重要性,共同决定文档相关性。 5....
The ewhine search system use zoie. #安装 git clone :jimrok/ewhine_search.git cd ewhine_search java -jar server.jar 如果在Mac的控制台下,使用 java -Dfile.encoding=UTF-8 -jar server.jar #索引 在ewhine_...
张琨分享的内容涉及了“三人行网络教育平台”的Web架构设计和实现,本文将围绕这一主题,深入讲解服务器拓扑结构、缓存系统设计、存储设计架构以及具体服务与应用的设计。 首先,总体架构的设计应考虑满足当前业务...
这个项目可能包含了用户Zoie Boyd的个人信息、作品集、博客文章或者其他相关的内容。 【描述】"zoieboyd.github.io" 的描述没有给出具体的细节,但从一般的GitHub网页实践来看,这可能是Zoie Boyd利用GitHub Pages...
javascript封装一个模态框:头(标题、关闭按钮)、内容、尾(操作按钮:取消和确定)。标题可以自定义,默认值是“标题”; 内容区域可以是文本或者富文本,操作按钮可以控制显隐和显示文案; 点击操作按钮执行相...
(6) 分析 MAC 地址和 ARP。 2. 实验内容简要描述 (1) 产生两个 VLAN,并验证配置结果; (2) 为每个 VLAN 分配交换机成员端口; (3) 进行删除 VLAN 的操作; (4) 理解 VLAN1 为什么不能被删除。 本次实验环境包括:与...
实验5 传输介质 1. 实验目的 (1) 了解双绞线的结构; (2) 了解制作双绞线的标准;...(4) 采用制作直通线连通计算机和交换机,测试是否连通; (5) 采用制作交叉线连通计算机和计算机,测试是否连通。
实验2 Wireshark的安装与基本操作 1. 实验目的 学习 Wireshark 的基本操作,抓取和分析有线局域网的数据包,熟悉一些应用层命令和协议。