
经过上次的改造,可以实现对实体对象中的特定成员创建索引,但从实际的应用上来看,需要检索的数据内容格式多种多样,可能会有HTML、XML等。另外一些自定义的数据结构可能是以JSON等特殊规则形式存储的,对于这些情况就需要单独对数据进行分割处理。
索引文件的结构类似上图中的树状结构:
索引(Index)
段(Segment)
文档(document)
域(Field)
而为了达到快速检索查询的目的,Lucene.NET的数据结构可不止这么简单。在后续的文章了让我们一步步解密吧。
1.2.常用Fieldnew StringField(“sField”, StringField, Field.Store.YES);
new LongField(“lField”, LongField, Field.Store.YES);
new StoredField(“sField2”, StoredField, Field.Store.YES);
new TextField(“tField”, TextField, Field.Store.YES);
1.3.数据库表与索引的关系FieldType fieldType = new FieldType();// 重构FieldType类
fieldType.setIndexed(true);// set 是否索引
fieldType.setStored(true);// set 是否存储
fieldType.setTokenized(true);// set 是否分词
fieldType.setOmitNorms(false);// set 是否可以设置权重
Field field = new Field(“Field”, Field, fieldType);
当为数据实体创建索引时,我们不仅要了解索引文件的结构,也要了解索引与数据库实体的关系,这样才能更好的利用索引进行查询检索。
上图描述了数据库表与索引文件的大致对应关系,索引Index对应数据库表Table,文档document对应数据库表中的一条记录Row,域Field对应这条记录中的每个列字段Column。而词Term则是将列字段值分词后端结果。
举个例子:
数据库表Company:
| RowId | CompanyName | CompanyAddress |
|---|---|---|
| 1 | 南京大地建设有限公司 | 南京市玄武区北京东路12号 |
| 2 | 江苏中南实业有限公司 | 南京市鼓楼区上海路7号 |
对应到索引Company中是两个document:
| FieldName | FieldValue |
|---|---|
| CompanyName | 南京大地建设有限公司 |
| CompanyAddress | 南京市玄武区北京东路12号 |
第一个Field的值“南京大地建设有限公司”可以被分割为“南京”、“大地”、“建设”、“有限”、“公司”这几个Term。
第二个Filed的值“南京市玄武区北京东路12号”可以被分割为“南京”、“南京市”、“玄武”、“玄武区”、“北京”、“北京东路”、“东路”、“12”、“号”这几个Term。
| FieldName | FieldValue |
|---|---|
| CompanyName | 江苏中南实业有限公司 |
| CompanyAddress | 南京市鼓楼区上海路7号 |
document2中Field中值的分割也与document1类似。
2.为不同数据结构创建索引 2.1.定义数据类型枚举注意:数据库表与索引的对应结构并不总是这样的,document、Field的创建和Term的分割是可以自定义的。另外document并不像数据实体那样存在唯一标识,需要自己实现为业务数据建立关系。
public enum FieldDataType
{
///
/// Html文本
///
Html,
///
/// JSON文本
///
Json,
///
/// 纯文本
///
Text,
///
/// Xml文本
///
Xml,
///
/// Csv文本
///
Csv,
///
/// 年份yyyy
///
DateYear,
///
/// 时间
///
DateTime,
///
/// 数字
///
Int32,
///
/// 数字
///
Int64,
///
/// 小数
///
Double
}
2.2.修改自定义索引属性
[AttributeUsage(AttributeTargets.Property)]
public class IndexAttribute:Attribute
{
public IndexAttribute()
{
IsStore = Field.Store.YES;
FieldType = FieldDataType.Text;
}
///
/// 名称
///
public string FieldName { get; set; }
///
/// 是否存储
///
public Field.Store IsStore { get; set; }
///
/// 数据格式
///
public FieldDataType FieldType { get; set; }
}
2.3.根据实体字段的数据类型创建索引
///2.4.不同类型字段的预处理/// 为实体创建索引 /// /// /// 是否启用过滤 public virtual void CreateIndexByEntity(IEntityentity,bool isFiltered=true) { var config = new IndexWriterConfig(LuceneVersion.LUCENE_48, Analyzer); using (IndexWriter writer = new IndexWriter(Directory, config)) { //创建文档 document doc = new document(); var type = entity.GetType(); //为实体所在的类的名称创建Field,目的是对实体进行标识,便于以后检索 doc.Add(new StringField(CoreConstant.EntityType,type.AssemblyQualifiedName,Field.Store.YES)); var properties = type.GetProperties(); //遍历实体的成员集合 foreach (var propertyInfo in properties) { var propertyValue = propertyInfo.GetValue(entity); if (propertyValue==null) { continue; } string fieldName = propertyInfo.Name;//成员字段名称 if (isFiltered) { var attributes = propertyInfo.GetCustomAttributes ();//获取自定义属性集合 foreach (var attribute in attributes) { string name = string.IsNullOrEmpty(attribute.FieldName) ? fieldName : attribute.FieldName; switch (attribute.FieldType) { case FieldDataType.DateTime: doc.Add(new StringField(fieldName, ((DateTime)propertyValue).ToString("yyyy-MM-dd HH:mm:ss"), attribute.IsStore)); break; case FieldDataType.DateYear: break; case FieldDataType.Int32: doc.Add(new Int32Field(fieldName, (Int32)propertyValue, attribute.IsStore)); break; case FieldDataType.Int64: doc.Add(new Int64Field(fieldName, (Int64)propertyValue, attribute.IsStore)); break; case FieldDataType.Double: doc.Add(new DoubleField(fieldName, (double)propertyValue, attribute.IsStore)); break; case FieldDataType.Html: doc.Add(new TextField(fieldName, propertyValue.ToString().ClearHtml(), attribute.IsStore)); break; case FieldDataType.Json: doc.Add(new TextField(fieldName, propertyValue.ToString().ClearJson(), attribute.IsStore)); break; case FieldDataType.Xml: doc.Add(new TextField(fieldName, propertyValue.ToString().ClearXml(), attribute.IsStore)); break; case FieldDataType.Csv: doc.Add(new TextField(fieldName, propertyValue.ToString().ClearCsv(), attribute.IsStore)); break; default: doc.Add(new TextField(fieldName, propertyValue.ToString(), attribute.IsStore)); break; } } } else { 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(); } }
////// 移除指定字符 /// /// /// ///internal static string ClearChar(this string source, IEnumerable chars) { return string.IsNullOrEmpty(source) ? string.Empty : new string(source.Where(t => !chars.Contains(t)).ToArray()); } /// /// 移除HTML标签 /// /// ///internal static string ClearHtml(this string source) { string result = Regex.Replace(source, "<[^>]+>", ""); return Regex.Replace(result, "&[^;]+;", ""); } /// /// 移除XML标签 /// /// ///internal static string ClearXml(this string source) { return Regex.Replace(source, "<[^>]+>", ""); }
每天进步一点点,翘首期盼明天。