经过上一篇的改造,可以作为公共方法使用了,但是通用性和抽象性还不够,所以继续从通用性着手来扩展方法。
对前面的代码进行分析发现,多个方法都涉及到分析器Analyzer、存储目录Directory、索引操作器配置IndexWriterConfig的实例化,那么就考虑将这些对象作为属性抽离出来。
/// <summary> /// 分析器 /// </summary> public Analyzer Analyzer { get; set; } /// <summary> /// 存储 /// </summary> public Directory Directory { get; set; }其中分析器Analyzer和存储路径Directory可以在构造函数中实例化。公共属性提取后整个操作类中的相关方法就修改为如下所示。
public SearchManager(Directory directory, Analyzer analyzer) { Directory = directory; Analyzer = analyzer; IndexConfig = new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer); } #region 创建索引 /// <summary> /// 创建索引 /// </summary> /// <param name="fields">Field域集合,也可以是其他结构</param> public virtual void CreateIndex(Dictionary<string, string> fields) { using (IndexWriter writer = new IndexWriter(Directory, IndexConfig)) { //创建文档 Document doc = new Document(); foreach (var field in fields) { //创建数据域。此处使用了StringField域。此外还能用Int32Field、DoubleField等。 //StringField:将字段索引,但不会进行分词。 //TextField:将字段分词后进行索引 Field f = new TextField(field.Key, field.Value, Field.Store.YES); //添加数据域至文档 doc.Add(f); } //索引操作器增加文档 writer.AddDocument(doc); //刷新索引 writer.Flush(true, true); writer.Commit(); } } #endregion
通过研究Lucene.NET的代码发现,分析器Analyzer及存储路径Directory均为基类,各自下面还有一系列的派生类。
分析器Analyzer的派生类对应的类库有:
这是Lucene.NET内置的分析器:
除此之外还有很多第三方的分析器,比如盘古PanGu、结巴JieBa、IKAnalyzer等。
存储Directory的派生类有:
可以存储的位置包括文件、内存等。
因此将分析器Analyzer及存储路径Directory修改为泛型,整个操作类也修改为泛型类。
public interface ISearchManager<TAnalyzer,TDirectory> where TAnalyzer:Analyzer where TDirectory:Directory { /// <summary> /// 分析器 /// </summary> TAnalyzer Analyzer { get; set; } /// <summary> /// 存储 /// </summary> TDirectory Directory { get; set; } /// <summary> /// 创建索引 /// </summary> /// <param name="fields"></param> void CreateIndex(Dictionary<string, string> fields); /// <summary> /// 删除索引 /// </summary> /// <param name="docId">文档</param> void DeleteIndex(int docId); /// <summary> /// 删除所有索引 /// </summary> void DeleteAllIndex(); /// <summary> /// 更新索引 /// </summary> /// <param name="doc">文档</param> /// <param name="dic">要更新的域</param> void UpdateIndex(Document doc, Dictionary<string, string> dic); /// <summary> /// 索引计数 /// </summary> /// <returns></returns> int CountIndex(); /// <summary> /// 关键词查询 /// </summary> /// <param name="keyword"></param> /// <param name="field"></param> /// <returns></returns> Dictionary<Document, float> SearchIndex(string keyword, string field); /// <summary> /// 为实体对象创建索引 /// </summary> /// <param name="entity"></param> void CreateIndexByEntity(IEntity<string> entity); } public interface ISearchManager:ISearchManager<Analyzer,Directory> { }
在常见的业务逻辑中,使用的最多的是对数据实体对象的增删改查操作。我们希望在将实体插入到数据库之前,将实体对象的数据分割并索引化,便于以后的检索。
根据实体创建索引就不能只使用StringField或者TextField了,毕竟实体属性的数据类型可是多种多样,而且属性的名称也是无法预测的,因此就有必要创建一个单独的方法用于为实体创建索引。
/// <summary> /// 为实体创建索引 /// </summary> /// <param name="entity"></param> public virtual void CreateIndexByEntity(IEntity<string> entity) { using (IndexWriter writer = new IndexWriter(Directory, IndexConfig)) { //创建文档 Document doc = new Document(); var type = entity.GetType(); var properties = type.GetProperties(); foreach (var propertyInfo in properties) { var propertyValue = propertyInfo.GetValue(this); if (propertyValue==null) { continue; } string fieldName = propertyInfo.Name; switch (propertyValue) { case DateTime time: doc.Add(new StringField(fieldName,time.ToString("yyyy-MM-dd HH:mm:ss"),Field.Store.YES)); break; case int num: doc.Add(new Int32Field(fieldName,num,Field.Store.YES)); break; case long num: doc.Add(new Int64Field(fieldName,num,Field.Store.YES)); break; case double num: doc.Add(new DoubleField(fieldName,num,Field.Store.YES)); break; default: doc.Add(new TextField(fieldName, propertyValue.ToString(),Field.Store.YES)); break; } } writer.AddDocument(doc); //刷新索引 writer.Flush(true, true); writer.Commit(); } }
上面的方法存在一个小问题,那就是会为所有属性字段创建索引,但是实际使用中并不需要为所有属性字段创建索引,因此增加一个属性标签用于标记实体中需要创建索引的字段。
[AttributeUsage(AttributeTargets.Property)] public class IndexAttribute:Attribute { public IndexAttribute() { IsStore = Field.Store.YES; FieldType = FieldDataType.Text; } /// <summary> /// 名称 /// </summary> public string FieldName { get; set; } /// <summary> /// 是否存储 /// </summary> public Field.Store IsStore { get; set; } /// <summary> /// 数据格式 /// </summary> public FieldDataType FieldType { get; set; } }
修改日期 | 修改人 | 备注 |
2021-09-30 08:57:58[当前版本] | 潘帅 | 1.1 |
2021-09-27 21:12:51 | 潘帅 | 1.1 |
2021-09-27 18:08:36 | 潘帅 | 1.0 |
附件类型 |
|
|
|