今天突然想来看一下全文检索,于是就了解了一下Lucene.Net,然后把公司目前的产品表拿来练手,写了这么个Demo。
先看一下Demo的代码
public class ProductRepository { private Logger logger = new Logger(typeof(ProductRepository)); /// <summary> /// 分页获取商品数据 /// </summary> /// <param name="tableNum"></param> /// <param name="pageIndex">从1开始</param> /// <param name="pageSize"></param> /// <returns></returns> public List<Product> QueryList(int tableNum, int pageIndex, int pageSize) { string sql = string.Format("SELECT top {2} ProductID,ProductCode,ProductName,PreferentialPrice AS Price,ProductImage AS ImageUrl FROM Product WHERE ProductID>{1};", tableNum.ToString("000"), pageSize * Math.Max(0, pageIndex - 1), pageSize); return SqlHelper.QueryList<Product>(sql); } }
internal class LuceneTest { public static void Show(string searchInput) { FSDirectory dir = FSDirectory.Open(StaticConstant.TestIndexPath); IndexSearcher searcher = new IndexSearcher(dir);//查找器 QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "title", new PanGuAnalyzer());//解析器 { string keyword = searchInput; { Query query = parser.Parse(keyword); TopDocs docs = searcher.Search(query, null, 10000);//找到的数据 int i = 0; foreach (ScoreDoc sd in docs.ScoreDocs) { if (i++ < 1000) { Document doc = searcher.Doc(sd.Doc); Console.WriteLine("***************************************"); Console.Write(string.Format(" id={0}", doc.Get("id"))); Console.Write(string.Format(" title={0}", doc.Get("title"))); Console.Write(string.Format(" time={0}", doc.Get("time"))); Console.Write(string.Format(" price={0}", doc.Get("price"))); Console.WriteLine("***************************************"); } } Console.WriteLine($"一共命中了{docs.TotalHits}个"); } } } /// <summary> /// 初始化索引 /// </summary> public static void InitIndex() { List<Product> productList = GetList(); FSDirectory directory = FSDirectory.Open(StaticConstant.TestIndexPath);//FSDirectory表示存放在文件中 using (IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED))//索引写入器 { foreach (Product product in productList) { Document doc = new Document();//一条数据 doc.Add(new Field("id", product.ProductID.ToString(), Field.Store.NO, Field.Index.NOT_ANALYZED));//一个字段 列名 值 是否保存值 是否分词 doc.Add(new Field("title", product.ProductName, Field.Store.YES, Field.Index.ANALYZED)); doc.Add(new Field("imageurl", product.ImageUrl, Field.Store.NO, Field.Index.NOT_ANALYZED)); doc.Add(new Field("content", "this is lucene working,powerful tool ", Field.Store.YES, Field.Index.ANALYZED)); doc.Add(new NumericField("price", Field.Store.YES, true).SetDoubleValue((double)(product.Price))); doc.Add(new NumericField("time", Field.Store.YES, true).SetIntValue(int.Parse(DateTime.Now.ToString("yyyyMMdd")))); writer.AddDocument(doc);//写进去 } writer.Optimize();//优化合并 } } /// <summary> /// 数据库取5000条数据测试 /// </summary> /// <returns></returns> private static List<Product> GetList() { ProductRepository repository = new ProductRepository(); List<Product> productList = repository.QueryList(1, 1, 5000); return productList; } }
class Program { static void Main(string[] args) { LuceneTest.InitIndex(); string input; while ((input = Console.ReadLine()).ToUpper() != "EXIT") { if (input == "") continue; LuceneTest.Show(input); } } }
现在来看一下LuceneTest的InitIndex方法。
FSDirectory directory = FSDirectory.Open(StaticConstant.TestIndexPath); 该语句表示将创建的索引保存到文件中;
IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED) 接着我们创建了一个writer,并指定存放索引的目录为刚创建的directory,使用的分析器为PanGuAnalyzer,第三个参数说明如果已经有索引文件在索引目录下,我们将覆盖它们。
我们循环遍历productList,创建Document,然后往Document添加Filed。这里说明一下Document是表示含文档的数据结构,定义了存储文档的数据结构,Field类定义了document一个域。Field有两个属性可选:存储和索引。通过存储属性你可以控制是否对这个Field进行存储;通过索引属性你可以控制是否对该Field进行索引。最后将document加入到IndexWriter里,使用IndexWriter.Optimize()进行优化合并。
之后我们可以看到在我们制定的目录下生成了下面的文件
最后我们在Show方法中使用 IndexSearcher 进行检索。