一忽冻、概述
在查詢的過(guò)程中,如果我們想然某些索引不被查詢到此疹,可以將相關(guān)索引刪除僧诚,但是刪除后如果在后面又需要讓其被查詢,這樣需要重建索引蝗碎,這樣反反復(fù)復(fù)顯然很麻煩湖笨。 于是我們可以自定義過(guò)濾器,控制部分查詢權(quán)限蹦骑。自定義過(guò)濾器原理其實(shí)很簡(jiǎn)單慈省,就是為每個(gè)索引再創(chuàng)建一個(gè)標(biāo)記,此標(biāo)記只能是0或者1眠菇,當(dāng)一個(gè)索引的標(biāo)記為0時(shí)這個(gè)索引是不會(huì)被查詢到的边败,反之則可以。所有的索引的標(biāo)記就像一個(gè)bit序列一樣捎废。
二笑窜、入門(mén)(工程lucene_filter0
)
首先我們?cè)诠ぞ哳?lèi)FileIndexUtil.java
中創(chuàng)建索引的方法中添加一個(gè)字段索引。
public static void index(boolean hasNew) {
IndexWriter writer = null;
try {
writer = new IndexWriter(directory, new IndexWriterConfig(
Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
if (hasNew) {
writer.deleteAll();//如果我們要新建索引登疗,那么將之前創(chuàng)建的刪除
}
File file = new File("E:/myeclipse/Lucene/somefile");
Document document = null;
Random random = new Random();
int index = 0;//這里為id創(chuàng)建一個(gè)索引
for (File f : file.listFiles()) {
//將每個(gè)索引的評(píng)分設(shè)置成一個(gè)隨機(jī)數(shù)
int score = random.nextInt(600);
document = new Document();
document.add(new Field("id", String.valueOf(index++), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
document.add(new Field("content", new FileReader(f)));
document.add(new Field("filename", f.getName(),
Field.Store.YES, Field.Index.NOT_ANALYZED));
document.add(new Field("path", f.getAbsolutePath(),
Field.Store.YES, Field.Index.NOT_ANALYZED));
document.add(new NumericField("date", Field.Store.YES, true)
.setLongValue(f.lastModified()));
// 最后我們將字節(jié)數(shù)轉(zhuǎn)換成kb
document.add(new NumericField("size", Field.Store.YES, true)
.setIntValue((int) (f.length())));
//這里我們使用score評(píng)分來(lái)創(chuàng)建索引排截,沒(méi)有存儲(chǔ)嫌蚤,搜索出來(lái)的時(shí)候?yàn)閚ull
//這里我自己隨機(jī)設(shè)置的一個(gè)評(píng)分
document.add(new NumericField("score", Field.Store.NO, true).setIntValue(score));
writer.addDocument(document);
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
說(shuō)明:可以看到這里我們添加了id
的索引。然后重建索引断傲。
定義一個(gè)查詢方法類(lèi):
CustomFilter.java
package cn.itcast.util;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
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;
public class CustomFilter {
public void searchByCustomFilter(){
try {
IndexSearcher searcher = new IndexSearcher(IndexReader.open(FileIndexUtil.getDirectory()));
Query q = new TermQuery(new Term("content", "java"));
TopDocs tds = null;
tds = searcher.search(q, new MyIdFilter(), 100);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("id:" + sd.doc + "脱吱,評(píng)分:" + sd.score
+ ",名稱(chēng):" + doc.get("filename") + "认罩,路徑:" + doc.get("path")
+ "急凰,文件大小:" + doc.get("size")
+ ", bit id: " + doc.get("id"));
}
searcher.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
自定義過(guò)濾器MyIdFilter.java
package cn.itcast.util;
import java.io.IOException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.util.OpenBitSet;
public class MyIdFilter extends Filter {
//存儲(chǔ)要?jiǎng)h除的id
private String[] delIds = {"10"};
@Override
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
//這就相當(dāng)于為每個(gè)索引創(chuàng)建一個(gè)標(biāo)記猜年,比如設(shè)置標(biāo)記為0不顯示,設(shè)置為1顯示疾忍,這樣就達(dá)到了過(guò)濾的效果乔外。
//此對(duì)象創(chuàng)建好之后所有對(duì)象都是0
OpenBitSet obs = new OpenBitSet(reader.maxDoc());
//obs.set(5);//將doc id設(shè)置為1
//先把元素填滿,該文檔的對(duì)應(yīng)元素就會(huì)被設(shè)置為1
obs.set(0, reader.maxDoc() - 1);
int[] docs = new int[1];//存放位置
int[] freqs = new int[1];//次數(shù)
//獲取id所在的位置并且將其設(shè)置為0
for(String delId : delIds){
//獲取TermDocs
TermDocs tds = reader.termDocs(new Term("id", delId));
//會(huì)將查詢出來(lái)的對(duì)象的位置存儲(chǔ)到docs中,出現(xiàn)的頻率存儲(chǔ)在freqs一罩,返回查詢出來(lái)的條數(shù)
int count = tds.read(docs, freqs);
if(count == 1){//本來(lái)也只有一條數(shù)據(jù)
obs.clear(docs[0]);//將這個(gè)位置的元素刪除
}
}
return obs;
}
}
測(cè)試TestCustomFilter.java
:
@Test
public void test01(){
CustomFilter filter = new CustomFilter();
filter.searchByCustomFilter();
}
說(shuō)明:可以看到在CustomFiltet.java
中我們?cè)趧?chuàng)建搜索的時(shí)候?qū)⒆远x過(guò)濾器傳遞進(jìn)去了杨幼。這樣就可以實(shí)現(xiàn)過(guò)濾查詢。測(cè)試結(jié)果:
可以看到我們將
id
為10的索引給過(guò)濾掉了聂渊。但是這種寫(xiě)法顯然不是很好差购,比如這里將要過(guò)濾的索引的id
都寫(xiě)死了。
三汉嗽、改進(jìn)(工程lucene_filter1
)
這里我們首先編寫(xiě)一個(gè)接口FilterAccessor.java
欲逃,此接口專(zhuān)門(mén)用來(lái)設(shè)置我們要處理的域、域相關(guān)的值饼暑、是否要處理稳析。
package cn.itcast.util;
//這里存儲(chǔ)過(guò)濾器要處理的內(nèi)容,之前我們是寫(xiě)死的
public interface FilterAccessor {
public String[] values();//要處理的值弓叛,比如id值
public String getField();//表示要處理的域彰居,比如id
public boolean set();//是否要進(jìn)行處理(將值設(shè)置進(jìn)去)
}
說(shuō)明:這里的是否要處理的意思是,如果我們將一些id
值設(shè)置進(jìn)去撰筷,就表示我們只想讓這些id
索引被查詢到陈惰。將其他的沒(méi)有設(shè)置的索引過(guò)濾掉。
MyIdFilter.java
package cn.itcast.util;
import java.io.IOException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.util.OpenBitSet;
public class MyIdFilter extends Filter {
// 存儲(chǔ)要?jiǎng)h除的id
private FilterAccessor accessor;
public MyIdFilter(FilterAccessor accessor) {
this.accessor = accessor;
}
@Override
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
OpenBitSet obs = new OpenBitSet(reader.maxDoc());
if(accessor.set()){
set(reader, obs);
}else {
clear(reader, obs);
}
return obs;
}
private void set(IndexReader reader, OpenBitSet obs) {
try {
int[] docs = new int[1];
int[] freqs = new int[1];
for (String delId : accessor.values()) {
TermDocs tds = reader.termDocs(new Term(accessor.getField(), delId));
int count = tds.read(docs, freqs);
if (count == 1) {
obs.set(docs[0]);//讓標(biāo)記為1
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void clear(IndexReader reader, OpenBitSet obs) {
try {
obs.set(0, reader.maxDoc() - 1);
int[] docs = new int[1];
int[] freqs = new int[1];
for (String delId : accessor.values()) {
TermDocs tds = reader.termDocs(new Term(accessor.getField(), delId));
int count = tds.read(docs, freqs);
if (count == 1) {
obs.clear(docs[0]);//讓標(biāo)記為0
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
說(shuō)明:從這里可以看到毕籽,我們會(huì)將傳遞進(jìn)來(lái)的值對(duì)應(yīng)的索引的標(biāo)記設(shè)置為1抬闯,讓其被查詢到。否則設(shè)置標(biāo)記為0关筒,這樣便不能被查詢到画髓。
在類(lèi)CustomFilter.java
中我們將相關(guān)的值傳遞進(jìn)來(lái):
public void searchByCustomFilter(){
try {
IndexSearcher searcher = new IndexSearcher(IndexReader.open(FileIndexUtil.getDirectory()));
Query q = new TermQuery(new Term("content", "java"));
TopDocs tds = null;
tds = searcher.search(q, new MyIdFilter(new FilterAccessor() {
//設(shè)置要處理的域的值逐沙,也就是我只想讓下面的索引被查詢到
@Override
public String[] values() {
//return new String[]{"1", "5", "10"};
return new String[]{"json.config", "json.ini", "json.ssh"};
}
//true表示要進(jìn)行過(guò)濾處理
@Override
public boolean set() {
return true;
}
//設(shè)置要處理的域
@Override
public String getField() {
//return "id";
return "filename";
}
}), 100);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("id:" + sd.doc + "馒稍,評(píng)分:" + sd.score
+ "祖今,名稱(chēng):" + doc.get("filename") + ",路徑:" + doc.get("path")
+ "更米,文件大小:" + doc.get("size")
+ ", bit id: " + doc.get("id"));
}
searcher.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
說(shuō)明:這里我們是使用了一個(gè)內(nèi)部類(lèi)辽慕,其實(shí)更好的做法是為每個(gè)域創(chuàng)建相關(guān)的實(shí)現(xiàn)類(lèi)酬核。然后傳遞進(jìn)來(lái)。然后再次進(jìn)行測(cè)試碉纳。