基于JieBaNet+Lucene.Net实现全文搜索

实现效果:

  上一篇文章有附全文搜索结果的设计图,下面截一张开发完成上线后的实图:

  基本风格是模仿的百度搜索结果,绿色的分页略显小清新。

  目前已采集并创建索引的文章约3W多篇,索引文件不算太大,查询速度非常棒。

  

刀不磨要生锈,人不学要落后。每天都要学一些新东西。

基本技术介绍:

  还记得上一次做全文搜索是在2013年,主要核心设计与代码均是当时的架构师写的,自己只能算是全程参与。

  当时使用的是经典搭配:盘古分词+Lucene.net。

  前几篇文章有说到,盘古分词已经很多年不更新了,我在SupportYun系统一直引用的JieBaNet来做分词技术。

  那么是否也有成型的JieBaNet+Lucene.Net的全文搜索方案呢?

  经过多番寻找,在GitHub上面找到一个简易的例子:https://github.com/anderscui/jiebaForLuceneNet

  博主下面要讲的实现方案就是从这个demo得到的启发,大家有兴趣可以去看看这个demo。

  博主使用的具体版本:Lucene.net 3.0.3.0 ,JieBaNet 0.38.3.0(做过简易的调整与扩展,前面文章有讲到)

  首先我们对Lucene.Net的分词器Tokenizer、分析器Analyzer做一个基于JieBaNet的扩展。

  1.基于LuceneNet扩展的JieBa分析器JiebaForLuceneAnalyzer  

 1     /// <summary>
 2     /// 基于LuceneNet扩展的JieBa分析器
 3     /// </summary>
 4     public class JiebaForLuceneAnalyzer : Analyzer
 5     {
 6         protected static readonly ISet<string> DefaultStopWords = StopAnalyzer.ENGLISH_STOP_WORDS_SET;
 7
 8         private static ISet<string> StopWords;
 9
10         static JiebaForLuceneAnalyzer()
11         {
12             StopWords = new HashSet<string>();
13             var stopWordsFile = Path.GetFullPath(JiebaNet.Analyser.ConfigManager.StopWordsFile);
14             if (File.Exists(stopWordsFile))
15             {
16                 var lines = File.ReadAllLines(stopWordsFile);
17                 foreach (var line in lines)
18                 {
19                     StopWords.Add(line.Trim());
20                 }
21             }
22             else
23             {
24                 StopWords = DefaultStopWords;
25             }
26         }
27
28         public override TokenStream TokenStream(string fieldName, TextReader reader)
29         {
30             var seg = new JiebaSegmenter();
31             TokenStream result = new JiebaForLuceneTokenizer(seg, reader);
32             result = new LowerCaseFilter(result);
33             result = new StopFilter(true, result, StopWords);
34             return result;
35         }
36     }

  2.基于LuceneNet扩展的JieBa分词器:JiebaForLuceneTokenizer

 1     /// <summary>
 2     /// 基于Lucene的JieBa分词扩展
 3     /// </summary>
 4     public class JiebaForLuceneTokenizer:Tokenizer
 5     {
 6         private readonly JiebaSegmenter segmenter;
 7         private readonly ITermAttribute termAtt;
 8         private readonly IOffsetAttribute offsetAtt;
 9         private readonly ITypeAttribute typeAtt;
10
11         private readonly List<Token> tokens;
12         private int position = -1;
13
14         public JiebaForLuceneTokenizer(JiebaSegmenter seg, TextReader input):this(seg, input.ReadToEnd()) { }
15
16         public JiebaForLuceneTokenizer(JiebaSegmenter seg, string input)
17         {
18             segmenter = seg;
19             termAtt = AddAttribute<ITermAttribute>();
20             offsetAtt = AddAttribute<IOffsetAttribute>();
21             typeAtt = AddAttribute<ITypeAttribute>();
22
23             var text = input;
24             tokens = segmenter.Tokenize(text, TokenizerMode.Search).ToList();
25         }
26
27         public override bool IncrementToken()
28         {
29             ClearAttributes();
30             position++;
31             if (position < tokens.Count)
32             {
33                 var token = tokens[position];
34                 termAtt.SetTermBuffer(token.Word);
35                 offsetAtt.SetOffset(token.StartIndex, token.EndIndex);
36                 typeAtt.Type = "Jieba";
37                 return true;
38             }
39
40             End();
41             return false;
42         }
43
44         public IEnumerable<Token> Tokenize(string text, TokenizerMode mode = TokenizerMode.Search)
45         {
46             return segmenter.Tokenize(text, mode);
47         }
48     }

理想如果不向现实做一点点屈服,那么理想也将归于尘土。

实现方案设计:

  我们做全文搜索的设计时一定会考虑的一个问题就是:我们系统是分很多模块的,不同模块的字段差异很大,怎么才能实现同一个索引,既可以单个模块搜索又可以全站搜索,甚至按一些字段做条件来搜索呢?

  这些也是SupportYun系统需要考虑的问题,因为目前的数据就天然的拆分成了活动、文章两个类别,字段也大有不同。博主想实现的是一个可以全站搜索(结果包括活动、文章),也可以在文章栏目/活动栏目分别搜索,并且可以按几个指定字段来做搜索条件。

  要做一个这样的全文搜索功能,我们需要从程序设计上来下功夫。下面就介绍一下博主的设计方案:

  一、索引创建

    

    1.我们设计一个IndexManager来处理最基本的索引创建、更新、删除操作。

 

    2.创建、更新使用到的标准数据类:IndexContent。

    我们设计TableName(对应DB表名)、RowId(对应DB主键)、CollectTime(对应DB数据创建时间)、ModuleType(所属系统模块)、Title(检索标题)、IndexTextContent(检索文本)等六个基础字段,所有模块需要创建索引必须构建该6个字段(大家可据具体情况扩展)。

    然后设计10个预留字段Tag1-Tag10,用以兼容各大模块其他不同字段。

    预留字段的存储、索引方式可独立配置。

 

    其中BaseIndexContent含有六个基础字段。

    3.创建一个子模块索引构建器的接口:IIndexBuilder。

    各子模块通过继承实现IIndexBuilder,来实现索引的操作。

 

    4.下面我们以活动模块为例,来实现索引创建。

    a)首先创建一个基于活动模块的数据类:ActivityIndexContent,可以将我们需要索引或存储的字段都设计在内。

 

    b)我们再创建ActivityIndexBuilder并继承IIndexBuilder,实现其创建、更新、删除方法。

 

    代码就不解释了,很简单。主要就是调用IndexManager来执行操作。

    我们只需要在需要创建活动数据索引的业务点,构建ActivityIndexBuilder对象,并构建ActivityIndexContent集合作为参数,调用BuildIndex方法即可。

  二、全文搜索

    全文搜索我们采用同样的设计方式。

    1.设计一个抽象的搜索类:BaseIndexSearch,所有搜索模块(包括全站)均需继承它来实现搜索效果。

  1     public abstract class BaseIndexSearch<TIndexSearchResultItem>
  2         where TIndexSearchResultItem : IndexSearchResultItem
  3     {
  4         /// <summary>
  5         /// 索引存储目录
  6         /// </summary>
  7         private static readonly string IndexStorePath = ConfigurationManager.AppSettings["IndexStorePath"];
  8         private readonly string[] fieldsToSearch;
  9         protected static readonly SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<em>", "</em>");
 10         private static IndexSearcher indexSearcher = null;
 11
 12         /// <summary>
 13         /// 索引内容命中片段大小
 14         /// </summary>
 15         public int FragmentSize { get; set; }
 16
 17         /// <summary>
 18         /// 构造方法
 19         /// </summary>
 20         /// <param name="fieldsToSearch">搜索文本字段</param>
 21         protected BaseIndexSearch(string[] fieldsToSearch)
 22         {
 23             FragmentSize = 100;
 24             this.fieldsToSearch = fieldsToSearch;
 25         }
 26
 27         /// <summary>
 28         /// 创建搜索结果实例
 29         /// </summary>
 30         /// <returns></returns>
 31         protected abstract TIndexSearchResultItem CreateIndexSearchResultItem();
 32
 33         /// <summary>
 34         /// 修改搜索结果(主要修改tag字段对应的属性)
 35         /// </summary>
 36         /// <param name="indexSearchResultItem">搜索结果项实例</param>
 37         /// <param name="content">用户搜索内容</param>
 38         /// <param name="docIndex">索引库位置</param>
 39         /// <param name="doc">当前位置内容</param>
 40         /// <returns>搜索结果</returns>
 41         protected abstract void ModifyIndexSearchResultItem(ref TIndexSearchResultItem indexSearchResultItem, string content, int docIndex, Document doc);
 42
 43         /// <summary>
 44         /// 修改筛选器(各模块)
 45         /// </summary>
 46         /// <param name="filter"></param>
 47         protected abstract void ModifySearchFilter(ref Dictionary<string, string> filter);
 48
 49         /// <summary>
 50         /// 全库搜索
 51         /// </summary>
 52         /// <param name="content">搜索文本内容</param>
 53         /// <param name="filter">查询内容限制条件,默认为null,不限制条件.</param>
 54         /// <param name="fieldSorts">对字段进行排序</param>
 55         /// <param name="pageIndex">查询结果当前页,默认为1</param>
 56         /// <param name="pageSize">查询结果每页结果数,默认为20</param>
 57         public PagedIndexSearchResult<TIndexSearchResultItem> Search(string content
 58             , Dictionary<string, string> filter = null, List<FieldSort> fieldSorts = null
 59             , int pageIndex = 1, int pageSize = 20)
 60         {
 61             try
 62             {
 63                 if (!string.IsNullOrEmpty(content))
 64                 {
 65                     content = ReplaceIndexSensitiveWords(content);
 66                     content = GetKeywordsSplitBySpace(content,
 67                         new JiebaForLuceneTokenizer(new JiebaSegmenter(), content));
 68                 }
 69                 if (string.IsNullOrEmpty(content) || pageIndex < 1)
 70                 {
 71                     throw new Exception("输入参数不符合要求(用户输入为空,页码小于等于1)");
 72                 }
 73
 74                 var stopWatch = new Stopwatch();
 75                 stopWatch.Start();
 76
 77                 Analyzer analyzer = new JiebaForLuceneAnalyzer();
 78                 // 索引条件创建
 79                 var query = MakeSearchQuery(content, analyzer);
 80                 // 筛选条件构建
 81                 filter = filter == null ? new Dictionary<string, string>() : new Dictionary<string, string>(filter);
 82                 ModifySearchFilter(ref filter);
 83                 Filter luceneFilter = MakeSearchFilter(filter);
 84
 85                 #region------------------------------执行查询---------------------------------------
 86
 87                 TopDocs topDocs;
 88                 if (indexSearcher == null)
 89                 {
 90                     var dir = new DirectoryInfo(IndexStorePath);
 91                     FSDirectory entityDirectory = FSDirectory.Open(dir);
 92                     IndexReader reader = IndexReader.Open(entityDirectory, true);
 93                     indexSearcher = new IndexSearcher(reader);
 94                 }
 95                 else
 96                 {
 97                     IndexReader indexReader = indexSearcher.IndexReader;
 98                     if (!indexReader.IsCurrent())
 99                     {
100                         indexSearcher.Dispose();
101                         indexSearcher = new IndexSearcher(indexReader.Reopen());
102                     }
103                 }
104                 // 收集器容量为所有
105                 int totalCollectCount = pageIndex*pageSize;
106                 Sort sort = GetSortByFieldSorts(fieldSorts);
107                 topDocs = indexSearcher.Search(query, luceneFilter, totalCollectCount, sort ?? Sort.RELEVANCE);
108
109                 #endregion
110
111                 #region-----------------------返回结果生成-------------------------------
112
113                 ScoreDoc[] hits = topDocs.ScoreDocs;
114                 var start = (pageIndex - 1)*pageSize + 1;
115                 var end = Math.Min(totalCollectCount, hits.Count());
116
117                 var result = new PagedIndexSearchResult<TIndexSearchResultItem>
118                 {
119                     PageIndex = pageIndex,
120                     PageSize = pageSize,
121                     TotalRecords = topDocs.TotalHits
122                 };
123
124                 for (var i = start; i <= end; i++)
125                 {
126                     var scoreDoc = hits[i - 1];
127                     var doc = indexSearcher.Doc(scoreDoc.Doc);
128
129                     var indexSearchResultItem = CreateIndexSearchResultItem();
130                     indexSearchResultItem.DocIndex = scoreDoc.Doc;
131                     indexSearchResultItem.ModuleType = doc.Get("ModuleType");
132                     indexSearchResultItem.TableName = doc.Get("TableName");
133                     indexSearchResultItem.RowId = Guid.Parse(doc.Get("RowId"));
134                     if (!string.IsNullOrEmpty(doc.Get("CollectTime")))
135                     {
136                         indexSearchResultItem.CollectTime = DateTime.Parse(doc.Get("CollectTime"));
137                     }
138                     var title = GetHighlighter(formatter, FragmentSize).GetBestFragment(content, doc.Get("Title"));
139                     indexSearchResultItem.Title = string.IsNullOrEmpty(title) ? doc.Get("Title") : title;
140                     var text = GetHighlighter(formatter, FragmentSize)
141                         .GetBestFragment(content, doc.Get("IndexTextContent"));
142                     indexSearchResultItem.Content = string.IsNullOrEmpty(text)
143                         ? (doc.Get("IndexTextContent").Length > 100
144                             ? doc.Get("IndexTextContent").Substring(0, 100)
145                             : doc.Get("IndexTextContent"))
146                         : text;
147                     ModifyIndexSearchResultItem(ref indexSearchResultItem, content, scoreDoc.Doc, doc);
148                     result.Add(indexSearchResultItem);
149                 }
150                 stopWatch.Stop();
151                 result.Elapsed = stopWatch.ElapsedMilliseconds*1.0/1000;
152
153                 return result;
154
155                 #endregion
156             }
157             catch (Exception exception)
158             {
159                 LogUtils.ErrorLog(exception);
160                 return null;
161             }
162         }
163
164         private Sort GetSortByFieldSorts(List<FieldSort> fieldSorts)
165         {
166             if (fieldSorts == null)
167             {
168                 return null;
169             }
170             return new Sort(fieldSorts.Select(fieldSort => new SortField(fieldSort.FieldName, SortField.FLOAT, !fieldSort.Ascend)).ToArray());
171         }
172
173         private static Filter MakeSearchFilter(Dictionary<string, string> filter)
174         {
175             Filter luceneFilter = null;
176             if (filter != null && filter.Keys.Any())
177             {
178                 var booleanQuery = new BooleanQuery();
179                 foreach (KeyValuePair<string, string> keyValuePair in filter)
180                 {
181                     var termQuery = new TermQuery(new Term(keyValuePair.Key, keyValuePair.Value));
182                     booleanQuery.Add(termQuery, Occur.MUST);
183                 }
184                 luceneFilter = new QueryWrapperFilter(booleanQuery);
185             }
186             return luceneFilter;
187         }
188
189         private Query MakeSearchQuery(string content, Analyzer analyzer)
190         {
191             var query = new BooleanQuery();
192             // 总查询参数
193             // 属性查询
194             if (!string.IsNullOrEmpty(content))
195             {
196                 QueryParser parser = new MultiFieldQueryParser(Version.LUCENE_30, fieldsToSearch, analyzer);
197                 Query queryObj;
198                 try
199                 {
200                     queryObj = parser.Parse(content);
201                 }
202                 catch (ParseException parseException)
203                 {
204                     throw new Exception("在FileLibraryIndexSearch中构造Query时出错。", parseException);
205                 }
206                 query.Add(queryObj, Occur.MUST);
207             }
208             return query;
209         }
210
211         private string GetKeywordsSplitBySpace(string keywords, JiebaForLuceneTokenizer jiebaForLuceneTokenizer)
212         {
213             var result = new StringBuilder();
214
215             var words = jiebaForLuceneTokenizer.Tokenize(keywords);
216
217             foreach (var word in words)
218             {
219                 if (string.IsNullOrWhiteSpace(word.Word))
220                 {
221                     continue;
222                 }
223
224                 result.AppendFormat("{0} ", word.Word);
225             }
226
227             return result.ToString().Trim();
228         }
229
230         private string ReplaceIndexSensitiveWords(string str)
231         {
232             str = str.Replace("+", "");
233             str = str.Replace("+", "");
234             str = str.Replace("-", "");
235             str = str.Replace("-", "");
236             str = str.Replace("!", "");
237             str = str.Replace("!", "");
238             str = str.Replace("(", "");
239             str = str.Replace(")", "");
240             str = str.Replace("(", "");
241             str = str.Replace(")", "");
242             str = str.Replace(":", "");
243             str = str.Replace(":", "");
244             str = str.Replace("^", "");
245             str = str.Replace("[", "");
246             str = str.Replace("]", "");
247             str = str.Replace("【", "");
248             str = str.Replace("】", "");
249             str = str.Replace("{", "");
250             str = str.Replace("}", "");
251             str = str.Replace("{", "");
252             str = str.Replace("}", "");
253             str = str.Replace("~", "");
254             str = str.Replace("~", "");
255             str = str.Replace("*", "");
256             str = str.Replace("*", "");
257             str = str.Replace("?", "");
258             str = str.Replace("?", "");
259             return str;
260         }
261
262         protected Highlighter GetHighlighter(Formatter formatter, int fragmentSize)
263         {
264             var highlighter = new Highlighter(formatter, new Segment()) { FragmentSize = fragmentSize };
265             return highlighter;
266         }
267     }

    几个protected abstract方法,是需要继承的子类来实现的。

    其中为了实现搜索结果对命中关键词进行高亮显示,特引用了盘古分词的Highlighter。原则是此处应该是参照盘古分词的源码,自己使用JieBaNet来做实现的,由于工期较紧,直接引用了盘古。

    2.我们设计一个IndexSearchResultItem,表示搜索结果的基类。

 

    3.我们来看看具体的实现,先来看全站搜索的SearchService

 1     public class IndexSearch : BaseIndexSearch<IndexSearchResultItem>
 2     {
 3         public IndexSearch()
 4             : base(new[] { "IndexTextContent", "Title" })
 5         {
 6         }
 7
 8         protected override IndexSearchResultItem CreateIndexSearchResultItem()
 9         {
10             return new IndexSearchResultItem();
11         }
12
13         protected override void ModifyIndexSearchResultItem(ref IndexSearchResultItem indexSearchResultItem, string content,
14             int docIndex, Document doc)
15         {
16             //不做修改
17         }
18
19         protected override void ModifySearchFilter(ref Dictionary<string, string> filter)
20         {
21             //不做筛选条件修改
22         }
23     }

    是不是非常简单。由于我们此处搜索的是全站,结果展示直接用基类,取出基本字段即可。

    4.再列举一个活动的搜索实现。

    a)我们首先创建一个活动搜索结果类ActivityIndexSearchResultItem,继承自结果基类IndexSearchResultItem

 

    b)然后创建活动模块的搜索服务:ActivityIndexSearch,同样需要继承BaseIndexSearch,这时候ActivityIndexSearch只需要相对全站搜索修改几个参数即可。

 1     public class ActivityIndexSearch: BaseIndexSearch<ActivityIndexSearchResultItem>
 2     {
 3         public ActivityIndexSearch()
 4             : base(new[] { "IndexTextContent", "Title" })
 5         {
 6         }
 7
 8         protected override ActivityIndexSearchResultItem CreateIndexSearchResultItem()
 9         {
10             return new ActivityIndexSearchResultItem();
11         }
12
13         protected override void ModifyIndexSearchResultItem(ref ActivityIndexSearchResultItem indexSearchResultItem, string content,
14             int docIndex, Document doc)
15         {
16             indexSearchResultItem.ActivityTypes = doc.Get("Tag1");
17             indexSearchResultItem.Url = doc.Get("Tag2");
18             indexSearchResultItem.SourceName = doc.Get("Tag3");
19             indexSearchResultItem.SourceOfficialHotline = doc.Get("Tag4");
20             indexSearchResultItem.SourceUrl = doc.Get("Tag5");
21             indexSearchResultItem.CityId=new Guid(doc.Get("Tag6"));
22             indexSearchResultItem.Address = doc.Get("Tag7");
23             indexSearchResultItem.ActivityDate = doc.Get("Tag8");
24         }
25
26         protected override void ModifySearchFilter(ref Dictionary<string, string> filter)
27         {
28             filter.Add("ModuleType", "活动");
29         }
30     }

    筛选条件加上模块=活动,返回结果数据类指定,活动特有字段返回赋值。

    业务调用就非常简单了。

    全站全文搜索:我们直接new IndexSearch(),然后调用其Search()方法

    活动全文搜索:我们直接new ActivityIndexSearch(),然后调用其Search()方法

    Search()方法几个参数:

    ///<param name="content">搜索文本内容</param>
    /// <param name="filter">查询内容限制条件,默认为null,不限制条件.</param>
    /// <param name="fieldSorts">对字段进行排序</param>
    /// <param name="pageIndex">查询结果当前页,默认为1</param>
    /// <param name="pageSize">查询结果每页结果数,默认为20</param>

如果我们用软能力而不是用技术能力来区分程序员的好坏 – 是不是有那么点反常和变态。

原文地址:https://www.cnblogs.com/liujianshe1990-/p/11690881.html

时间: 2024-12-20 10:03:17

基于JieBaNet+Lucene.Net实现全文搜索的相关文章

记一次企业级爬虫系统升级改造(五):基于JieBaNet+Lucene.Net实现全文搜索

实现效果: 上一篇文章有附全文搜索结果的设计图,下面截一张开发完成上线后的实图: 基本风格是模仿的百度搜索结果,绿色的分页略显小清新. 目前已采集并创建索引的文章约3W多篇,索引文件不算太大,查询速度非常棒. 刀不磨要生锈,人不学要落后.每天都要学一些新东西. 基本技术介绍: 还记得上一次做全文搜索是在2013年,主要核心设计与代码均是当时的架构师写的,自己只能算是全程参与. 当时使用的是经典搭配:盘古分词+Lucene.net. 前几篇文章有说到,盘古分词已经很多年不更新了,我在Support

初识Lucene 4.5全文搜索

近期想研究下lucene,但网络上的教程大多都是lucne 3.x版本的讲解.可是lucene版本的更新速度快的惊人,目前已经到了4.8版了,只好去查阅官方文档.虽然英文不大好,但稍微对比了下发现3.x版本至4.x版本的修改非常之大.接下来我就以4.5版来操作,分享下我对luence的初步认识. 先给大家看一张图(来至<Lucene  in  action>): 此图很形象的描述了lucene的基本流程,简而言之就是:1.创建索引:2.检索索引. 太深的道理与原理我目前也还是一知半解,所以就以

JavaScript全文搜索之相关度评分

全文搜索,与机器学习领域其他大多数问题不同,是一个 Web程序员在日常工作中经常遇到的问题.客户可能要求你在某个地方提供一个搜索框,然后你会写一个类似 WHERE title LIKE %:query% 的 SQL 语句实现搜索功能.一开始,这是没问题,直到有一天,客户找到你跟你说,"搜索出错啦!" 当然,实际上搜索并没有"出错",只是搜索的结果并不是客户想要的.一般的用户并不清楚如何做精确匹配,所以得到的搜索结果质量很差.为了解决问题,你决定使用全文搜索.经过一阵

Apache Solr采用Java开发、基于Lucene的全文搜索服务器

http://docs.spring.io/spring-data/solr/ 首先介绍一下solr: Apache Solr (读音: SOLer) 是一个开源.高性能.采用Java开发.基于Lucene的全文搜索服务器,文档通过Http利用XML加到一个搜索集合中,查询该集合也是通过 http收到一个XML/JSON响应来实现.Solr 中存储的资源是以 Document 为对象进行存储的.每个文档由一系列的 Field 构成,每个 Field 表示资源的一个属性.Solr 中的每个 Doc

Lucene及全文搜索实现原理

Lucene及全文搜索实现原理 全文搜索 全文搜索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式.这个过程类似于通过字典中的检索字表查字的过程.全文搜索搜索引擎数据库中的数据. ????全文搜索的过程主要分为两个部分,索引的建立以及索引的搜索. 国内外的全文搜索常用的检索模型主要有向量模型,布尔模型等. 布尔模型 布尔模型是第一个信息检索的模型,可能也是最

初识Lucene 4.5.0 全文搜索--(二)

上一篇文章初识Lucene 4.5.0--(一)已经介绍了如何创建索引与检索索引.接下来就是删除与更新啦~ 一.删除索引. 原本3.x版本时 IndexWriter 与 IndexReader 都是有删除方法的,让我们先来看看lucene 3.6 api文档中的IndexReader的描述: 从4.0开始已经被删除了,所以现在只能用IndexWriter中的方法来进行删除.有哪些方法呢?继续看文档(lucene 4.5 api): 除了上面的六个外还有一个方法tryDeleteDocument(

Lucene全文搜索原理与使用

本文中主要是对于Lucene全文搜索的基础原理进行简单的分析,以及Lucene实现全文搜索的流程,之后就是Lucene在Java中的最简单使用:创建索引,查询索引库: 本文中使用的Lucene主要是4.10.3和6.0.0,两个版本的原理相同,但是API的使用并不相同: 1.结构化数据与非结构化数据 2.非结构化数据搜索 3.全文搜索 4.搜索如何实现 5.Lucene实现全文搜索流程 6.Lucene的API使用 1.结构化数据与非结构化数据 结构化数据:指具有固定格式或有限长度的数据,如数据

基于whoosh的flask全文搜索插件flask-msearch

flask 的全文搜索插件很少,有个Flask-WhooshAlchemy,最近发现个flask-msearch,使用了下感觉不错. 安装flask-msearch pip install flask-msearch flask配置 from flask_msearch import Search ... search = Search() search.init_app(app) #如果出现"sqlalchemy ValueError"报错,那么修改为 #db = SQLalchem

MySQL 全文搜索支持, mysql 5.6.4支持Innodb的全文检索和类memcache的nosql支持

背景:搞个个人博客的全文搜索得用like啥的,现在mysql版本号已经大于5.6.4了也就支持了innodb的全文搜索了,刚查了下目前版本号都到MySQL Community Server 5.6.19 了,所以,一些小的应用可以用它做全文搜索了,像sphinx和Lucene这样偏重的.需要配置或开发的,节省了成本. 这儿有一个原创的Mysql全文搜索的文章, mysql的全文搜索功能:http://blog.csdn.net/bravekingzhang/article/details/672