Browse Source

feat: Add an interface for querying the ES expression tree

pull/1552/head
colin 1 month 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.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Elasticsearch; namespace LINGYUN.Abp.Elasticsearch;
/// <summary> /// <summary>
/// 表达式查询转换器 - 将 LINQ 表达式转换为 Elasticsearch Query /// 表达式查询转换器 - 将 LINQ 表达式转换为 Elasticsearch Query
/// </summary> /// </summary>
/// <typeparam name="TDocument">文档类型</typeparam> public class ExpressionQueryTranslator : IExpressionQueryTranslator, ISingletonDependency
public class ExpressionQueryTranslator<TDocument>
{ {
private readonly bool _defaultNestedBehavior; private readonly IIndexMappingProvider _indexMappingProvider;
private readonly IndexMappingInfo _mappingInfo;
/// <summary>
/// 构造函数(使用已有的映射信息)
/// </summary>
public ExpressionQueryTranslator( public ExpressionQueryTranslator(
IndexMappingInfo mappingInfo, IIndexMappingProvider indexMappingProvider)
bool defaultNestedBehavior = false)
{ {
_mappingInfo = mappingInfo; _indexMappingProvider = indexMappingProvider;
_defaultNestedBehavior = defaultNestedBehavior;
} }
/// <summary> /// <summary>
/// 同步翻译表达式(需要已加载映射) /// 翻译表达式
/// </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)); 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 节点翻译 #region 节点翻译
@ -555,9 +553,7 @@ public class ExpressionQueryTranslator<TDocument>
inner = TranslateNode(predicate.Body, prefix: collectionField.Path, mappingInfo); inner = TranslateNode(predicate.Body, prefix: collectionField.Path, mappingInfo);
} }
var shouldUseNested = collectionField.IsNested || var shouldUseNested = collectionField.IsNested || (mappingInfo?.IsNested(collectionField.Path) ?? false);
(mappingInfo?.IsNested(collectionField.Path) ?? false) ||
_defaultNestedBehavior;
return shouldUseNested return shouldUseNested
? new NestedQuery(collectionField.Path, inner) ? 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 // 检查路径的父级是否是 Nested
var parts = path.Split('.'); 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)); var parentPath = string.Join(".", parts.Take(i + 1));
if (IsNested(parentPath)) if (IsNested(parentPath))
@ -125,7 +125,7 @@ public class IndexMappingInfo
public string? GetNestedParentPath(string path) public string? GetNestedParentPath(string path)
{ {
var parts = path.Split('.'); 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)); var parentPath = string.Join(".", parts.Take(i + 1));
if (IsNested(parentPath)) 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.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Xunit; using Xunit;
namespace LINGYUN.Abp.Elasticsearch.Tests; 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() public ExpressionQueryTranslatorTests()
{ {
_indexMappingProvider = GetRequiredService<IIndexMappingProvider>(); _expressionQueryTranslator = GetRequiredService<IExpressionQueryTranslator>();
}
private async Task<ExpressionQueryTranslator<TDocument>> CreateTranslator<TDocument>(
bool defaultNestedBehavior = false)
{
var indexMapping = await _indexMappingProvider.GetMappingAsync(TestDocumentIndexNames.Index);
return new ExpressionQueryTranslator<TDocument>(
indexMapping,
defaultNestedBehavior);
} }
#region 基础查询测试 #region 基础查询测试
[Fact] [Fact]
public async Task Translate_ConstantTrue_ReturnsMatchAllQuery() public async virtual Task Translate_ConstantTrue_ReturnsMatchAllQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => true); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => true);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -43,42 +34,26 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_ConstantFalse_ReturnsMatchNoneQuery() public async virtual Task Translate_ConstantFalse_ReturnsMatchNoneQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => false); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => false);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
query.MatchNone.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 #endregion
#region 相等性查询测试 #region 相等性查询测试
[Fact] [Fact]
public async Task Translate_Equal_NumericField_ReturnsTermQuery() public async virtual Task Translate_Equal_NumericField_ReturnsTermQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var idValue = 100; var idValue = 100;
var query = translator.Translate(x => x.Id == idValue); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Id == idValue);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -88,14 +63,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_StringField_ReturnsTermQueryWithKeyword() public async virtual Task Translate_Equal_StringField_ReturnsTermQueryWithKeyword()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var stringValue = "test"; var stringValue = "test";
var query = translator.Translate(x => x.Name == stringValue); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name == stringValue);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -105,14 +77,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_BooleanField_ReturnsTermQuery() public async virtual Task Translate_Equal_BooleanField_ReturnsTermQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var boolValue = true; var boolValue = true;
var query = translator.Translate(x => x.IsActive == boolValue); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.IsActive == boolValue);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -122,14 +91,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_EnumField_ReturnsTermQuery() public async virtual Task Translate_Equal_EnumField_ReturnsTermQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var enumValue = TestEnum.Active; 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -139,14 +105,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_StringEnumField_ReturnsTermQueryWithString() public async virtual Task Translate_Equal_StringEnumField_ReturnsTermQueryWithString()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var enumValue = TestEnum.Pending; 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -156,13 +119,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NotEqual_StringField_ReturnsBoolMustNot() public async virtual Task Translate_NotEqual_StringField_ReturnsBoolMustNot()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name != "test"); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name != "test");
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -177,14 +137,11 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_NullableEnumField_ReturnsTermQuery() public async virtual Task Translate_Equal_NullableEnumField_ReturnsTermQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var enumValue = TestEnum.Inactive; 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -194,13 +151,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_DecimalField_ReturnsTermQuery() public async virtual Task Translate_Equal_DecimalField_ReturnsTermQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Salary == 5000.50m); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Salary == 5000.50m);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -209,14 +163,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Equal_NullableDateField_ReturnsDateRangeQuery() public async virtual Task Translate_Equal_NullableDateField_ReturnsDateRangeQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var date = new DateTime(2024, 3, 15, 10, 30, 0); var date = new DateTime(2024, 3, 15, 10, 30, 0);
// Act // Act
var query = translator.Translate(x => x.UpdatedTime == date); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime == date);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -231,13 +184,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region Null 比较测试 #region Null 比较测试
[Fact] [Fact]
public async Task Translate_EqualNull_ReturnsMustNotExistsQuery() public async virtual Task Translate_EqualNull_ReturnsMustNotExistsQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name == null); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name == null);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -252,13 +202,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NotEqualNull_ReturnsExistsQuery() public async virtual Task Translate_NotEqualNull_ReturnsExistsQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name != null); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name != null);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -267,13 +214,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NullableType_IsNull_ReturnsMustNotExists() public async virtual Task Translate_NullableType_IsNull_ReturnsMustNotExists()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.UpdatedTime == null); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime == null);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -284,13 +228,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NullableType_IsNotNull_ReturnsExistsQuery() public async virtual Task Translate_NullableType_IsNotNull_ReturnsExistsQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.UpdatedTime != null); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime != null);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -299,13 +240,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NullableEnum_IsNull_ReturnsMustNotExists() public async virtual Task Translate_NullableEnum_IsNull_ReturnsMustNotExists()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.NullableStatus == null); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.NullableStatus == null);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -320,13 +258,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 逻辑运算测试 #region 逻辑运算测试
[Fact] [Fact]
public async Task Translate_AndAlso_TwoConditions_ReturnsBoolFilterWithTwoQueries() public async virtual Task Translate_AndAlso_TwoConditions_ReturnsBoolFilterWithTwoQueries()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -336,13 +271,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_AndAlso_ThreeConditions_ReturnsBoolFilterWithThreeQueries() public async virtual Task Translate_AndAlso_ThreeConditions_ReturnsBoolFilterWithThreeQueries()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -352,13 +284,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_OrElse_TwoConditions_ReturnsBoolShouldWithTwoQueries() public async virtual Task Translate_OrElse_TwoConditions_ReturnsBoolShouldWithTwoQueries()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -370,13 +299,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_OrElse_ThreeConditions_ReturnsBoolShouldWithThreeQueries() public async virtual Task Translate_OrElse_ThreeConditions_ReturnsBoolShouldWithThreeQueries()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -388,13 +314,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_AndAlsoWithOrElse_ReturnsNestedBoolQuery() public async virtual Task Translate_AndAlsoWithOrElse_ReturnsNestedBoolQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -411,13 +334,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Not_ReturnsNegatedBooleanQuery() public async virtual Task Translate_Not_ReturnsNegatedBooleanQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => !x.IsActive); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => !x.IsActive);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -428,13 +348,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_Not_OnComparison_ReturnsMustNotQuery() public async virtual Task Translate_Not_OnComparison_ReturnsMustNotQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => !(x.Age > 18)); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => !(x.Age > 18));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -449,13 +366,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 范围查询测试 #region 范围查询测试
[Fact] [Fact]
public async Task Translate_GreaterThan_ReturnsRangeQuery() public async virtual Task Translate_GreaterThan_ReturnsRangeQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Age > 18); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > 18);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -466,13 +380,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_GreaterThanOrEqual_ReturnsRangeQuery() public async virtual Task Translate_GreaterThanOrEqual_ReturnsRangeQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Age >= 18); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age >= 18);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -483,13 +394,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_LessThan_ReturnsRangeQuery() public async virtual Task Translate_LessThan_ReturnsRangeQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Age < 65); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age < 65);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -500,13 +408,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_LessThanOrEqual_ReturnsRangeQuery() public async virtual Task Translate_LessThanOrEqual_ReturnsRangeQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Age <= 65); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age <= 65);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -517,14 +422,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_DateComparison_GreaterThan_ReturnsDateRangeQuery() public async virtual Task Translate_DateComparison_GreaterThan_ReturnsDateRangeQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var date = new DateTime(2024, 1, 15); var date = new DateTime(2024, 1, 15);
// Act // Act
var query = translator.Translate(x => x.CreatedTime > date); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.CreatedTime > date);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -534,15 +438,14 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_DateComparison_BetweenDates_ReturnsDateRangeQuery() public async virtual Task Translate_DateComparison_BetweenDates_ReturnsDateRangeQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var startDate = new DateTime(2024, 1, 1); var startDate = new DateTime(2024, 1, 1);
var endDate = new DateTime(2024, 12, 31); var endDate = new DateTime(2024, 12, 31);
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -555,13 +458,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_DecimalComparison_ReturnsRangeQuery() public async virtual Task Translate_DecimalComparison_ReturnsRangeQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Salary >= 3000.50m); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Salary >= 3000.50m);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -575,13 +475,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 字符串方法测试 #region 字符串方法测试
[Fact] [Fact]
public async Task Translate_StringContains_KeywordField_ReturnsWildcardQuery() public async virtual Task Translate_StringContains_KeywordField_ReturnsWildcardQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name!.Contains("test")); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Contains("test"));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -591,13 +488,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringStartsWith_KeywordField_ReturnsWildcardQuery() public async virtual Task Translate_StringStartsWith_KeywordField_ReturnsWildcardQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name!.StartsWith("test")); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.StartsWith("test"));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -607,13 +501,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringEndsWith_KeywordField_ReturnsWildcardQuery() public async virtual Task Translate_StringEndsWith_KeywordField_ReturnsWildcardQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name!.EndsWith("test")); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.EndsWith("test"));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -623,13 +514,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringContains_WildcardField_ReturnsWildcardQuery() public async virtual Task Translate_StringContains_WildcardField_ReturnsWildcardQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Exceptions!.Contains("error")); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Exceptions!.Contains("error"));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -639,13 +527,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringContains_TextWithoutKeyword_ReturnsMatchPhraseQuery() public async virtual Task Translate_StringContains_TextWithoutKeyword_ReturnsMatchPhraseQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -655,13 +540,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringStartsWith_TextWithoutKeyword_ReturnsMatchPhrasePrefixQuery() public async virtual Task Translate_StringStartsWith_TextWithoutKeyword_ReturnsMatchPhrasePrefixQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Description!.StartsWith("hello")); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Description!.StartsWith("hello"));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -671,13 +553,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringContains_EscapesWildcardCharacters() public async virtual Task Translate_StringContains_EscapesWildcardCharacters()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -686,13 +565,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringContains_EscapesQuestionMark() public async virtual Task Translate_StringContains_EscapesQuestionMark()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -701,13 +577,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringEquals_ReturnsTermQuery() public async virtual Task Translate_StringEquals_ReturnsTermQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name!.Equals("test")); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Equals("test"));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -722,14 +595,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 集合方法测试 #region 集合方法测试
[Fact] [Fact]
public async Task Translate_EnumerableContains_Tags_ReturnsTermsQuery() public async virtual Task Translate_EnumerableContains_Tags_ReturnsTermsQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var tags = new List<string> { "tag1", "tag2", "tag3" }; var tags = new List<string> { "tag1", "tag2", "tag3" };
// Act // Act
var query = translator.Translate(x => tags.Contains(x.Name!)); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => tags.Contains(x.Name!));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -738,14 +610,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_CollectionContains_SingleItem_ReturnsTermQuery() public async virtual Task Translate_CollectionContains_SingleItem_ReturnsTermQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var tags = new List<string> { "tag1" }; var tags = new List<string> { "tag1" };
// Act // Act
var query = translator.Translate(x => tags.Contains(x.Name!)); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => tags.Contains(x.Name!));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -754,14 +625,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_EnumerableContains_EmptyCollection_ReturnsMatchNoneQuery() public async virtual Task Translate_EnumerableContains_EmptyCollection_ReturnsMatchNoneQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var tags = new List<string>(); var tags = new List<string>();
// Act // Act
var query = translator.Translate(x => tags.Contains(x.Name!)); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => tags.Contains(x.Name!));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -769,13 +639,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_EnumerableAny_WithoutPredicate_ReturnsNestedExistsQuery() public async virtual Task Translate_EnumerableAny_WithoutPredicate_ReturnsNestedExistsQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Items!.Any()); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Items!.Any());
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -785,13 +652,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_EnumerableAny_WithPredicate_ReturnsNestedQuery() public async virtual Task Translate_EnumerableAny_WithPredicate_ReturnsNestedQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -802,13 +666,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_EnumerableAny_WithComplexPredicate_ReturnsNestedBoolQuery() public async virtual Task Translate_EnumerableAny_WithComplexPredicate_ReturnsNestedBoolQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -820,13 +681,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_EnumerableAll_ReturnsNestedQuery() public async virtual Task Translate_EnumerableAll_ReturnsNestedQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -840,13 +698,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 嵌套属性测试 #region 嵌套属性测试
[Fact] [Fact]
public async Task Translate_NestedObjectProperty_ReturnsQueryWithNestedPath() public async virtual Task Translate_NestedObjectProperty_ReturnsQueryWithNestedPath()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Address!.City == "Beijing"); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Address!.City == "Beijing");
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -855,13 +710,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NestedObjectStringContains_ReturnsWildcardQuery() public async virtual Task Translate_NestedObjectStringContains_ReturnsWildcardQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -871,13 +723,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NestedObjectProperty_NotEqualNull_ReturnsExistsQuery() public async virtual Task Translate_NestedObjectProperty_NotEqualNull_ReturnsExistsQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Address!.City != null); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Address!.City != null);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -890,13 +739,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region Nullable 属性测试 #region Nullable 属性测试
[Fact] [Fact]
public async Task Translate_NullableDate_HasValue_ReturnsExistsQuery() public async virtual Task Translate_NullableDate_HasValue_ReturnsExistsQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.UpdatedTime.HasValue); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.UpdatedTime.HasValue);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -909,16 +755,15 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 复杂表达式测试 #region 复杂表达式测试
[Fact] [Fact]
public async Task Translate_ComplexExpression_ReturnsCorrectQuery() public async virtual Task Translate_ComplexExpression_ReturnsCorrectQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var minAge = 18; var minAge = 18;
var maxAge = 65; var maxAge = 65;
var namePrefix = "test"; var namePrefix = "test";
// Act // Act
var query = translator.Translate(x => var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x =>
(x.Age >= minAge && x.Age <= maxAge) && (x.Age >= minAge && x.Age <= maxAge) &&
(x.Name != null && x.Name.StartsWith(namePrefix)) && (x.Name != null && x.Name.StartsWith(namePrefix)) &&
x.IsActive == true && x.IsActive == true &&
@ -976,15 +821,14 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_ExpressionWithCapturedVariable_ReturnsCorrectQuery() public async virtual Task Translate_ExpressionWithCapturedVariable_ReturnsCorrectQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var targetName = "John"; var targetName = "John";
var targetAge = 30; var targetAge = 30;
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1003,14 +847,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_ExpressionWithDateTimeVariable_ReturnsDateRangeQuery() public async virtual Task Translate_ExpressionWithDateTimeVariable_ReturnsDateRangeQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var targetDate = new DateTime(2024, 6, 15); var targetDate = new DateTime(2024, 6, 15);
// Act // Act
var query = translator.Translate(x => x.CreatedTime == targetDate); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.CreatedTime == targetDate);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1025,23 +868,18 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
#region 边界情况测试 #region 边界情况测试
[Fact] [Fact]
public async Task Translate_UnsupportedMethod_ThrowsNotSupportedException() public async virtual Task Translate_UnsupportedMethod_ThrowsNotSupportedException()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act & Assert // 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] [Fact]
public async Task Translate_MultipleWhereConditions_ReturnsCorrectQuery() public async virtual Task Translate_MultipleWhereConditions_ReturnsCorrectQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x =>
(x.Age > 18 || x.Age < 65) && (x.Age > 18 || x.Age < 65) &&
x.Name != null && x.Name != null &&
!x.IsActive); !x.IsActive);
@ -1054,14 +892,13 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NestedAnyWithContains_ReturnsNestedTermsQuery() public async virtual Task Translate_NestedAnyWithContains_ReturnsNestedTermsQuery()
{ {
// Arrange // Arrange
var translator = await CreateTranslator<TestDocument>();
var validIds = new List<int> { 1, 2, 3, 4, 5 }; var validIds = new List<int> { 1, 2, 3, 4, 5 };
// Act // 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 // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1072,13 +909,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_StringEquals_NullValue_ReturnsMustNotExists() public async virtual Task Translate_StringEquals_NullValue_ReturnsMustNotExists()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.Name!.Equals(null)); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Equals(null));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1089,13 +923,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
} }
[Fact] [Fact]
public async Task Translate_NotEqual_NullableEnum_ReturnsCorrectQuery() public async virtual Task Translate_NotEqual_NullableEnum_ReturnsCorrectQuery()
{ {
// Arrange
var translator = await CreateTranslator<TestDocument>();
// Act // Act
var query = translator.Translate(x => x.NullableStatus != TestEnum.Active); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.NullableStatus != TestEnum.Active);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1116,13 +947,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
[InlineData(TestEnum.Active, 1)] [InlineData(TestEnum.Active, 1)]
[InlineData(TestEnum.Inactive, 2)] [InlineData(TestEnum.Inactive, 2)]
[InlineData(TestEnum.Pending, 3)] [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 // Act
var query = translator.Translate(x => x.Status == enumValue); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Status == enumValue);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1135,13 +963,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
[InlineData(18)] [InlineData(18)]
[InlineData(30)] [InlineData(30)]
[InlineData(65)] [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 // Act
var query = translator.Translate(x => x.Age > age); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Age > age);
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();
@ -1155,13 +980,10 @@ public class ExpressionQueryTranslatorTests : AbpElasticsearchTestBase
[InlineData("test")] [InlineData("test")]
[InlineData("hello")] [InlineData("hello")]
[InlineData("world")] [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 // Act
var query = translator.Translate(x => x.Name!.Contains(searchValue)); var query = await _expressionQueryTranslator.TranslateAsync<TestDocument>(TestDocumentIndexNames.Index, x => x.Name!.Contains(searchValue));
// Assert // Assert
query.ShouldNotBeNull(); query.ShouldNotBeNull();

Loading…
Cancel
Save