清明节放假期间,就一直在思考着Lucene增量索引问题,通过google我了解到了Zoie的存在,于是就开始2天的学习Zoie之旅,对Zoie的原理以及使用基本掌握,还特地使用Zoie实现了Lucene索引增量索引。不过Zoie已经好久没更新了,Zoie目前版本只支持到Lucene4.3.0,为此我花了2个多小时,修改了Zoie的源码将它升级使其支持Lucene5.0,我修改过的Zoie最新源码我已经上传到我的百度网盘,下载地址如下:
如果你使用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&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,
或者加裙
一起交流学习!
相关推荐
**基于Lucene技术的增量索引** 在信息技术领域,全文搜索引擎是处理大量数据查询的关键工具。Apache Lucene是一个开源的全文检索库,被广泛应用于构建高效、可扩展的搜索功能。本文将深入探讨如何利用Lucene实现...
《Lucene分词搜索、增量索引与全量索引详解》 在现代信息检索系统中,搜索引擎扮演着至关重要的角色。Apache Lucene是一个高性能、全文本搜索库,被广泛应用于构建复杂的数据检索应用。本文将深入探讨Lucene的分词...
**Lucene5学习之创建索引入门示例** 在IT领域,搜索引擎的开发与优化是一项关键技术,而Apache Lucene作为一款高性能、全文本搜索库,是许多开发者进行文本检索的首选工具。本文将深入探讨如何使用Lucene5来创建一...
《Lucene 5 + Zoie 实现近实时索引详解》 在当今大数据时代,搜索引擎技术扮演着至关重要的角色,而Lucene作为一款强大的全文搜索引擎库,被广泛应用于各种信息检索系统。然而,传统的Lucene在处理大规模数据时,...
《Lucene5学习之多线程创建索引》 在深入了解Lucene5的多线程索引创建之前,我们先来了解一下Lucene的基本概念。Lucene是一个高性能、全文本搜索库,由Apache软件基金会开发。它提供了强大的文本分析、索引和搜索...
### Lucene对XML文档建立索引的技术解析与实践 #### 一、引言 随着互联网技术的迅猛发展,非结构化数据(如XML文档)在企业和组织中的应用日益广泛。如何高效地处理这些非结构化的数据,特别是进行快速检索成为了一...
《Lucene5学习之Facet(续)》 在深入探讨Lucene5的Facet功能之前,我们先来了解一下什么是Faceting。Faceting是搜索引擎提供的一种功能,它允许用户通过分类或属性对搜索结果进行细分,帮助用户更精确地探索和理解...
本文将围绕“Lucene5学习之拼音搜索”这一主题,详细介绍其拼音搜索的实现原理和实际应用。 首先,我们需要理解拼音搜索的重要性。在中文环境中,由于汉字的复杂性,用户往往习惯于通过输入词语的拼音来寻找信息。...
在深入探讨Lucene删除索引这一主题之前,我们先来理解一下Lucene的基本概念。Lucene是一个开源的全文搜索引擎库,由Apache软件基金会开发。它提供了高性能、可扩展的搜索和索引功能,广泛应用于各种应用程序中,如...
**标题:“Lucene5学习之SpellCheck拼写纠错”** 在深入探讨Lucene5的SpellCheck功能之前,首先需要理解Lucene是什么。Lucene是一个开源的全文检索库,由Apache软件基金会开发,它提供了高性能、可扩展的文本搜索...
“Lucene5学习之排序-Sort”这个标题表明了我们要探讨的是关于Apache Lucene 5版本中的排序功能。Lucene是一个高性能、全文检索库,它提供了强大的文本搜索能力。在这个主题中,我们将深入理解如何在Lucene 5中对...
这篇博客“Lucene5学习之自定义Collector”显然聚焦于如何在Lucene 5版本中通过自定义Collector来优化搜索结果的收集过程。Collector是Lucene搜索框架中的一个重要组件,它负责在搜索过程中收集匹配的文档,并根据...
通过阅读和运行这些代码,你可以更深入地理解Lucene.NET 2.9.2的工作原理,并学习如何在实际项目中应用。 总结来说,Lucene.NET 2.9.2是一个强大的全文检索库,提供了一套完善的API来处理索引管理、查询和结果处理...
"Lucene5学习之Group分组统计" 这个标题指出我们要讨论的是关于Apache Lucene 5版本中的一个特定功能——Grouping。在信息检索领域,Lucene是一个高性能、全文搜索引擎库,而Grouping是它提供的一种功能,允许用户对...
《深入探索Lucene5 Spatial:地理位置搜索》 在信息技术飞速发展的今天,地理位置搜索已经成为许多应用和服务不可或缺的一部分。Apache Lucene作为一个强大的全文搜索引擎库,其在5.x版本中引入了Spatial模块,使得...
总结,学习“跟益达学Solr5之增量索引MySQL数据库表数据”,你将掌握如何设置Solr与MySQL的集成,使用DIH进行增量索引,以及优化索引性能。这不仅有助于提升搜索效率,也是企业级应用中数据检索不可或缺的技能。
《使用Lucene.NET对数据库建立索引及搜索》 在信息技术领域,搜索引擎是不可或缺的一部分,尤其是在处理大量数据时。Lucene.NET是一个强大的全文搜索引擎库,它允许开发人员在应用程序中集成高级搜索功能。本文将...
在Lucene5中,Suggester是实现关键词提示的核心类,它负责构建索引并提供查询建议。Suggester的构建基于两部分数据:一个是输入的关键词,另一个是与这些关键词相关的权重或其他元数据。关键词可以是任何字符串,如...
本文将深入探讨“Lucene5学习之自定义排序”这一主题,帮助你理解如何在Lucene5中实现自定义的排序规则。 首先,Lucene的核心功能之一就是提供高效的全文检索能力,但默认的搜索结果排序通常是基于相关度得分...