C军

不玩博客了!

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、解析搜索请求

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

  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一模一样,因此多余的代码不贴了。注意,这种方式在各个索引目录都比较大的情况下,效率提高会比较明显。

posted on 2014-04-02 12:07  逆心  阅读(1918)  评论(1编辑  收藏  举报