`
lxwt909
  • 浏览: 573490 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Lucene5学习之增量索引(Zoie)

阅读更多

      清明节放假期间,就一直在思考着Lucene增量索引问题,通过google我了解到了Zoie的存在,于是就开始2天的学习Zoie之旅,对Zoie的原理以及使用基本掌握,还特地使用Zoie实现了Lucene索引增量索引。不过Zoie已经好久没更新了,Zoie目前版本只支持到Lucene4.3.0,为此我花了2个多小时,修改了Zoie的源码将它升级使其支持Lucene5.0,我修改过的Zoie最新源码我已经上传到我的百度网盘,下载地址如下:

    Zoie-for-Lucene5.0源码

    Zoie-for-Lucene4.3源码

    Zoie-5.0.0-各模块打包好的jar包

如果你使用Maven的话,那依赖的Zoie相关jar你需要手动install到你本地的仓库里,特此提醒。Zoie5.0相关jar包上面提供的3个下载地址中最后一个是我打包好的jar,你引入一个zoie-core即可。

 

 

    先来说说Zoie的增量索引原理吧,摘自Zoie的官方Wiki:

利用两个内存索引一个硬盘索引实现实时搜索的原理

(1) 当系统启动的时候,索引处在Sleeping状态,这时Mem结构中,只有索引A,索引B为null,索引A为_currentWritable,_currentReadOnly为null,_diskIndexReader为硬盘索引的IndexReader。由于内存中索引的IndexReader是每添加完文档后立刻更新的,而且速度很快,而硬盘上的索引一旦打开,在下次合并之前,一直使用,可以保证新添加的文档能够马上被搜索到。

 (2) 当A中的文档数量达到一定的数量的时候,需要同硬盘上的索引进行合并,因此要进入Working状态。合并是一个相对比较长的过程,这时候会创建内存索引B,在合并过程中新添加的文档全部索引到B中。此时的Mem结构中,有内存索引A,内存索引B,索引A为currentReadOnly,索引B为currentWritable,diskIndexReader为硬盘索引的IndexReader。此时要获得ZoieSystem的IndexReader,则三个IndexReader全都返回,由于索引B的IndexReader是添加文档后立刻更新的,因而能够保证新添加的文档能够马上被搜索到,这个时候虽然索引A已经在同硬盘索引进行合并,然而由于硬盘索引的IndexReader还没有重新打开,因而索引A中的数据不会被重复搜到。

 (3) 当索引A中的数据已经完全合并到硬盘上之后,则要重新打开硬盘索引的IndexReader,打开完毕后,创建一个新的Mem结构,原来的索引B作为索引A,为currentWritable,原来的索引A被抛弃,设为null,currentReadOnly也设为null,diskIndexReader为新打开的硬盘索引的IndexReader。然后通过无缝切换用新的Mem结构替代旧的Mem结构,然后索引进入Sleeping状态。

上面的文字说的不够通俗易懂,我用更直白的话再解释一下:Zoie的增量索引原理是这样的,首先你可能会有一个定时器去不断检索是否有新增数据,发现了新数据,那首先会把新增的数据索引到内存目录A(RAMDirectory-A)中,当总不能一直让内存目录A中写索引啊,毕竟你占用的是内存,所以为内存目录A设定一个阀值,超过这个限定阀值就触发内存目录A中索引flush到硬盘索引目录C中,当内存目录A中索引还没有完全写入到硬盘索引目录C中且硬盘索引目录C的IndexReader还没有重新open的话,你通过IndexSearcher是查询不到的,这时就设计了一个内存索引目录B,即在内存索引目录A在往硬盘索引目录C中写索引的同时,也往内存索引目录B中写做个备份,这时使用Lucene中的MultiReader把B和C作为一个索引目录进行查询,之所以不包括A是因为A还没写完,前面博客我已经将过了Lucene的多目录搜索,你应该懂的,所以你新增数据之所以能几乎近实时的被你搜索到,是因为写入到了一个备份的索引目录B中,然后联合硬盘索引目录C(因为硬盘索引目录C在内存索引目录A里的索引合并到C过程还没完成之前,新增的索引在C中是搜索不到),当A和C的合并过程完成后,硬盘索引目录C的IndexReasder重新打开,保证新增的索引能被IndexSearcher搜索到,同时把B作为A,废弃A,再进入下一个循环.......
     核心就是利用两个内存目录,一个内存目录用来联合硬盘索引目录进行多目录查询,另一个目录只管进行索引合并到硬盘索引目录操作即可。

 

    放假花了2天使用Zoie写了一个增量索引的Demo,示例程序就是模拟数据库表数据动态插入新数据后,能立马被准实时被索引到,且被搜索到。下面就直接上代码了:

    

package com.yida.framework.lucene5.incrementindex;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.ansj.lucene5.AnsjAnalyzer;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.search.similarities.DefaultSimilarity;

import proj.zoie.api.DataConsumer.DataEvent;
import proj.zoie.api.ZoieException;
import proj.zoie.api.indexing.ZoieIndexableInterpreter;
import proj.zoie.impl.indexing.DefaultIndexReaderDecorator;
import proj.zoie.impl.indexing.ZoieConfig;
import proj.zoie.impl.indexing.ZoieSystem;

/**
 * Zoie增量索引测试
 * @author Lanxiaowei
 *
 */
@SuppressWarnings({"unchecked","rawtypes"})
public class ZoieIndex {
	
	/**最大增量索引数量*/
	public static final long MAX_INCREMENT_INDEX_NUMBER = Long.MAX_VALUE;
	/**索引目录*/
	public String userIndexPath;
	
	public ZoieSystem zoieSystem;
	
	/**队列中放入多少项才触发索引*/
	private  int zoieBatchSize;
	
	/**等待多长时间才触发索引*/
	private int zoieBatchDelay;
    
    /**分词器*/
    private Analyzer analyzer;
    
    private PersonDao personDao;
    
	public ZoieIndex(String userIndexPath, Analyzer analyzer,
			PersonDao personDao) {
		super();
		this.userIndexPath = userIndexPath;
		this.analyzer = analyzer;
		this.personDao = personDao;
	}

	public ZoieIndex(String userIndexPath, Analyzer analyzer,
			PersonDao personDao, int zoieBatchSize, int zoieBatchDelay) {
		super();
		this.userIndexPath = userIndexPath;
		this.analyzer = analyzer;
		this.personDao = personDao;
		this.zoieBatchSize = zoieBatchSize;
		this.zoieBatchDelay = zoieBatchDelay;
	}



	public void init() throws ZoieException {
    	//如果索引目录不存在则新建
    	File idxDir = new File(userIndexPath);
        if(!idxDir.exists()){
            idxDir.mkdir();
        }
        
        //分词器设置为ansj-seg分词器
        analyzer = new AnsjAnalyzer();
        
        //数据转换器[JavaBea-->Document]
        ZoieIndexableInterpreter interpreter = new CustomPersonZoieIndexableInterpreter(analyzer);
        //Lucene的IndexReader装饰者,包装成zoie的IndexReader
        DefaultIndexReaderDecorator readerDecorator = new DefaultIndexReaderDecorator();
        
        //Zoie初始化相关配置
        ZoieConfig zoieConfig = new ZoieConfig();
        zoieConfig.setBatchDelay(zoieBatchDelay);
        zoieConfig.setBatchSize(zoieBatchSize);
        //设置分词器
        zoieConfig.setAnalyzer(analyzer);
        //设置相似性评分器
        zoieConfig.setSimilarity(new DefaultSimilarity());
        // 开启NRT索引
        zoieConfig.setRtIndexing(true);
        zoieSystem = new ZoieSystem(idxDir, interpreter, readerDecorator, zoieConfig);
        zoieSystem.start();
        zoieSystem.getAdminMBean().flushToDiskIndex();
    }
	
	/**
	 * 更新索引数据
	 * @throws ZoieException 
	 */
	public void updateIndexData() throws ZoieException {
		//先从数据库查出新增加的数据
		List<Person> persons = personDao.findPersonBefore3S();
		if(persons == null || persons.size() == 0) {
			System.out.println("No increment data right now.please wait a while.");
			return;
		}
		List<DataEvent<Person>> dataEventList = new ArrayList<DataEvent<Person>>();
		for(Person person : persons) {
			dataEventList.add(new DataEvent<Person>(person, "1.0", person.isDeleteFlag()));
		}
		//消费数据
		zoieSystem.consume(dataEventList);
	}
	
	public void destroy(){
		// 将内存索引刷新到磁盘索引中
        zoieSystem.shutdown(); 
        System.out.println(".........将内存索引刷新到磁盘索引中.........");
    }

	public String getUserIndexPath() {
		return userIndexPath;
	}

	public void setUserIndexPath(String userIndexPath) {
		this.userIndexPath = userIndexPath;
	}

	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 Analyzer getAnalyzer() {
		return analyzer;
	}

	public void setAnalyzer(Analyzer analyzer) {
		this.analyzer = analyzer;
	}

	public PersonDao getPersonDao() {
		return personDao;
	}

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}
}

    

package com.yida.framework.lucene5.incrementindex;

import java.util.TimerTask;

import proj.zoie.api.ZoieException;

/**
 * Zoie定时增量索引任务
 * @author LANXIAOWEI
 *
 */
public class ZoieIndexTimerTask extends TimerTask {
	private ZoieIndex zoieIndex;
	
	@Override
	public void run() {
		try {
			zoieIndex.init();
			zoieIndex.updateIndexData();
		} catch (ZoieException e) {
			e.printStackTrace();
		}
	}

	public ZoieIndexTimerTask(ZoieIndex zoieIndex) {
		super();
		this.zoieIndex = zoieIndex;
	}

	public ZoieIndex getZoieIndex() {
		return zoieIndex;
	}

	public void setZoieIndex(ZoieIndex zoieIndex) {
		this.zoieIndex = zoieIndex;
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.FSDirectory;

/**
 * Zoie查询测试
 * 
 * @author Lanxiaowei
 * 
 */
public class ZoieSearchTest {
	public static void main(String[] args) throws IOException {
		// 参数定义
		String directoryPath = "C:/zoieindex";
		String fieldName = "nativePlace";
		String queryString = "*香港*";

		Query query = new WildcardQuery(new Term(fieldName,queryString));
		List<Document> list = query(directoryPath,query);
		if (list == null || list.size() == 0) {
			System.out.println("No results found.");
			return;
		}
		for (Document doc : list) {
			String personName = doc.get("personName");
			String nativePlace = doc.get("nativePlace");
			String hobby = doc.get("hobby");
			System.out.println("personName:" + personName);
			System.out.println("nativePlace:" + nativePlace);
			System.out.println("hobby:" + hobby);
		}
	}
	
	/**
	 * 创建索引阅读器
	 * @param directoryPath  索引目录
	 * @return
	 * @throws IOException   可能会抛出IO异常
	 */
	public static IndexReader createIndexReader(String directoryPath) throws IOException {
		return DirectoryReader.open(FSDirectory.open(Paths.get(directoryPath, new String[0])));
	}
	
	/**
	 * 创建索引查询器
	 * @param directoryPath   索引目录
	 * @return
	 * @throws IOException
	 */
	public static IndexSearcher createIndexSearcher(String directoryPath) throws IOException {
		return new IndexSearcher(createIndexReader(directoryPath));
	}
	
	/**
	 * 创建索引查询器
	 * @param reader
	 * @return
	 */
	public static IndexSearcher createIndexSearcher(IndexReader reader) {
		return new IndexSearcher(reader);
	}
	
	public static List<Document> query(String directoryPath,Query query) throws IOException {
		IndexSearcher searcher = createIndexSearcher(directoryPath);
		TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);
		List<Document> docList = new ArrayList<Document>();
		ScoreDoc[] docs = topDocs.scoreDocs;
		for (ScoreDoc scoreDoc : docs) {
			int docID = scoreDoc.doc;
			Document document = searcher.doc(docID);
			docList.add(document);
		}
		searcher.getIndexReader().close();
		return docList;
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import java.util.Timer;

import org.ansj.lucene5.AnsjAnalyzer;
import org.apache.lucene.analysis.Analyzer;
/**
 * 增量索引测试
 * @author Lanxiaowei
 *
 */
public class ZoieTest {
	public static void main(String[] args) throws Exception {
		
		String userIndexPath = "C:/zoieindex";
		Analyzer analyzer = new AnsjAnalyzer();
		PersonDao personDao = new PersonDaoImpl();
		int zoieBatchSize = 10;
		int zoieBatchDelay = 1000;
		
		//先读取数据库表中已有数据创建索引
		CreateIndexTest createIndexTest = new CreateIndexTest(personDao, userIndexPath);
		createIndexTest.index();
		
		//再往数据库表中插入一条数据,模拟数据动态变化
		PersonDaoTest.addPerson();
		
		
		ZoieIndex zoindex = new ZoieIndex(userIndexPath, analyzer, personDao, 
			zoieBatchSize, zoieBatchDelay);
		Timer timer = new Timer("myTimer",false);
		timer.scheduleAtFixedRate(new ZoieIndexTimerTask(zoindex),10L,3000L);
		
		//睡眠2分钟
		Thread.sleep(2*60*1000L);
		//2分钟后定时器取消
		timer.cancel();
		System.out.println("Timer cancled.");
		
		/**把索引flush到硬盘*/
		zoindex.destroy();
		System.out.println("finished.");
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;

import org.ansj.lucene5.AnsjAnalyzer;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import com.yida.framework.lucene5.util.LuceneUtils;

/**
 * 读取数据库表中数据创建索引
 * @author Lanxiaowei
 *
 */
public class CreateIndexTest {
	private PersonDao personDao;
	/**索引目录*/
	private String indexDir;
	
	public static void main(String[] args) throws IOException {
		String userIndexPath = "C:/zoieindex";
		PersonDao personDao = new PersonDaoImpl();
		//先读取数据库表中已有数据创建索引
		CreateIndexTest createIndexTest = new CreateIndexTest(personDao, userIndexPath);
		createIndexTest.index();
	}
	
	public CreateIndexTest(PersonDao personDao, String indexDir) {
		super();
		this.personDao = personDao;
		this.indexDir = indexDir;
	}

	public void index() throws IOException {
		List<Person> persons = personDao.findAll();
		if(null == persons || persons.size() == 0) {
			return;
		}
		Directory dir = FSDirectory.open(Paths.get(indexDir));
		Analyzer analyzer = new AnsjAnalyzer();
		IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
		indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
		IndexWriter writer = new IndexWriter(dir, indexWriterConfig);
		for(Person person : persons) {
			Document document = new Document();
			document.add(new Field("id",person.getId().toString(),Field.Store.YES,
				Field.Index.NOT_ANALYZED,Field.TermVector.NO));
			document.add(new StringField("personName", person.getPersonName(), Field.Store.YES));
			document.add(new StringField("sex", person.getSex(), Field.Store.YES));
			document.add(new LongField("birth", person.getBirth().getTime(), Field.Store.YES));
			document.add(new TextField("nativePlace", person.getNativePlace(), Field.Store.YES));
			document.add(new StringField("job", person.getJob(), Field.Store.YES));
			document.add(new IntField("salary", person.getSalary(), Field.Store.YES));
			document.add(new StringField("hobby", person.getHobby(), Field.Store.YES));
			document.add(new StringField("deleteFlag", person.isDeleteFlag() + "", Field.Store.YES));
			//Zoie需要的UID[注意:这个域必须加,且必须是NumericDocValuesField类型,至于UID的域值是什么没关系,只要保证它是唯一的即可]
			document.add(new NumericDocValuesField("_ID", person.getId()));
			LuceneUtils.addIndex(writer, document);
		}
		writer.close();
		dir.close();
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;

public class DBHelper {
	private static DataSource dataSource;
	
	public static QueryRunner getQueryRunner(){
        if(DBHelper.dataSource == null){
            BasicDataSource dbcpDataSource = new BasicDataSource();
            dbcpDataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8");
            dbcpDataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dbcpDataSource.setUsername("root");
            dbcpDataSource.setPassword("123");
            dbcpDataSource.setDefaultAutoCommit(true);
            dbcpDataSource.setMaxActive(100);
            dbcpDataSource.setMaxIdle(30);
            dbcpDataSource.setMaxWait(500);
            DBHelper.dataSource = (DataSource)dbcpDataSource;
        }
        return new QueryRunner(DBHelper.dataSource);
    }
}

    

package com.yida.framework.lucene5.incrementindex;

import org.apache.lucene.analysis.Analyzer;

import proj.zoie.api.indexing.AbstractZoieIndexableInterpreter;
import proj.zoie.api.indexing.ZoieIndexable;

/**
 * 自定义Person-->Document的数据转换器的生产者
 * @author Lanxiaowei
 *
 */
public class CustomPersonZoieIndexableInterpreter extends AbstractZoieIndexableInterpreter<Person>{
	private Analyzer analyzer;
	
	@Override
	public ZoieIndexable convertAndInterpret(Person person) {
		return new PersonZoieIndexable(person, analyzer);
	}
	
	public CustomPersonZoieIndexableInterpreter() {}
	
	public CustomPersonZoieIndexableInterpreter(Analyzer analyzer) {
		super();
		this.analyzer = analyzer;
	}

	public Analyzer getAnalyzer() {
		return analyzer;
	}
	public void setAnalyzer(Analyzer analyzer) {
		this.analyzer = analyzer;
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;

import proj.zoie.api.indexing.AbstractZoieIndexable;
/**
 * JavaBean与Document的转换器
 * @author Lanxiaowei
 *
 */
public class PersonZoieIndexable extends AbstractZoieIndexable {
	private Person person;
	private Analyzer analyzer;

	public PersonZoieIndexable(Person person) {
		super();
		this.person = person;
	}

	public PersonZoieIndexable(Person person, Analyzer analyzer) {
		super();
		this.person = person;
		this.analyzer = analyzer;
	}

	public Document buildDocument() {
		System.out.println("Person --> Document begining.");
		Document document = new Document();
		document.add(new Field("id",person.getId().toString(),Field.Store.YES,
			Field.Index.NOT_ANALYZED,Field.TermVector.NO));
		document.add(new StringField("personName", person.getPersonName(), Field.Store.YES));
		document.add(new StringField("sex", person.getSex(), Field.Store.YES));
		document.add(new LongField("birth", person.getBirth().getTime(), Field.Store.YES));
		document.add(new TextField("nativePlace", person.getNativePlace(), Field.Store.YES));
		document.add(new StringField("job", person.getJob(), Field.Store.YES));
		document.add(new IntField("salary", person.getSalary(), Field.Store.YES));
		document.add(new StringField("hobby", person.getHobby(), Field.Store.YES));
		document.add(new StringField("deleteFlag", person.isDeleteFlag() + "", Field.Store.YES));
		return document;
	}

	@Override
	public IndexingReq[] buildIndexingReqs() {
	    return new IndexingReq[] {new IndexingReq(buildDocument(), analyzer)};
	}

	@Override
	public long getUID() {
		return person.getId();
	}

	@Override
	public boolean isDeleted() {
		return person.isDeleteFlag();
	}
	
	public Analyzer getAnalyzer() {
		return analyzer;
	}

	public void setAnalyzer(Analyzer analyzer) {
		this.analyzer = analyzer;
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import java.util.Date;

public class Person {
	private Long id;
	private String personName;
	/**性别:1(男)/0(女)*/
	private String sex;
	private Date birth;
	/**籍贯*/
	private String nativePlace;
	private String job;
	private Integer salary;
	/**兴趣爱好*/
	private String hobby;
	
	/**删除标记: true已删除/false未删除*/
	private boolean deleteFlag;
	/**最后一次更新时间*/
	private Date updatedTime;
	
	public Person() {}
	
	
	
	public Person(String personName, String sex, Date birth,
			String nativePlace, String job, Integer salary, String hobby) {
		super();
		this.personName = personName;
		this.sex = sex;
		this.birth = birth;
		this.nativePlace = nativePlace;
		this.job = job;
		this.salary = salary;
		this.hobby = hobby;
	}
	
	public Person(String personName, String sex, Date birth,
			String nativePlace, String job, Integer salary, String hobby,boolean deleteFlag) {
		super();
		this.personName = personName;
		this.sex = sex;
		this.birth = birth;
		this.nativePlace = nativePlace;
		this.job = job;
		this.salary = salary;
		this.hobby = hobby;
		this.deleteFlag = deleteFlag;
	}

	public Person(String personName, String sex, Date birth,
			String nativePlace, String job, Integer salary, String hobby,
			boolean deleteFlag, Date updatedTime) {
		super();
		this.personName = personName;
		this.sex = sex;
		this.birth = birth;
		this.nativePlace = nativePlace;
		this.job = job;
		this.salary = salary;
		this.hobby = hobby;
		this.deleteFlag = deleteFlag;
		this.updatedTime = updatedTime;
	}

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getPersonName() {
		return personName;
	}
	public void setPersonName(String personName) {
		this.personName = personName;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public Date getBirth() {
		return birth;
	}
	public void setBirth(Date birth) {
		this.birth = birth;
	}
	public String getNativePlace() {
		return nativePlace;
	}
	public void setNativePlace(String nativePlace) {
		this.nativePlace = nativePlace;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public Integer getSalary() {
		return salary;
	}
	public void setSalary(Integer salary) {
		this.salary = salary;
	}
	public String getHobby() {
		return hobby;
	}
	public void setHobby(String hobby) {
		this.hobby = hobby;
	}

	public boolean isDeleteFlag() {
		return deleteFlag;
	}

	public void setDeleteFlag(boolean deleteFlag) {
		this.deleteFlag = deleteFlag;
	}

	public Date getUpdatedTime() {
		return updatedTime;
	}

	public void setUpdatedTime(Date updatedTime) {
		this.updatedTime = updatedTime;
	}



	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import java.util.List;

public interface PersonDao {
	/**
	 * 新增
	 * @return
	 */
	public boolean save(Person person);
	
	/**
	 * 更新
	 * @param person
	 * @return
	 */
	public boolean update(Person person);
	
	/**
	 * 根据ID删除
	 * @param id
	 * @return
	 */
	public boolean delete(Long id);
	
	/**
	 * 根据ID查询
	 * @param id
	 * @return
	 */
	public Person findById(Long id);
	
	/**
	 * 查询所有
	 * @return
	 */
	public List<Person> findAll();
	
	/**
	 * 查询3秒之前的数据,用于测试
	 * @return
	 */
	public List<Person> findPersonBefore3S();
}

   

package com.yida.framework.lucene5.incrementindex;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

public class PersonDaoImpl implements PersonDao {
	private QueryRunner queryRunner = DBHelper.getQueryRunner();
	/**
	 * 新增
	 * @return
	 */
	public boolean save(Person person) {
		int result = 0;
		try {
			result = queryRunner.update("insert into person(personName,sex,birth,nativePlace,job,salary,hobby,deleteFlag,updatedTime) " + 
					"values(?,?,?,?,?,?,?,?,?)" , new Object[] {
					person.getPersonName(),
					person.getSex(),
					person.getBirth(),
					person.getNativePlace(),
					person.getJob(),
					person.getSalary(),
					person.getHobby(),
					person.isDeleteFlag(),
					new Date()
			});
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return result == 1;
	}
	
	/**
	 * 根据ID更新
	 * @param person
	 * @return
	 */
	public boolean update(Person person) {
		int result = 0;
		try {
			result = queryRunner.update(
					"update person set personName = ?, sex = ?, birth = ?, " + 
					"nativePlace = ?, job = ?, salary = ?, hobby = ?,deleteFlag = ?, " +
					"updatedTime = ? where id = ?" 
					, new Object[] {
					person.getPersonName(),
					person.getSex(),
					person.getBirth(),
					person.getNativePlace(),
					person.getJob(),
					person.getSalary(),
					person.getHobby(),
					person.isDeleteFlag(),
					new Date(),
					person.getId()
			});
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return result == 1;
	}
	
	/**
	 * 根据ID删除
	 * @param id
	 * @return
	 */
	public boolean delete(Long id) {
		int result = 0;
		try {
			result = queryRunner.update("delete from person where id = ?", id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return result == 1;
	}
	
	/**
	 * 根据ID查询
	 * @param id
	 * @return
	 */
	public Person findById(Long id) {
		Person person = null;
		try {
			person = queryRunner.query("select * from person where id = ?", new BeanHandler<Person>(Person.class),id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return person;
	}
	
	/**
	 * 查询所有
	 * @return
	 */
	public List<Person> findAll() {
		List<Person> persons = null;
		try {
			persons = queryRunner.query("select * from person", new BeanListHandler<Person>(Person.class));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return persons;
	}
	
	/**
	 * 查询3秒之前的数据,用于测试
	 * @return
	 */
	public List<Person> findPersonBefore3S() {
		List<Person> persons = null;
		try {
			persons = queryRunner.query("select * from person where updatedTime >= DATE_SUB(NOW(),INTERVAL 3 SECOND)", new BeanListHandler<Person>(Person.class));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return persons;
	}
}

   

package com.yida.framework.lucene5.incrementindex;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * PersonDao测试
 * @author Lanxiaowei
 *
 */
public class PersonDaoTest {
	private static final DateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd");
	public static void main(String[] args) throws Exception {
		addPerson();
		
	}
	
	/**
	 * 添加一个Person测试
	 * @throws ParseException
	 */
	public static void addPerson() throws ParseException {
		PersonDao personDao = new PersonDaoImpl();
		String personName = "张国荣";
		String sex = "1";
		String birthString = "1956-09-12";
		Date birth = dateFormate.parse(birthString);
		String nativePlace = "中国香港九龙";
		String job = "歌手";
		Integer salary = 16000;
		String hobby = "演员&音乐";
		boolean deleteFlag = false;
		Person person = new Person(personName, sex, birth, nativePlace, job, salary, hobby, deleteFlag);
		boolean success = personDao.save(person);
		System.out.println(success ? "Person save successful." : "Person save fauilure.");
	}
}

   数据库建表SQL:

CREATE TABLE `person` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `personName` varchar(60) DEFAULT NULL,
  `sex` char(1) DEFAULT NULL,
  `birth` datetime DEFAULT NULL,
  `nativePlace` varchar(200) DEFAULT NULL,
  `job` varchar(60) DEFAULT NULL,
  `salary` int(11) DEFAULT NULL,
  `hobby` varchar(200) DEFAULT NULL,
  `deleteFlag` bit(1) DEFAULT NULL,
  `updatedTime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;


INSERT INTO `person` VALUES (1, '兰小伟', '1', '1987-6-5 00:00:00', '湖北省武穴市', '码农', 16000, '看书写代码听歌看电影玩玩CS看看斯诺克比赛', '', '2015-4-6 18:01:30');
INSERT INTO `person` VALUES (2, '李小龙', '1', '1940-2-26 00:00:00', '中国广东顺德均安镇', '武术大师', 16000, '武术&音乐', '', '2015-4-6 18:14:04');

   建表SQL文件和demo源码我会在底下的附件里上传,自己去下载。SQL文件怎么导入到MySQL我就不多说了哈,如果这个你不会,请自己Google。运行ZoieTest类进行测试,在运行之前,请先打开DBHelper工具类,修改里面的MySQL数据库的帐号密码,因为你的MySQL登录帐号密码可能跟我的不一样,还有记得把Zoie-core的jar包install到本地仓库,否则你maven的pom.xml会报错。运行ZoieTest测试类之后,你就可以不断运行ZoieSearchTest测试来查询数据,来查看新增的数据是否已经被索引且能被搜索到。

    

   如果你还有什么问题请加我Q-Q:7-3-6-0-3-1-3-0-5,

或者加裙
一起交流学习!

  • 大小: 9 KB
  • 大小: 13.4 KB
  • 大小: 11 KB
1
0
分享到:
评论
5 楼 大树Kris 2016-05-25  
磁盘索引只读,删除文档如何处理呢?
4 楼 永志_爱戴 2016-04-03  
《这时就设计了一个内存索引目录B,即在内存索引目录A在往硬盘索引目录C中写索引的同时,也往内存索引目录B中写做个备份,这时使用Lucene中的MultiReader把B和C作为一个索引目录进行查询,之所以不包括A是因为A还没写完,》
博主的这句话感觉有些问题。
目录A在往硬盘索引目录写索引的时候,不应该是往索引目录B中写备份吧。往索引目录B里写的应该是要建的新的索引文档。而且使用IndexReaer进行索引时,应该不仅仅是返回B和C的索引目录,还要包括A的索引目录的Reader。


3 楼 wc149162536 2016-01-29  
有没有人用楼主的代码测试成功啊?  我是运行到ZoieIndex类 init方法 89 行 
zoieSystem = new ZoieSystem(idxDir, interpreter, readerDecorator, zoieConfig);
有错误,导致zoieSystem没有能被初始化,在线程里面报了空指针错误。我也是检查了好多地方没发觉错误,感觉是不是楼主的zoie-core  jar包没有没有修改准确呢? 大胆揣测,冒犯之处请多见谅
2 楼 jack40883 2015-05-03  
  确实,有部分包http://maven.oschina.net/上还没有...找起来麻烦版本还是个大问题..为了方便我们学习拜托了..别用maven吧...
1 楼 jack40883 2015-04-09  
麻烦大神下次能不用maven

相关推荐

    基于lucene技术的增量索引

    **基于Lucene技术的增量索引** 在信息技术领域,全文搜索引擎是处理大量数据查询的关键工具。Apache Lucene是一个开源的全文检索库,被广泛应用于构建高效、可扩展的搜索功能。本文将深入探讨如何利用Lucene实现...

    lucene分词搜索,增量索引及全量索引

    《Lucene分词搜索、增量索引与全量索引详解》 在现代信息检索系统中,搜索引擎扮演着至关重要的角色。Apache Lucene是一个高性能、全文本搜索库,被广泛应用于构建复杂的数据检索应用。本文将深入探讨Lucene的分词...

    Lucene5学习之创建索引入门示例

    **Lucene5学习之创建索引入门示例** 在IT领域,搜索引擎的开发与优化是一项关键技术,而Apache Lucene作为一款高性能、全文本搜索库,是许多开发者进行文本检索的首选工具。本文将深入探讨如何使用Lucene5来创建一...

    lucene5+zoie实现近实时索引

    《Lucene 5 + Zoie 实现近实时索引详解》 在当今大数据时代,搜索引擎技术扮演着至关重要的角色,而Lucene作为一款强大的全文搜索引擎库,被广泛应用于各种信息检索系统。然而,传统的Lucene在处理大规模数据时,...

    Lucene5学习之多线程创建索引

    《Lucene5学习之多线程创建索引》 在深入了解Lucene5的多线程索引创建之前,我们先来了解一下Lucene的基本概念。Lucene是一个高性能、全文本搜索库,由Apache软件基金会开发。它提供了强大的文本分析、索引和搜索...

    lucene 对 xml建立索引

    ### Lucene对XML文档建立索引的技术解析与实践 #### 一、引言 随着互联网技术的迅猛发展,非结构化数据(如XML文档)在企业和组织中的应用日益广泛。如何高效地处理这些非结构化的数据,特别是进行快速检索成为了一...

    Lucene5学习之Facet(续)

    《Lucene5学习之Facet(续)》 在深入探讨Lucene5的Facet功能之前,我们先来了解一下什么是Faceting。Faceting是搜索引擎提供的一种功能,它允许用户通过分类或属性对搜索结果进行细分,帮助用户更精确地探索和理解...

    Lucene5学习之拼音搜索

    本文将围绕“Lucene5学习之拼音搜索”这一主题,详细介绍其拼音搜索的实现原理和实际应用。 首先,我们需要理解拼音搜索的重要性。在中文环境中,由于汉字的复杂性,用户往往习惯于通过输入词语的拼音来寻找信息。...

    Lucene之删除索引

    在深入探讨Lucene删除索引这一主题之前,我们先来理解一下Lucene的基本概念。Lucene是一个开源的全文搜索引擎库,由Apache软件基金会开发。它提供了高性能、可扩展的搜索和索引功能,广泛应用于各种应用程序中,如...

    Lucene5学习之SpellCheck拼写纠错

    **标题:“Lucene5学习之SpellCheck拼写纠错”** 在深入探讨Lucene5的SpellCheck功能之前,首先需要理解Lucene是什么。Lucene是一个开源的全文检索库,由Apache软件基金会开发,它提供了高性能、可扩展的文本搜索...

    Lucene5学习之排序-Sort

    “Lucene5学习之排序-Sort”这个标题表明了我们要探讨的是关于Apache Lucene 5版本中的排序功能。Lucene是一个高性能、全文检索库,它提供了强大的文本搜索能力。在这个主题中,我们将深入理解如何在Lucene 5中对...

    Lucene5学习之自定义Collector

    这篇博客“Lucene5学习之自定义Collector”显然聚焦于如何在Lucene 5版本中通过自定义Collector来优化搜索结果的收集过程。Collector是Lucene搜索框架中的一个重要组件,它负责在搜索过程中收集匹配的文档,并根据...

    lucene.net 2.9.2 实现索引生成,修改,查询,删除实例

    通过阅读和运行这些代码,你可以更深入地理解Lucene.NET 2.9.2的工作原理,并学习如何在实际项目中应用。 总结来说,Lucene.NET 2.9.2是一个强大的全文检索库,提供了一套完善的API来处理索引管理、查询和结果处理...

    Lucene5学习之Group分组统计

    "Lucene5学习之Group分组统计" 这个标题指出我们要讨论的是关于Apache Lucene 5版本中的一个特定功能——Grouping。在信息检索领域,Lucene是一个高性能、全文搜索引擎库,而Grouping是它提供的一种功能,允许用户对...

    Lucene5学习之Spatial地理位置搜索

    《深入探索Lucene5 Spatial:地理位置搜索》 在信息技术飞速发展的今天,地理位置搜索已经成为许多应用和服务不可或缺的一部分。Apache Lucene作为一个强大的全文搜索引擎库,其在5.x版本中引入了Spatial模块,使得...

    跟益达学Solr5之增量索引MySQL数据库表数据

    总结,学习“跟益达学Solr5之增量索引MySQL数据库表数据”,你将掌握如何设置Solr与MySQL的集成,使用DIH进行增量索引,以及优化索引性能。这不仅有助于提升搜索效率,也是企业级应用中数据检索不可或缺的技能。

    用Lucene.net对数据库建立索引及搜索

    《使用Lucene.NET对数据库建立索引及搜索》 在信息技术领域,搜索引擎是不可或缺的一部分,尤其是在处理大量数据时。Lucene.NET是一个强大的全文搜索引擎库,它允许开发人员在应用程序中集成高级搜索功能。本文将...

    Lucene5学习之Suggest关键字提示

    在Lucene5中,Suggester是实现关键词提示的核心类,它负责构建索引并提供查询建议。Suggester的构建基于两部分数据:一个是输入的关键词,另一个是与这些关键词相关的权重或其他元数据。关键词可以是任何字符串,如...

    Lucene5学习之自定义排序

    本文将深入探讨“Lucene5学习之自定义排序”这一主题,帮助你理解如何在Lucene5中实现自定义的排序规则。 首先,Lucene的核心功能之一就是提供高效的全文检索能力,但默认的搜索结果排序通常是基于相关度得分...

Global site tag (gtag.js) - Google Analytics