Browse Source

feat: Add an interface for querying the ES expression tree

pull/1552/head
colin 1 week ago
parent
commit
f51c4d619e
  1. 259
      aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/ExpressionQueryService.cs
  2. 30
      aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/ExpressionQueryTranslator.cs
  3. 46
      aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/IExpressionQueryService.cs
  4. 11
      aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/IExpressionQueryTranslator.cs
  5. 4
      aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/IndexMappingInfo.cs
  6. 7
      aspnet-core/tests/LINGYUN.Abp.Elasticsearch.Tests/LINGYUN/Abp/Elasticsearch/DefaultExpressionQueryTranslatorTests.cs
  7. 436
      aspnet-core/tests/LINGYUN.Abp.Elasticsearch.Tests/LINGYUN/Abp/Elasticsearch/ExpressionQueryTranslator_Tests.cs

259
aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/ExpressionQueryService.cs

@ -0,0 +1,259 @@
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.QueryDsl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Elasticsearch;
public class ExpressionQueryService : IExpressionQueryService, ITransientDependency
{
private readonly IElasticsearchClientFactory _clientFactory;
private readonly IExpressionQueryTranslator _expressionQueryTranslator;
public ExpressionQueryService(
IElasticsearchClientFactory clientFactory,
IExpressionQueryTranslator expressionQueryTranslator)
{
_clientFactory = clientFactory;
_expressionQueryTranslator = expressionQueryTranslator;
}
public async virtual Task<long> GetCountAsync<TDocument>(
string indexName,
Expression<Func<TDocument, bool>> expression,
CancellationToken cancellationToken = default)
{
var client = _clientFactory.Create();
var query = await _expressionQueryTranslator.TranslateAsync(indexName, expression);
var response = await client.CountAsync<TDocument>(dsl =>
dsl.Indices(indexName).Query(query),
cancellationToken);
return response.Count;
}
public async virtual Task<List<TDocument>> GetListAsync<TDocument>(
string indexName,
Expression<Func<TDocument, bool>> expression,
string? sorting = null,
int maxResultCount = 50,
int skipCount = 0,
Fields? sourceExcludes = null,
Fields? sourceIncludes = null,
CancellationToken cancellationToken = default)
{
var client = _clientFactory.Create();
var query = await _expressionQueryTranslator.TranslateAsync(indexName, expression);
SortOptions[]? sorts = null;
if (!sorting.IsNullOrWhiteSpace())
{
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase)
? SortOrder.Asc : SortOrder.Desc;
sorts = new SortOptions[1]
{
new SortOptions
{
Field = new FieldSort(new Field(sorting)),
}
};
}
// 数量超过10000且存在排序时才可以使用SearchAfter特性
return skipCount >= 10000 && sorts != null
? await SearchAfter<TDocument>(
client,
indexName,
query,
sorts,
maxResultCount,
skipCount,
sourceExcludes,
sourceIncludes,
cancellationToken)
: await SearchFromSize<TDocument>(
client,
indexName,
query,
sorts,
maxResultCount,
skipCount,
sourceExcludes,
sourceIncludes,
cancellationToken);
}
private async Task<List<TDocument>> SearchFromSize<TDocument>(
ElasticsearchClient client,
string indexName,
Query query,
SortOptions[]? sorts = null,
int maxResultCount = 50,
int skipCount = 0,
Fields? sourceExcludes = null,
Fields? sourceIncludes = null,
CancellationToken cancellationToken = default)
{
var searchResponse = await client.SearchAsync<TDocument>(dsl =>
{
dsl.Indices(indexName)
.Query(query)
.From(skipCount)
.Size(maxResultCount);
if (sorts != null)
{
dsl.Sort(sorts);
}
if (sourceExcludes != null)
{
dsl.SourceExcludes(sourceExcludes);
}
if (sourceIncludes != null)
{
dsl.SourceIncludes(sourceIncludes);
}
}, cancellationToken);
if (!searchResponse.IsSuccess())
{
return [];
}
return searchResponse.Documents.ToList();
}
private async Task<List<TDocument>> SearchAfter<TDocument>(
ElasticsearchClient client,
string indexName,
Query query,
SortOptions[] sorts,
int maxResultCount = 50,
int skipCount = 0,
Fields? sourceExcludes = null,
Fields? sourceIncludes = null,
CancellationToken cancellationToken = default)
{
var searchAfter = await GetSearchAfterValue<TDocument>(
client,
indexName,
query,
sorts,
skipCount,
cancellationToken);
if (searchAfter == null || !searchAfter.Any())
{
return [];
}
var searchResponse = await client.SearchAsync<TDocument>(dsl =>
{
dsl.Indices(indexName)
.Query(query)
.Sort(sorts)
.Size(maxResultCount)
.SearchAfter(searchAfter);
if (sourceExcludes != null)
{
dsl.SourceExcludes(sourceExcludes);
}
if (sourceIncludes != null)
{
dsl.SourceIncludes(sourceIncludes);
}
}, cancellationToken);
if (!searchResponse.IsSuccess())
{
return [];
}
return searchResponse.Documents.ToList();
}
private async Task<List<FieldValue>?> GetSearchAfterValue<TDocument>(
ElasticsearchClient client,
string indexName,
Query query,
SortOptions[] sorts,
int skipCount,
CancellationToken cancellationToken = default)
{
// 10000以内直接取最后一条数据
if (skipCount < 10000)
{
var response = await client.SearchAsync<TDocument>(
dsl => dsl.Indices(indexName)
.Query(query)
.Sort(sorts)
.From(skipCount)
.Size(1),
cancellationToken);
if (!response.IsSuccess() || response.Hits == null || !response.Hits.Any())
{
return null;
}
var hit = response.Hits.FirstOrDefault();
return hit?.Sort?.ToList();
}
// 获取第9999条数据Hits作为searchAfter
var firstResponse = await client.SearchAsync<TDocument>(
dsl => dsl.Indices(indexName)
.Query(query)
.Sort(sorts)
.SourceIncludes([])
.From(9999)
.Size(1),
cancellationToken);
if (!firstResponse.IsSuccess() || firstResponse.Hits == null || !firstResponse.Hits.Any())
{
return null;
}
var firstHit = firstResponse.Hits.FirstOrDefault();
if (firstHit?.Sort == null || !firstHit.Sort.Any())
{
return null;
}
var remaining = skipCount - 10000;
// 获取skipCount最近一条数据作为searchAfter
var secondResponse = await client.SearchAsync<TDocument>(
dsl => dsl.Indices(indexName)
.Query(query)
.Sort(sorts)
.SourceIncludes([])
.SearchAfter(firstHit.Sort.ToList())
.Size(remaining),
cancellationToken);
if (!secondResponse.IsSuccess() || secondResponse.Hits == null || !secondResponse.Hits.Any())
{
return null;
}
if (secondResponse.Hits.Count < remaining)
{
return null;
}
var lastHit = secondResponse.Hits.LastOrDefault();
if (lastHit?.Sort == null || !lastHit.Sort.Any())
{
return null;
}
return lastHit.Sort.ToList();
}
}

30
aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/ExpressionQueryTranslator.cs

@ -7,38 +7,36 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Elasticsearch;
/// <summary>
/// 表达式查询转换器 - 将 LINQ 表达式转换为 Elasticsearch Query
/// </summary>
/// <typeparam name="TDocument">文档类型</typeparam>
public class ExpressionQueryTranslator<TDocument>
public class ExpressionQueryTranslator : IExpressionQueryTranslator, ISingletonDependency
{
private readonly bool _defaultNestedBehavior;
private readonly IndexMappingInfo _mappingInfo;
private readonly IIndexMappingProvider _indexMappingProvider;
/// <summary>
/// 构造函数(使用已有的映射信息)
/// </summary>
public ExpressionQueryTranslator(
IndexMappingInfo mappingInfo,
bool defaultNestedBehavior = false)
IIndexMappingProvider indexMappingProvider)
{
_mappingInfo = mappingInfo;
_defaultNestedBehavior = defaultNestedBehavior;
_indexMappingProvider = indexMappingProvider;
}
/// <summary>
/// 同步翻译表达式(需要已加载映射)
/// 翻译表达式
/// </summary>
public virtual Query Translate(Expression<Func<TDocument, bool>> expression)
public async virtual Task<Query> TranslateAsync<TDocument>(string indexName, Expression<Func<TDocument, bool>> expression)
{
Check.NotNullOrWhiteSpace(indexName, nameof(indexName));
Check.NotNull(expression, nameof(expression));
return TranslateNode(expression.Body, prefix: null, _mappingInfo);
var indexMapping = await _indexMappingProvider.GetMappingAsync(indexName);
return TranslateNode(expression.Body, prefix: null, indexMapping);
}
#region 节点翻译
@ -555,9 +553,7 @@ public class ExpressionQueryTranslator<TDocument>
inner = TranslateNode(predicate.Body, prefix: collectionField.Path, mappingInfo);
}
var shouldUseNested = collectionField.IsNested ||
(mappingInfo?.IsNested(collectionField.Path) ?? false) ||
_defaultNestedBehavior;
var shouldUseNested = collectionField.IsNested || (mappingInfo?.IsNested(collectionField.Path) ?? false);
return shouldUseNested
? new NestedQuery(collectionField.Path, inner)

46
aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/IExpressionQueryService.cs

@ -0,0 +1,46 @@
using Elastic.Clients.Elasticsearch;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Elasticsearch;
public interface IExpressionQueryService
{
/// <summary>
/// 查询符合条件的文档数量
/// </summary>
/// <typeparam name="TDocument">文档类型</typeparam>
/// <param name="indexName">文档索引</param>
/// <param name="expression">查询表达式树</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<long> GetCountAsync<TDocument>(
string indexName,
Expression<Func<TDocument, bool>> expression,
CancellationToken cancellationToken = default);
/// <summary>
/// 查询符合条件的文档列表
/// </summary>
/// <typeparam name="TDocument">文档类型</typeparam>
/// <param name="indexName">文档索引</param>
/// <param name="expression">查询表达式树</param>
/// <param name="sorting">排序字段</param>
/// <param name="maxResultCount">最大返回数据大小</param>
/// <param name="skipCount">跳过数据大小</param>
/// <param name="sourceExcludes">包含字段</param>
/// <param name="sourceIncludes">忽略字段</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<TDocument>> GetListAsync<TDocument>(
string indexName,
Expression<Func<TDocument, bool>> expression,
string? sorting = null,
int maxResultCount = 50,
int skipCount = 0,
Fields? sourceExcludes = null,
Fields? sourceIncludes = null,
CancellationToken cancellationToken = default);
}

11
aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/IExpressionQueryTranslator.cs

@ -0,0 +1,11 @@
using Elastic.Clients.Elasticsearch.QueryDsl;
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Elasticsearch;
public interface IExpressionQueryTranslator
{
Task<Query> TranslateAsync<TDocument>(string indexName, Expression<Func<TDocument, bool>> expression);
}

4
aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN/Abp/Elasticsearch/IndexMappingInfo.cs

@ -107,7 +107,7 @@ public class IndexMappingInfo
// 检查路径的父级是否是 Nested
var parts = path.Split('.');
for (int i = 0; i < parts.Length - 1; i++)
for (var i = 0; i < parts.Length - 1; i++)
{
var parentPath = string.Join(".", parts.Take(i + 1));
if (IsNested(parentPath))
@ -125,7 +125,7 @@ public class IndexMappingInfo
public string? GetNestedParentPath(string path)
{
var parts = path.Split('.');
for (int i = parts.Length - 1; i >= 0; i--)
for (var i = parts.Length - 1; i >= 0; i--)
{
var parentPath = string.Join(".", parts.Take(i + 1));
if (IsNested(parentPath))

7
aspnet-core/tests/LINGYUN.Abp.Elasticsearch.Tests/LINGYUN/Abp/Elasticsearch/DefaultExpressionQueryTranslatorTests.cs

@ -0,0 +1,7 @@
using LINGYUN.Abp.Elasticsearch.Tests;
namespace LINGYUN.Abp.Elasticsearch;
public class DefaultExpressionQueryTranslatorTests : ExpressionQueryTranslatorTests<AbpElasticsearchTestModule>
{
}

436
aspnet-core/tests/LINGYUN.Abp.Elasticsearch.Tests/LINGYUN/Abp/Elasticsearch/ExpressionQueryTranslator_Tests.cs

@ -4,38 +4,29 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Xunit;
namespace LINGYUN.Abp.Elasticsearch.Tests;
public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
public abstract class ExpressionQueryTranslatorTests<TStartupModule> : AbpIntegratedTest<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly IIndexMappingProvider _indexMappingProvider;
private readonly IExpressionQueryTranslator _expressionQueryTranslator;
public ExpressionQueryTranslatorTests()
{
_indexMappingProvider = GetRequiredService<IIndexMappingProvider>();
}
private async Task<ExpressionQueryTranslator<TDocument>> CreateTranslator<TDocument>(
bool defaultNestedBehavior = false)
{
var indexMapping = await _indexMappingProvider.GetMappingAsync(TestDocumentIndexNames.Index);
return new ExpressionQueryTranslator<TDocument>(
indexMapping,
defaultNestedBehavior);
_expressionQueryTranslator = GetRequiredService<IExpressionQueryTranslator>();
}
#region 基础查询测试
[Fact]
public async Task Translate_ConstantTrue_ReturnsMatchAllQuery()
public async virtual Task Translate_ConstantTrue_ReturnsMatchAllQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => true);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => true);
// Assert
query.ShouldNotBeNull();
@ -43,42 +34,26 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_ConstantFalse_ReturnsMatchNoneQuery()
public async virtual Task Translate_ConstantFalse_ReturnsMatchNoneQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => false);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => false);
// Assert
query.ShouldNotBeNull();
query.MatchNone.ShouldNotBeNull();
}
[Fact]
public async Task Translate_NullExpression_ThrowsArgumentNullException()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act & Assert
Should.Throw<ArgumentNullException>(() => translator.Translate(null!));
}
#endregion
#region 相等性查询测试
[Fact]
public async Task Translate_Equal_NumericField_ReturnsTermQuery()
public async virtual Task Translate_Equal_NumericField_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var idValue = 100;
var query = translator.Translate(x => x.Id == idValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Id == idValue);
// Assert
query.ShouldNotBeNull();
@ -88,14 +63,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_StringField_ReturnsTermQueryWithKeyword()
public async virtual Task Translate_Equal_StringField_ReturnsTermQueryWithKeyword()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var stringValue = "test";
var query = translator.Translate(x => x.Name == stringValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name == stringValue);
// Assert
query.ShouldNotBeNull();
@ -105,14 +77,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_BooleanField_ReturnsTermQuery()
public async virtual Task Translate_Equal_BooleanField_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var boolValue = true;
var query = translator.Translate(x => x.IsActive == boolValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.IsActive == boolValue);
// Assert
query.ShouldNotBeNull();
@ -122,14 +91,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_EnumField_ReturnsTermQuery()
public async virtual Task Translate_Equal_EnumField_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var enumValue = TestEnum.Active;
var query = translator.Translate(x => x.Status == enumValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Status == enumValue);
// Assert
query.ShouldNotBeNull();
@ -139,14 +105,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_StringEnumField_ReturnsTermQueryWithString()
public async virtual Task Translate_Equal_StringEnumField_ReturnsTermQueryWithString()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var enumValue = TestEnum.Pending;
var query = translator.Translate(x => x.StringValueStatus == enumValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.StringValueStatus == enumValue);
// Assert
query.ShouldNotBeNull();
@ -156,13 +119,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NotEqual_StringField_ReturnsBoolMustNot()
public async virtual Task Translate_NotEqual_StringField_ReturnsBoolMustNot()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name != "test");
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name != "test");
// Assert
query.ShouldNotBeNull();
@ -177,14 +137,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_NullableEnumField_ReturnsTermQuery()
public async virtual Task Translate_Equal_NullableEnumField_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var enumValue = TestEnum.Inactive;
var query = translator.Translate(x => x.NullableStatus == enumValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.NullableStatus == enumValue);
// Assert
query.ShouldNotBeNull();
@ -194,13 +151,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_DecimalField_ReturnsTermQuery()
public async virtual Task Translate_Equal_DecimalField_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Salary == 5000.50m);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Salary == 5000.50m);
// Assert
query.ShouldNotBeNull();
@ -209,14 +163,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Equal_NullableDateField_ReturnsDateRangeQuery()
public async virtual Task Translate_Equal_NullableDateField_ReturnsDateRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var date = new DateTime(2024, 3, 15, 10, 30, 0);
// Act
var query = translator.Translate(x => x.UpdatedTime == date);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime == date);
// Assert
query.ShouldNotBeNull();
@ -231,13 +184,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region Null 比较测试
[Fact]
public async Task Translate_EqualNull_ReturnsMustNotExistsQuery()
public async virtual Task Translate_EqualNull_ReturnsMustNotExistsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name == null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name == null);
// Assert
query.ShouldNotBeNull();
@ -252,13 +202,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NotEqualNull_ReturnsExistsQuery()
public async virtual Task Translate_NotEqualNull_ReturnsExistsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name != null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name != null);
// Assert
query.ShouldNotBeNull();
@ -267,13 +214,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NullableType_IsNull_ReturnsMustNotExists()
public async virtual Task Translate_NullableType_IsNull_ReturnsMustNotExists()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.UpdatedTime == null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime == null);
// Assert
query.ShouldNotBeNull();
@ -284,13 +228,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NullableType_IsNotNull_ReturnsExistsQuery()
public async virtual Task Translate_NullableType_IsNotNull_ReturnsExistsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.UpdatedTime != null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime != null);
// Assert
query.ShouldNotBeNull();
@ -299,13 +240,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NullableEnum_IsNull_ReturnsMustNotExists()
public async virtual Task Translate_NullableEnum_IsNull_ReturnsMustNotExists()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.NullableStatus == null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.NullableStatus == null);
// Assert
query.ShouldNotBeNull();
@ -320,13 +258,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 逻辑运算测试
[Fact]
public async Task Translate_AndAlso_TwoConditions_ReturnsBoolFilterWithTwoQueries()
public async virtual Task Translate_AndAlso_TwoConditions_ReturnsBoolFilterWithTwoQueries()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age > 18 && x.IsActive == true);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > 18 && x.IsActive == true);
// Assert
query.ShouldNotBeNull();
@ -336,13 +271,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_AndAlso_ThreeConditions_ReturnsBoolFilterWithThreeQueries()
public async virtual Task Translate_AndAlso_ThreeConditions_ReturnsBoolFilterWithThreeQueries()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age > 18 && x.IsActive == true && x.Name != null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > 18 && x.IsActive == true && x.Name != null);
// Assert
query.ShouldNotBeNull();
@ -352,13 +284,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_OrElse_TwoConditions_ReturnsBoolShouldWithTwoQueries()
public async virtual Task Translate_OrElse_TwoConditions_ReturnsBoolShouldWithTwoQueries()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age > 18 || x.IsActive == true);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > 18 || x.IsActive == true);
// Assert
query.ShouldNotBeNull();
@ -370,13 +299,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_OrElse_ThreeConditions_ReturnsBoolShouldWithThreeQueries()
public async virtual Task Translate_OrElse_ThreeConditions_ReturnsBoolShouldWithThreeQueries()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age > 18 || x.IsActive == true || x.Name != null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > 18 || x.IsActive == true || x.Name != null);
// Assert
query.ShouldNotBeNull();
@ -388,13 +314,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_AndAlsoWithOrElse_ReturnsNestedBoolQuery()
public async virtual Task Translate_AndAlsoWithOrElse_ReturnsNestedBoolQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => (x.Age > 18 || x.IsActive == true) && x.Name != null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => (x.Age > 18 || x.IsActive == true) && x.Name != null);
// Assert
query.ShouldNotBeNull();
@ -411,13 +334,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Not_ReturnsNegatedBooleanQuery()
public async virtual Task Translate_Not_ReturnsNegatedBooleanQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => !x.IsActive);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => !x.IsActive);
// Assert
query.ShouldNotBeNull();
@ -428,13 +348,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_Not_OnComparison_ReturnsMustNotQuery()
public async virtual Task Translate_Not_OnComparison_ReturnsMustNotQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => !(x.Age > 18));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => !(x.Age > 18));
// Assert
query.ShouldNotBeNull();
@ -449,13 +366,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 范围查询测试
[Fact]
public async Task Translate_GreaterThan_ReturnsRangeQuery()
public async virtual Task Translate_GreaterThan_ReturnsRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age > 18);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > 18);
// Assert
query.ShouldNotBeNull();
@ -466,13 +380,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_GreaterThanOrEqual_ReturnsRangeQuery()
public async virtual Task Translate_GreaterThanOrEqual_ReturnsRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age >= 18);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age >= 18);
// Assert
query.ShouldNotBeNull();
@ -483,13 +394,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_LessThan_ReturnsRangeQuery()
public async virtual Task Translate_LessThan_ReturnsRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age < 65);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age < 65);
// Assert
query.ShouldNotBeNull();
@ -500,13 +408,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_LessThanOrEqual_ReturnsRangeQuery()
public async virtual Task Translate_LessThanOrEqual_ReturnsRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age <= 65);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age <= 65);
// Assert
query.ShouldNotBeNull();
@ -517,14 +422,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_DateComparison_GreaterThan_ReturnsDateRangeQuery()
public async virtual Task Translate_DateComparison_GreaterThan_ReturnsDateRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var date = new DateTime(2024, 1, 15);
// Act
var query = translator.Translate(x => x.CreatedTime > date);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.CreatedTime > date);
// Assert
query.ShouldNotBeNull();
@ -534,15 +438,14 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_DateComparison_BetweenDates_ReturnsDateRangeQuery()
public async virtual Task Translate_DateComparison_BetweenDates_ReturnsDateRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var startDate = new DateTime(2024, 1, 1);
var endDate = new DateTime(2024, 12, 31);
// Act
var query = translator.Translate(x => x.CreatedTime >= startDate && x.CreatedTime <= endDate);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.CreatedTime >= startDate && x.CreatedTime <= endDate);
// Assert
query.ShouldNotBeNull();
@ -555,13 +458,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_DecimalComparison_ReturnsRangeQuery()
public async virtual Task Translate_DecimalComparison_ReturnsRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Salary >= 3000.50m);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Salary >= 3000.50m);
// Assert
query.ShouldNotBeNull();
@ -575,13 +475,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 字符串方法测试
[Fact]
public async Task Translate_StringContains_KeywordField_ReturnsWildcardQuery()
public async virtual Task Translate_StringContains_KeywordField_ReturnsWildcardQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.Contains("test"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Contains("test"));
// Assert
query.ShouldNotBeNull();
@ -591,13 +488,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringStartsWith_KeywordField_ReturnsWildcardQuery()
public async virtual Task Translate_StringStartsWith_KeywordField_ReturnsWildcardQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.StartsWith("test"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.StartsWith("test"));
// Assert
query.ShouldNotBeNull();
@ -607,13 +501,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringEndsWith_KeywordField_ReturnsWildcardQuery()
public async virtual Task Translate_StringEndsWith_KeywordField_ReturnsWildcardQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.EndsWith("test"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.EndsWith("test"));
// Assert
query.ShouldNotBeNull();
@ -623,13 +514,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringContains_WildcardField_ReturnsWildcardQuery()
public async virtual Task Translate_StringContains_WildcardField_ReturnsWildcardQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Exceptions!.Contains("error"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Exceptions!.Contains("error"));
// Assert
query.ShouldNotBeNull();
@ -639,13 +527,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringContains_TextWithoutKeyword_ReturnsMatchPhraseQuery()
public async virtual Task Translate_StringContains_TextWithoutKeyword_ReturnsMatchPhraseQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Description!.Contains("hello world"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Description!.Contains("hello world"));
// Assert
query.ShouldNotBeNull();
@ -655,13 +540,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringStartsWith_TextWithoutKeyword_ReturnsMatchPhrasePrefixQuery()
public async virtual Task Translate_StringStartsWith_TextWithoutKeyword_ReturnsMatchPhrasePrefixQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Description!.StartsWith("hello"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Description!.StartsWith("hello"));
// Assert
query.ShouldNotBeNull();
@ -671,13 +553,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringContains_EscapesWildcardCharacters()
public async virtual Task Translate_StringContains_EscapesWildcardCharacters()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.Contains("test*value"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Contains("test*value"));
// Assert
query.ShouldNotBeNull();
@ -686,13 +565,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringContains_EscapesQuestionMark()
public async virtual Task Translate_StringContains_EscapesQuestionMark()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.Contains("test?value"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Contains("test?value"));
// Assert
query.ShouldNotBeNull();
@ -701,13 +577,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringEquals_ReturnsTermQuery()
public async virtual Task Translate_StringEquals_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.Equals("test"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Equals("test"));
// Assert
query.ShouldNotBeNull();
@ -722,14 +595,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 集合方法测试
[Fact]
public async Task Translate_EnumerableContains_Tags_ReturnsTermsQuery()
public async virtual Task Translate_EnumerableContains_Tags_ReturnsTermsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var tags = new List<string> { "tag1", "tag2", "tag3" };
// Act
var query = translator.Translate(x => tags.Contains(x.Name!));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => tags.Contains(x.Name!));
// Assert
query.ShouldNotBeNull();
@ -738,14 +610,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_CollectionContains_SingleItem_ReturnsTermQuery()
public async virtual Task Translate_CollectionContains_SingleItem_ReturnsTermQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var tags = new List<string> { "tag1" };
// Act
var query = translator.Translate(x => tags.Contains(x.Name!));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => tags.Contains(x.Name!));
// Assert
query.ShouldNotBeNull();
@ -754,14 +625,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_EnumerableContains_EmptyCollection_ReturnsMatchNoneQuery()
public async virtual Task Translate_EnumerableContains_EmptyCollection_ReturnsMatchNoneQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var tags = new List<string>();
// Act
var query = translator.Translate(x => tags.Contains(x.Name!));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => tags.Contains(x.Name!));
// Assert
query.ShouldNotBeNull();
@ -769,13 +639,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_EnumerableAny_WithoutPredicate_ReturnsNestedExistsQuery()
public async virtual Task Translate_EnumerableAny_WithoutPredicate_ReturnsNestedExistsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Items!.Any());
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Items!.Any());
// Assert
query.ShouldNotBeNull();
@ -785,13 +652,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_EnumerableAny_WithPredicate_ReturnsNestedQuery()
public async virtual Task Translate_EnumerableAny_WithPredicate_ReturnsNestedQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Items!.Any(i => i.Id == 10));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Items!.Any(i => i.Id == 10));
// Assert
query.ShouldNotBeNull();
@ -802,13 +666,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_EnumerableAny_WithComplexPredicate_ReturnsNestedBoolQuery()
public async virtual Task Translate_EnumerableAny_WithComplexPredicate_ReturnsNestedBoolQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Items!.Any(i => i.Price > 100 && i.Price < 1000));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Items!.Any(i => i.Price > 100 && i.Price < 1000));
// Assert
query.ShouldNotBeNull();
@ -820,13 +681,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_EnumerableAll_ReturnsNestedQuery()
public async virtual Task Translate_EnumerableAll_ReturnsNestedQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Items!.All(i => i.Price > 0));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Items!.All(i => i.Price > 0));
// Assert
query.ShouldNotBeNull();
@ -840,13 +698,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 嵌套属性测试
[Fact]
public async Task Translate_NestedObjectProperty_ReturnsQueryWithNestedPath()
public async virtual Task Translate_NestedObjectProperty_ReturnsQueryWithNestedPath()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Address!.City == "Beijing");
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Address!.City == "Beijing");
// Assert
query.ShouldNotBeNull();
@ -855,13 +710,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NestedObjectStringContains_ReturnsWildcardQuery()
public async virtual Task Translate_NestedObjectStringContains_ReturnsWildcardQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Address!.City!.Contains("bei"));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Address!.City!.Contains("bei"));
// Assert
query.ShouldNotBeNull();
@ -871,13 +723,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NestedObjectProperty_NotEqualNull_ReturnsExistsQuery()
public async virtual Task Translate_NestedObjectProperty_NotEqualNull_ReturnsExistsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Address!.City != null);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Address!.City != null);
// Assert
query.ShouldNotBeNull();
@ -890,13 +739,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region Nullable 属性测试
[Fact]
public async Task Translate_NullableDate_HasValue_ReturnsExistsQuery()
public async virtual Task Translate_NullableDate_HasValue_ReturnsExistsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.UpdatedTime.HasValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime.HasValue);
// Assert
query.ShouldNotBeNull();
@ -909,16 +755,15 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 复杂表达式测试
[Fact]
public async Task Translate_ComplexExpression_ReturnsCorrectQuery()
public async virtual Task Translate_ComplexExpression_ReturnsCorrectQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var minAge = 18;
var maxAge = 65;
var namePrefix = "test";
// Act
var query = translator.Translate(x =>
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x =>
(x.Age >= minAge && x.Age <= maxAge) &&
(x.Name != null && x.Name.StartsWith(namePrefix)) &&
x.IsActive == true &&
@ -976,15 +821,14 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_ExpressionWithCapturedVariable_ReturnsCorrectQuery()
public async virtual Task Translate_ExpressionWithCapturedVariable_ReturnsCorrectQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var targetName = "John";
var targetAge = 30;
// Act
var query = translator.Translate(x => x.Name == targetName && x.Age == targetAge);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name == targetName && x.Age == targetAge);
// Assert
query.ShouldNotBeNull();
@ -1003,14 +847,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_ExpressionWithDateTimeVariable_ReturnsDateRangeQuery()
public async virtual Task Translate_ExpressionWithDateTimeVariable_ReturnsDateRangeQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var targetDate = new DateTime(2024, 6, 15);
// Act
var query = translator.Translate(x => x.CreatedTime == targetDate);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.CreatedTime == targetDate);
// Assert
query.ShouldNotBeNull();
@ -1025,23 +868,18 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 边界情况测试
[Fact]
public async Task Translate_UnsupportedMethod_ThrowsNotSupportedException()
public async virtual Task Translate_UnsupportedMethod_ThrowsNotSupportedException()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act & Assert
Should.Throw<NotSupportedException>(() => translator.Translate(x => x.Name!.ToUpper() == "TEST"));
await Should.ThrowAsync<NotSupportedException>(async () =>
await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.ToUpper() == "TEST"));
}
[Fact]
public async Task Translate_MultipleWhereConditions_ReturnsCorrectQuery()
public async virtual Task Translate_MultipleWhereConditions_ReturnsCorrectQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x =>
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x =>
(x.Age > 18 || x.Age < 65) &&
x.Name != null &&
!x.IsActive);
@ -1054,14 +892,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NestedAnyWithContains_ReturnsNestedTermsQuery()
public async virtual Task Translate_NestedAnyWithContains_ReturnsNestedTermsQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
var validIds = new List<int> { 1, 2, 3, 4, 5 };
// Act
var query = translator.Translate(x => x.Items!.Any(i => validIds.Contains(i.Id)));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Items!.Any(i => validIds.Contains(i.Id)));
// Assert
query.ShouldNotBeNull();
@ -1072,13 +909,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_StringEquals_NullValue_ReturnsMustNotExists()
public async virtual Task Translate_StringEquals_NullValue_ReturnsMustNotExists()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.Equals(null));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Equals(null));
// Assert
query.ShouldNotBeNull();
@ -1089,13 +923,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
}
[Fact]
public async Task Translate_NotEqual_NullableEnum_ReturnsCorrectQuery()
public async virtual Task Translate_NotEqual_NullableEnum_ReturnsCorrectQuery()
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.NullableStatus != TestEnum.Active);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.NullableStatus != TestEnum.Active);
// Assert
query.ShouldNotBeNull();
@ -1116,13 +947,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
[InlineData(TestEnum.Active, 1)]
[InlineData(TestEnum.Inactive, 2)]
[InlineData(TestEnum.Pending, 3)]
public async Task Translate_Equal_EnumField_ReturnsCorrectValue(TestEnum enumValue, long expectedValue)
public async virtual Task Translate_Equal_EnumField_ReturnsCorrectValue(TestEnum enumValue, long expectedValue)
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Status == enumValue);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Status == enumValue);
// Assert
query.ShouldNotBeNull();
@ -1135,13 +963,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
[InlineData(18)]
[InlineData(30)]
[InlineData(65)]
public async Task Translate_GreaterThan_VariousAges_ReturnsCorrectRange(int age)
public async virtual Task Translate_GreaterThan_VariousAges_ReturnsCorrectRange(int age)
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Age > age);
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > age);
// Assert
query.ShouldNotBeNull();
@ -1155,13 +980,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
[InlineData("test")]
[InlineData("hello")]
[InlineData("world")]
public async Task Translate_StringContains_VariousValues_ReturnsCorrectWildcard(string searchValue)
public async virtual Task Translate_StringContains_VariousValues_ReturnsCorrectWildcard(string searchValue)
{
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act
var query = translator.Translate(x => x.Name!.Contains(searchValue));
var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Contains(searchValue));
// Assert
query.ShouldNotBeNull();

Loading…
Cancel
Save