From 96afeab2fe2513735f6b7beda8f795420692f674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jian=E7=8E=84=E5=86=B0?= Date: Tue, 12 Apr 2022 14:52:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bing.Elasticsearch.WinformSample.csproj | 4 + src/Bing.Elasticsearch/Bing.Elasticsearch.xml | 258 +++++++++++++++++- .../ElasticsearchContext.cs | 13 +- src/Bing.Elasticsearch/Extensions.Service.cs | 4 + .../Extensions/ElasticClientExtensions.cs | 35 ++- src/Bing.Elasticsearch/Internals/Helper.cs | 10 +- .../Mapping/DefaultElasticMapping.cs | 42 +++ .../Mapping/ElasticMapping.cs | 120 ++++++++ .../Mapping/ElasticMappingFactory.cs | 95 +++++++ .../Mapping/ElasticMappingTypeFinder.cs | 40 +++ .../Mapping/IElasticMapping.cs | 45 +++ .../Mapping/IElasticMappingFactory.cs | 22 ++ .../Mapping/IElasticMappingTypeFinder.cs | 11 + .../Options/ElasticsearchOptions.cs | 5 + .../Repositories/EsRepository.cs | 49 ++-- .../project.dependency.props | 5 +- tests/Bing.Elasticsearch.Tests/EsIndexTest.cs | 44 +++ .../EsRepositoryTest.cs | 29 +- tests/Bing.Elasticsearch.Tests/EsTestBase.cs | 43 +++ .../Mapping/TestModel1Map.cs | 30 ++ .../Models/TestModel1.cs | 50 ++++ .../Models/TestModel5.cs | 12 +- 22 files changed, 904 insertions(+), 62 deletions(-) create mode 100644 src/Bing.Elasticsearch/Mapping/DefaultElasticMapping.cs create mode 100644 src/Bing.Elasticsearch/Mapping/ElasticMapping.cs create mode 100644 src/Bing.Elasticsearch/Mapping/ElasticMappingFactory.cs create mode 100644 src/Bing.Elasticsearch/Mapping/ElasticMappingTypeFinder.cs create mode 100644 src/Bing.Elasticsearch/Mapping/IElasticMapping.cs create mode 100644 src/Bing.Elasticsearch/Mapping/IElasticMappingFactory.cs create mode 100644 src/Bing.Elasticsearch/Mapping/IElasticMappingTypeFinder.cs create mode 100644 tests/Bing.Elasticsearch.Tests/EsIndexTest.cs create mode 100644 tests/Bing.Elasticsearch.Tests/EsTestBase.cs create mode 100644 tests/Bing.Elasticsearch.Tests/Mapping/TestModel1Map.cs create mode 100644 tests/Bing.Elasticsearch.Tests/Models/TestModel1.cs diff --git a/samples/Bing.Elasticsearch.WinformSample/Bing.Elasticsearch.WinformSample.csproj b/samples/Bing.Elasticsearch.WinformSample/Bing.Elasticsearch.WinformSample.csproj index 81409e8..04e1772 100644 --- a/samples/Bing.Elasticsearch.WinformSample/Bing.Elasticsearch.WinformSample.csproj +++ b/samples/Bing.Elasticsearch.WinformSample/Bing.Elasticsearch.WinformSample.csproj @@ -6,6 +6,10 @@ true + + + + diff --git a/src/Bing.Elasticsearch/Bing.Elasticsearch.xml b/src/Bing.Elasticsearch/Bing.Elasticsearch.xml index 5e851d8..9c434d0 100644 --- a/src/Bing.Elasticsearch/Bing.Elasticsearch.xml +++ b/src/Bing.Elasticsearch/Bing.Elasticsearch.xml @@ -29,12 +29,18 @@ ES选项配置 - + + + ES映射工厂 + + + 初始化一个类型的实例 ES客户端提供程序 索引名称解析器 + ES映射工厂 ES选项配置 @@ -358,6 +364,16 @@ 每个主分片的副分片数量 取消令牌 + + + 创建索引 + + 实体类型 + ES客户端 + ES映射工厂 + 索引名 + 取消令牌 + ES上下文扩展 @@ -685,12 +701,252 @@ 文档类型 索引名称。注意:必须小写 + + + 安全获取索引名称 + + 文档类型 + 索引名称。注意:必须小写 + 获取ES标识 标识 + + + 默认映射 + + + + + 初始化一个类型的实例 + + 类型 + ES选项配置 + + + + 配置索引 + + 创建索引描述符 + + + + 配置索引设置 + + 索引设置描述符 + + + + ES 映射 + + + + + 类型 + + + + + 索引名称 + + + + + 是否拥有多个索引 + + + + + ES选项配置 + + + + + 初始化一个类型的实例 + + 类型 + ES选项配置 + + + + 映射 + + 创建索引描述符 + + + + 配置索引 + + 创建索引描述符 + + + + 配置索引别名 + + 别名描述符 + + + + 配置索引设置 + + 索引设置描述符 + + + + ES映射 + + 实体类型 + + + + 初始化一个类型的实例 + + 类型 + ES选项配置 + + + + 配置索引映射 + + 映射 + + + + 配置索引 + + 创建索引描述符 + + + + ES映射工厂 + + + + + 反射实例类型 + + + + + ES选项配置 + + + + + ES映射 类型查找器 + + + + + 映射字典 + + + + + 初始化一个类型的实例 + + ES选项配置 + ES映射 类型查找器 + + + + 初始化映射 + + + + + 获取ES映射类 + + 类型 + + + + 获取ES映射类 + + 泛型类型 + + + + ES映射 类型查找器 + + + + + 所有程序集查找器 + + + + + 初始化一个类型的实例 + + 所有程序集查找器 + + + + + + + ES映射 + + + + + 类型 + + + + + 索引名称 + + + + + 是否拥有多个索引 + + + + + 映射 + + 创建索引描述符 + + + + ES映射 + + 实体类型 + + + + 配置索引映射 + + 映射 + + + + ES映射工厂 + + + + + 获取ES映射类 + + 类型 + + + + 获取ES映射类 + + 泛型类型 + + + + ES映射 类型查找器 + + 高亮参数 diff --git a/src/Bing.Elasticsearch/ElasticsearchContext.cs b/src/Bing.Elasticsearch/ElasticsearchContext.cs index 6d2777b..dc692be 100644 --- a/src/Bing.Elasticsearch/ElasticsearchContext.cs +++ b/src/Bing.Elasticsearch/ElasticsearchContext.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Bing.Elasticsearch.Internals; +using Bing.Elasticsearch.Mapping; using Bing.Elasticsearch.Options; using Bing.Elasticsearch.Provider; using Bing.Elasticsearch.Repositories; @@ -39,16 +40,23 @@ public class ElasticsearchContext : IElasticsearchContext /// private readonly ElasticsearchOptions _options; + /// + /// ES映射工厂 + /// + private readonly IElasticMappingFactory _mappingFactory; + /// /// 初始化一个类型的实例 /// /// ES客户端提供程序 /// 索引名称解析器 + /// ES映射工厂 /// ES选项配置 - public ElasticsearchContext(IElasticClientProvider provider, IIndexNameResolver resolver, IOptions options) + public ElasticsearchContext(IElasticClientProvider provider, IIndexNameResolver resolver, IElasticMappingFactory mappingFactory, IOptions options) { _provider = provider; _resolver = resolver; + _mappingFactory = mappingFactory; _client = provider.GetClient(); _options = options.Value; } @@ -117,7 +125,8 @@ public async Task CreateIndexAsync(string index, string ali /// 取消令牌 public async Task CreateIndexAsync(string index, string alias = null, CancellationToken cancellationToken = default) where TDocument : class { - await _client.CreateIndexAsync(index, _options.NumberOfShards, _options.NumberOfReplicas, cancellationToken); + //await _client.CreateIndexAsync(index, _options.NumberOfShards, _options.NumberOfReplicas, cancellationToken); + await _client.CreateIndexAsync(_mappingFactory, index, cancellationToken); if (alias.IsEmpty() == false) await _client.Indices.PutAliasAsync(index, alias, ct: cancellationToken); } diff --git a/src/Bing.Elasticsearch/Extensions.Service.cs b/src/Bing.Elasticsearch/Extensions.Service.cs index f3e7796..4fe8685 100644 --- a/src/Bing.Elasticsearch/Extensions.Service.cs +++ b/src/Bing.Elasticsearch/Extensions.Service.cs @@ -1,4 +1,5 @@ using System; +using Bing.Elasticsearch.Mapping; using Bing.Elasticsearch.Options; using Bing.Elasticsearch.Provider; using Bing.Elasticsearch.Repositories; @@ -20,9 +21,12 @@ public static partial class Extensions public static void AddElasticsearch(this IServiceCollection services, Action setupAction) { services.Configure(setupAction); + services.GetOrAddAllAssemblyFinder(); + services.GetOrAddTypeFinder(assemblyFinder => new ElasticMappingTypeFinder(assemblyFinder)); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddScoped(); + services.TryAddSingleton(); services.TryAddScoped(typeof(IEsRepository<>), typeof(EsRepository<>)); } } diff --git a/src/Bing.Elasticsearch/Extensions/ElasticClientExtensions.cs b/src/Bing.Elasticsearch/Extensions/ElasticClientExtensions.cs index 8ab2c22..66f68b0 100644 --- a/src/Bing.Elasticsearch/Extensions/ElasticClientExtensions.cs +++ b/src/Bing.Elasticsearch/Extensions/ElasticClientExtensions.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Bing.Elasticsearch.Internals; +using Bing.Elasticsearch.Mapping; using Nest; // ReSharper disable once CheckNamespace @@ -31,10 +32,9 @@ public static async Task CreateIndexAsync(this IElasticClient client, var existsResult = await client.Indices.ExistsAsync(indexName, null, cancellationToken); if (existsResult.Exists) return; - var result = await client.Indices.CreateAsync( indexName, - x => x.Map(m=>m.AutoMap()) + x => x.Map(m => m.AutoMap()) .Settings(o => o.NumberOfShards(numberOfShards) .NumberOfReplicas(numberOfReplicas) @@ -43,5 +43,36 @@ public static async Task CreateIndexAsync(this IElasticClient client, if (!result.Acknowledged) throw new ElasticsearchException($"索引[{indexName}]创建失败:{result.ServerError.Error.Reason}"); } + + /// + /// 创建索引 + /// + /// 实体类型 + /// ES客户端 + /// ES映射工厂 + /// 索引名 + /// 取消令牌 + public static async Task CreateIndexAsync(this IElasticClient client, + IElasticMappingFactory factory, + string indexName = "", + CancellationToken cancellationToken = default) + where T : class + { + indexName = Helper.SafeIndexName(indexName); + var existsResult = await client.Indices.ExistsAsync(indexName, null, cancellationToken); + if (existsResult.Exists) + return; + var mapping = factory.GetMapping(); + var result = await client.Indices.CreateAsync( + indexName, + x => + { + mapping.Map(x); + return x; + }, + cancellationToken); + if (!result.Acknowledged) + throw new ElasticsearchException($"索引[{indexName}]创建失败:{result.ServerError.Error.Reason}"); + } } } diff --git a/src/Bing.Elasticsearch/Internals/Helper.cs b/src/Bing.Elasticsearch/Internals/Helper.cs index 374757b..776691f 100644 --- a/src/Bing.Elasticsearch/Internals/Helper.cs +++ b/src/Bing.Elasticsearch/Internals/Helper.cs @@ -20,11 +20,17 @@ internal static class Helper /// /// 文档类型 /// 索引名称。注意:必须小写 - public static string SafeIndexName(string index = null) + public static string SafeIndexName(string index = null) => SafeIndexName(typeof(TDocument), index); + + /// + /// 安全获取索引名称 + /// + /// 文档类型 + /// 索引名称。注意:必须小写 + public static string SafeIndexName(Type type, string index = null) { if (index.IsEmpty() == false) return index; - var type = typeof(TDocument); if (!IndexCacheDict.ContainsKey(type)) { var elasticsearchTypeAttribute = type.GetAttribute(); diff --git a/src/Bing.Elasticsearch/Mapping/DefaultElasticMapping.cs b/src/Bing.Elasticsearch/Mapping/DefaultElasticMapping.cs new file mode 100644 index 0000000..670406d --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/DefaultElasticMapping.cs @@ -0,0 +1,42 @@ +using System; +using Bing.Elasticsearch.Options; +using Nest; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// 默认映射 + /// + public class DefaultElasticMapping : ElasticMapping, IElasticMapping + { + /// + /// 初始化一个类型的实例 + /// + /// 类型 + /// ES选项配置 + public DefaultElasticMapping(Type type, ElasticsearchOptions options) : base(type, options) + { + } + + /// + /// 配置索引 + /// + /// 创建索引描述符 + public override CreateIndexDescriptor ConfigureIndex(CreateIndexDescriptor idx) + { + idx = base.ConfigureIndex(idx); + return idx.Map(x => x.AutoMap(Type)); + } + + /// + /// 配置索引设置 + /// + /// 索引设置描述符 + public override IPromise ConfigureIndexSettings(IndexSettingsDescriptor settings) + { + return settings.NumberOfShards(Options.NumberOfShards) + .NumberOfReplicas(Options.NumberOfReplicas) + .Setting("max_result_window", int.MaxValue); + } + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Mapping/ElasticMapping.cs b/src/Bing.Elasticsearch/Mapping/ElasticMapping.cs new file mode 100644 index 0000000..85b26c9 --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/ElasticMapping.cs @@ -0,0 +1,120 @@ +using System; +using Bing.Elasticsearch.Internals; +using Bing.Elasticsearch.Options; +using Nest; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// ES 映射 + /// + public class ElasticMapping : IElasticMapping + { + /// + /// 类型 + /// + public Type Type { get; protected set; } + + /// + /// 索引名称 + /// + public string Name { get; protected set; } + + /// + /// 是否拥有多个索引 + /// + public bool HasMultipleIndexes { get; protected set; } + + /// + /// ES选项配置 + /// + protected ElasticsearchOptions Options { get; } + + /// + /// 初始化一个类型的实例 + /// + /// 类型 + /// ES选项配置 + public ElasticMapping(Type type, ElasticsearchOptions options) + { + Type = type; + Name = Helper.SafeIndexName(type); + Options = options; + } + + /// + /// 映射 + /// + /// 创建索引描述符 + public void Map(CreateIndexDescriptor descriptor) + { + ConfigureIndex(descriptor); + } + + /// + /// 配置索引 + /// + /// 创建索引描述符 + public virtual CreateIndexDescriptor ConfigureIndex(CreateIndexDescriptor idx) + { + return idx + .Aliases(ConfigureIndexAliases) + .Settings(ConfigureIndexSettings); + } + + /// + /// 配置索引别名 + /// + /// 别名描述符 + public virtual IPromise ConfigureIndexAliases(AliasesDescriptor aliases) + { + return aliases; + } + + /// + /// 配置索引设置 + /// + /// 索引设置描述符 + public virtual IPromise ConfigureIndexSettings(IndexSettingsDescriptor settings) + { + return settings; + } + } + + /// + /// ES映射 + /// + /// 实体类型 + public class ElasticMapping : ElasticMapping, IElasticMapping where T : class + { + /// + /// 初始化一个类型的实例 + /// + /// 类型 + /// ES选项配置 + public ElasticMapping(Type type, ElasticsearchOptions options) : base(type, options) + { + Type = typeof(T); + Name = Helper.SafeIndexName(type); + } + + /// + /// 配置索引映射 + /// + /// 映射 + public virtual TypeMappingDescriptor ConfigureIndexMapping(TypeMappingDescriptor map) + { + return map.AutoMap(); + } + + /// + /// 配置索引 + /// + /// 创建索引描述符 + public override CreateIndexDescriptor ConfigureIndex(CreateIndexDescriptor idx) + { + idx = base.ConfigureIndex(idx); + return idx.Map(ConfigureIndexMapping); + } + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Mapping/ElasticMappingFactory.cs b/src/Bing.Elasticsearch/Mapping/ElasticMappingFactory.cs new file mode 100644 index 0000000..bf5bb33 --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/ElasticMappingFactory.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Bing.Elasticsearch.Options; +using Bing.Extensions; +using Bing.Reflection; +using Microsoft.Extensions.Options; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// ES映射工厂 + /// + public class ElasticMappingFactory : IElasticMappingFactory + { + /// + /// 反射实例类型 + /// + private static readonly Type RefInstanceType = typeof(ElasticMapping<>); + + /// + /// ES选项配置 + /// + private readonly ElasticsearchOptions _options; + + /// + /// ES映射 类型查找器 + /// + private readonly IElasticMappingTypeFinder _finder; + + /// + /// 映射字典 + /// + protected Dictionary MappingDict { get; } + + /// + /// 初始化一个类型的实例 + /// + /// ES选项配置 + /// ES映射 类型查找器 + public ElasticMappingFactory(IOptions options, IElasticMappingTypeFinder finder) + { + _options = options.Value; + _finder = finder; + MappingDict = new Dictionary(); + InitMapping(); + } + + /// + /// 初始化映射 + /// + protected virtual void InitMapping() + { + var maps = _finder.FindAll(true); + foreach (var map in maps) + { + var interfaces = map.GetInterfaces(); + foreach (var @interface in interfaces) + { + var genericArgs = @interface.GetGenericArguments(); + if (genericArgs.Length == 1) + { + var targetType = genericArgs[0]; + if (targetType.FullName == null) + continue; + var implementType = RefInstanceType.MakeGenericType(targetType); + MappingDict[targetType] = (IElasticMapping)Activator.CreateInstance(implementType, targetType, _options); + } + } + + + } + } + + /// + /// 获取ES映射类 + /// + /// 类型 + public IElasticMapping GetMapping(Type type) + { + if (MappingDict.TryGetValue(type, out var mapping)) + { + return mapping; + } + return new DefaultElasticMapping(type, _options); + } + + /// + /// 获取ES映射类 + /// + /// 泛型类型 + public IElasticMapping GetMapping() => GetMapping(typeof(T)); + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Mapping/ElasticMappingTypeFinder.cs b/src/Bing.Elasticsearch/Mapping/ElasticMappingTypeFinder.cs new file mode 100644 index 0000000..f5d9d83 --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/ElasticMappingTypeFinder.cs @@ -0,0 +1,40 @@ +using System; +using System.Linq; +using Bing.Extensions; +using Bing.Finders; +using Bing.Reflection; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// ES映射 类型查找器 + /// + public class ElasticMappingTypeFinder : FinderBase, IElasticMappingTypeFinder + { + /// + /// 所有程序集查找器 + /// + private readonly IAllAssemblyFinder _allAssemblyFinder; + + /// + /// 初始化一个类型的实例 + /// + /// 所有程序集查找器 + public ElasticMappingTypeFinder(IAllAssemblyFinder allAssemblyFinder) => _allAssemblyFinder = + allAssemblyFinder ?? throw new ArgumentNullException(nameof(allAssemblyFinder)); + + /// + protected override Type[] FindAllItems() + { + var assemblies = _allAssemblyFinder.FindAll(true); + var types = assemblies + .SelectMany(assembly => assembly.GetTypes().Where(type => + type.IsClass && + !type.IsAbstract && + !type.IsInterface && + typeof(IElasticMapping<>).IsGenericAssignableFrom(type))) + .ToArray(); + return types; + } + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Mapping/IElasticMapping.cs b/src/Bing.Elasticsearch/Mapping/IElasticMapping.cs new file mode 100644 index 0000000..c42e456 --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/IElasticMapping.cs @@ -0,0 +1,45 @@ +using System; +using Nest; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// ES映射 + /// + public interface IElasticMapping + { + /// + /// 类型 + /// + Type Type { get; } + + /// + /// 索引名称 + /// + string Name { get; } + + /// + /// 是否拥有多个索引 + /// + bool HasMultipleIndexes { get; } + + /// + /// 映射 + /// + /// 创建索引描述符 + void Map(CreateIndexDescriptor descriptor); + } + + /// + /// ES映射 + /// + /// 实体类型 + public interface IElasticMapping : IElasticMapping where T : class + { + /// + /// 配置索引映射 + /// + /// 映射 + TypeMappingDescriptor ConfigureIndexMapping(TypeMappingDescriptor map); + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Mapping/IElasticMappingFactory.cs b/src/Bing.Elasticsearch/Mapping/IElasticMappingFactory.cs new file mode 100644 index 0000000..e45617a --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/IElasticMappingFactory.cs @@ -0,0 +1,22 @@ +using System; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// ES映射工厂 + /// + public interface IElasticMappingFactory + { + /// + /// 获取ES映射类 + /// + /// 类型 + IElasticMapping GetMapping(Type type); + + /// + /// 获取ES映射类 + /// + /// 泛型类型 + IElasticMapping GetMapping(); + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Mapping/IElasticMappingTypeFinder.cs b/src/Bing.Elasticsearch/Mapping/IElasticMappingTypeFinder.cs new file mode 100644 index 0000000..2a87cd6 --- /dev/null +++ b/src/Bing.Elasticsearch/Mapping/IElasticMappingTypeFinder.cs @@ -0,0 +1,11 @@ +using Bing.Reflection; + +namespace Bing.Elasticsearch.Mapping +{ + /// + /// ES映射 类型查找器 + /// + public interface IElasticMappingTypeFinder : ITypeFinder + { + } +} \ No newline at end of file diff --git a/src/Bing.Elasticsearch/Options/ElasticsearchOptions.cs b/src/Bing.Elasticsearch/Options/ElasticsearchOptions.cs index 4e517f5..c5c0f07 100644 --- a/src/Bing.Elasticsearch/Options/ElasticsearchOptions.cs +++ b/src/Bing.Elasticsearch/Options/ElasticsearchOptions.cs @@ -69,6 +69,11 @@ public class ElasticsearchOptions /// public string Prefix { get; set; } + /// + /// 是否检查索引 + /// + public bool CheckIndex { get; set; } = true; + /// /// 兼容版本(>=7.0) /// diff --git a/src/Bing.Elasticsearch/Repositories/EsRepository.cs b/src/Bing.Elasticsearch/Repositories/EsRepository.cs index 4afeb17..df3baff 100644 --- a/src/Bing.Elasticsearch/Repositories/EsRepository.cs +++ b/src/Bing.Elasticsearch/Repositories/EsRepository.cs @@ -6,6 +6,8 @@ using System.Threading.Tasks; using Bing.Elasticsearch.Internals; using Bing.Elasticsearch.Model; +using Bing.Elasticsearch.Options; +using Microsoft.Extensions.Options; using Nest; namespace Bing.Elasticsearch.Repositories @@ -31,13 +33,20 @@ public class EsRepository : IEsRepository where TEntity : clas /// protected virtual string IndexName { get; set; } + /// + /// ES选项配置 + /// + protected ElasticsearchOptions Options { get; set; } + /// /// 初始化一个类型的实例 /// /// ES上下文 - public EsRepository(IElasticsearchContext context) + /// ES选项配置 + public EsRepository(IElasticsearchContext context, IOptions options) { Context = context ?? throw new ArgumentNullException(nameof(context)); + Options = options.Value; _client = Context.GetClient(); IndexName = Helper.SafeIndexName(IndexName); } @@ -47,7 +56,7 @@ public EsRepository(IElasticsearchContext context) /// /// 实体 /// 取消令牌 - public async Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default) + public virtual async Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); await ExistOrCreateAsync(indexName, cancellationToken); @@ -61,7 +70,7 @@ public async Task InsertAsync(TEntity entity, CancellationToken cancellationToke /// /// 实体集合 /// 取消令牌 - public async Task InsertManyAsync(IEnumerable entities, CancellationToken cancellationToken = default) + public virtual async Task InsertManyAsync(IEnumerable entities, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); await ExistOrCreateAsync(indexName, cancellationToken); @@ -75,7 +84,7 @@ public async Task InsertManyAsync(IEnumerable entities, CancellationTok /// /// 实体集合 /// 取消令牌 - public async Task BulkAsync(IEnumerable entities, CancellationToken cancellationToken = default) + public virtual async Task BulkAsync(IEnumerable entities, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); await ExistOrCreateAsync(indexName, cancellationToken); @@ -89,7 +98,7 @@ public async Task BulkAsync(IEnumerable entities, CancellationToken can /// /// 实体 /// 取消令牌 - public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) + public virtual async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); var response = await Context.DeleteAsync(entity, indexName, cancellationToken); @@ -102,7 +111,7 @@ public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToke /// /// 标识 /// 取消令牌 - public async Task DeleteAsync(object id, CancellationToken cancellationToken = default) + public virtual async Task DeleteAsync(object id, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); var response = await Context.DeleteAsync(id, indexName, cancellationToken); @@ -115,7 +124,7 @@ public async Task DeleteAsync(object id, CancellationToken cancellationToken = d /// /// 查询删除描述符 /// 取消令牌 - public async Task DeleteByQueryAsync(DeleteByQueryDescriptor descriptor, CancellationToken cancellationToken = default) + public virtual async Task DeleteByQueryAsync(DeleteByQueryDescriptor descriptor, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); descriptor = descriptor.Index(Context.GetIndexName(IndexName)); @@ -130,7 +139,7 @@ public async Task DeleteByQueryAsync(DeleteByQueryDescriptor descriptor /// /// 实体 /// 取消令牌 - public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) + public virtual async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); var response = await Context.UpdateAsync(entity, indexName, cancellationToken); @@ -144,7 +153,7 @@ public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToke /// 标识 /// 实体 /// 取消令牌 - public async Task UpdateAsync(object id, TEntity entity, CancellationToken cancellationToken = default) + public virtual async Task UpdateAsync(object id, TEntity entity, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); var response = await Context.UpdateAsync(id, entity, indexName, cancellationToken); @@ -157,7 +166,7 @@ public async Task UpdateAsync(object id, TEntity entity, CancellationToken cance /// /// 查询更新描述符 /// 取消令牌 - public async Task UpdateByQueryAsync(UpdateByQueryDescriptor descriptor, CancellationToken cancellationToken = default) + public virtual async Task UpdateByQueryAsync(UpdateByQueryDescriptor descriptor, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); descriptor = descriptor.Index(Context.GetIndexName(IndexName)); @@ -172,7 +181,7 @@ public async Task UpdateByQueryAsync(UpdateByQueryDescriptor descriptor /// /// 标识 /// 取消令牌 - public async Task FindByIdAsync(object id, CancellationToken cancellationToken = default) + public virtual async Task FindByIdAsync(object id, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); return await Context.FindByIdAsync(id, indexName, cancellationToken); @@ -182,21 +191,21 @@ public async Task FindByIdAsync(object id, CancellationToken cancellati /// 通过标识集合查找 /// /// 标识集合 - public Task> FindByIdsAsync(params string[] ids) => + public virtual Task> FindByIdsAsync(params string[] ids) => FindByIdsAsync((IEnumerable)ids); /// /// 通过标识集合查找 /// /// 标识集合 - public Task> FindByIdsAsync(params long[] ids) => FindByIdsAsync((IEnumerable)ids); + public virtual Task> FindByIdsAsync(params long[] ids) => FindByIdsAsync((IEnumerable)ids); /// /// 通过标识集合查找 /// /// 标识集合 /// 取消令牌 - public async Task> FindByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default) + public virtual async Task> FindByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); return await Context.FindByIdsAsync(ids, indexName, cancellationToken); @@ -207,7 +216,7 @@ public async Task> FindByIdsAsync(IEnumerable ids, /// /// 标识集合 /// 取消令牌 - public async Task> FindByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default) + public virtual async Task> FindByIdsAsync(IEnumerable ids, CancellationToken cancellationToken = default) { var indexName = Helper.SafeIndexName(IndexName); return await Context.FindByIdsAsync(ids, indexName, cancellationToken); @@ -218,7 +227,7 @@ public async Task> FindByIdsAsync(IEnumerable ids, Ca /// /// 查询描述器 /// 取消令牌 - public async Task> SearchAsync(SearchDescriptor descriptor, CancellationToken cancellationToken = default) + public virtual async Task> SearchAsync(SearchDescriptor descriptor, CancellationToken cancellationToken = default) { var result = new List(); descriptor = descriptor.Index(Context.GetIndexName(IndexName)); @@ -245,7 +254,7 @@ public async Task> SearchAsync(SearchDescriptor d /// /// 查询描述符 /// 取消令牌 - public async Task>> HitsSearchAsync(SearchDescriptor descriptor, CancellationToken cancellationToken = default) + public virtual async Task>> HitsSearchAsync(SearchDescriptor descriptor, CancellationToken cancellationToken = default) { descriptor = descriptor.Index(Context.GetIndexName(IndexName)); Func, ISearchRequest> selector = x => descriptor; @@ -259,7 +268,7 @@ public async Task>> HitsSearchAsync(SearchDescriptor查询描述符 /// 键名 /// 取消令牌 - public async Task> AggregationsSearchAsync(SearchDescriptor descriptor, string key, CancellationToken cancellationToken = default) + public virtual async Task> AggregationsSearchAsync(SearchDescriptor descriptor, string key, CancellationToken cancellationToken = default) { descriptor = descriptor.Index(Context.GetIndexName(IndexName)); Func, ISearchRequest> selector = x => descriptor; @@ -277,8 +286,10 @@ public async Task> AggregationsSearchAsync(SearchDescript /// /// 索引名称 /// 取消令牌 - protected async Task ExistOrCreateAsync(string indexName, CancellationToken cancellationToken = default) + protected virtual async Task ExistOrCreateAsync(string indexName, CancellationToken cancellationToken = default) { + if (!Options.CheckIndex) + return; indexName = Context.GetIndexName(indexName); var result = await _client.Indices.ExistsAsync(indexName, null, cancellationToken); if (result.Exists) diff --git a/src/Bing.Elasticsearch/project.dependency.props b/src/Bing.Elasticsearch/project.dependency.props index b70abc8..a0ce409 100644 --- a/src/Bing.Elasticsearch/project.dependency.props +++ b/src/Bing.Elasticsearch/project.dependency.props @@ -1,8 +1,9 @@  + - - + + \ No newline at end of file diff --git a/tests/Bing.Elasticsearch.Tests/EsIndexTest.cs b/tests/Bing.Elasticsearch.Tests/EsIndexTest.cs new file mode 100644 index 0000000..e054165 --- /dev/null +++ b/tests/Bing.Elasticsearch.Tests/EsIndexTest.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Bing.Elasticsearch.Mapping; +using Bing.Elasticsearch.Tests.Models; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Bing.Elasticsearch.Tests +{ + /// + /// ES索引测试 + /// + public class EsIndexTest: EsTestBase + { + [Fact] + public async Task Test_CreateIndexAsync() + { + using var scope = ServiceProvider.CreateScope(); + var factory = scope.ServiceProvider.GetService(); + var mapping = factory.GetMapping(); + var context = scope.ServiceProvider.GetService(); + var result=await context.GetClient().Indices.CreateAsync("test_model_1", (x) => + { + mapping.Map(x); + return x; + }); + Assert.True(result.IsValid); + } + + [Fact] + public async Task Test_CreateIndexAsync_Default() + { + using var scope = ServiceProvider.CreateScope(); + var factory = scope.ServiceProvider.GetService(); + var mapping = factory.GetMapping(); + var context = scope.ServiceProvider.GetService(); + var result = await context.GetClient().Indices.CreateAsync("test_model_5", (x) => + { + mapping.Map(x); + return x; + }); + Assert.True(result.IsValid); + } + } +} \ No newline at end of file diff --git a/tests/Bing.Elasticsearch.Tests/EsRepositoryTest.cs b/tests/Bing.Elasticsearch.Tests/EsRepositoryTest.cs index 1eb0af8..9833d91 100644 --- a/tests/Bing.Elasticsearch.Tests/EsRepositoryTest.cs +++ b/tests/Bing.Elasticsearch.Tests/EsRepositoryTest.cs @@ -13,13 +13,8 @@ namespace Bing.Elasticsearch.Tests /// /// ES仓储测试 /// - public class EsRepositoryTest + public class EsRepositoryTest:EsTestBase { - /// - /// 服务提供程序 - /// - protected IServiceProvider ServiceProvider { get; } - private readonly List _students = new List { new StudentSample @@ -60,28 +55,6 @@ public class EsRepositoryTest } }; - public EsRepositoryTest() - { - ServiceProvider = GetServiceProvider(); - } - - protected IServiceProvider GetServiceProvider() - { - var services = new ServiceCollection(); - services.AddElasticsearch(o => - { - o.Urls = new List - { - "http://10.186.135.120:9200", - "http://10.186.135.125:9200", - "http://10.186.135.135:9200", - }; - o.DefaultIndex = "bing_es_sample"; - o.Prefix = "bing_sample"; - }); - return services.BuildServiceProvider(); - } - [Fact] public void Test_GetEsRepository() { diff --git a/tests/Bing.Elasticsearch.Tests/EsTestBase.cs b/tests/Bing.Elasticsearch.Tests/EsTestBase.cs new file mode 100644 index 0000000..77f0930 --- /dev/null +++ b/tests/Bing.Elasticsearch.Tests/EsTestBase.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; + +namespace Bing.Elasticsearch.Tests +{ + /// + /// ES测试基类 + /// + public class EsTestBase + { + /// + /// 服务提供程序 + /// + protected IServiceProvider ServiceProvider { get; } + + public EsTestBase() => ServiceProvider = GetServiceProvider(); + + /// + /// 获取服务提供程序 + /// + protected IServiceProvider GetServiceProvider() + { + var services = new ServiceCollection(); + services.AddElasticsearch(o => + { + o.Urls = new List + { + "http://10.186.103.244:9201", + "http://10.186.103.244:9202", + "http://10.186.103.244:9203", + }; + o.DefaultIndex = "bing_es_sample"; + o.Prefix = "bing_sample"; + o.UserName = "elastic"; + o.Password = "gzdevops2022"; + o.NumberOfShards = 3; + o.NumberOfReplicas = 1; + }); + return services.BuildServiceProvider(); + } + } +} \ No newline at end of file diff --git a/tests/Bing.Elasticsearch.Tests/Mapping/TestModel1Map.cs b/tests/Bing.Elasticsearch.Tests/Mapping/TestModel1Map.cs new file mode 100644 index 0000000..09f6c38 --- /dev/null +++ b/tests/Bing.Elasticsearch.Tests/Mapping/TestModel1Map.cs @@ -0,0 +1,30 @@ +using System; +using Bing.Elasticsearch.Mapping; +using Bing.Elasticsearch.Options; +using Bing.Elasticsearch.Tests.Models; +using Nest; + +namespace Bing.Elasticsearch.Tests.Mapping +{ + public class TestModel1Map : ElasticMapping + { + /// + /// 初始化一个类型的实例 + /// + /// 类型 + /// ES选项配置 + public TestModel1Map(Type type, ElasticsearchOptions options) : base(type, options) + { + } + + /// + /// 配置索引映射 + /// + /// 映射 + public override TypeMappingDescriptor ConfigureIndexMapping(TypeMappingDescriptor map) + { + return map.AutoMap() + .Properties(p => p.Number(f => f.Name(e => e.Id).Type(NumberType.Long))); + } + } +} \ No newline at end of file diff --git a/tests/Bing.Elasticsearch.Tests/Models/TestModel1.cs b/tests/Bing.Elasticsearch.Tests/Models/TestModel1.cs new file mode 100644 index 0000000..93a9848 --- /dev/null +++ b/tests/Bing.Elasticsearch.Tests/Models/TestModel1.cs @@ -0,0 +1,50 @@ +using System; +using Nest; + +namespace Bing.Elasticsearch.Tests.Models +{ + public class TestModel1 + { + /// + /// 系统编号 + /// + public long Id { get; set; } + + /// + /// keyword 不分词 + /// + [Keyword(Name = "Name", Index = true)] + public string Name { get; set; } + + /// + /// text 分词,Analyzer = "ik_max_word" + /// + [Text(Name = "Test_Dic",Analyzer = "ik_max_word")] + public string Dic { get; set; } + + /// + /// 状态 + /// + public int State { get; set; } + + /// + /// 逻辑删除 + /// + public bool Deleted { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// float 值 + /// + public float Fvalue { get; set; } + + /// + /// double 值 + /// + public double Dvalue { get; set; } + } +} \ No newline at end of file diff --git a/tests/Bing.Elasticsearch.Tests/Models/TestModel5.cs b/tests/Bing.Elasticsearch.Tests/Models/TestModel5.cs index fb68890..27c6ece 100644 --- a/tests/Bing.Elasticsearch.Tests/Models/TestModel5.cs +++ b/tests/Bing.Elasticsearch.Tests/Models/TestModel5.cs @@ -12,25 +12,25 @@ public class TestModel5 /// /// 系统编号 /// - [Number(NumberType.Long,Name = "Id")] + [Number(NumberType.Long, Name = "Id")] public long Id { get; set; } /// /// keyword 不分词 /// - [Keyword(Name = "Name",Index = true)] // 不需要分词的字符串,name=名称,index=是否建立索引 + [Keyword(Name = "Name", Index = true)] // 不需要分词的字符串,name=名称,index=是否建立索引 public string Name { get; set; } /// /// text 分词,Analyzer = "ik_max_word" /// - [Text(Name = "Dic",Index = true)] // 需要分词的字符串,name=名称,index=是否建立索引,Analyzer=分词器 + [Text(Name = "Dic", Index = true, Analyzer = "ik_max_word")] // 需要分词的字符串,name=名称,index=是否建立索引,Analyzer=分词器 public string Dic { get; set; } /// /// 状态 /// - [Number(NumberType.Integer,Name = "State")]// 数字类型+名称 + [Number(NumberType.Integer, Name = "State")]// 数字类型+名称 public int State { get; set; } /// @@ -48,13 +48,13 @@ public class TestModel5 /// /// float 值 /// - [Number(NumberType.Float,Name = "Fvalue")] + [Number(NumberType.Float, Name = "Fvalue")] public float Fvalue { get; set; } /// /// double 值 /// - [Number(NumberType.Double,Name = "Dvalue")] + [Number(NumberType.Double, Name = "Dvalue")] public double Dvalue { get; set; } } }