从零开始搭建.NET Core版搜索引擎(三)--通用方法扩展

经过上一篇的改造,可以作为公共方法使用了,但是通用性和抽象性还不够,所以继续从通用性着手来扩展方法。


1.泛型扩展

对前面的代码进行分析发现,多个方法都涉及到分析器Analyzer、存储目录Directory、索引操作器配置IndexWriterConfig的实例化,那么就考虑将这些对象作为属性抽离出来。

1.1.公共属性提取

/// <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

1.2.扩展为泛型方法

通过研究Lucene.NET的代码发现,分析器Analyzer及存储路径Directory均为基类,各自下面还有一系列的派生类。

分析器Analyzer的派生类对应的类库有:

在这里插入图片描述

这是Lucene.NET内置的分析器:

  • WhiteSpaceAnalyzer:仅去除空格,对字符没有lowercase化,不支持中文,也不支持破折号分词。
  • SimpleAnayzer:功能强于WhiteSpaceAnalyzer,提供对字符的lowercase化,不支持中文,保留停用词。
  • StopAnalyzer:功能强于SimpleAnayzer,增加了停用词功能,不支持中文。
  • StandardAnalyzer:英文处理能力增强,保留了email地址及XY&Z的形式,支持中文采用单词分词。
  • ChineseAnalyzer:中文分词器,一个汉字是一个token。
  • CJKAnalyzer:中文分词器,两个汉字是一个token。
  • SmartChineseAnalyzer:中文分词器,每一个词语是一个token。

除此之外还有很多第三方的分析器,比如盘古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>
    {

    }

2.为实体创建索引

在常见的业务逻辑中,使用的最多的是对数据实体对象的增删改查操作。我们希望在将实体插入到数据库之前,将实体对象的数据分割并索引化,便于以后的检索。

根据实体创建索引就不能只使用StringField或者TextField了,毕竟实体属性的数据类型可是多种多样,而且属性的名称也是无法预测的,因此就有必要创建一个单独的方法用于为实体创建索引。

2.1.创建索引(实体)方法

        /// <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();
            }
        }

2.2.索引属性标识

上面的方法存在一个小问题,那就是会为所有属性字段创建索引,但是实际使用中并不需要为所有属性字段创建索引,因此增加一个属性标签用于标记实体中需要创建索引的字段。

    [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; }
    }

对属性字段进行如下标注:

    public class DataEntity : Entity<string>
    {

        [Index(FieldName = "", IsStore = Field.Store.YES, FieldType = FieldDataType.Text)]
        public virtual string DataValue { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        public virtual string Remark { get; set; }

    }

项目地址:https://github.com/ludewig/Muyan.Search

本次改造先到这里,下次继续。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值