Lucene.NET使用入门(二)【简单的搜索网站示例】

发布于 2018-10-21 | 作者: 风灵使 | 来源: blog.csdn.net | 转载于: blog.csdn.net

项目结构:


LuceneSearch.Data层

SampleData.cs

namespace LuceneSearch.Model {
    public class SampleData {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
}

SelectedList.cs

nnamespace LuceneSearch.Model {
  public class SelectedList {
    public string Value { get; set; }
    public string Text { get; set; }
  }
}

SampleDataRepository.cs

using System.Collections.Generic;
using System.Linq;
using LuceneSearch.Model;

namespace LuceneSearch.Repository
{
    public static class SampleDataRepository
    {
        public static SampleData Get(int id)
        {
            return GetAll().SingleOrDefault(x => x.Id.Equals(id));
        }
        public static List GetAll()
        {
            return new
              List {
                           new SampleData {Id = 1, Name = "Belgrad", Description = "City in Serbia"},
                           new SampleData {Id = 2, Name = "Moscow", Description = "City in Russia"},
                           new SampleData {Id = 3, Name = "Chicago", Description = "City in USA"},
                           new SampleData {Id = 4, Name = "Mumbai", Description = "City in India"},
                           new SampleData {Id = 5, Name = "Hong-Kong", Description = "City in Hong-Kong"},
                         };
        }
    }
}

LuceneSearch.Library层

GoLucene.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using LuceneSearch.Model;
using Version = Lucene.Net.Util.Version;

namespace LuceneSearch.Service
{
    public static class GoLucene
    {
        // 属性
        public static string _luceneDir =Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath, "lucene_index");
        private static FSDirectory _directoryTemp;
        private static FSDirectory _directory
        {
            get
            {
                if (_directoryTemp == null) _directoryTemp = FSDirectory.Open(new DirectoryInfo(_luceneDir));
                if (IndexWriter.IsLocked(_directoryTemp)) IndexWriter.Unlock(_directoryTemp);
                var lockFilePath = Path.Combine(_luceneDir, "write.lock");
                if (File.Exists(lockFilePath)) File.Delete(lockFilePath);
                return _directoryTemp;
            }
        }

        // 搜索方法
        public static IEnumerable GetAllIndexRecords()
        {
            // 验证搜索索引
            if (!System.IO.Directory.EnumerateFiles(_luceneDir).Any()) return new List();

            // 设置lucene搜索器
            var searcher = new IndexSearcher(_directory, false);
            var reader = IndexReader.Open(_directory, false);
            var docs = new List();
            var term = reader.TermDocs();
            // v 2.9.4: use 'term.Doc()'
            // v 3.0.3: use 'term.Doc'
            while (term.Next()) docs.Add(searcher.Doc(term.Doc));
            reader.Dispose();
            searcher.Dispose();
            return _mapLuceneToDataList(docs);
        }
        public static IEnumerable Search(string input, string fieldName = "")
        {
            if (string.IsNullOrEmpty(input)) return new List();

            var terms = input.Trim().Replace("-", " ").Split(' ')
                .Where(x => !string.IsNullOrEmpty(x)).Select(x => x.Trim() + "*");
            input = string.Join(" ", terms);

            return _search(input, fieldName);
        }
        public static IEnumerable SearchDefault(string input, string fieldName = "")
        {
            return string.IsNullOrEmpty(input) ? new List() : _search(input, fieldName);
        }

        // 主要搜索方法
        private static IEnumerable _search(string searchQuery, string searchField = "")
        {
            // 验证
            if (string.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", ""))) return new List();

            // 设置Lucene搜索器
            using (var searcher = new IndexSearcher(_directory, false))
            {
                var hits_limit = 1000;
                var analyzer = new StandardAnalyzer(Version.LUCENE_30);

                // 搜索单字段
                if (!string.IsNullOrEmpty(searchField))
                {
                    var parser = new QueryParser(Version.LUCENE_30, searchField, analyzer);
                    var query = parseQuery(searchQuery, parser);
                    var hits = searcher.Search(query, hits_limit).ScoreDocs;
                    var results = _mapLuceneToDataList(hits, searcher);
                    analyzer.Close();
                    searcher.Dispose();
                    return results;
                }
                // 搜索多个字段(按照RELEVANCE排序)
                else
                {
                    var parser = new MultiFieldQueryParser
                        (Version.LUCENE_30, new[] { "Id", "Name", "Description" }, analyzer);
                    var query = parseQuery(searchQuery, parser);
                    var hits = searcher.Search(query, null, hits_limit, Sort.INDEXORDER).ScoreDocs;
                    var results = _mapLuceneToDataList(hits, searcher);
                    analyzer.Close();
                    searcher.Dispose();
                    return results;
                }
            }
        }
        private static Query parseQuery(string searchQuery, QueryParser parser)
        {
            Query query;
            try
            {
                query = parser.Parse(searchQuery.Trim());
            }
            catch (ParseException)
            {
                query = parser.Parse(QueryParser.Escape(searchQuery.Trim()));
            }
            return query;
        }

        // 将Lucene搜索索引映射到数据
        private static IEnumerable _mapLuceneToDataList(IEnumerable hits)
        {
            return hits.Select(_mapLuceneDocumentToData).ToList();
        }
        private static IEnumerable _mapLuceneToDataList(IEnumerable hits, IndexSearcher searcher)
        {
            // v 2.9.4: use 'hit.doc'
            // v 3.0.3: use 'hit.Doc'
            return hits.Select(hit => _mapLuceneDocumentToData(searcher.Doc(hit.Doc))).ToList();
        }
        private static SampleData _mapLuceneDocumentToData(Document doc)
        {
            return new SampleData
            {
                Id = Convert.ToInt32(doc.Get("Id")),
                Name = doc.Get("Name"),
                Description = doc.Get("Description")
            };
        }

        // 添加/更新/清除  搜索索引数据
        public static void AddUpdateLuceneIndex(SampleData sampleData)
        {
            AddUpdateLuceneIndex(new List { sampleData });
        }
        public static void AddUpdateLuceneIndex(IEnumerable sampleDatas)
        {
            // 初始lucene
            var analyzer = new StandardAnalyzer(Version.LUCENE_30);
            using (var writer = new IndexWriter(_directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
            {
                // add data to lucene search index (replaces older entries if any)
                //将数据添加到lucene搜索索引(如果有的话替换旧条目)
                foreach (var sampleData in sampleDatas) _addToLuceneIndex(sampleData, writer);

                // 关闭
                analyzer.Close();
                writer.Dispose();
            }
        }
        public static void ClearLuceneIndexRecord(int record_id)
        {
            // 初始lucene
            var analyzer = new StandardAnalyzer(Version.LUCENE_30);
            using (var writer = new IndexWriter(_directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
            {
                // 删除旧索引条目
                var searchQuery = new TermQuery(new Term("Id", record_id.ToString()));
                writer.DeleteDocuments(searchQuery);

                // 关闭
                analyzer.Close();
                writer.Dispose();
            }
        }
        public static bool ClearLuceneIndex()
        {
            try
            {
                var analyzer = new StandardAnalyzer(Version.LUCENE_30);
                using (var writer = new IndexWriter(_directory, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED))
                {
                    // 删除旧索引条目
                    writer.DeleteAll();

                    // 关闭
                    analyzer.Close();
                    writer.Dispose();
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }
        public static void Optimize()
        {
            var analyzer = new StandardAnalyzer(Version.LUCENE_30);
            using (var writer = new IndexWriter(_directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
            {
                analyzer.Close();
                writer.Optimize();
                writer.Dispose();
            }
        }
        private static void _addToLuceneIndex(SampleData sampleData, IndexWriter writer)
        {
            // 删除旧索引条目
            var searchQuery = new TermQuery(new Term("Id", sampleData.Id.ToString()));
            writer.DeleteDocuments(searchQuery);

            // 添加新的索引条目
            var doc = new Document();

            // 添加映射到db字段的lucene字段
            doc.Add(new Field("Id", sampleData.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            doc.Add(new Field("Name", sampleData.Name, Field.Store.YES, Field.Index.ANALYZED));
            doc.Add(new Field("Description", sampleData.Description, Field.Store.YES, Field.Index.ANALYZED));

            // 添加条目到索引
            writer.AddDocument(doc);
        }

    }
}

LuceneSearch.Mvc层

HomeController.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using LuceneSearch.Repository;
using LuceneSearch.Service;
using LuceneSearch.Model;
using MvcLuceneSampleApp.ViewModels;

namespace MvcLuceneSampleApp.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index(string searchTerm, string searchField, bool? searchDefault, int? limit)
        {
            //创建默认的Lucene搜索索引目录
            if (!Directory.Exists(GoLucene._luceneDir))
            {
                Directory.CreateDirectory(GoLucene._luceneDir);
            }

            //执行Lucene搜索
            List _searchResults;
            if (searchDefault == true)
            {
                _searchResults = (string.IsNullOrEmpty(searchField) ? GoLucene.SearchDefault(searchTerm) : GoLucene.SearchDefault(searchTerm, searchField)).ToList();
            }
            else
            {
                _searchResults = (string.IsNullOrEmpty(searchField)
                                  ? GoLucene.Search(searchTerm)
                                  : GoLucene.Search(searchTerm, searchField)).ToList();
            }

            if (string.IsNullOrEmpty(searchTerm) && !_searchResults.Any())
            {
                _searchResults = GoLucene.GetAllIndexRecords().ToList();
            }

            //设置和返回视图模型
            var search_field_list = new
                List {
                                         new SelectedList {Text = "(所有字段)", Value = ""},
                                         new SelectedList {Text = "Id", Value = "Id"},
                                         new SelectedList {Text = "Name", Value = "Name"},
                                         new SelectedList {Text = "Description", Value = "Description"}
                                     };

            //限制显示数据库记录数
            var limitDb = limit == null ? 3 : Convert.ToInt32(limit);
            List allSampleData;
            if (limitDb > 0)
            {
                allSampleData = SampleDataRepository.GetAll().ToList().Take(limitDb).ToList();
                ViewBag.Limit = SampleDataRepository.GetAll().Count - limitDb;
            }
            else allSampleData = SampleDataRepository.GetAll();

            return View(new IndexViewModel
            {
                AllSampleData = allSampleData,
                SearchIndexData = _searchResults,
                SampleData = new SampleData { Id = 9, Name = "埃尔帕索", Description = "德克萨斯城市" },
                SearchFieldList = search_field_list,
            });
        }

        public ActionResult Search(string searchTerm, string searchField, string searchDefault)
        {
            return RedirectToAction("Index", new { searchTerm, searchField, searchDefault });
        }

        public ActionResult CreateIndex()
        {
            GoLucene.AddUpdateLuceneIndex(SampleDataRepository.GetAll());
            TempData["Result"] = "搜索索引已成功创建!";
            return RedirectToAction("Index");
        }

        [HttpPost]
        public ActionResult AddToIndex(SampleData sampleData)
        {
            GoLucene.AddUpdateLuceneIndex(sampleData);
            TempData["Result"] = "记录被成功添加到搜索索引!";
            return RedirectToAction("Index");
        }

        public ActionResult ClearIndex()
        {
            if (GoLucene.ClearLuceneIndex())
                TempData["Result"] = "搜索索引已成功清除!";
            else
                TempData["ResultFail"] = "索引已锁定,无法清除,请稍后重试或手动清除!";
            return RedirectToAction("Index");
        }

        public ActionResult ClearIndexRecord(int id)
        {
            GoLucene.ClearLuceneIndexRecord(id);
            TempData["Result"] = "搜索索引记录已成功删除!";
            return RedirectToAction("Index");
        }

        public ActionResult OptimizeIndex()
        {
            GoLucene.Optimize();
            TempData["Result"] = "搜索索引成功优化!";
            return RedirectToAction("Index");
        }

    }
}

IndexViewModel.cs

using System.Collections.Generic;
using LuceneSearch.Model;

namespace MvcLuceneSampleApp.ViewModels
{
    public class IndexViewModel
    {
        public int Limit { get; set; }
        public bool SearchDefault { get; set; }
        public SampleData SampleData { get; set; }
        public IEnumerable AllSampleData { get; set; }
        public IEnumerable SearchIndexData { get; set; }
        public IList SearchFieldList { get; set; }
        public string SearchTerm { get; set; }
        public string SearchField { get; set; }
    }
}

Index.cshtml

@model MvcLuceneSampleApp.ViewModels.IndexViewModel

@TempData["Result"] @TempData["ResultFail"]
数据库记录 (@Html.ActionLink("从数据库创建搜索索引 [+]", "CreateIndex")) @foreach (var record in Model.AllSampleData) { }
@Html.LabelFor(m => Model.SampleData.Id) @Html.LabelFor(m => Model.SampleData.Name) @Html.LabelFor(m => Model.SampleData.Description)
@record.Id @record.Name @record.Description
@if (ViewBag.Limit > 0) {
@ViewBag.Limit 条更多记录... (查看全部)
}
搜索(自定义,适用于大多数基本场景)

尝试这些搜索: "1 3 5", "City", "Russia India", "bel mos ind"

@using (Html.BeginForm("Search", "Home")) {
@Html.CheckBoxFor(m => m.SearchDefault) @Html.LabelFor(m => m.SearchDefault, "使用默认的Lucene查询")
@Html.TextBoxFor(m => m.SearchTerm, new { @class = "big", style = "width:650px;", autocomplete = "off" })
@Html.DropDownListFor(m => m.SearchField, Model.SearchFieldList.Select(x => new SelectListItem { Text = x.Text, Value = x.Value }), new { style = "width:150px;" })
}
Search index (@Html.ActionLink("优化", "OptimizeIndex")) (@Html.ActionLink("清除 [X]", "ClearIndex")) @if (Model.SearchIndexData.Any()) { @foreach (var record in Model.SearchIndexData) { }
@Html.LabelFor(m => Model.SampleData.Id) @Html.LabelFor(m => Model.SampleData.Name) @Html.LabelFor(m => Model.SampleData.Description)
@record.Id @record.Name @record.Description @Html.ActionLink("删除", "ClearIndexRecord", new { record.Id })
} else {
找不到搜索索引记录...
}
添加/更新 搜索索引记录 使用现有的ID进行更新 @using (Html.BeginForm("AddToIndex", "Home")) {

@Html.LabelFor(m => m.SampleData.Id)
@Html.TextBoxFor(m => m.SampleData.Id, new { style = "width:30px;" })

@Html.LabelFor(m => m.SampleData.Name)
@Html.TextBoxFor(m => m.SampleData.Name, new { style = "width:100px;" })

@Html.LabelFor(m => m.SampleData.Description)
@Html.TextBoxFor(m => m.SampleData.Description, new { style = "width:120px;" })

}

_Layout.cshtml

@{
    Layout = null;
}
<!DOCTYPE html>

<html>
<head>
  <title>Lucene.Net | MVC</title>
  
  
  
  
  

  
  
  
  

</head>
<body>

    

Lucene.Net for MVC => 创建更容易!

简单的Lucene.Net使用示例站点

@RenderBody()
</body> </html>

_Layout.cshtml

@{
    Layout = null;
}
<!DOCTYPE html>

<html>
<head>
    <title>Lucene.Net | MVC</title>
    
    
    
    
    

    
    
    
    

</head>
<body>

    

Lucene.Net for MVC => 创建更容易!

简单的Lucene.Net使用示例站点

@RenderBody()
</body> </html>

运行结果如图: