1. 程式人生 > >Lucene的基本概念----轉載yufenfei的文章

Lucene的基本概念----轉載yufenfei的文章

人員 功能 except delet color exce writer oid 數據結構

Lucene的基本概念

Lucene是什麽?

Lucene是一款高性能、可擴展的信息檢索工具庫。信息檢索是指文檔搜索、文檔內信息搜索或者文檔相關的元數據搜索等操作。

信息檢索流程如下:

1、 將即將檢索的資源集合放到本地,並使用某種特定的結構存儲,稱為索引,這個索引的集合稱為索引庫。由於索引庫的結構按照專門為快速查詢設計的,所以查詢的速度非常的快;

2、 搜索操作時都是在本地的索引庫中進行查找;

所以對於全文檢索功能的開發,要做兩方面:索引庫管理(維護索引庫中的數據)、在索引庫中進行搜索。而Lucene就是操作索引庫的工具;

索引庫是什麽樣子?

索引庫是一個目錄,裏面是一些二級制文件,就如同數據庫,所有的數據也是以文件的形式存放在文件系統中的。我們不能直接操作這些二級制文件,而是使用Lucene提供的API完成相應的操作,就像數據庫使用SQL語句一樣。

對索引庫的操作可以分為兩種:管理與查詢。

1、 管理索引庫使用的IndexWriter;

2、從索引庫中查詢使用IndexSearcher。

Lucene的數據結構為 Document與Field。

Document代表是一條數據,Field代表數據中的一個屬性。一個Document中有多個Field,Field的值為String型,因為Lucene只處理文本;

我們只需要把我們的程序中的對象轉換為Doucemnt,就可以交給Lucene管理了,搜索的結果中的數據列表也是Document的集合;

OK,我們來做一個實例,還原一下整個流程

1、創建一個用戶類,用於實例化用戶數據

Java代碼
  1. public class User {
  2. private Long id;
  3. private String name;
  4. private int age;
  5. private String sex;
  6. private Date birthday;
  7. public User(Long id, String name, int age, String sex, Date birthday) {
  8. super();
  9. this.id = id;
  10. this.name = name;
  11. this.age = age;
  12. this.sex = sex;
  13. this.birthday = birthday;
  14. }
  15. //get/set方法,這裏省略
  16. }
public class User {

	private Long id;
	private String name;
	private int age;
	private String sex;
	private Date birthday;
	public User(Long id, String name, int age, String sex, Date birthday) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.sex = sex;
		this.birthday = birthday;
	}
   //get/set方法,這裏省略
}

2、生成即將檢索的資源數據

Java代碼
  1. public class DataUtil {
  2. /**
  3. * 檢索資源數據的準備;
  4. * 這裏的數據可以來源數據庫、文件系統等
  5. * @return
  6. */
  7. public static List<User> getUsers(){
  8. List<User> list =new ArrayList<User>();
  9. User user =new User(1L,"張三1",20,"man",new Date());
  10. list.add(user);
  11. user =new User(2L,"張三2",20,"man",new Date());
  12. list.add(user);
  13. user =new User(3L,"張三3",20,"woman",new Date());
  14. list.add(user);
  15. user =new User(4L,"張三4",20,"man",new Date());
  16. list.add(user);
  17. user =new User(5L,"張三5",20,"man",new Date());
  18. list.add(user);
  19. user =new User(6L,"張三6",20,"woman",new Date());
  20. list.add(user);
  21. return list;
  22. }
  23. }
public class DataUtil {
	/**
	 * 檢索資源數據的準備;
	 *   這裏的數據可以來源數據庫、文件系統等
	 * @return
	 */
	public static List<User> getUsers(){
		List<User> list =new ArrayList<User>();
		User user =new User(1L,"張三1",20,"man",new Date());
		list.add(user);
		user =new User(2L,"張三2",20,"man",new Date());
		list.add(user);
		user =new User(3L,"張三3",20,"woman",new Date());
		list.add(user);
		user =new User(4L,"張三4",20,"man",new Date());
		list.add(user);
		user =new User(5L,"張三5",20,"man",new Date());
		list.add(user);
		user =new User(6L,"張三6",20,"woman",new Date());
		list.add(user);
		return list;
	}
}

3、Lucene創建索引庫及查詢

Java代碼
  1. public class IndexWriterDemo {
  2. /**
  3. * 將即將檢索的資源寫入索引庫
  4. * @param writer
  5. * @throws Exception
  6. */
  7. public void buildDocs(IndexWriter writer)throws Exception {
  8. writer.deleteAll();//清空索引庫裏已存在的文檔(document)
  9. List<User> list = DataUtil.getUsers();//得到數據資源
  10. System.out.println("buildDocs()->總人數為 :"+list.size());
  11. for(User user :list){
  12. Document doc = new Document();//創建索引庫的文檔
  13. doc.add(new Field("id",String.valueOf(user.getId()),Store.YES,Index.NO));
  14. doc.add(new Field("name",user.getName(),Store.YES,Index.ANALYZED));
  15. doc.add(new Field("age",String.valueOf(user.getAge()),Store.YES,Index.ANALYZED));
  16. doc.add(new Field("sex",user.getSex(),Store.YES,Index.ANALYZED));
  17. doc.add(new Field("birthday",String.valueOf(user.getBirthday()),Store.YES,Index.ANALYZED));
  18. writer.addDocument(doc);//將文檔寫入索引庫
  19. }
  20. int count =writer.numDocs();
  21. writer.forceMerge(100);//合並索引庫文件
  22. writer.close();
  23. System.out.println("buildDocs()->存入索引庫的數量:"+count);
  24. }
  25. /**
  26. * 從索引庫中搜索你要查詢的數據
  27. * @param searcher
  28. * @throws IOException
  29. */
  30. public void searcherDocs(IndexSearcher searcher) throws IOException{
  31. Term term =new Term("sex", "man");//查詢條件,意思是我要查找性別為“man”的人
  32. TermQuery query =new TermQuery(term);
  33. TopDocs docs =searcher.search(query, 100);//查找
  34. System.out.println("searcherDoc()->男生人數:"+docs.totalHits);
  35. for(ScoreDoc doc:docs.scoreDocs){//獲取查找的文檔的屬性數據
  36. int docID=doc.doc;
  37. Document document =searcher.doc(docID);
  38. String str="ID:"+document.get("id")+",姓名:"+document.get("name")+",性別:"+document.get("sex");
  39. System.out.println("人員信息:"+str);
  40. }
  41. }
  42. }
public class IndexWriterDemo {
	/**
	 * 將即將檢索的資源寫入索引庫
	 * @param writer
	 * @throws Exception
	 */
	public void buildDocs(IndexWriter writer)throws Exception {
		writer.deleteAll();//清空索引庫裏已存在的文檔(document)
		List<User> list = DataUtil.getUsers();//得到數據資源
		System.out.println("buildDocs()->總人數為 :"+list.size());
		for(User user :list){
			Document doc = new Document();//創建索引庫的文檔
			doc.add(new Field("id",String.valueOf(user.getId()),Store.YES,Index.NO));
			doc.add(new Field("name",user.getName(),Store.YES,Index.ANALYZED));
			doc.add(new Field("age",String.valueOf(user.getAge()),Store.YES,Index.ANALYZED));
			doc.add(new Field("sex",user.getSex(),Store.YES,Index.ANALYZED));
			doc.add(new Field("birthday",String.valueOf(user.getBirthday()),Store.YES,Index.ANALYZED));
			writer.addDocument(doc);//將文檔寫入索引庫
		}
		int count =writer.numDocs();
		writer.forceMerge(100);//合並索引庫文件
		writer.close();
		System.out.println("buildDocs()->存入索引庫的數量:"+count);
	}

	/**
	 * 從索引庫中搜索你要查詢的數據
	 * @param searcher
	 * @throws IOException
	 */
	public void searcherDocs(IndexSearcher searcher) throws IOException{
		Term term =new Term("sex", "man");//查詢條件,意思是我要查找性別為“man”的人
		TermQuery query =new TermQuery(term);
		TopDocs docs =searcher.search(query, 100);//查找
		System.out.println("searcherDoc()->男生人數:"+docs.totalHits);
		for(ScoreDoc doc:docs.scoreDocs){//獲取查找的文檔的屬性數據
			int docID=doc.doc;
			Document document =searcher.doc(docID);
			String str="ID:"+document.get("id")+",姓名:"+document.get("name")+",性別:"+document.get("sex");
			System.out.println("人員信息:"+str);
		}
	}
  }

4、測試

Java代碼
  1. public class TestIndexWriterRAMDirectory {
  2. private IndexWriter writer=null;
  3. private Directory directory=null;
  4. private IndexReader reader = null;
  5. private IndexSearcher searcher=null;
  6. private IndexWriterDemo demo =new IndexWriterDemo();
  7. @Before
  8. public void setUp() throws Exception {
  9. directory = new RAMDirectory();
  10. IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36,new SimpleAnalyzer(Version.LUCENE_36));
  11. writer = new IndexWriter(directory,config);
  12. }
  13. @Test
  14. public void testAddDoc()throws Exception {
  15. /**生成索引庫*/
  16. demo.buildDocs(writer);
  17. /**查詢數據*/
  18. reader = IndexReader.open(directory);
  19. searcher =new IndexSearcher(reader);
  20. demo.searcherDocs(searcher);
  21. }
  22. }
public class TestIndexWriterRAMDirectory {
	private IndexWriter writer=null;
	private Directory directory=null;
	private IndexReader reader = null;
	private IndexSearcher searcher=null;
	private IndexWriterDemo demo =new IndexWriterDemo();
	
	@Before
	public void setUp() throws Exception {
		directory = new RAMDirectory();
		IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36,new SimpleAnalyzer(Version.LUCENE_36));
		writer = new IndexWriter(directory,config);
	}

	@Test
	public void testAddDoc()throws Exception {
		/**生成索引庫*/
		demo.buildDocs(writer);
		
		/**查詢數據*/
		reader = IndexReader.open(directory);
		searcher =new IndexSearcher(reader);
		demo.searcherDocs(searcher);
	}
}

測試結果

Java代碼
  1. buildDocs()->總人數為 :6
  2. buildDocs()->存入索引庫的數量:6
  3. searcherDoc()->男生人數:4
  4. 人員信息:ID:1,姓名:張三1,性別:man
  5. 人員信息:ID:2,姓名:張三2,性別:man
  6. 人員信息:ID:4,姓名:張三4,性別:man
  7. 人員信息:ID:5,姓名:張三5,性別:man
buildDocs()->總人數為 :6
buildDocs()->存入索引庫的數量:6
searcherDoc()->男生人數:4
人員信息:ID:1,姓名:張三1,性別:man
人員信息:ID:2,姓名:張三2,性別:man
人員信息:ID:4,姓名:張三4,性別:man
人員信息:ID:5,姓名:張三5,性別:man

OK,代碼完畢

實例的Lucene版本為:lucene-3.6.1

在這再次說下Lucene檢索的整個流程(請參考demo的代碼)

1、建立索引的執行過程

在建立索引時,先要把文檔存到索引庫中,還要更新詞匯表。

操作步驟如下:

(1)、把數據對象轉換成相應的Document,其中的屬性轉為Field;

(2)、調用工具IndexWriter的addDocument(doc),把Document添加到索引庫中;

(3)、Lucene做的操作:

把文檔存到索引庫中,並自動指定一個內部編號,用來唯一標識這個條數據;內部編號類似與這條數據的地址,在索引庫內部的數據進行調整後,這個編號就可能會改變,同時詞匯表中的引用的編號也會做相應的改變,以保 證正確。

更新詞匯表。把文本中的詞找出來放到詞匯表中,簡歷與文檔的對應關系。要把那些詞放到詞匯表中呢?這就用到一個叫Analyzer(分詞器)的工具。他的作用是把一段文本中的詞按照規則取出所包含的所有詞。對應的是Analyzer類,這是一個抽象類,切分詞的具體規則是由其子類實現。

在把對象的屬性轉化為 Field時,相關代碼為:

doc.add(new Field(“title”,article.getTitle(), Store.YES, Index.Analyzed))

其中第三個參數的意思為

Store.NO 不存儲屬性的值;

Store.YES 存儲屬性的值

第四個參數

Index.NO 不建立索引

Index.ANALYZED 分詞後建立索引

Index.NOT_ANALYZED 不分詞,把整個內容作為一個詞建立索引

Store是影響搜索出的結構是否有指定屬性的原始內容。

Index是影響是否可以從這個屬性中查詢,或者是查詢時可以查其中的某些詞,還是要把整個內容作為一個詞進行查詢。

2、從索引庫中搜索的執行過程(QueryParse、TopDocs、ScoreDoc)

在進行搜索時,先在詞匯表中查找,得到符合條件的文檔編號列表。再根據文檔編號真正的取數據(Document)

操作步驟如下:

(1)、把要查詢字符串轉為Query對象。這就像在Hiberante總是用HQL查詢時,也要先調用Session.createQuery(hql)轉成Hibernate的Query對象一樣。把查詢字符串轉換成Query是使用QueryParser,或者使用MultiFieldQueryParser。查詢字符串也要先經過Analyzer(分詞器)。要求檢索時使用Analyzer要與監理索引使用的Analzyer要一致,否則可能搜索不出正確的結果。

(2)、調用IndexSearcher.search(),進行查詢,得到結果。此方法返回未TopDocs,是包含結果的多個信息的一個對象。其中有totalHits代表記錄數,ScoreDoc的數組。ScoreDoc是代表一個結果的相關度得分與文檔編號等信息的對象。

(3)、取出要用到的數據列表。調用IndexSearcher.doc(scoreDoc.doc)以取出指定編號對應的Document數據,在分頁時要用到:一次只取一頁的數據。

lucene.rar

Lucene的基本概念----轉載yufenfei的文章