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();
    }
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

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