zoukankan      html  css  js  c++  java
  • 高级搜索 《第五篇》

    一、解析搜索请求

      搜索请求的概念是,用户输入关键词,然后程序去分析关键词,获取用户搜索的真实意图。

      Lucene提供了一套QueryParser类,用来解析搜索请求。这个类是可以使用的。

      1、QueryParser的基本使用

      QueryParser用来分析用户输入的关键词,将关键词转换成Query对象。其构造方法如下所示:

        QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "title", new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30));
        Query q = parser.Parse("飞");
        Console.WriteLine(q);   //title:飞

       这里,QueryParser解析成了:title:飞。

      2、使用QueryParser解析多个关键词

      如果将字符串“love mother”传递给搜索引擎,搜索引擎将如何去搜索?

      它可以执行如下操作:

    1. 搜索到所有含有"love"的记录以及所有含有"mother"的记录,然后将两部分记录加在一起,这是逻辑或的关系。
    2. 搜索到所有含有"love"的记录以及所有含有"mother"的记录,然后取它们的公共部分,这是逻辑与的关系。

      示例:

        QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "title", new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30));
        Query q = parser.Parse("love mother");
        Console.WriteLine(q);   //title:love title:mother

      从结果来看,Lucene把输入的关键词按照逻辑或的关系进行了处理。

      QueryParser解析的结果是: title:love title:mother 意思是,在“title”字段搜索“love”,在“title”字段搜索“mother”,两部分结果加到一起。我们可以修改这种逻辑,使其按照逻辑与的关系进行处理,也就是取出两个关键词的搜索结果的公共部分。

        QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "title", new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30));
        parser.DefaultOperator = Lucene.Net.QueryParsers.QueryParser.Operator.AND;
        Query q = parser.Parse("love mother");
        Console.WriteLine(q);   //+title:love +title:mother

      Lucene引擎对关键词使用了逻辑与运算,解析结果为:

      +title:love +title:mother

      意思是,在“title”字段搜索“love”,在“title”字段搜索“mother”,两部分结果取公共部分。

      QueryParser类还有其他的一些用法,比如进行通配符识别和范围搜索识别等。但是,不建议使用QueryParser类来做搜索请求解析的工作,最好永远不要用这个类。对搜索请求的处理应该在搜索引擎程序前面一层的应用程序中去做,我们在这一层将用户输入的搜索关键词做好了解析之后,交给Query类去封装。

    二、高级搜索

      高级搜索的内容包括:

    • 多字段搜索;
    • 多索引搜索;
    • 多线程搜索;

      1、多字段搜索

      一个文档中含有“标题”,“正文”等字段,搜索一个关键词,不管它在标题中出现还是在正文中出现都算符合条件,这就是多字段搜索。

      1、利用BooleanQuery实现多字段搜索

      要实现多字段搜索很容易,利用前面学到的BooleanQuery就可以做到。首先设置两个TermQuery,然后在它们之间做逻辑运算即可。如下面的示例实现,title:张且body:打。

    复制代码
        //建立索引
        using (IndexWriter writer = new IndexWriter(ramdirectory, analyzer, maxFieldLength))
        {
            Document document1 = new Document();
            document1.Add(new Field("title", "张飞牛人", Field.Store.YES, Field.Index.ANALYZED));
            document1.Add(new Field("body", "张飞是一个牛人", Field.Store.YES, Field.Index.ANALYZED));
            writer.AddDocument(document1);
    
            Document document2 = new Document();
            document2.Add(new Field("title", "张飞打架", Field.Store.YES, Field.Index.ANALYZED));
            document2.Add(new Field("body", "张飞很能打", Field.Store.YES, Field.Index.ANALYZED));
            writer.AddDocument(document2);
        }
    
        //查找
        using (IndexSearcher searcher = new IndexSearcher(ramdirectory))
        {
            Term t1 = new Term("title", "张");
            TermQuery q1 = new TermQuery(t1);
            Term t2 = new Term("body", "打");
            TermQuery q2 = new TermQuery(t2);
    
            BooleanQuery q = new BooleanQuery();
            q.Add(q1, Occur.MUST);
            q.Add(q2, Occur.MUST);
    
            TopDocs docs = searcher.Search(q, null, 10);   
            for (int i = 0; i < docs.TotalHits; i++)
            {
                Document doc = searcher.Doc(docs.ScoreDocs[i].Doc); //输出 张飞打架
                Console.WriteLine(doc.Get("title"));
            }
        }
    复制代码

      2、利用MultiFieldQueryParser实现多字段搜索

      除了通过使用BooleanQuery实现多字段搜索之外,Lucene还专门提供了一个MultiFieldQuery Parser类,用来实现多字段搜索。其实,只不过是个封装,用起来简单,内部还是用BooleanQuery实现的。

      其构造方法如下:

      MultiFieldQueryParser(Lucene.Net.Util.Version matchVersion, string[] fields, Analyzer analyzer)

      第一个参数是多个字段名称组成的数组,第二个参数是分析器对象实例。

        string[] fields = { "title","body" };
        MultiFieldQueryParser mp = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, fields, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30));
        Query q = mp.Parse("打");

      以上是“或”关系。

      2、多索引搜索

      为了减少单个索引目录的大小,时常将索引放在许多目录中,这些索引的结构都是一致的。

      比如有一个城市的网站搜索引擎,随着时间的增长,我们可能会将索引的目录按照年份分成2003、2004、2005等。旧的索引目录被搜索的几率小,所以将其单独分出去,这样,可以减小新的索引目录,加快搜索速度。

      但是有些时候,必须实现多个索引的同时搜索,因为我们需要存放在这些索引中的信息。要实现多索引搜索,只需要对每个索引目录都用IndexSearcher搜索一遍,最后将搜索结果合并起来。

      Lucene中专门提供了MultiSearcher类来实现多索引搜索。使用MultiSearcher类实现多索引搜索的方法如下:

    复制代码
        static void Main(string[] args)
        {
            Analyzer analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
            IndexWriter.MaxFieldLength maxFieldLength = new IndexWriter.MaxFieldLength(10000);
            DirectoryInfo dir = new DirectoryInfo(@"D:1");
            Lucene.Net.Store.Directory directory1 = new SimpleFSDirectory(dir);
    
            DirectoryInfo dir2 = new DirectoryInfo(@"D:2");
            Lucene.Net.Store.Directory directory2 = new SimpleFSDirectory(dir2);
    
            //建立索引
            using (IndexWriter writer = new IndexWriter(directory1, analyzer, maxFieldLength))
            {
                Document document1 = new Document();
                document1.Add(new Field("title", "张飞很能打", Field.Store.YES, Field.Index.ANALYZED));
                writer.AddDocument(document1);
            }
    
            //建立索引
            using (IndexWriter writer = new IndexWriter(directory2, analyzer, maxFieldLength))
            {
                Document document2 = new Document();
                document2.Add(new Field("title", "关羽也很能打", Field.Store.YES, Field.Index.ANALYZED));
                writer.AddDocument(document2);
            }
    
            IndexSearcher searcher1 = new IndexSearcher(directory1);
            IndexSearcher searcher2 = new IndexSearcher(directory2);
            IndexSearcher[] searchers = { searcher1, searcher2 };
        
            //查找
            using (MultiSearcher MySearcher = new MultiSearcher(searchers))
            {
                Term t = new Term("title","打");
                Query query = new TermQuery(t);
                TopDocs docs = MySearcher.Search(query, null, 10);
                for (int i = 0; i < docs.TotalHits; i++)
                {
                    Document doc = MySearcher.Doc(docs.ScoreDocs[i].Doc); //输出 张飞打架 或关系
                    Console.WriteLine(doc.Get("title"));
                }
            }
            Console.ReadKey();
        }
    复制代码

      最后释放搜索器资源的时候,只需要关闭MultiSearcher的实例,在关闭它的时候,相应的几个IndexSearcher也都被关闭了。

      即调用了MySearch.Dispose();相当于调用了searcher1.Dispose();和searcher2.Dispose();

      3、多线程搜索

      MultiSearcher的实现机制是对IndexSearcher数组中的每个IndexSearcher对象执行搜索,得到的搜索结果之后合并到一起。在其中一个IndexSearcher执行搜索时,其他的IndexSearcher处于等待状态,因此,依然是一个目录一个目录地搜索,一个目录被搜索,其他目录在队列中等待。

      可以改良这种搜索方式,就是用多线程。同时检索多个索引目录。Lucene提供了ParallelMultiSearcher来实现多线程搜索。这个类是MultiSearcher的直接子类。其使用方法与MultiSearcher一致(一模一样)。

        IndexSearcher searcher1 = new IndexSearcher(directory1);
        IndexSearcher searcher2 = new IndexSearcher(directory2);
        IndexSearcher[] searchers = { searcher1, searcher2 };
        
        //查找
        using (ParallelMultiSearcher MySearcher = new ParallelMultiSearcher(searchers))

      使用方式与MultiSearcher一模一样,因此多余的代码不贴了。注意,这种方式在各个索引目录都比较大的情况下,效率提高会比较明显。

  • 相关阅读:
    netty集成springboot
    NIO-BufferAPI
    python专题知识追寻者对OS的理解
    python专题文件操作
    我终于学会了使用python操作postgresql
    跟着知识追寻者学BeautifulSoup,你学不会打不还口,骂不还手
    那些不懂hystrix的秘密
    如何成为一名Web前端开发人员?
    css剪裁GIF背景图片动画特效
    css 变量教程
  • 原文地址:https://www.cnblogs.com/zxtceq/p/5461915.html
Copyright © 2011-2022 走看看