关闭

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

标签: 搜索lucene网站
222人阅读 评论(1) 收藏 举报
分类:

项目结构:

这里写图片描述


这里写图片描述

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

namespace 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<SampleData> GetAll()
        {
            return new
              List<SampleData> {
                           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<SampleData> GetAllIndexRecords()
        {
            // 验证搜索索引
            if (!System.IO.Directory.EnumerateFiles(_luceneDir).Any()) return new List<SampleData>();

            // 设置lucene搜索器
            var searcher = new IndexSearcher(_directory, false);
            var reader = IndexReader.Open(_directory, false);
            var docs = new List<Document>();
            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<SampleData> Search(string input, string fieldName = "")
        {
            if (string.IsNullOrEmpty(input)) return new List<SampleData>();

            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<SampleData> SearchDefault(string input, string fieldName = "")
        {
            return string.IsNullOrEmpty(input) ? new List<SampleData>() : _search(input, fieldName);
        }

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

            // 设置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<SampleData> _mapLuceneToDataList(IEnumerable<Document> hits)
        {
            return hits.Select(_mapLuceneDocumentToData).ToList();
        }
        private static IEnumerable<SampleData> _mapLuceneToDataList(IEnumerable<ScoreDoc> 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> { sampleData });
        }
        public static void AddUpdateLuceneIndex(IEnumerable<SampleData> 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<SampleData> _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<SelectedList> {
                                         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<SampleData> 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<SampleData> AllSampleData { get; set; }
        public IEnumerable<SampleData> SearchIndexData { get; set; }
        public IList<SelectedList> SearchFieldList { get; set; }
        public string SearchTerm { get; set; }
        public string SearchField { get; set; }
    }
}

Index.cshtml

@model MvcLuceneSampleApp.ViewModels.IndexViewModel

<div class="content_left">
    <span style="color: green;">@TempData["Result"]</span>
    <span style="color: red;">@TempData["ResultFail"]</span>
</div>
<div class="clear"></div>
<div class="col_12">
    <fieldset>
        <legend>数据库记录 (@Html.ActionLink("从数据库创建搜索索引 [+]", "CreateIndex"))</legend>
        <table class="grid">
            <tr>
                <th>@Html.LabelFor(m => Model.SampleData.Id)</th>
                <th>@Html.LabelFor(m => Model.SampleData.Name)</th>
                <th>@Html.LabelFor(m => Model.SampleData.Description)</th>
            </tr>
            @foreach (var record in Model.AllSampleData)
            {
                <tr>
                    <td>@record.Id</td>
                    <td>@record.Name</td>
                    <td>@record.Description</td>
                </tr>
            }
        </table>

        @if (ViewBag.Limit > 0)
        {
            <div class="margin_top10"> <b>@ViewBag.Limit</b> 条更多记录... (<a href="\?limit=0">查看全部</a>)</div>
        }
    </fieldset>
</div>
<div class="col_12">
    <fieldset>
        <legend>搜索(自定义,适用于大多数基本场景)</legend>
        <div class="content_left margin_top5">
            <p>尝试这些搜索: <em>"1 3 5", "City", "Russia India", "bel mos ind"</em></p>
        </div>
        @using (Html.BeginForm("Search", "Home"))
        {
            <div class="content_left margin_top13 margin_left30">
                @Html.CheckBoxFor(m => m.SearchDefault)
                @Html.LabelFor(m => m.SearchDefault, "使用默认的Lucene查询")
            </div>
            <div class="content_left margin_right20">
                @Html.TextBoxFor(m => m.SearchTerm, new { @class = "big", style = "width:650px;", autocomplete = "off" })
            </div>
            <div class="content_left margin_top15 margin_right30">
                @Html.DropDownListFor(m => m.SearchField, Model.SearchFieldList.Select(x => new SelectListItem { Text = x.Text, Value = x.Value }),
          new { style = "width:150px;" })
            </div>
            <div class="content_right margin_top8">
                <input type="submit" value="搜索" />
            </div>
            <div class="clear"></div>  }
    </fieldset>
</div>

<div class="col_8">
    <fieldset>
        <legend>
            Search index
            (@Html.ActionLink("优化", "OptimizeIndex"))
            (@Html.ActionLink("清除 [X]", "ClearIndex"))
        </legend>

        @if (Model.SearchIndexData.Any())
        {
            <table class="grid">
                <tr>
                    <th>@Html.LabelFor(m => Model.SampleData.Id)</th>
                    <th>@Html.LabelFor(m => Model.SampleData.Name)</th>
                    <th>@Html.LabelFor(m => Model.SampleData.Description)</th>
                    <th></th>
                </tr>
                @foreach (var record in Model.SearchIndexData)
                {
                    <tr>
                        <td>@record.Id</td>
                        <td>@record.Name</td>
                        <td>@record.Description</td>
                        <td>@Html.ActionLink("删除", "ClearIndexRecord", new { record.Id })</td>
                    </tr>
                }
            </table>
        }
        else
        {
            <text><br />找不到搜索索引记录...<br /></text>
        }
    </fieldset>
</div>
<div class="col_4">
    <fieldset class="add_record">
        <legend>添加/更新 搜索索引记录</legend>
        使用现有的ID进行更新
        @using (Html.BeginForm("AddToIndex", "Home"))
        {
            <div class="form_horizontal">
                <p>
                    @Html.LabelFor(m => m.SampleData.Id)<br />
                    @Html.TextBoxFor(m => m.SampleData.Id, new { style = "width:30px;" })
                </p>
                <p>
                    @Html.LabelFor(m => m.SampleData.Name)<br />
                    @Html.TextBoxFor(m => m.SampleData.Name, new { style = "width:100px;" })
                </p>
                <p>
                    @Html.LabelFor(m => m.SampleData.Description)<br />
                    @Html.TextBoxFor(m => m.SampleData.Description, new { style = "width:120px;" })
                </p>
            </div>
            <div class="clear"></div>
            <input type="submit" value="添加/更新 记录" />  }
    </fieldset>
</div>
<script type="text/javascript">
    $(document).ready(function () {
        $('#SearchTerm').focus();
        $('.grid tr:even').css("background", "silver");
    });
</script>

_Layout.cshtml

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

<html>
<head>
  <title>Lucene.Net | MVC</title>
    <link href="@Url.Content("~/Content/style.css")" rel="stylesheet" type="text/css" />
  <link href="@Url.Content("~/Content/kickstart/kickstart.css")" rel="stylesheet" type="text/css" />
  <link href="@Url.Content("~/Content/style.css")" rel="stylesheet" type="text/css" />
  <link href="@Url.Content("~/Content/style.lib.css")" rel="stylesheet" type="text/css" />
  <link href="@Url.Content("~/Content/style.local.css")" rel="stylesheet" type="text/css" />

    <script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>
    <script type="text/javascript">window.jQuery || document.write('<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")"><\/script>')</script>
  <script src="@Url.Content("~/Scripts/kickstart.js")" type="text/javascript"></script>
  <script src="@Url.Content("~/Scripts/prettify.js")" type="text/javascript"></script>

</head>
<body>

    <div class="header clearfix">
      <div class="top_menu"><a href="/">主页</a></div>
        <h2>Lucene.Net for MVC => 创建更容易!</h2>
        <h3>简单的Lucene.Net使用示例站点</h3>
    </div>

  <div class="content clearfix">
        @RenderBody()
    </div>

    <div class="footer">© @DateTime.Now.Year
    </div>

</body>
</html>

_Layout.cshtml

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

<html>
<head>
    <title>Lucene.Net | MVC</title>
    <link href="@Url.Content("~/Content/style.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/kickstart/kickstart.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/style.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/style.lib.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/style.local.css")" rel="stylesheet" type="text/css" />

    <script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>
    <script type="text/javascript">window.jQuery || document.write('<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")"><\/script>')</script>
    <script src="@Url.Content("~/Scripts/kickstart.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/prettify.js")" type="text/javascript"></script>

</head>
<body>

    <div class="header clearfix">
        <div class="top_menu"><a href="/">主页</a></div>
        <h2>Lucene.Net for MVC => 创建更容易!</h2>
        <h3>简单的Lucene.Net使用示例站点</h3>
    </div>

    <div class="content clearfix">
        @RenderBody()
    </div>

    <div class="footer">
        © @DateTime.Now.Year
    </div>

</body>
</html>

运行结果如图:

这里写图片描述

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1001118次
    • 积分:14728
    • 等级:
    • 排名:第898名
    • 原创:311篇
    • 转载:706篇
    • 译文:72篇
    • 评论:265条
    博客专栏
    文章分类
    打赏
    如果你觉得我的文章对您有用,请随意打赏。 微信 支付宝