11、自定义过滤器(lucene笔记)

一、概述

在查询的过程中,如果我们想然某些索引不被查询到,可以将相关索引删除,但是删除后如果在后面又需要让其被查询,这样需要重建索引,这样反反复复显然很麻烦。 于是我们可以自定义过滤器,控制部分查询权限。自定义过滤器原理其实很简单,就是为每个索引再创建一个标记,此标记只能是0或者1,当一个索引的标记为0时这个索引是不会被查询到的,反之则可以。所有的索引的标记就像一个bit序列一样。

二、入门(工程lucene_filter0

首先我们在工具类FileIndexUtil.java中创建索引的方法中添加一个字段索引。

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();//如果我们要新建索引,那么将之前创建的删除
        }
        File file = new File("E:/myeclipse/Lucene/somefile");

        Document document = null;
        Random random = new Random();
        int index = 0;//这里为id创建一个索引
        for (File f : file.listFiles()) {
            //将每个索引的评分设置成一个随机数
            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()));
            // 最后我们将字节数转换成kb
            document.add(new NumericField("size", Field.Store.YES, true)
                    .setIntValue((int) (f.length())));
            //这里我们使用score评分来创建索引,没有存储,搜索出来的时候为null
            //这里我自己随机设置的一个评分
            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();
            }
        }
    }
}

说明:可以看到这里我们添加了id的索引。然后重建索引。

定义一个查询方法类:
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 + ",评分:" + sd.score 
                        + ",名称:" + 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();
        }
    }
}

自定义过滤器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 {
    
    //存储要删除的id
    private String[] delIds = {"10"};
    
    @Override
    public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
        
        //这就相当于为每个索引创建一个标记,比如设置标记为0不显示,设置为1显示,这样就达到了过滤的效果。
        //此对象创建好之后所有对象都是0
        OpenBitSet obs = new OpenBitSet(reader.maxDoc());
        //obs.set(5);//将doc id设置为1
        //先把元素填满,该文档的对应元素就会被设置为1
        obs.set(0, reader.maxDoc() - 1);
        int[] docs = new int[1];//存放位置
        int[] freqs = new int[1];//次数
        //获取id所在的位置并且将其设置为0
        for(String delId : delIds){
            //获取TermDocs
            TermDocs tds = reader.termDocs(new Term("id", delId));
            //会将查询出来的对象的位置存储到docs中,出现的频率存储在freqs,返回查询出来的条数
            int count = tds.read(docs, freqs);
            if(count == 1){//本来也只有一条数据
                obs.clear(docs[0]);//将这个位置的元素删除
            }
        }
        return obs;
    }
}

测试TestCustomFilter.java

@Test
public void test01(){
    CustomFilter filter = new CustomFilter();
    filter.searchByCustomFilter();
}

说明:可以看到在CustomFiltet.java中我们在创建搜索的时候将自定义过滤器传递进去了。这样就可以实现过滤查询。测试结果:

1

可以看到我们将id为10的索引给过滤掉了。但是这种写法显然不是很好,比如这里将要过滤的索引的id都写死了。

三、改进(工程lucene_filter1

这里我们首先编写一个接口FilterAccessor.java,此接口专门用来设置我们要处理的域、域相关的值、是否要处理。

package cn.itcast.util;

//这里存储过滤器要处理的内容,之前我们是写死的
public interface FilterAccessor {
    
    public String[] values();//要处理的值,比如id值
    
    public String getField();//表示要处理的域,比如id
    
    public boolean set();//是否要进行处理(将值设置进去)
}

说明:这里的是否要处理的意思是,如果我们将一些id值设置进去,就表示我们只想让这些id索引被查询到。将其他的没有设置的索引过滤掉。

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 {

    // 存储要删除的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]);//让标记为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]);//让标记为0
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

说明:从这里可以看到,我们会将传递进来的值对应的索引的标记设置为1,让其被查询到。否则设置标记为0,这样便不能被查询到。

在类CustomFilter.java中我们将相关的值传递进来:

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() {
            //设置要处理的域的值,也就是我只想让下面的索引被查询到
            @Override
            public String[] values() {
                //return new String[]{"1", "5", "10"};
                return new String[]{"json.config", "json.ini", "json.ssh"};
            }
            //true表示要进行过滤处理
            @Override
            public boolean set() {
                return true;
            }
            //设置要处理的域
            @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 + ",评分:" + sd.score 
                    + ",名称:" + 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();
    }
}

说明:这里我们是使用了一个内部类,其实更好的做法是为每个域创建相关的实现类。然后传递进来。然后再次进行测试。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,027评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,975评论 6 342
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,602评论 0 17
  • 还记得小时候家附近有一家小型的酒坊,那浓香的酒味弥漫在周围的空间中。感觉它很特别,所以我有事没事也会去看一下他...
    诗he远方阅读 468评论 1 1
  • 今天是5月20号,这一天本就普普通通的周六,和其他日子一样没什么特别。 就是因为520三个数字让人觉得有纪念意义在...
    阿行PPT阅读 540评论 0 1