上一集当中我们完成了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); } } }}
来测试看看
可以看见 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(); } }}
再来看看输出结果
可以发现 Score :0.7554128, Id :2, Name :橘子, Description :医生给娜美最珍贵的宝藏。这笔索引已经被移除啰!
可以发现笔者于更新或删除时都是输入单一字来做异动,除了表达可以对索引做複合更动外,
是因为更新与删除索引同样会使用到分词器(analyzer),
所输入的索引值非ID等数值时必须要配合分词器的分词能力才能取得所想异动的索引喔!
Boost是什么呢?
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才能刷新栏位的权重值喔。
很明显的搜寻出来的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",功能与字面上的意思一样为"必须要有","必须没有"与"有无都包含"。
查询出来的分数就不一样啰!
以上就是这一次的分享,Lucene是一款容易入门但是要实际上战场却又十分複杂的功能,想要达成真正高效能的全文检索,在前期的文件规画配置与资料的权重配比都是一个巨大的挑战。未来会继续分享关于Lucene的其他有趣功能,还请继续期待呦!
另外也可以到我的GitHub下载範例来参考呦!
参考文件:
黑暗大大的全文检索笔记MakbleCSDN Jack2013tong 文章