使用Lucene.Net达成全文检索!基础解说(二)

上一集当中我们完成了Lucene基本操作中的Create与Read,这一集会将CRUD中的Update与Delete的操作方法告诉你,并且本集会着重于讲解关于"Norms"与权重(Boost)在Lucene中的使用操作。

对于Lucene还没有一些基本认识的朋友,建议先回到上一篇文章中阅读呦!

更新其实就是将存在的索引删除并重新建立Document,不存在的则直接新增。
首先準备一组资料準备更新

List<Product> GetUpdateProductsInformation(){    return new List<Product>    {        new Product{ Id = 6, Name = "香蕉", Description = "运动完后吃根香蕉补充养分。"},        new Product{ Id = 2, Name = "橘子", Description = "橘子跟柳丁你分得出来吗?"}    };}

欲更新的Document必须与创建所引时使用的Document栏位相同

void Update(string key, List<Product> information, DirectoryInfo dir, StandardAnalyzer analyzer){    using( var directory = FSDirectory.Open(dir))    {        using(var indexWriter = new IndexWriter(directory, analyzer, false, IndexWriter.MaxFieldLength.LIMITED))        {            foreach (var index in information)            {                var document = new Document();                document.Add(new Field("Id", index.Id.ToString(), Field.Store.YES, Field.Index.NO));                document.Add(new Field("Name", index.Name, Field.Store.YES, Field.Index.ANALYZED));                document.Add(new Field("Description", index.Description, Field.Store.YES, Field.Index.ANALYZED));                indexWriter.UpdateDocument(new Term("Name", key) ,document);            }        }    }}

来测试看看
http://img2.58codes.com/2024/20145396gKXNevsTvC.png
可以看见 Name = 橘子 的索引已经改为我们新準备的资料啰。
再来是删除!
与更新非常相似,只需要使用deleteDocument()就可以了。

void Delete(string key, DirectoryInfo dir, StandardAnalyzer analyzer){    using (var directory = FSDirectory.Open(dir))    {        using (var indexWriter = new IndexWriter(directory, analyzer, false, IndexWriter.MaxFieldLength.LIMITED))        {            indexWriter.DeleteDocuments(new Term("Name", key));            indexWriter.Optimize();            indexWriter.Commit();        }    }}

再来看看输出结果
http://img2.58codes.com/2024/201453964goy4nSQSk.png
 可以发现 Score :0.7554128, Id :2, Name :橘子, Description :医生给娜美最珍贵的宝藏。这笔索引已经被移除啰!
  
可以发现笔者于更新或删除时都是输入单一字来做异动,除了表达可以对索引做複合更动外,
是因为更新与删除索引同样会使用到分词器(analyzer),
所输入的索引值非ID等数值时必须要配合分词器的分词能力才能取得所想异动的索引喔!

Boost是什么呢?
Boost 分为 :

Index Time Boost : 在建立索引时就计算好的值。例如上一篇中提到的(NORMS)Query Time Boost : 查询时赋与搜寻条件不同的值以影响结果。
我们先来测试Index Time Boost的部分
void CreateIndexWithBoost(List<Product> information, DirectoryInfo dir, StandardAnalyzer analyzer){    using (var directory = FSDirectory.Open(dir))    {        using (var indexWriter = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED))        {            foreach (var index in information)            {                var document = new Document();                document.Add(new Field("Id", index.Id.ToString(), Field.Store.YES, Field.Index.NO));                document.Add(new Field("Name", index.Name, Field.Store.YES, Field.Index.ANALYZED));                document.Add(new Field("Description", index.Description, Field.Store.YES, Field.Index.ANALYZED));                document.GetField("Name").Boost = 1.5F;                document.GetField("Description").Boost = 0.5F;                indexWriter.AddDocument(document);            }            indexWriter.Optimize();            indexWriter.Commit();        }    }}

并记得重新CreateIndex才能刷新栏位的权重值喔。
http://img2.58codes.com/2024/20145396iALDp3uQfh.png
很明显的搜寻出来的Score分数变动了! 但是有没有发现明明Name栏位的Boost改成了1.5,苹果的数值却仍然只有一半呢?
这是因为我们的Search中所参照的栏位为Description,所以在计算Score的时候其实是完全没有参与的喔!
另外要记得,使用Index Time Boost的时候,欲给予铨重分配的栏位Field.Index不能使用NO_NORMS,不然这个栏位并不会纪录权重的资料。

再来我们试试看Query Time Boost

void SearchWithBoost(string searchValue, DirectoryInfo dir, StandardAnalyzer analyzer){    using (var directory = FSDirectory.Open(_dir))    {        using (var indexSearcher = new IndexSearcher(directory))        {            var query = new QueryParser(Lucene.Net.Util.Version.LUCENE_CURRENT, "Name", analyzer).Parse(searchValue);            var query2 = new QueryParser(Lucene.Net.Util.Version.LUCENE_CURRENT, "Description", analyzer).Parse(searchValue);            query.Boost = 2.0F;            query2.Boost = 0.5F;            BooleanQuery booleanQuery = new BooleanQuery();            booleanQuery.Add(query, Occur.SHOULD);            booleanQuery.Add(query2, Occur.SHOULD);            var hits = indexSearcher.Search(booleanQuery, 20);            if (!hits.ScoreDocs.Any())            {                Console.WriteLine("查无相关结果。");                return;            }            Document doc;            foreach (var hit in hits.ScoreDocs)            {                doc = indexSearcher.Doc(hit.Doc);                Console.WriteLine("Score :" + hit.Score + ", Id :" + doc.Get("Id") + ", Name :" + doc.Get("Name") + ", Description :" + doc.Get("Description"));            }        }    }}

这次我们搜寻两个栏位"Name"与"Description",并使用 BooleanQuery来将其组合。
BooleanQuery中的 Occur有三种参数 : "MUST","MUST_NOT","SHOULD",功能与字面上的意思一样为"必须要有","必须没有"与"有无都包含"。

 http://img2.58codes.com/2024/20145396kPqa6RCVsI.png
查询出来的分数就不一样啰!

以上就是这一次的分享,Lucene是一款容易入门但是要实际上战场却又十分複杂的功能,想要达成真正高效能的全文检索,在前期的文件规画配置与资料的权重配比都是一个巨大的挑战。未来会继续分享关于Lucene的其他有趣功能,还请继续期待呦!
另外也可以到我的GitHub下载範例来参考呦!

参考文件:

黑暗大大的全文检索笔记MakbleCSDN Jack2013tong 文章

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章