lucene5--增量索引(Zoie)(近实时搜索的实现)
2017-08-28 15:35
495 查看
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,示例程序就是模拟数据库表数据动态插入新数据后,能立马被准实时被索引到,且被搜索到。下面就直接上代码了: Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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.");
}
}
Java代码
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();
}
}
Java代码
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&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);
}
}
Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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();
}
Java代码
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;
}
}
Java代码
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: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测试来查询数据,来查看新增的数据是否已经被索引且能被搜索到。
资源地址 http://download.csdn.net/download/asdfsadfasdfsa/10273285
(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,示例程序就是模拟数据库表数据动态插入新数据后,能立马被准实时被索引到,且被搜索到。下面就直接上代码了: Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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.");
}
}
Java代码
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();
}
}
Java代码
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&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);
}
}
Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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;
}
}
Java代码
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();
}
Java代码
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;
}
}
Java代码
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: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测试来查询数据,来查看新增的数据是否已经被索引且能被搜索到。
资源地址 http://download.csdn.net/download/asdfsadfasdfsa/10273285
相关文章推荐
- Lucene.net 实现近实时搜索(NRT)和增量索引
- c#使用Lucene.net创建索引,实现搜索的代码示例
- Lucene实现SearchManager近实时搜索
- lucene3.5通过NRTManager和SearchManager实现近实时搜索
- 用lucene为数据库搜索建立增量索引
- 用lucene为数据库搜索建立 增量索引
- lucene全文搜索之四:创建索引搜索器、6种文档搜索器实现以及搜索结果分析(结合IKAnalyzer分词器的搜索器)基于lucene5.5.3
- sphinx 增量索引 实现近实时更新
- sphinx 增量索引 实现近实时更新
- Sphinx实时索引,用增量索引实现索引更新
- java lucene实现近实时搜索及高亮显示的代码例子下载
- sphinx 增量索引 实现近实时更新
- Lucene小练八(实现了索引和搜索)
- sphinx通过增量索引实现近实时更新
- lucene全文搜索之四:创建索引搜索器、6种文档搜索器实现以及搜索结果分析(结合IKAnalyzer分词器的搜索器)基于lucene5.5.3
- sphinx 增量索引 实现近实时更新
- sphinx 增量索引 实现实时更新
- 用lucene为数据库搜索建立增量索引
- 用lucene为数据库搜索建立增量索引
- 用lucene为数据库搜索建立增量索引