从零开始搭建.NET Core版搜索引擎(一)--Lucene基础概念及示例

40 篇文章 2 订阅
19 篇文章 5 订阅

近期使用了ELK系列组件,不由的感慨其功能的丰富和强大,伴随而来的就是另一个想法“ELK太重,用在简单的小型项目上有些鸡肋,能不能做一个轻量版的搜索引擎用于微小型项目呢?”。
下面将会有一系列的文章来描述整个过程,每篇不会太长,但每篇相比前篇都会有所改进。


1.Lucene.Net简介

做搜索引擎必然绕不开Lucene。Lucene是Apache基金会的开源项目。无论在开源还是专有领域,Lucene 可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。Elasticsearch同样也是基于Lucene的开源搜索引擎。Lucene.NET则是Lucene的.NET版本。

GitHub:Lucene.NET

1.1.基本概念

  • IndexWriter(索引操作器)

lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。但不要被Writer这个叫法所迷惑,索引的删除和更新也是由它来实现的。

  • Analyzer(分析器)

主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器、StopAnalyzer分析器、WhitespaceAnalyzer分析器等。同时也可以通过集成接口来实现自定义的分析器,自带的分析器对于中文来讲确实不太友好。

  • Directory(索引存放位置)

lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。

  • Document(文档)

Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。

  • Field(域)

类似于数据库中的一个字段,存储了key-value值。Field有两个属性可选:存储和索引。通过存储属性你可以控制是否对这个Field进行存储;通过索引属性你可以控制是否对该Field进行索引。

  • IndexSearcher(索引检索器)

是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;

  • Query(查询对象)

Query类似关系型数据库中的SQL语句。与关系型数据库类似,Lucene提供了以下的基本查询:精确查询xxx = ? TermQuery、范围查询 xxx BETWEEN? AND ? PointRangeQuery、模糊查询 xxx LIKE ‘%?%’ PrefixQuery、RegexpQuery、组合查询 (…) AND (…) OR (…) BooleanQuery

  • QueryParser(查询生成器)

是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。同时还提供了MultiFieldQueryParser多条件的查询生成器。

  • Hits(命中/匹配结果)

在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在Lucene中,搜索的结果的集合是用Hits类的实例来表示的。

1.2.工作原理

Lucene的工作流程大体上可分为三个部分:索引部分、分词部分、搜索部分。
源字符串首先经过analyzer处理,包括:分词,分成一个个单词(可同时去除stopword)。 将源中需要的信息加入Document的各个Field中,并把需要索引的Field索引起来,把需要存储的Field存储起来。 将索引写入存储器,存储器可以是内存或磁盘。
用户提供搜索关键词,经过analyzer处理。根据查询条件生成查询对象,然后搜索索引找出对应的Document。 用户根据需要从找到的Document中提取需要的Field。最后将命中的结果以Hits的形式返回。
由于Lucene只提供了最基础的功能,结合其提供的框架功能,在每个流程节点都可以根据需要进行组合、扩展、实现,进而构建成功能丰富可用的搜索引擎。

2.简单示例

2.1.安装Lucene.NET

在这里插入图片描述
目前正式发行版是3.X,最新的4.8.x只有预发行版。

2.2.创建索引

        /// <summary>
        /// 创建索引
        /// </summary>
        /// <param name="fields">Field域集合,也可以是其他结构</param>
        /// <param name="route"></param>
        public void CreateIndex(Dictionary<string, string> fields, string route)
        {
            //创建索引操作器配置项,包含Lucene版本定义和Analyzer分析器定义。此处使用4.8版本和标准分析器。
            IndexWriterConfig config = new IndexWriterConfig(LuceneVersion.LUCENE_48, new StandardAnalyzer(LuceneVersion.LUCENE_48));
            //定义索引存储路径。此处使用磁盘文件存储,存储位置为项目运行目录下的“Lucene”
            Lucene.Net.Store.Directory dir = new NIOFSDirectory("Lucene/"+route);
            //实例化索引操作器。此处构造函数为上面定义索引存储路径和配置项。
            using (IndexWriter writer = new IndexWriter(dir, config))
            {
                //创建文档
                Document doc = new Document();
                foreach (var field in fields)
                {
                    //创建数据域。此处使用了StringField域。此外还能用Int32Field、DoubleField等。
                    Field f = new StringField(field.Key, field.Value, Field.Store.YES);
                    //添加数据域至文档
                    doc.Add(f);
                }
                //索引操作器增加文档
                writer.AddDocument(doc);
                //刷新索引
                writer.Flush(true, true);
            }
        }

2.3.查询

        /// <summary>
        /// 关键词查询
        /// </summary>
        /// <param name="keyword">关键词</param>
        /// <param name="field">域</param>
        /// <returns></returns>
        public Dictionary<Document,float> SearchIndex(string keyword,string field)
        {
            //定义返回数据结构
            Dictionary<Document, float> dic = new Dictionary<Document, float>();
            //打开索引存储路径
            Lucene.Net.Store.Directory dir = FSDirectory.Open("Lucene");
            //实例化索引读取器
            using (Lucene.Net.Index.DirectoryReader reader = DirectoryReader.Open(dir))
            {
                //实例化索引检索器
                IndexSearcher searcher = new IndexSearcher(reader);
                //创建查询生成器。此处使用了基础的查询生成器QueryPaser,构造函数参数为版本、查询的域、分析器。
                //也可以使用MultiFieldQueryPaser多域查询生成器。
                QueryParser parser = new QueryParser(LuceneVersion.LUCENE_48, field,
                    new StandardAnalyzer(LuceneVersion.LUCENE_48));
                //生成查询对象。
                Query query = parser.Parse(keyword);
                //检索并返回结果。检索条件为返回符合程度最高的前10条。
                TopDocs matches = searcher.Search(query, 10);
                //遍历检索结构,构造需要返回的数据结构
                foreach (var match in matches.ScoreDocs)
                {
                    //获取匹配结果中的文档
                    Document doc = searcher.Doc(match.Doc);
                    //获取匹配文档的分数
                    dic.Add(doc,match.Score);
                }
            }

            return dic;
        }

2.4.测试示例

//添加索引示例
public void AddDemo()
{
    SearchManager searcher = new SearchManager();
    Dictionary<string, string> dic = new Dictionary<string, string>();
    dic.Add("Name", "南京工大建设监理咨询有限公司");
    dic.Add("Address", "南京市中山北路200号");
    searcher.CreateIndex(dic, "data");
}

//查询示例
public List<string> SearchDemo(string key,string field)
{
    SearchManager searcher = new SearchManager();
    List<string> list = new List<string>();
    Dictionary<Document, float> docs = searcher.SearchIndex(key, field);
    foreach (var doc in docs)
    {
        string tmp = string.Format("doc:{0},score:{1}", doc.Key.ToString(), doc.Value.ToString());
        list.Add(tmp);
    }
    return list;
}

以上就是一个简单的示例,用来说明Lucene.NET的索引创建及检索过程。后续会在这个基础之上进行不断完善。


项目地址:https://github.com/ludewig/Muyan.Search

不积跬步无以至千里,不积小流无以致江海。

  • 1
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字50 设计师:CSDN官方博客 返回首页
评论

打赏作者

ludewig

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值