Lucene搜索方法總結
1.多字段搜索
使用 multifieldqueryparser 可以指定多個搜索字段。
query query = multifieldqueryparser.parse(”name*”, new string[] { fieldname, fieldvalue }, analyzer);
indexreader reader = indexreader.open(directory);
indexsearcher searcher = new indexsearcher(reader);
hits hits = searcher.search(query);
2.多條件搜索
除了使用 queryparser.parse 分解複雜的搜索語法外,還可以通過組合多個 query 來達到目的。
query query1 = new termquery(new term(fieldvalue, “name1′)); // 詞語搜索
query query2 = new wildcardquery(new term(fieldname, “name*”)); // 通配符
//query query3 = new prefixquery(new term(fieldname, “name1′)); // 字段搜索 field:keyword,自動在結尾添加 *
//query query4 = new rangequery(new term(fieldnumber, numbertools.longtostring(11l)), new term(fieldnumber, numbertools.longtostring(13l)), true); // 範圍搜索
//query query5 = new filteredquery(query, filter); // 帶過濾條件的搜索
booleanquery query = new booleanquery();
query.add(query1, booleanclause.occur.must);
query.add(query2, booleanclause.occur.must);
indexsearcher searcher = new indexsearcher(reader);
hits hits = searcher.search(query);
/**
* 由TermQuery和BooleanQuery構建的多個域檢索
* @throws Exception
*/
public static void searchIndex4TermQuery() throws Exception{
TermQuery query1 = null;
TermQuery query2 = null;
TermQuery query3 = null;
TermQuery query4 = null;
TermQuery query5 = null;
TermQuery query6 = null;
BooleanQuery bquerymain = null;
BooleanQuery bquery1 = null;
BooleanQuery bquery2 = null;
BooleanQuery bquery3 = null;
Hits hits = null;
IndexSearcher searcher = new IndexSearcher("e:\\java\\index");
query1 = new TermQuery(new Term("name", "word1"));
query2 = new TermQuery(new Term("name", "word2"));
query3 = new TermQuery(new Term("name", "word3"));
query4 = new TermQuery(new Term("name", "word4"));
query5 = new TermQuery(new Term("name", "word5"));
query6 = new TermQuery(new Term("name", "word6"));
// 構造布爾查詢(可根據你的要求隨意組合)
bquerymain = new BooleanQuery();
bquery1 = new BooleanQuery();
bquery2 = new BooleanQuery();
bquery3 = new BooleanQuery();
bquery1.add(query1, BooleanClause.Occur.MUST);
bquery1.add(query3, BooleanClause.Occur.MUST);
bquery2.add(query3, BooleanClause.Occur.MUST);
bquery2.add(query4, BooleanClause.Occur.MUST);
bquery3.add(query5, BooleanClause.Occur.MUST);
bquery3.add(query6, BooleanClause.Occur.MUST_NOT);
bquerymain.add(bquery1, BooleanClause.Occur.SHOULD);
bquerymain.add(bquery2, BooleanClause.Occur.SHOULD);
bquerymain.add(bquery3, BooleanClause.Occur.MUST);
/**
* 根據你的要求建一個BooleanQuery對象,然後來查詢
*/
hits = searcher.search(bquery3);
printResult(hits, bquery1.toString());
}
3.過濾
使用 filter 對搜索結果進行過濾,可以獲得更小範圍內更精確的結果。
舉個例子,我們搜索上架時間在 2005-10-1 到 2005-10-30 之間的商品。
對於日期時間,我們需要轉換一下才能添加到索引庫,同時還必須是索引字段。 // index
document.add(fielddate, datefield.datetostring(date), field.store.yes, field.index.un_tokenized);
//…
// search
filter filter = new datefilter(fielddate, datetime.parse(”2005-10-1′), datetime.parse(”2005-10-30′));
hits hits = searcher.search(query, filter);
除了日期時間,還可以使用整數。比如搜索價格在 100 ~ 200 之間的商品。
lucene.net numbertools 對於數字進行了補位處理,如果需要使用浮點數可以自己參考源碼進行。 // index
document.add(new field(fieldnumber, numbertools.longtostring((long)price), field.store.yes, field.index.un_tokenized));
//…
// search
filter filter = new rangefilter(fieldnumber, numbertools.longtostring(100l), numbertools.longtostring(200l), true, true);
hits hits = searcher.search(query, filter);
使用 query 作爲過濾條件。 queryfilter filter = new queryfilter(queryparser.parse(”name2′, fieldvalue, analyzer));
我們還可以使用 filteredquery 進行多條件過濾。
filter filter = new datefilter(fielddate, datetime.parse(”2005-10-10′), datetime.parse(”2005-10-15′));
filter filter2 = new rangefilter(fieldnumber, numbertools.longtostring(11l), numbertools.longtostring(13l), true, true);
query query = queryparser.parse(”name*”, fieldname, analyzer);
query = new filteredquery(query, filter);
query = new filteredquery(query, filter2);
indexsearcher searcher = new indexsearcher(reader);
hits hits = searcher.search(query);
4.分佈搜索
我們可以使用 multireader或 multisearcher 搜索多個索引庫。
multireader reader = new multireader(new indexreader[] { indexreader.open(@”c:\index”), indexreader.open(@”\\server\index”) });
indexsearcher searcher = new indexsearcher(reader);
hits hits = searcher.search(query);
或
indexsearcher searcher1 = new indexsearcher(reader1);
indexsearcher searcher2 = new indexsearcher(reader2);
multisearcher searcher = new multisearcher(new searchable[] { searcher1, searcher2 });
hits hits = searcher.search(query);
還可以使用 parallelmultisearcher 進行多線程並行搜索。
5.顯示搜索語法字符串
我們組合了很多種搜索條件,或許想看看與其對等的搜索語法串是什麼樣的。 booleanquery query = new booleanquery();
query.add(query1, true, false);
query.add(query2, true, false);
//…
console.writeline(”syntax: {0}”, query.tostring());
輸出:
syntax: +(name:name* value:name*) +number:[0000000000000000b to 0000000000000000d]
呵呵,就這麼簡單。
6.如何刪除索引
lucene提供了兩種從索引中刪除document的方法,一種是
void deleteDocument(int docNum)
這種方法是根據document在索引中的編號來刪除,每個document加進索引後都會有個唯一編號,所以根據編號刪除是一種精確刪除,但是這個編號是索引的內部結構,一般我們不會知道某個文件的編號到底是幾,所以用處不大。另一種是
void deleteDocuments(Term term)
這種方法實際上是首先根據參數term執行一個搜索操作,然後把搜索到的結果批量刪除了。我們可以通過這個方法提供一個嚴格的查詢條件,達到刪除指定document的目的。
下面給出一個例子:
Directory dir = FSDirectory.getDirectory(PATH, false);
IndexReader reader = IndexReader.open(dir);
Term term = new Term(field, key);
reader.deleteDocuments(term);
reader.close();
ms還有操作
deleteDocuments(Term);
deleteDocuments(Term[]);
deleteDocuments(Query);
deleteDocuments(Query[]);
7.如何更新索引
注:據多人反應,新版本的lucene以及提供了更新索引的方法。
writer.updateDocument(doc);
————————————————————javaeye分割線——————————————
lucene並沒有提供專門的索引更新方法,我們需要先將相應的document刪除,然後再將新的document加入索引。例如:
Directory dir = FSDirectory.getDirectory(PATH, false);
IndexReader reader = IndexReader.open(dir);
Term term = new Term(“title”, “lucene introduction”);
reader.deleteDocuments(term);
reader.close();
IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true);
Document doc = new Document();
doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED));
doc.add(new Field("content", "lucene is funny", Field.Store.YES, Field.Index.TOKENIZED));
writer.addDocument(doc);
writer.optimize();
writer.close();
8.多樣化的搜索
/** *** 一個關鍵字,對一個字段進行查詢 **** */
QueryParser qp = new QueryParser("content",analyzer);
query = qp.parse(keyword);
Hits hits = searcher.search(query);
/** *** 模糊查詢 **** */
Term term = new Term("content",keyword);
FuzzyQuery fq = new FuzzyQuery(term);
Hits hits = searcher.search(fq);
/** *** 一個關鍵字,在兩個字段中查詢 **** */
/*
* 1.BooleanClause.Occur[]的三種類型: MUST : + and MUST_NOT : - not SHOULD : or
* 2.下面查詢的意思是:content中必須包含該關鍵字,而title有沒有都無所謂
* 3.下面的這個查詢中,Occur[]的長度必須和Fields[]的長度一致。每個限制條件對應一個字段
*/
BooleanClause.Occur[] flags = new BooleanClause.Occur[]{BooleanClause.Occur.SHOULD,BooleanClause.Occur.MUST};
query=MultiFieldQueryParser.parse(keyword,new String[]{"title","content"},flags,analyzer);
/** *** 兩個(多個)關鍵字對兩個(多個)字段進行查詢,默認匹配規則 **** */
/*
* 1.關鍵字的個數必須和字段的個數相等
* 2.由於沒有指定匹配規定,默認爲"SHOULD" 因此,下面查詢的意思是:"title"中含有keyword1 或 "content"含有keyword2.
* 在此例中,把keyword1和keyword2相同
*/
query=MultiFieldQueryParser.parse(new String[]{keyword,keyword},new
String[]{"title","content"},analyzer);
/** *** 兩個(多個)關鍵字對兩個(多個)字段進行查詢,手工指定匹配規則 **** */
/*
* 1.必須 關鍵字的個數 == 字段名的個數 == 匹配規則的個數
* 2.下面查詢的意思是:"title"必須不含有keyword1,並且"content"中必須含有keyword2
*/
BooleanClause.Occur[] flags = new
BooleanClause.Occur[]{BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.MUST};
query=MultiFieldQueryParser.parse(new String[]{keyword,keyword},new
String[]{"title","content"},flags,analyzer);
/** *** 對日期型字段進行查詢 **** */
/** *** 對數字範圍進行查詢 **** */
/*
* 1.兩個條件必須是同一個字段
* 2.前面一個條件必須比後面一個條件小,否則找不到數據
* 3.new RangeQuery中的第三個參數,表示是否包含"=" true: >= 或 <= false: > 或 <
* 4.找出 55>=id>=53 or 60>=id>=57:
*/
Term lowerTerm1 = new Term("id","53");
Term upperTerm1 = new Term("id","55");
RangeQuery rq1 = new RangeQuery(lowerTerm1,upperTerm1,true);
Term lowerTerm2 = new Term("id","57");
Term upperTerm2 = new Term("id","60");
RangeQuery rq2 = new RangeQuery(lowerTerm2,upperTerm2,true);
BooleanQuery bq = new BooleanQuery();
bq.add(rq1,BooleanClause.Occur.SHOULD);
bq.add(rq2,BooleanClause.Occur.SHOULD);
Hits hits = searcher.search(bq);
9.結果排序
排序的關鍵點有兩個:
1:首先你要排序的字段必須是被index的,並且是untokenized的。
如:
doc.add(new Field("click", dv.get("click").toString(), Field.Store.NO, Field.Index.UN_TOKENIZED));
2:在檢索時候:
如:
/***** 排序 *****/
/*
* 1.被排序的字段必須被索引過(Indexecd),在索引時不能 用 Field.Index.TOKENIZED
* (用UN_TOKENIZED可以正常實現.用NO時查詢正常,但排序不能正常設置升降序)
* 2.SortField類型
* SCORE、DOC、AUTO、STRING、INT、FLOAT、CUSTOM 此類型主要是根據字段的類型選擇
* 3.SortField的第三個參數代表是否是降序true:降序 false:升序
*/
Sort sort = new Sort(new SortField[]{new SortField("click", SortField.INT, true)});
Hits hits = searcher.search(querystring,sort);
/*
* 按日期排序
*/
Sort sort = new Sort(new SortField[]{new SortField("createTime", SortField.INT, false)});
/***** 過濾器 ******/
QueryParser qp1 = new QueryParser("content",analyzer);
Query fquery = qp1.parse("我");
BooleanQuery bqf = new BooleanQuery();
bqf.add(fquery,BooleanClause.Occur.SHOULD);
QueryFilter qf = new QueryFilter(bqf);
Hits hits = searcher.search(query);
10.將小索引文件合併到大的索引文件中去(此方法性能不佳)
/** 將小索引文件合併到大的索引文件中去
* @param from 將要合併到to文件的文件
* @param to 將from文件合併到該文件
* @param analyzer
*/
private void mergeIndex(File from,File to,Analyzer analyzer)
{
IndexWriter indexWriter = null;
try{
System.out.println("正在合併索引文件!\t");
indexWriter = new IndexWriter(to,analyzer, false);
indexWriter.setMergeFactor(100000);
indexWriter.setMaxFieldLength(Integer.MAX_VALUE);
indexWriter.setMaxBufferedDocs(Integer.MAX_VALUE);
indexWriter.setMaxMergeDocs(Integer.MAX_VALUE);
FSDirectory[] fs = {FSDirectory.getDirectory(from,false)};
indexWriter.addIndexes(fs);
indexWriter.optimize();
indexWriter.close();
System.out.println("已完成合並!\t");
}
catch(Exception e)
{
Utility.writeLog("合併索引文件出錯!mergeIndex()"+e.getMessage(),"");
}
finally
{
try{
if(indexWriter!=null)
indexWriter.close();
}
catch(Exception e ){
}
}
}
合併時間是從每天的凌晨3點鐘開始,一直到早上9點左右,足足用5個小時才合併完成,其中大索引文件大小爲4G,小索引爲10MB.
11.問題2:單字共現頻率的局部統計的原理
解答:
高頻字串統計的理論基礎是N - 元模型。
設W1 W2 ...WN 是長度爲N 的字串,則字串W 的似然度爲
p ( W) = p ( w i | w1 w2 ...w i - 1) (1)
上面公式的意義反映連續個N 字之間的結合程度,如果若干種不同的歷史組合W1 W2 ...WN的最後N - 1 個字相同,就把它們都看作一類。在這一假設下,每一個字出現的概率不再與前面的歷史有關,只與最近的N - 1 個字相關,字串的先驗概率爲
p ( W) = p ( w i - ( n - 1) w i - ( n - 2) ...w i - 1) (2)
當p ( W) 超過一定的閾值時,說明這N 個字的結合能力較強,我們就可以認爲該字串能被看成一個“詞”。
正是根據以上所說原理,預先對待分詞文本每個單字進行出現次數統計並記錄它們在文中出現的位置(存儲方式如附件圖例所示),預處理後我們遍歷單字頻次統計 列表出現次數大於2的所有單字在文中出現的位置i,判斷位置i+1的單字出現次數是否也大於2,若是則判斷位置i+2的單字出現次數是否也大於2,如此類 推直至位置i+n+1的單字出現次數小於2,獲得候選詞組 w(i,i+1...i+n)並放入候選詞彙集合,最後對候選詞彙集合進行前綴後綴處理獲得合適的高頻詞彙集合result
12.索引合併
writer.addIndexes(indexDirs);