Lucene.NET使用入门(二)【简单的搜索网站示例】
2017-10-20 13:42
1086 查看
项目结构:
LuceneSearch.Data层
SampleData.csnamespace 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.csusing 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.csusing 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>
运行结果如图:
相关文章推荐
- 使用Lucene.NET实现简单的站内搜索
- Lucene.NET使用入门(一)【实现快速搜索】
- RabbitMQ .NET消息队列使用入门(一)【简单示例】
- c#使用Lucene.net创建索引,实现搜索的代码示例
- RabbitMQ .NET消息队列使用入门(一)【简单示例】
- 使用Lucene.net提升网站搜索速度整合记录
- 使用Lucene.net提升网站搜索速度整合记录
- lucene入门-简单的WEB搜索界面
- JAVA:lucene 入门学习,简单实例模访google搜索
- Lucene.Net无障碍学习和使用:搜索篇
- Lucene2.9.2 + 盘古分词2.3.1(一) 入门: 建立简单索引,搜索(原创)
- ASP.NET WebForm 分页控件 AspNetPager 使用的简单示例
- Lucene.Net:使用eaglet的盘古分词进行分词和搜索(转载)
- phonegap 开发入门 PhoneGap官方网站上有详细的入门示例教程,这里,我针对使用PhoneGap进行Android移动应用的开发对其官网的Get Started进行一些介绍、补充。 Ste
- ASP.NET报表简单使用示例
- 电子商务网站搜索架构方案(lucene.net搜索架构方案) 转
- 使用lucene.net2.0 搜索数据库的例子<转自csdn>
- array grid的简单使用.根据官方示例改写(asp.net).json数据格式.
- Lucene.Net入门教程及示例
- 使用Lucene.NET进行分词、搜索