diff --git a/.idea/.idea.Foundatio.Parsers/.idea/projectSettingsUpdater.xml b/.idea/.idea.Foundatio.Parsers/.idea/projectSettingsUpdater.xml
index 4bb9f4d2..64af657f 100644
--- a/.idea/.idea.Foundatio.Parsers/.idea/projectSettingsUpdater.xml
+++ b/.idea/.idea.Foundatio.Parsers/.idea/projectSettingsUpdater.xml
@@ -1,6 +1,7 @@
-
+
+
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index feef7153..f72de2fa 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,9 +1,10 @@
{
"cSpell.words": [
+ "aggs",
+ "Foundatio",
"Lucene",
"Niemyjski",
- "Xunit",
- "aggs"
+ "Xunit"
],
"msbuildProjectTools.nuget.includePreRelease": true
}
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index c9db4c86..73c1cfe3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,6 @@
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:7.17.24
+ image: docker.elastic.co/elasticsearch/elasticsearch:8.16.1
environment:
discovery.type: single-node
xpack.security.enabled: 'false'
@@ -19,7 +19,7 @@ services:
depends_on:
elasticsearch:
condition: service_healthy
- image: docker.elastic.co/kibana/kibana:7.17.24
+ image: docker.elastic.co/kibana/kibana:8.16.1
ports:
- 5601:5601
networks:
diff --git a/src/Foundatio.Parsers.ElasticQueries/AggregationMap.cs b/src/Foundatio.Parsers.ElasticQueries/AggregationMap.cs
new file mode 100644
index 00000000..e619bf98
--- /dev/null
+++ b/src/Foundatio.Parsers.ElasticQueries/AggregationMap.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace Foundatio.Parsers.ElasticQueries;
+
+public record AggregationMap(string Name, object Value)
+{
+ public string Name { get; set; } = Name;
+ public object Value { get; set; } = Value;
+ public List Aggregations { get; } = new();
+ public Dictionary Meta { get; } = new();
+}
diff --git a/src/Foundatio.Parsers.ElasticQueries/ElasticMappingResolver.cs b/src/Foundatio.Parsers.ElasticQueries/ElasticMappingResolver.cs
index 67584ba2..697e15e8 100644
--- a/src/Foundatio.Parsers.ElasticQueries/ElasticMappingResolver.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/ElasticMappingResolver.cs
@@ -1,32 +1,35 @@
-using System;
+using System;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.Linq;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.IndexManagement;
+using Elastic.Clients.Elasticsearch.Mapping;
using Exceptionless.DateTimeExtensions;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries;
public class ElasticMappingResolver
{
- private ITypeMapping _serverMapping;
- private readonly ITypeMapping _codeMapping;
+ private TypeMapping _serverMapping;
+ private readonly TypeMapping _codeMapping;
private readonly Inferrer _inferrer;
private readonly ConcurrentDictionary _mappingCache = new();
private readonly ILogger _logger;
public static ElasticMappingResolver NullInstance = new(() => null);
- public ElasticMappingResolver(Func getMapping, Inferrer inferrer = null, ILogger logger = null)
+ public ElasticMappingResolver(Func getMapping, Inferrer inferrer = null, ILogger logger = null)
{
GetServerMappingFunc = getMapping;
_inferrer = inferrer;
_logger = logger ?? NullLogger.Instance;
}
- public ElasticMappingResolver(ITypeMapping codeMapping, Inferrer inferrer, Func getMapping, ILogger logger = null)
+ public ElasticMappingResolver(TypeMapping codeMapping, Inferrer inferrer, Func getMapping, ILogger logger = null)
: this(getMapping, inferrer, logger)
{
_codeMapping = codeMapping;
@@ -37,7 +40,7 @@ public ElasticMappingResolver(ITypeMapping codeMapping, Inferrer inferrer, Func<
///
public void RefreshMapping()
{
- _logger.LogInformation("Mapping refresh triggered.");
+ _logger.LogInformation("Mapping refresh triggered");
_serverMapping = null;
_lastMappingUpdate = null;
}
@@ -53,7 +56,7 @@ public FieldMapping GetMapping(string field, bool followAlias = false)
if (_mappingCache.TryGetValue(field, out var mapping))
{
- if (followAlias && mapping.Found && mapping.Property is IFieldAliasProperty fieldAlias)
+ if (followAlias && mapping.Found && mapping.Property is FieldAliasProperty fieldAlias)
{
_logger.LogTrace("Cached alias mapping: {Field}={FieldPath}:{FieldType}", field, mapping.FullPath, mapping.Property?.Type);
return GetMapping(fieldAlias.Path.Name);
@@ -67,11 +70,11 @@ public FieldMapping GetMapping(string field, bool followAlias = false)
if (mapping.ServerMapTime >= _lastMappingUpdate && !GetServerMapping())
{
- _logger.LogTrace("Cached mapping (not found): {field}=", field);
+ _logger.LogTrace("Cached mapping (not found): {Field}=", field);
return mapping;
}
- _logger.LogTrace("Cached mapping (not found), got new server mapping.");
+ _logger.LogTrace("Cached mapping (not found), got new server mapping");
}
string[] fieldParts = field.Split('.');
@@ -83,13 +86,13 @@ public FieldMapping GetMapping(string field, bool followAlias = false)
{
string fieldPart = fieldParts[depth];
IProperty fieldMapping = null;
- if (currentProperties == null || !currentProperties.TryGetValue(fieldPart, out fieldMapping))
+ if (currentProperties == null || !currentProperties.TryGetProperty(fieldPart, out fieldMapping))
{
- // check to see if there is an name match
+ // check to see if there is a name match
if (currentProperties != null)
- fieldMapping = currentProperties.Values.FirstOrDefault(m =>
+ fieldMapping = ((IDictionary)currentProperties).Values.FirstOrDefault(m =>
{
- string propertyName = _inferrer.PropertyName(m?.Name);
+ string propertyName = _inferrer.PropertyName(m?.TryGetName());
return propertyName != null && propertyName.Equals(fieldPart, StringComparison.OrdinalIgnoreCase);
});
@@ -122,13 +125,14 @@ public FieldMapping GetMapping(string field, bool followAlias = false)
}
// coded properties sometimes have null Name properties
- if (fieldMapping.Name == null && fieldMapping is IPropertyWithClrOrigin clrOrigin && clrOrigin.ClrOrigin != null)
- fieldMapping.Name = new PropertyName(clrOrigin.ClrOrigin);
+ string name = fieldMapping.TryGetName();
+ // if (name == null && fieldMapping is IPropertyWithClrOrigin clrOrigin && clrOrigin.ClrOrigin != null)
+ // name = new PropertyName(clrOrigin.ClrOrigin);
if (depth == 0)
- resolvedFieldName += _inferrer.PropertyName(fieldMapping.Name);
+ resolvedFieldName += _inferrer.PropertyName(name);
else
- resolvedFieldName += "." + _inferrer.PropertyName(fieldMapping.Name);
+ resolvedFieldName += "." + _inferrer.PropertyName(name);
if (depth == fieldParts.Length - 1)
{
@@ -136,19 +140,19 @@ public FieldMapping GetMapping(string field, bool followAlias = false)
_mappingCache.AddOrUpdate(field, resolvedMapping, (_, _) => resolvedMapping);
_logger.LogTrace("Resolved mapping: {Field}={FieldPath}:{FieldType}", field, resolvedMapping.FullPath, resolvedMapping.Property?.Type);
- if (followAlias && resolvedMapping.Property is IFieldAliasProperty fieldAlias)
+ if (followAlias && resolvedMapping.Property is FieldAliasProperty fieldAlias)
return GetMapping(fieldAlias.Path.Name);
return resolvedMapping;
}
- if (fieldMapping is IObjectProperty objectProperty)
+ if (fieldMapping is ObjectProperty objectProperty)
{
currentProperties = objectProperty.Properties;
}
else
{
- if (fieldMapping is ITextProperty textProperty)
+ if (fieldMapping is TextProperty textProperty)
currentProperties = textProperty.Fields;
else
break;
@@ -229,13 +233,14 @@ public string GetNonAnalyzedFieldName(string field, string preferredSubField = n
if (mapping?.Property == null || !IsPropertyAnalyzed(mapping.Property))
return field;
- var multiFieldProperty = mapping.Property as ICoreProperty;
- if (multiFieldProperty?.Fields == null)
+ var multiFieldProperty = mapping.Property;
+ var fields = multiFieldProperty.GetFields();
+ if ((IDictionary)fields is not { Count: > 0 })
return mapping.FullPath;
- var nonAnalyzedProperty = multiFieldProperty.Fields.OrderByDescending(kvp => kvp.Key.Name == preferredSubField).FirstOrDefault(kvp =>
+ var nonAnalyzedProperty = fields.OrderByDescending(kvp => kvp.Key.Name == preferredSubField).FirstOrDefault(kvp =>
{
- if (kvp.Value is IKeywordProperty)
+ if (kvp.Value is KeywordProperty)
return true;
if (!IsPropertyAnalyzed(kvp.Value))
@@ -265,7 +270,7 @@ public bool IsPropertyAnalyzed(string field)
public bool IsPropertyAnalyzed(IProperty property)
{
- if (property is ITextProperty textProperty)
+ if (property is TextProperty textProperty)
return !textProperty.Index.HasValue || textProperty.Index.Value;
return false;
@@ -276,7 +281,7 @@ public bool IsNestedPropertyType(string field)
if (String.IsNullOrEmpty(field))
return false;
- return GetMappingProperty(field, true) is INestedProperty;
+ return GetMappingProperty(field, true) is NestedProperty;
}
public bool IsGeoPropertyType(string field)
@@ -284,7 +289,7 @@ public bool IsGeoPropertyType(string field)
if (String.IsNullOrEmpty(field))
return false;
- return GetMappingProperty(field, true) is IGeoPointProperty;
+ return GetMappingProperty(field, true) is GeoPointProperty;
}
public bool IsNumericPropertyType(string field)
@@ -292,7 +297,16 @@ public bool IsNumericPropertyType(string field)
if (String.IsNullOrEmpty(field))
return false;
- return GetMappingProperty(field, true) is INumberProperty;
+ var property = GetMappingProperty(field, true);
+ return property is ByteNumberProperty
+ or DoubleNumberProperty
+ or FloatNumberProperty
+ or HalfFloatNumberProperty
+ or IntegerNumberProperty
+ or LongNumberProperty
+ or ScaledFloatNumberProperty
+ or ShortNumberProperty
+ or UnsignedLongNumberProperty;
}
public bool IsBooleanPropertyType(string field)
@@ -300,7 +314,7 @@ public bool IsBooleanPropertyType(string field)
if (String.IsNullOrEmpty(field))
return false;
- return GetMappingProperty(field, true) is IBooleanProperty;
+ return GetMappingProperty(field, true) is BooleanProperty;
}
public bool IsDatePropertyType(string field)
@@ -308,7 +322,7 @@ public bool IsDatePropertyType(string field)
if (String.IsNullOrEmpty(field))
return false;
- return GetMappingProperty(field, true) is IDateProperty;
+ return GetMappingProperty(field, true) is DateProperty;
}
public FieldType GetFieldType(string field)
@@ -323,44 +337,62 @@ public FieldType GetFieldType(string field)
return property.Type switch
{
- "geo_point" => FieldType.GeoPoint,
- "geo_shape" => FieldType.GeoShape,
- "ip" => FieldType.Ip,
- "binary" => FieldType.Binary,
- "keyword" => FieldType.Keyword,
- "string" or "text" => FieldType.Text,
- "date" => FieldType.Date,
- "boolean" => FieldType.Boolean,
- "completion" => FieldType.Completion,
- "nested" => FieldType.Nested,
- "object" => FieldType.Object,
- "murmur3" => FieldType.Murmur3Hash,
- "token_count" => FieldType.TokenCount,
- "percolator" => FieldType.Percolator,
- "integer" => FieldType.Integer,
- "long" => FieldType.Long,
- "short" => FieldType.Short,
- "byte" => FieldType.Byte,
- "float" => FieldType.Float,
- "half_float" => FieldType.HalfFloat,
- "scaled_float" => FieldType.ScaledFloat,
- "double" => FieldType.Double,
- "integer_range" => FieldType.IntegerRange,
- "float_range" => FieldType.FloatRange,
- "long_range" => FieldType.LongRange,
- "double_range" => FieldType.DoubleRange,
- "date_range" => FieldType.DateRange,
- "ip_range" => FieldType.IpRange,
+ "version"=> FieldType.Version,
+ "token_count"=> FieldType.TokenCount,
+ "text"=> FieldType.Text,
+ "sparse_vector"=> FieldType.SparseVector,
+ "short"=> FieldType.Short,
+ "shape"=> FieldType.Shape,
+ "semantic_text"=> FieldType.SemanticText,
+ "search_as_you_type"=> FieldType.SearchAsYouType,
+ "scaled_float"=> FieldType.ScaledFloat,
+ "rank_features"=> FieldType.RankFeatures,
+ "rank_feature"=> FieldType.RankFeature,
+ "percolator"=> FieldType.Percolator,
+ "object"=> FieldType.Object,
+ "none"=> FieldType.None,
+ "nested"=> FieldType.Nested,
+ "murmur3"=> FieldType.Murmur3,
+ "match_only_text"=> FieldType.MatchOnlyText,
+ "long_range"=> FieldType.LongRange,
+ "long"=> FieldType.Long,
+ "keyword"=> FieldType.Keyword,
+ "join"=> FieldType.Join,
+ "ip_range"=> FieldType.IpRange,
+ "ip"=> FieldType.Ip,
+ "integer_range"=> FieldType.IntegerRange,
+ "integer"=> FieldType.Integer,
+ "icu_collation_keyword"=> FieldType.IcuCollationKeyword,
+ "histogram"=> FieldType.Histogram,
+ "half_float"=> FieldType.HalfFloat,
+ "geo_shape"=> FieldType.GeoShape,
+ "geo_point"=> FieldType.GeoPoint,
+ "float_range"=> FieldType.FloatRange,
+ "float"=> FieldType.Float,
+ "flattened"=> FieldType.Flattened,
+ "double_range"=> FieldType.DoubleRange,
+ "double"=> FieldType.Double,
+ "dense_vector"=> FieldType.DenseVector,
+ "date_range"=> FieldType.DateRange,
+ "date_nanos"=> FieldType.DateNanos,
+ "date"=> FieldType.Date,
+ "constant_keyword"=> FieldType.ConstantKeyword,
+ "completion"=> FieldType.Completion,
+ "byte"=> FieldType.Byte,
+ "boolean"=> FieldType.Boolean,
+ "binary"=> FieldType.Binary,
+ "alias"=> FieldType.Alias,
+ "aggregate_metric_double"=> FieldType.AggregateMetricDouble,
_ => FieldType.None,
};
}
- private IProperties MergeProperties(IProperties codeProperties, IProperties serverProperties)
+ private Properties MergeProperties(Properties codeProperties, Properties serverProperties)
{
if (codeProperties == null && serverProperties == null)
return null;
- IProperties mergedCodeProperties = null;
+ Properties mergedCodeProperties = null;
// resolve code mapping property expressions using inferrer
if (codeProperties != null)
{
@@ -369,7 +401,7 @@ private IProperties MergeProperties(IProperties codeProperties, IProperties serv
foreach (var kvp in codeProperties)
{
var propertyName = kvp.Key;
- if (_inferrer != null && (String.IsNullOrEmpty(kvp.Key.Name) || kvp.Value is IFieldAliasProperty))
+ if (_inferrer != null && (String.IsNullOrEmpty(kvp.Key.Name) || kvp.Value is FieldAliasProperty))
propertyName = _inferrer.PropertyName(kvp.Key) ?? kvp.Key;
mergedCodeProperties[propertyName] = kvp.Value;
@@ -380,14 +412,14 @@ private IProperties MergeProperties(IProperties codeProperties, IProperties serv
// resolve field alias
foreach (var kvp in codeProperties)
{
- if (kvp.Value is not IFieldAliasProperty aliasProperty)
+ if (kvp.Value is not FieldAliasProperty aliasProperty)
continue;
mergedCodeProperties[kvp.Key] = new FieldAliasProperty
{
- LocalMetadata = aliasProperty.LocalMetadata,
+ //LocalMetadata = aliasProperty.LocalMetadata,
Path = _inferrer?.Field(aliasProperty.Path) ?? aliasProperty.Path,
- Name = aliasProperty.Name
+ // Name = aliasProperty.Name
};
}
}
@@ -397,23 +429,27 @@ private IProperties MergeProperties(IProperties codeProperties, IProperties serv
if (mergedCodeProperties == null || serverProperties == null)
return mergedCodeProperties ?? serverProperties;
- IProperties properties = new Properties();
+ Properties properties = new Properties();
foreach (var serverProperty in serverProperties)
{
var merged = serverProperty.Value;
- if (mergedCodeProperties.TryGetValue(serverProperty.Key, out var codeProperty))
- merged.LocalMetadata = codeProperty.LocalMetadata;
+ // if (mergedCodeProperties.TryGetProperty(serverProperty.Key, out var codeProperty))
+ // merged.LocalMetadata = codeProperty.LocalMetadata;
- switch (merged)
+ if (mergedCodeProperties.TryGetProperty(serverProperty.Key, out var codeProperty))
{
- case IObjectProperty objectProperty:
- var codeObjectProperty = codeProperty as IObjectProperty;
- objectProperty.Properties = MergeProperties(codeObjectProperty?.Properties, objectProperty.Properties);
- break;
- case ITextProperty textProperty:
- var codeTextProperty = codeProperty as ITextProperty;
- textProperty.Fields = MergeProperties(codeTextProperty?.Fields, textProperty.Fields);
- break;
+ switch (merged)
+ {
+ case ObjectProperty objectProperty:
+ var codeObjectProperty = codeProperty as ObjectProperty;
+ objectProperty.Properties =
+ MergeProperties(codeObjectProperty?.Properties, objectProperty.Properties);
+ break;
+ case TextProperty textProperty:
+ var codeTextProperty = codeProperty as TextProperty;
+ textProperty.Fields = MergeProperties(codeTextProperty?.Fields, textProperty.Fields);
+ break;
+ }
}
properties.Add(serverProperty.Key, merged);
@@ -421,7 +457,7 @@ private IProperties MergeProperties(IProperties codeProperties, IProperties serv
foreach (var codeProperty in mergedCodeProperties)
{
- if (properties.TryGetValue(codeProperty.Key, out _))
+ if (properties.TryGetProperty(codeProperty.Key, out _))
continue;
properties.Add(codeProperty.Key, codeProperty.Value);
@@ -430,7 +466,7 @@ private IProperties MergeProperties(IProperties codeProperties, IProperties serv
return properties;
}
- private Func GetServerMappingFunc { get; set; }
+ private Func GetServerMappingFunc { get; set; }
private DateTime? _lastMappingUpdate = null;
private bool GetServerMapping()
{
@@ -450,12 +486,12 @@ private bool GetServerMapping()
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting server mapping: " + ex.Message);
+ _logger.LogError(ex, "Error getting server mapping: {Message}", ex.Message);
return false;
}
}
- public static ElasticMappingResolver Create(Func, ITypeMapping> mappingBuilder, IElasticClient client, ILogger logger = null) where T : class
+ public static ElasticMappingResolver Create(Func, TypeMapping> mappingBuilder, ElasticsearchClient client, ILogger logger = null) where T : class
{
logger ??= NullLogger.Instance;
@@ -471,7 +507,7 @@ public static ElasticMappingResolver Create(Func, IT
}, logger);
}
- public static ElasticMappingResolver Create(Func, ITypeMapping> mappingBuilder, IElasticClient client, string index, ILogger logger = null) where T : class
+ public static ElasticMappingResolver Create(Func, TypeMapping> mappingBuilder, ElasticsearchClient client, string index, ILogger logger = null) where T : class
{
logger ??= NullLogger.Instance;
@@ -487,14 +523,13 @@ public static ElasticMappingResolver Create(Func, IT
}, logger);
}
- public static ElasticMappingResolver Create(Func, ITypeMapping> mappingBuilder, Inferrer inferrer, Func getMapping, ILogger logger = null) where T : class
+ public static ElasticMappingResolver Create(Func, TypeMapping> mappingBuilder, Inferrer inferrer, Func getMapping, ILogger logger = null) where T : class
{
- var codeMapping = new TypeMappingDescriptor();
- codeMapping = mappingBuilder(codeMapping) as TypeMappingDescriptor;
+ var codeMapping = mappingBuilder(new TypeMappingDescriptor());
return new ElasticMappingResolver(codeMapping, inferrer, getMapping, logger: logger);
}
- public static ElasticMappingResolver Create(IElasticClient client, ILogger logger = null)
+ public static ElasticMappingResolver Create(ElasticsearchClient client, ILogger logger = null)
{
logger ??= NullLogger.Instance;
@@ -510,7 +545,7 @@ public static ElasticMappingResolver Create(IElasticClient client, ILogger lo
}, client.Infer, logger);
}
- public static ElasticMappingResolver Create(IElasticClient client, string index, ILogger logger = null)
+ public static ElasticMappingResolver Create(ElasticsearchClient client, string index, ILogger logger = null)
{
logger ??= NullLogger.Instance;
@@ -526,7 +561,7 @@ public static ElasticMappingResolver Create(IElasticClient client, string index,
}, client.Infer, logger);
}
- public static ElasticMappingResolver Create(Func getMapping, Inferrer inferrer, ILogger logger = null)
+ public static ElasticMappingResolver Create(Func getMapping, Inferrer inferrer, ILogger logger = null)
{
return new ElasticMappingResolver(getMapping, inferrer, logger: logger);
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParser.cs b/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParser.cs
index a9abce24..c1ec84db 100644
--- a/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParser.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParser.cs
@@ -1,14 +1,16 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Aggregations;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
using Pegasus.Common;
namespace Foundatio.Parsers.ElasticQueries;
@@ -158,7 +160,7 @@ public async Task ValidateQueryAsync(string query, QueryV
return context.GetValidationResult();
}
- public async Task BuildQueryAsync(string query, IElasticQueryVisitorContext context = null)
+ public async Task BuildQueryAsync(string query, IElasticQueryVisitorContext context = null)
{
context ??= new ElasticQueryVisitorContext();
context.QueryType = QueryTypes.Query;
@@ -169,7 +171,7 @@ public async Task BuildQueryAsync(string query, IElasticQueryVis
return await BuildQueryAsync(result, context).ConfigureAwait(false);
}
- public async Task BuildQueryAsync(IQueryNode query, IElasticQueryVisitorContext context = null)
+ public async Task BuildQueryAsync(IQueryNode query, IElasticQueryVisitorContext context = null)
{
context ??= new ElasticQueryVisitorContext();
var q = await query.GetQueryAsync() ?? new MatchAllQuery();
@@ -195,7 +197,7 @@ public async Task ValidateAggregationsAsync(string query,
return context.GetValidationResult();
}
- public async Task BuildAggregationsAsync(string aggregations, IElasticQueryVisitorContext context = null)
+ public async Task BuildAggregationsAsync(string aggregations, IElasticQueryVisitorContext context = null)
{
context ??= new ElasticQueryVisitorContext();
context.QueryType = QueryTypes.Aggregation;
@@ -207,7 +209,7 @@ public async Task BuildAggregationsAsync(string aggregatio
}
#pragma warning disable IDE0060 // Remove unused parameter
- public async Task BuildAggregationsAsync(IQueryNode aggregations, IElasticQueryVisitorContext context = null)
+ public async Task BuildAggregationsAsync(IQueryNode aggregations, IElasticQueryVisitorContext context = null)
{
if (aggregations == null)
return null;
@@ -227,7 +229,7 @@ public async Task ValidateSortAsync(string query, QueryVa
return context.GetValidationResult();
}
- public async Task> BuildSortAsync(string sort, IElasticQueryVisitorContext context = null)
+ public async Task> BuildSortAsync(string sort, IElasticQueryVisitorContext context = null)
{
context ??= new ElasticQueryVisitorContext();
context.QueryType = QueryTypes.Sort;
@@ -238,7 +240,7 @@ public async Task> BuildSortAsync(string sort, IElasticQ
return await BuildSortAsync(result, context).ConfigureAwait(false);
}
- public Task> BuildSortAsync(IQueryNode sort, IElasticQueryVisitorContext context = null)
+ public Task> BuildSortAsync(IQueryNode sort, IElasticQueryVisitorContext context = null)
{
context ??= new ElasticQueryVisitorContext();
return GetSortFieldsVisitor.RunAsync(sort, context);
diff --git a/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParserConfiguration.cs b/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParserConfiguration.cs
index 8b6de9cd..99b5f946 100644
--- a/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParserConfiguration.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/ElasticQueryParserConfiguration.cs
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Mapping;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries;
using Foundatio.Parsers.LuceneQueries.Visitors;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries;
@@ -285,35 +286,35 @@ public ElasticQueryParserConfiguration AddAggregationVisitorAfter(IChainableQ
#endregion
- public ElasticQueryParserConfiguration UseMappings(Func, TypeMappingDescriptor> mappingBuilder, IElasticClient client, string index) where T : class
+ public ElasticQueryParserConfiguration UseMappings(Func, TypeMapping> mappingBuilder, ElasticsearchClient client, string index) where T : class
{
MappingResolver = ElasticMappingResolver.Create(mappingBuilder, client, index, logger: _logger);
return this;
}
- public ElasticQueryParserConfiguration UseMappings(Func, TypeMappingDescriptor> mappingBuilder, Inferrer inferrer, Func getMapping) where T : class
+ public ElasticQueryParserConfiguration UseMappings(Func, TypeMapping> mappingBuilder, Inferrer inferrer, Func getMapping) where T : class
{
MappingResolver = ElasticMappingResolver.Create(mappingBuilder, inferrer, getMapping, logger: _logger);
return this;
}
- public ElasticQueryParserConfiguration UseMappings(IElasticClient client)
+ public ElasticQueryParserConfiguration UseMappings(ElasticsearchClient client)
{
MappingResolver = ElasticMappingResolver.Create(client, logger: _logger);
return this;
}
- public ElasticQueryParserConfiguration UseMappings(IElasticClient client, string index)
+ public ElasticQueryParserConfiguration UseMappings(ElasticsearchClient client, string index)
{
MappingResolver = ElasticMappingResolver.Create(client, index, logger: _logger);
return this;
}
- public ElasticQueryParserConfiguration UseMappings(Func getMapping, Inferrer inferrer = null)
+ public ElasticQueryParserConfiguration UseMappings(Func getMapping, Inferrer inferrer = null)
{
MappingResolver = ElasticMappingResolver.Create(getMapping, inferrer, logger: _logger);
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultAggregationNodeExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultAggregationNodeExtensions.cs
index dad8290b..84067be7 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultAggregationNodeExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultAggregationNodeExtensions.cs
@@ -1,12 +1,13 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Aggregations;
using Exceptionless.DateTimeExtensions;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Extensions;
@@ -15,7 +16,7 @@ public static class DefaultAggregationNodeExtensions
// NOTE: We may want to read this dynamically from server settings.
public const int MAX_BUCKET_SIZE = 10000;
- public static Task GetDefaultAggregationAsync(this IQueryNode node, IQueryVisitorContext context)
+ public static Task GetDefaultAggregationAsync(this IQueryNode node, IQueryVisitorContext context)
{
if (node is GroupNode groupNode)
return groupNode.GetDefaultAggregationAsync(context);
@@ -26,7 +27,7 @@ public static Task GetDefaultAggregationAsync(this IQueryNode n
return null;
}
- public static async Task GetDefaultAggregationAsync(this GroupNode node, IQueryVisitorContext context)
+ public static async Task GetDefaultAggregationAsync(this GroupNode node, IQueryVisitorContext context)
{
if (context is not IElasticQueryVisitorContext elasticContext)
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
@@ -41,51 +42,54 @@ public static async Task GetDefaultAggregationAsync(this GroupN
switch (node.GetOperationType())
{
case AggregationType.DateHistogram:
- return GetDateHistogramAggregation("date_" + originalField, field, node.UnescapedProximity, node.UnescapedBoost ?? node.GetTimeZone(await elasticContext.GetTimeZoneAsync()), context);
+ return GetDateHistogramAggregation($"date_{originalField}", field, node.UnescapedProximity, node.UnescapedBoost ?? node.GetTimeZone(await elasticContext.GetTimeZoneAsync()), context);
case AggregationType.Histogram:
- return GetHistogramAggregation("histogram_" + originalField, field, node.UnescapedProximity, node.UnescapedBoost, context);
+ return GetHistogramAggregation($"histogram_{originalField}", field, node.UnescapedProximity, node.UnescapedBoost, context);
case AggregationType.GeoHashGrid:
- var precision = GeoHashPrecision.Precision1;
- if (!String.IsNullOrEmpty(node.UnescapedProximity))
- Enum.TryParse(node.UnescapedProximity, out precision);
+ var precision = new GeohashPrecision(1);
+ if (!String.IsNullOrEmpty(node.UnescapedProximity) && Double.TryParse(node.UnescapedProximity, out double parsedPrecision))
+ {
+ if (parsedPrecision is < 1 or > 12)
+ throw new ArgumentOutOfRangeException(nameof(node.UnescapedProximity), "Precision must be between 1 and 12");
+
+ precision = new GeohashPrecision(parsedPrecision);
+ }
- return new GeoHashGridAggregation("geogrid_" + originalField)
+ return new AggregationMap($"geogrid_{originalField}", new GeohashGridAggregation { Field = field, Precision = precision })
{
- Field = field,
- Precision = precision,
- Aggregations = new AverageAggregation("avg_lat", null)
+ Aggregations =
{
- Script = new InlineScript($"doc['{node.Field}'].lat")
- } && new AverageAggregation("avg_lon", null)
- {
- Script = new InlineScript($"doc['{node.Field}'].lon")
+ new AggregationMap("avg_lat", new AverageAggregation { Script = new Script { Source = $"doc['{node.Field}'].lat" } }),
+ new AggregationMap("avg_lon", new AverageAggregation { Script = new Script { Source = $"doc['{node.Field}'].lon" } })
}
};
case AggregationType.Terms:
- var agg = new TermsAggregation("terms_" + originalField)
+ var termsAggregation = new TermsAggregation
{
Field = field,
Size = node.GetProximityAsInt32(),
- MinimumDocumentCount = node.GetBoostAsInt32(),
- Meta = new Dictionary { { "@field_type", property?.Type } }
+ MinDocCount = node.GetBoostAsInt32()
};
- if (agg.Size.HasValue && (agg.Size * 1.5 + 10) > MAX_BUCKET_SIZE)
- agg.ShardSize = Math.Max((int)agg.Size, MAX_BUCKET_SIZE);
+ if (termsAggregation.Size.HasValue && (termsAggregation.Size * 1.5 + 10) > MAX_BUCKET_SIZE)
+ termsAggregation.ShardSize = Math.Max((int)termsAggregation.Size, MAX_BUCKET_SIZE);
- return agg;
+ return new AggregationMap($"terms_{originalField}", termsAggregation)
+ {
+ Meta = { { "@field_type", property?.Type } }
+ };
case AggregationType.TopHits:
- return new TopHitsAggregation("tophits") { Size = node.GetProximityAsInt32() };
+ return new AggregationMap("tophits", new TopHitsAggregation { Size = node.GetProximityAsInt32() });
}
return null;
}
- public static async Task GetDefaultAggregationAsync(this TermNode node, IQueryVisitorContext context)
+ public static async Task GetDefaultAggregationAsync(this TermNode node, IQueryVisitorContext context)
{
if (context is not IElasticQueryVisitorContext elasticContext)
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
@@ -98,78 +102,104 @@ public static async Task GetDefaultAggregationAsync(this TermNo
switch (node.GetOperationType())
{
case AggregationType.Min:
- return new MinAggregation("min_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), Meta = new Dictionary { { "@field_type", property?.Type }, { "@timezone", timezone } } };
+ return new AggregationMap($"min_{originalField}", new MinAggregation { Field = aggField, Missing = node.GetProximityAsDouble() })
+ {
+ Meta = { { "@field_type", property?.Type }, { "@timezone", timezone } }
+ };
case AggregationType.Max:
- return new MaxAggregation("max_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), Meta = new Dictionary { { "@field_type", property?.Type }, { "@timezone", timezone } } };
+ return new AggregationMap($"max_{originalField}", new MaxAggregation { Field = aggField, Missing = node.GetProximityAsDouble() })
+ {
+ Meta = { { "@field_type", property?.Type }, { "@timezone", timezone } }
+ };
case AggregationType.Avg:
- return new AverageAggregation("avg_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), Meta = new Dictionary { { "@field_type", property?.Type } } };
+ return new AggregationMap($"avg_{originalField}", new AverageAggregation { Field = aggField, Missing = node.GetProximityAsDouble() })
+ {
+ Meta = { { "@field_type", property?.Type } }
+ };
case AggregationType.Sum:
- return new SumAggregation("sum_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), Meta = new Dictionary { { "@field_type", property?.Type } } };
+ return new AggregationMap($"sum_{originalField}", new SumAggregation { Field = aggField, Missing = node.GetProximityAsDouble() })
+ {
+ Meta = { { "@field_type", property?.Type } }
+ };
case AggregationType.Stats:
- return new StatsAggregation("stats_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), Meta = new Dictionary { { "@field_type", property?.Type } } };
+ return new AggregationMap($"stats_{originalField}", new StatsAggregation { Field = aggField, Missing = node.GetProximityAsDouble() })
+ {
+ Meta = { { "@field_type", property?.Type } }
+ };
case AggregationType.ExtendedStats:
- return new ExtendedStatsAggregation("exstats_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), Meta = new Dictionary { { "@field_type", property?.Type } } };
+ return new AggregationMap($"exstats_{originalField}", new ExtendedStatsAggregation { Field = aggField, Missing = node.GetProximityAsDouble() })
+ {
+ Meta = { { "@field_type", property?.Type } }
+ };
case AggregationType.Cardinality:
- return new CardinalityAggregation("cardinality_" + originalField, aggField) { Missing = node.GetProximityAsDouble(), PrecisionThreshold = node.GetBoostAsInt32() };
+ return new AggregationMap($"cardinality_{originalField}", new CardinalityAggregation
+ {
+ Field = aggField,
+ Missing = node.GetProximityAsDouble(),
+ PrecisionThreshold = node.GetBoostAsInt32()
+ });
case AggregationType.TopHits:
- return new TopHitsAggregation("tophits") { Size = node.GetProximityAsInt32() };
+ return new AggregationMap("tophits", new TopHitsAggregation { Size = node.GetProximityAsInt32() });
case AggregationType.Missing:
- return new MissingAggregation("missing_" + originalField) { Field = aggField };
+ return new AggregationMap($"missing_{originalField}", new MissingAggregation { Field = aggField });
case AggregationType.DateHistogram:
- return GetDateHistogramAggregation("date_" + originalField, aggField, node.UnescapedProximity, node.UnescapedBoost, context);
+ return GetDateHistogramAggregation($"date_{originalField}", aggField, node.UnescapedProximity, node.UnescapedBoost, context);
case AggregationType.Histogram:
- return GetHistogramAggregation("histogram_" + originalField, aggField, node.UnescapedProximity, node.UnescapedBoost, context);
+ return GetHistogramAggregation($"histogram_{originalField}", aggField, node.UnescapedProximity, node.UnescapedBoost, context);
case AggregationType.Percentiles:
- return GetPercentilesAggregation("percentiles_" + originalField, aggField, node.UnescapedProximity, node.UnescapedBoost, context);
+ return GetPercentilesAggregation($"percentiles_{originalField}", aggField, node.UnescapedProximity, node.UnescapedBoost, context);
case AggregationType.GeoHashGrid:
- var precision = GeoHashPrecision.Precision1;
- if (!String.IsNullOrEmpty(node.UnescapedProximity))
- Enum.TryParse(node.UnescapedProximity, out precision);
+ var precision = new GeohashPrecision(1);
+ if (!String.IsNullOrEmpty(node.UnescapedProximity) && Double.TryParse(node.UnescapedProximity, out double parsedPrecision))
+ {
+ if (parsedPrecision is < 1 or > 12)
+ throw new ArgumentOutOfRangeException(nameof(node.UnescapedProximity), "Precision must be between 1 and 12");
- return new GeoHashGridAggregation("geogrid_" + originalField)
+ precision = new GeohashPrecision(parsedPrecision);
+ }
+
+ return new AggregationMap($"geogrid_{originalField}", new GeohashGridAggregation { Field = aggField, Precision = precision })
{
- Field = aggField,
- Precision = precision,
- Aggregations = new AverageAggregation("avg_lat", null)
+ Aggregations =
{
- Script = new InlineScript($"doc['{node.Field}'].lat")
- } && new AverageAggregation("avg_lon", null)
- {
- Script = new InlineScript($"doc['{node.Field}'].lon")
+ new AggregationMap("avg_lat", new AverageAggregation { Script = new Script { Source = $"doc['{node.Field}'].lat" } }),
+ new AggregationMap("avg_lon", new AverageAggregation { Script = new Script { Source = $"doc['{node.Field}'].lon" } })
}
};
case AggregationType.Terms:
- var agg = new TermsAggregation("terms_" + originalField)
+ var termsAggregation = new TermsAggregation
{
Field = aggField,
Size = node.GetProximityAsInt32(),
- MinimumDocumentCount = node.GetBoostAsInt32(),
- Meta = new Dictionary { { "@field_type", property?.Type } }
+ MinDocCount = node.GetBoostAsInt32()
};
- if (agg.Size.HasValue && (agg.Size * 1.5 + 10) > MAX_BUCKET_SIZE)
- agg.ShardSize = Math.Max((int)agg.Size, MAX_BUCKET_SIZE);
+ if (termsAggregation.Size.HasValue && (termsAggregation.Size * 1.5 + 10) > MAX_BUCKET_SIZE)
+ termsAggregation.ShardSize = Math.Max((int)termsAggregation.Size, MAX_BUCKET_SIZE);
- return agg;
+ return new AggregationMap($"terms_{originalField}", termsAggregation)
+ {
+ Meta = { { "@field_type", property?.Type } }
+ };
}
return null;
}
- private static AggregationBase GetPercentilesAggregation(string originalField, string field, string proximity, string boost, IQueryVisitorContext context)
+ private static AggregationMap GetPercentilesAggregation(string originalField, string field, string proximity, string boost, IQueryVisitorContext context)
{
List percents = null;
if (!String.IsNullOrWhiteSpace(proximity))
@@ -183,48 +213,57 @@ private static AggregationBase GetPercentilesAggregation(string originalField, s
}
}
- return new PercentilesAggregation(originalField, field)
+ return new AggregationMap(originalField, new PercentilesAggregation
{
+ Field = field,
Percents = percents
- };
+ });
}
- private static AggregationBase GetHistogramAggregation(string originalField, string field, string proximity, string boost, IQueryVisitorContext context)
+ private static AggregationMap GetHistogramAggregation(string originalField, string field, string proximity, string boost, IQueryVisitorContext context)
{
double interval = 50;
if (Double.TryParse(proximity, out double prox))
interval = prox;
- return new HistogramAggregation(originalField)
+ return new AggregationMap(originalField, new HistogramAggregation
{
Field = field,
- MinimumDocumentCount = 0,
+ MinDocCount = 0,
Interval = interval
- };
+ });
}
- private static AggregationBase GetDateHistogramAggregation(string originalField, string field, string proximity, string boost, IQueryVisitorContext context)
+ private static AggregationMap GetDateHistogramAggregation(string originalField, string field, string proximity, string boost, IQueryVisitorContext context)
{
// NOTE: StartDate and EndDate are set in the Repositories QueryBuilderContext.
var start = context.GetDate("StartDate");
var end = context.GetDate("EndDate");
bool isValidRange = start.HasValue && start.Value > DateTime.MinValue && end.HasValue && end.Value < DateTime.MaxValue && start.Value <= end.Value;
- var bounds = isValidRange ? new ExtendedBounds { Minimum = start.Value, Maximum = end.Value } : null;
+ // TODO: https://github.com/elastic/elasticsearch-net/issues/8338
+ //var bounds = isValidRange ? new ExtendedBoundsDate { Min = start.Value, Max = end.Value } : null;
+ var bounds = isValidRange ? new ExtendedBoundsDate { Min = new FieldDateMath(DateMath.Anchored(start.Value).ToString()), Max = new FieldDateMath(DateMath.Anchored(end.Value).ToString()) } : null;
var interval = GetInterval(proximity, start, end);
string timezone = TryConvertTimeUnitToUtcOffset(boost);
- var agg = new DateHistogramAggregation(originalField)
+ var agg = new DateHistogramAggregation
{
Field = field,
- MinimumDocumentCount = 0,
+ MinDocCount = 0,
Format = "date_optional_time",
TimeZone = timezone,
- Meta = !String.IsNullOrEmpty(boost) ? new Dictionary { { "@timezone", boost } } : null,
ExtendedBounds = bounds
};
interval.Match(d => agg.CalendarInterval = d, f => agg.FixedInterval = f);
- return agg;
+
+ var aggregationMap = new AggregationMap(originalField, agg);
+ if (!String.IsNullOrEmpty(boost))
+ {
+ aggregationMap.Meta.Add("@timezone", boost);
+ }
+
+ return aggregationMap;
}
private static string TryConvertTimeUnitToUtcOffset(string boost)
@@ -242,60 +281,60 @@ private static string TryConvertTimeUnitToUtcOffset(string boost)
return null;
if (timezoneOffset.Value < TimeSpan.Zero)
- return "-" + timezoneOffset.Value.ToString("hh\\:mm");
+ return $"-{timezoneOffset.Value:hh\\:mm}";
- return "+" + timezoneOffset.Value.ToString("hh\\:mm");
+ return $"+{timezoneOffset.Value:hh\\:mm}";
}
- private static Union GetInterval(string proximity, DateTime? start, DateTime? end)
+ private static Union GetInterval(string proximity, DateTime? start, DateTime? end)
{
if (String.IsNullOrEmpty(proximity))
return GetInterval(start, end);
return proximity.Trim() switch
{
- "s" or "1s" or "second" => DateInterval.Second,
- "m" or "1m" or "minute" => DateInterval.Minute,
- "h" or "1h" or "hour" => DateInterval.Hour,
- "d" or "1d" or "day" => DateInterval.Day,
- "w" or "1w" or "week" => DateInterval.Week,
- "M" or "1M" or "month" => DateInterval.Month,
- "q" or "1q" or "quarter" => DateInterval.Quarter,
- "y" or "1y" or "year" => DateInterval.Year,
- _ => new Union(proximity),
+ "s" or "1s" or "second" => CalendarInterval.Second,
+ "m" or "1m" or "minute" => CalendarInterval.Minute,
+ "h" or "1h" or "hour" => CalendarInterval.Hour,
+ "d" or "1d" or "day" => CalendarInterval.Day,
+ "w" or "1w" or "week" => CalendarInterval.Week,
+ "M" or "1M" or "month" => CalendarInterval.Month,
+ "q" or "1q" or "quarter" => CalendarInterval.Quarter,
+ "y" or "1y" or "year" => CalendarInterval.Year,
+ _ => new Union(proximity),
};
}
- private static Union GetInterval(DateTime? utcStart, DateTime? utcEnd, int desiredDataPoints = 100)
+ private static Union GetInterval(DateTime? utcStart, DateTime? utcEnd, int desiredDataPoints = 100)
{
if (!utcStart.HasValue || !utcEnd.HasValue || utcStart.Value == DateTime.MinValue)
- return DateInterval.Day;
+ return CalendarInterval.Day;
var totalTime = utcEnd.Value - utcStart.Value;
var timePerBlock = TimeSpan.FromMinutes(totalTime.TotalMinutes / desiredDataPoints);
if (timePerBlock.TotalDays > 1)
{
timePerBlock = timePerBlock.Round(TimeSpan.FromDays(1));
- return (Time)timePerBlock;
+ return (Duration)timePerBlock;
}
if (timePerBlock.TotalHours > 1)
{
timePerBlock = timePerBlock.Round(TimeSpan.FromHours(1));
- return (Time)timePerBlock;
+ return (Duration)timePerBlock;
}
if (timePerBlock.TotalMinutes > 1)
{
timePerBlock = timePerBlock.Round(TimeSpan.FromMinutes(1));
- return (Time)timePerBlock;
+ return (Duration)timePerBlock;
}
timePerBlock = timePerBlock.Round(TimeSpan.FromSeconds(15));
if (timePerBlock.TotalSeconds < 1)
timePerBlock = TimeSpan.FromSeconds(15);
- return (Time)timePerBlock;
+ return (Duration)timePerBlock;
}
public static int? GetProximityAsInt32(this IFieldQueryWithProximityAndBoostNode node)
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultQueryNodeExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultQueryNodeExtensions.cs
index 4c2aba27..ab76b6f0 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultQueryNodeExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultQueryNodeExtensions.cs
@@ -1,16 +1,16 @@
-using System;
+using System;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Extensions;
public static class DefaultQueryNodeExtensions
{
- public static async Task GetDefaultQueryAsync(this IQueryNode node, IQueryVisitorContext context)
+ public static async Task GetDefaultQueryAsync(this IQueryNode node, IQueryVisitorContext context)
{
if (node is TermNode termNode)
return termNode.GetDefaultQuery(context);
@@ -27,12 +27,12 @@ public static async Task GetDefaultQueryAsync(this IQueryNode node, I
return null;
}
- public static QueryBase GetDefaultQuery(this TermNode node, IQueryVisitorContext context)
+ public static Query GetDefaultQuery(this TermNode node, IQueryVisitorContext context)
{
if (context is not IElasticQueryVisitorContext elasticContext)
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
- QueryBase query;
+ Query query;
string field = node.UnescapedField;
string[] defaultFields = node.GetDefaultFields(elasticContext.DefaultFields);
if (field == null && defaultFields != null && defaultFields.Length == 1)
@@ -58,17 +58,15 @@ public static QueryBase GetDefaultQuery(this TermNode node, IQueryVisitorContext
{
if (node.IsQuotedTerm)
{
- query = new MatchPhraseQuery
+ query = new MatchPhraseQuery(fields[0])
{
- Field = fields[0],
Query = node.UnescapedTerm
};
}
else
{
- query = new MatchQuery
+ query = new MatchQuery(fields[0])
{
- Field = fields[0],
Query = node.UnescapedTerm
};
}
@@ -78,10 +76,9 @@ public static QueryBase GetDefaultQuery(this TermNode node, IQueryVisitorContext
query = new MultiMatchQuery
{
Fields = fields,
- Query = node.UnescapedTerm
+ Query = node.UnescapedTerm,
+ Type = node.IsQuotedTerm ? TextQueryType.Phrase : null
};
- if (node.IsQuotedTerm)
- ((MultiMatchQuery)query).Type = TextQueryType.Phrase;
}
}
}
@@ -89,17 +86,15 @@ public static QueryBase GetDefaultQuery(this TermNode node, IQueryVisitorContext
{
if (!node.IsQuotedTerm && node.UnescapedTerm.EndsWith("*"))
{
- query = new PrefixQuery
+ query = new PrefixQuery(field)
{
- Field = field,
Value = node.UnescapedTerm.TrimEnd('*')
};
}
else
{
- query = new TermQuery
+ query = new TermQuery(field)
{
- Field = field,
Value = node.UnescapedTerm
};
}
@@ -108,7 +103,7 @@ public static QueryBase GetDefaultQuery(this TermNode node, IQueryVisitorContext
return query;
}
- public static async Task GetDefaultQueryAsync(this TermRangeNode node, IQueryVisitorContext context)
+ public static async Task GetDefaultQueryAsync(this TermRangeNode node, IQueryVisitorContext context)
{
if (context is not IElasticQueryVisitorContext elasticContext)
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
@@ -116,54 +111,54 @@ public static async Task GetDefaultQueryAsync(this TermRangeNode node
string field = node.UnescapedField;
if (elasticContext.MappingResolver.IsDatePropertyType(field))
{
- var range = new DateRangeQuery { Field = field, TimeZone = node.Boost ?? node.GetTimeZone(await elasticContext.GetTimeZoneAsync()) };
+ var range = new DateRangeQuery(field) { TimeZone = node.Boost ?? node.GetTimeZone(await elasticContext.GetTimeZoneAsync()) };
if (!String.IsNullOrWhiteSpace(node.UnescapedMin) && node.UnescapedMin != "*")
{
if (node.MinInclusive.HasValue && !node.MinInclusive.Value)
- range.GreaterThan = node.UnescapedMin;
+ range.Gt = node.UnescapedMin;
else
- range.GreaterThanOrEqualTo = node.UnescapedMin;
+ range.Gte = node.UnescapedMin;
}
if (!String.IsNullOrWhiteSpace(node.UnescapedMax) && node.UnescapedMax != "*")
{
if (node.MaxInclusive.HasValue && !node.MaxInclusive.Value)
- range.LessThan = node.UnescapedMax;
+ range.Lt = node.UnescapedMax;
else
- range.LessThanOrEqualTo = node.UnescapedMax;
+ range.Lte = node.UnescapedMax;
}
return range;
}
else
{
- var range = new TermRangeQuery { Field = field };
+ var range = new TermRangeQuery(field);
if (!String.IsNullOrWhiteSpace(node.UnescapedMin) && node.UnescapedMin != "*")
{
if (node.MinInclusive.HasValue && !node.MinInclusive.Value)
- range.GreaterThan = node.UnescapedMin;
+ range.Gt = node.UnescapedMin;
else
- range.GreaterThanOrEqualTo = node.UnescapedMin;
+ range.Gte = node.UnescapedMin;
}
if (!String.IsNullOrWhiteSpace(node.UnescapedMax) && node.UnescapedMax != "*")
{
if (node.MaxInclusive.HasValue && !node.MaxInclusive.Value)
- range.LessThan = node.UnescapedMax;
+ range.Lt = node.UnescapedMax;
else
- range.LessThanOrEqualTo = node.UnescapedMax;
+ range.Lte = node.UnescapedMax;
}
return range;
}
}
- public static QueryBase GetDefaultQuery(this ExistsNode node, IQueryVisitorContext context)
+ public static Query GetDefaultQuery(this ExistsNode node, IQueryVisitorContext context)
{
return new ExistsQuery { Field = node.UnescapedField };
}
- public static QueryBase GetDefaultQuery(this MissingNode node, IQueryVisitorContext context)
+ public static Query GetDefaultQuery(this MissingNode node, IQueryVisitorContext context)
{
return new BoolQuery
{
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultSortNodeExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultSortNodeExtensions.cs
index 3775c69e..cdcd4147 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultSortNodeExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/DefaultSortNodeExtensions.cs
@@ -1,15 +1,16 @@
using System;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Mapping;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Extensions;
public static class DefaultSortNodeExtensions
{
- public static IFieldSort GetDefaultSort(this TermNode node, IQueryVisitorContext context)
+ public static SortOptions GetDefaultSort(this TermNode node, IQueryVisitorContext context)
{
if (context is not IElasticQueryVisitorContext elasticContext)
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
@@ -17,13 +18,10 @@ public static IFieldSort GetDefaultSort(this TermNode node, IQueryVisitorContext
string field = elasticContext.MappingResolver.GetSortFieldName(node.UnescapedField);
var fieldType = elasticContext.MappingResolver.GetFieldType(field);
- var sort = new FieldSort
+ return SortOptions.Field(field, new FieldSort
{
- Field = field,
UnmappedType = fieldType == FieldType.None ? FieldType.Keyword : fieldType,
- Order = node.IsNodeOrGroupNegated() ? SortOrder.Descending : SortOrder.Ascending
- };
-
- return sort;
+ Order = node.IsNodeOrGroupNegated() ? SortOrder.Desc : SortOrder.Asc
+ });
}
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticExtensions.cs
index e12e54cf..de7d893d 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticExtensions.cs
@@ -6,14 +6,90 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using Elasticsearch.Net;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Aggregations;
+using Elastic.Clients.Elasticsearch.Mapping;
+using Elastic.Transport.Products.Elasticsearch;
using Microsoft.Extensions.Logging;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Extensions;
public static class ElasticExtensions
{
+ public static string TryGetName(this IProperty property)
+ {
+ // TODO until: https://github.com/elastic/elasticsearch-net/issues/8336
+ return null;
+ }
+
+ public static bool IsBucketAggregation(this object aggregation)
+ {
+ // NOTE FilterAggregate was called FilterAggregation in the past.
+ return aggregation is AdjacencyMatrixAggregation or AutoDateHistogramAggregation or ChildrenAggregation
+ or CompositeAggregation or DateHistogramAggregation or DateRangeAggregation or DiversifiedSamplerAggregation
+ or FilterAggregate or FiltersAggregation or GeoDistanceAggregation or GeohashGridAggregation
+ or GeotileGridAggregation or GlobalAggregation or HistogramAggregation or IpRangeAggregation
+ or MissingAggregation or MultiTermsAggregation or NestedAggregation or ParentAggregation or RangeAggregation
+ or RareTermsAggregation or ReverseNestedAggregation or SamplerAggregation or SignificantTermsAggregation
+ or SignificantTextAggregation or TermsAggregation or VariableWidthHistogramAggregation;
+ }
+
+ public static Properties GetFields(this IProperty property)
+ {
+ return property switch
+ {
+ AggregateMetricDoubleProperty p => p.Fields,
+ BinaryProperty p => p.Fields,
+ BooleanProperty p => p.Fields,
+ ByteNumberProperty p => p.Fields,
+ CompletionProperty p => p.Fields,
+ ConstantKeywordProperty p => p.Fields,
+ DateNanosProperty p => p.Fields,
+ DateProperty p => p.Fields,
+ DateRangeProperty p => p.Fields,
+ DenseVectorProperty p => p.Fields,
+ DoubleNumberProperty p => p.Fields,
+ DoubleRangeProperty p => p.Fields,
+ DynamicProperty p => p.Fields,
+ FieldAliasProperty p => p.Fields,
+ FlattenedProperty p => p.Fields,
+ FloatNumberProperty p => p.Fields,
+ FloatRangeProperty p => p.Fields,
+ GeoPointProperty p => p.Fields,
+ GeoShapeProperty p => p.Fields,
+ HalfFloatNumberProperty p => p.Fields,
+ HistogramProperty p => p.Fields,
+ IcuCollationProperty p => p.Fields,
+ IntegerNumberProperty p => p.Fields,
+ IntegerRangeProperty p => p.Fields,
+ IpProperty p => p.Fields,
+ IpRangeProperty p => p.Fields,
+ JoinProperty p => p.Fields,
+ KeywordProperty p => p.Fields,
+ LongNumberProperty p => p.Fields,
+ LongRangeProperty p => p.Fields,
+ MatchOnlyTextProperty p => p.Fields,
+ Murmur3HashProperty p => p.Fields,
+ NestedProperty p => p.Fields,
+ ObjectProperty p => p.Fields,
+ PercolatorProperty p => p.Fields,
+ PointProperty p => p.Fields,
+ RankFeatureProperty p => p.Fields,
+ RankFeaturesProperty p => p.Fields,
+ ScaledFloatNumberProperty p => p.Fields,
+ SearchAsYouTypeProperty p => p.Fields,
+ ShapeProperty p => p.Fields,
+ ShortNumberProperty p => p.Fields,
+ SparseVectorProperty p => p.Fields,
+ TextProperty p => p.Fields,
+ TokenCountProperty p => p.Fields,
+ UnsignedLongNumberProperty p => p.Fields,
+ VersionProperty p => p.Fields,
+ WildcardProperty p => p.Fields,
+ _ => null
+ };
+ }
+
public static TermsInclude AddValue(this TermsInclude include, string value)
{
if (include?.Values == null)
@@ -37,7 +113,7 @@ public static TermsExclude AddValue(this TermsExclude exclude, string value)
}
// TODO: Handle IFailureReason/BulkIndexByScrollFailure and other bulk response types.
- public static string GetErrorMessage(this IElasticsearchResponse elasticResponse, string message = null, bool normalize = false, bool includeResponse = false, bool includeDebugInformation = false)
+ public static string GetErrorMessage(this ElasticsearchResponse elasticResponse, string message = null, bool normalize = false, bool includeResponse = false, bool includeDebugInformation = false)
{
if (elasticResponse == null)
return String.Empty;
@@ -47,31 +123,30 @@ public static string GetErrorMessage(this IElasticsearchResponse elasticResponse
if (!String.IsNullOrEmpty(message))
sb.AppendLine(message);
- var response = elasticResponse as IResponse;
- if (includeDebugInformation && response?.DebugInformation != null)
- sb.AppendLine(response.DebugInformation);
+ if (includeDebugInformation && elasticResponse?.DebugInformation != null)
+ sb.AppendLine(elasticResponse.DebugInformation);
- if (response?.OriginalException != null)
- sb.AppendLine($"Original: [{response.OriginalException.GetType().Name}] {response.OriginalException.Message}");
+ if (elasticResponse.TryGetOriginalException(out var exception) && exception is not null)
+ sb.AppendLine($"Original: [{exception.GetType().Name}] {exception.Message}");
- if (response?.ServerError?.Error != null)
- sb.AppendLine($"Server Error (Index={response.ServerError.Error?.Index}): {response.ServerError.Error.Reason}");
+ if (elasticResponse.ElasticsearchServerError?.Error != null)
+ sb.AppendLine($"Server Error (Index={elasticResponse.ElasticsearchServerError.Error?.Index}): {elasticResponse.ElasticsearchServerError.Error.Reason}");
if (elasticResponse is BulkResponse bulkResponse)
sb.AppendLine($"Bulk: {String.Join("\r\n", bulkResponse.ItemsWithErrors.Select(i => i.Error))}");
- if (elasticResponse.ApiCall != null)
- sb.AppendLine($"[{elasticResponse.ApiCall.HttpStatusCode}] {elasticResponse.ApiCall.HttpMethod} {elasticResponse.ApiCall.Uri?.PathAndQuery}");
+ var apiCall = elasticResponse.ApiCallDetails;
+ if (apiCall is not null)
+ sb.AppendLine($"[{apiCall.HttpStatusCode}] {apiCall.HttpMethod} {apiCall.Uri?.PathAndQuery}");
- if (elasticResponse.ApiCall?.RequestBodyInBytes != null)
+ if (apiCall?.RequestBodyInBytes is not null)
{
- string body = Encoding.UTF8.GetString(elasticResponse.ApiCall?.RequestBodyInBytes);
+ string body = Encoding.UTF8.GetString(apiCall.RequestBodyInBytes);
if (normalize)
body = JsonUtility.Normalize(body);
sb.AppendLine(body);
}
- var apiCall = response.ApiCall;
if (includeResponse && apiCall.ResponseBodyInBytes != null && apiCall.ResponseBodyInBytes.Length > 0 && apiCall.ResponseBodyInBytes.Length < 20000)
{
string body = Encoding.UTF8.GetString(apiCall?.ResponseBodyInBytes);
@@ -88,52 +163,52 @@ public static string GetErrorMessage(this IElasticsearchResponse elasticResponse
return sb.ToString();
}
- public static string GetRequest(this IElasticsearchResponse elasticResponse, bool normalize = false, bool includeResponse = false, bool includeDebugInformation = false)
+ public static string GetRequest(this ElasticsearchResponse elasticResponse, bool normalize = false, bool includeResponse = false, bool includeDebugInformation = false)
{
return GetErrorMessage(elasticResponse, null, normalize, includeResponse, includeDebugInformation);
}
- public static async Task WaitForReadyAsync(this IElasticClient client, CancellationToken cancellationToken, ILogger logger = null)
+ public static async Task WaitForReadyAsync(this ElasticsearchClient client, CancellationToken cancellationToken, ILogger logger = null)
{
- var nodes = client.ConnectionSettings.ConnectionPool.Nodes.Select(n => n.Uri.ToString());
+ var nodes = client.ElasticsearchClientSettings.NodePool.Nodes.Select(n => n.Uri.ToString());
var startTime = DateTime.UtcNow;
while (!cancellationToken.IsCancellationRequested)
{
- var pingResponse = await client.PingAsync(ct: cancellationToken);
- if (pingResponse.IsValid)
+ var pingResponse = await client.PingAsync(cancellationToken);
+ if (pingResponse.IsValidResponse)
return true;
- if (logger != null && logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
+ if (logger != null && logger.IsEnabled(LogLevel.Information))
logger?.LogInformation("Waiting for Elasticsearch to be ready {Server} after {Duration:g}...", nodes, DateTime.UtcNow.Subtract(startTime));
await Task.Delay(1000, cancellationToken);
}
- if (logger != null && logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error))
+ if (logger != null && logger.IsEnabled(LogLevel.Error))
logger?.LogError("Unable to connect to Elasticsearch {Server} after attempting for {Duration:g}", nodes, DateTime.UtcNow.Subtract(startTime));
return false;
}
- public static bool WaitForReady(this IElasticClient client, CancellationToken cancellationToken, ILogger logger = null)
+ public static bool WaitForReady(this ElasticsearchClient client, CancellationToken cancellationToken, ILogger logger = null)
{
- var nodes = client.ConnectionSettings.ConnectionPool.Nodes.Select(n => n.Uri.ToString());
+ var nodes = client.ElasticsearchClientSettings.NodePool.Nodes.Select(n => n.Uri.ToString());
var startTime = DateTime.UtcNow;
while (!cancellationToken.IsCancellationRequested)
{
var pingResponse = client.Ping();
- if (pingResponse.IsValid)
+ if (pingResponse.IsValidResponse)
return true;
- if (logger != null && logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
+ if (logger != null && logger.IsEnabled(LogLevel.Information))
logger?.LogInformation("Waiting for Elasticsearch to be ready {Server} after {Duration:g}...", nodes, DateTime.UtcNow.Subtract(startTime));
Thread.Sleep(1000);
}
- if (logger != null && logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error))
+ if (logger != null && logger.IsEnabled(LogLevel.Error))
logger?.LogError("Unable to connect to Elasticsearch {Server} after attempting for {Duration:g}", nodes, DateTime.UtcNow.Subtract(startTime));
return false;
@@ -213,7 +288,6 @@ private static void Write(JsonElement element, Utf8JsonWriter writer)
default:
throw new NotImplementedException($"Kind: {element.ValueKind}");
-
}
}
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticMappingExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticMappingExtensions.cs
index d40836c4..c01507fb 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticMappingExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/ElasticMappingExtensions.cs
@@ -1,4 +1,7 @@
-namespace Nest;
+using Elastic.Clients.Elasticsearch.IndexManagement;
+using Elastic.Clients.Elasticsearch.Mapping;
+
+namespace Foundatio.Parsers.ElasticQueries.Extensions;
public static class ElasticMapping
{
@@ -15,7 +18,7 @@ public static TextPropertyDescriptor AddKeywordField(this TextPropertyDesc
///
public static TextPropertyDescriptor AddKeywordField(this TextPropertyDescriptor descriptor, string normalizer) where T : class
{
- return descriptor.Fields(f => f.Keyword(s => s.Name(KeywordFieldName).Normalizer(normalizer).IgnoreAbove(256)));
+ return descriptor.Fields(f => f.Keyword(KeywordFieldName, s => s.Normalizer(normalizer).IgnoreAbove(256)));
}
///
@@ -31,14 +34,14 @@ public static TextPropertyDescriptor AddKeywordField(this TextPropertyDesc
///
public static TextPropertyDescriptor AddSortField(this TextPropertyDescriptor descriptor, string normalizer = "sort") where T : class
{
- return descriptor.Fields(f => f.Keyword(s => s.Name(SortFieldName).Normalizer(normalizer).IgnoreAbove(256)));
+ return descriptor.Fields(f => f.Keyword(SortFieldName, s => s.Normalizer(normalizer).IgnoreAbove(256)));
}
public static TextPropertyDescriptor AddKeywordAndSortFields(this TextPropertyDescriptor descriptor, string sortNormalizer = "sort", string keywordNormalizer = null) where T : class
{
return descriptor.Fields(f => f
- .Keyword(s => s.Name(KeywordFieldName).Normalizer(keywordNormalizer).IgnoreAbove(256))
- .Keyword(s => s.Name(SortFieldName).Normalizer(sortNormalizer).IgnoreAbove(256)));
+ .Keyword(KeywordFieldName, s => s.Normalizer(keywordNormalizer).IgnoreAbove(256))
+ .Keyword(SortFieldName, s => s.Normalizer(sortNormalizer).IgnoreAbove(256)));
}
public static TextPropertyDescriptor AddKeywordAndSortFields(this TextPropertyDescriptor descriptor, bool keywordLowercase) where T : class
@@ -46,9 +49,9 @@ public static TextPropertyDescriptor AddKeywordAndSortFields(this TextProp
return descriptor.AddKeywordAndSortFields(keywordNormalizer: keywordLowercase ? "lowercase" : null);
}
- public static AnalysisDescriptor AddSortNormalizer(this AnalysisDescriptor descriptor)
+ public static IndexSettingsAnalysisDescriptor AddSortNormalizer(this IndexSettingsAnalysisDescriptor descriptor)
{
- return descriptor.Normalizers(d => d.Custom("sort", n => n.Filters("lowercase", "asciifolding")));
+ return descriptor.Normalizers(d => d.Custom("sort", n => n.Filter(["lowercase", "asciifolding"])));
}
public static string KeywordFieldName = "keyword";
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryNodeExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryNodeExtensions.cs
index 274108ae..e043af71 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryNodeExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryNodeExtensions.cs
@@ -1,27 +1,30 @@
-using System;
+using System;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Aggregations;
+using Elastic.Clients.Elasticsearch.Core.Search;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.LuceneQueries.Nodes;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Extensions;
public static class QueryNodeExtensions
{
private const string QueryKey = "@Query";
- public static Task GetQueryAsync(this IQueryNode node, Func> getDefaultValue = null)
+ public static Task GetQueryAsync(this IQueryNode node, Func> getDefaultValue = null)
{
if (!node.Data.TryGetValue(QueryKey, out object value))
{
if (getDefaultValue == null)
- return Task.FromResult(null);
+ return Task.FromResult(null);
return getDefaultValue?.Invoke();
}
- return Task.FromResult(value as QueryBase);
+ return Task.FromResult(value as Query);
}
- public static void SetQuery(this IQueryNode node, QueryBase container)
+ public static void SetQuery(this IQueryNode node, Query container)
{
node.Data[QueryKey] = container;
}
@@ -42,20 +45,20 @@ public static SourceFilter GetSourceFilter(this IQueryNode node, Func GetAggregationAsync(this IQueryNode node, Func> getDefaultValue = null)
+ public static Task GetAggregationAsync(this IQueryNode node, Func> getDefaultValue = null)
{
if (!node.Data.TryGetValue(AggregationKey, out object value))
{
if (getDefaultValue == null)
- return Task.FromResult(null);
+ return Task.FromResult(null);
return getDefaultValue?.Invoke();
}
- return Task.FromResult(value as AggregationBase);
+ return Task.FromResult(value as AggregationMap);
}
- public static void SetAggregation(this IQueryNode node, AggregationBase aggregation)
+ public static void SetAggregation(this IQueryNode node, AggregationMap aggregation)
{
node.Data[AggregationKey] = aggregation;
}
@@ -66,15 +69,15 @@ public static void RemoveAggregation(this IQueryNode node)
}
private const string SortKey = "@Sort";
- public static IFieldSort GetSort(this IQueryNode node, Func getDefaultValue = null)
+ public static SortOptions GetSort(this IQueryNode node, Func getDefaultValue = null)
{
if (!node.Data.TryGetValue(SortKey, out object value))
return getDefaultValue?.Invoke();
- return value as IFieldSort;
+ return value as SortOptions;
}
- public static void SetSort(this IQueryNode node, IFieldSort sort)
+ public static void SetSort(this IQueryNode node, SortOptions sort)
{
node.Data[SortKey] = sort;
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryVisitorContextExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryVisitorContextExtensions.cs
index d7c65606..64310b2b 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryVisitorContextExtensions.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Extensions/QueryVisitorContextExtensions.cs
@@ -1,10 +1,10 @@
using System;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Extensions;
diff --git a/src/Foundatio.Parsers.ElasticQueries/Extensions/SearchDescriptorExtensions.cs b/src/Foundatio.Parsers.ElasticQueries/Extensions/SearchDescriptorExtensions.cs
deleted file mode 100644
index f462b798..00000000
--- a/src/Foundatio.Parsers.ElasticQueries/Extensions/SearchDescriptorExtensions.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-using Nest;
-
-namespace Foundatio.Parsers.ElasticQueries.Extensions;
-
-public static class SearchDescriptorExtensions
-{
- public static SearchDescriptor Aggregations(this SearchDescriptor descriptor, AggregationContainer aggregations) where T : class
- {
- descriptor.Aggregations(f =>
- {
- ((IAggregationContainer)f).Aggregations = aggregations.Aggregations;
- return f;
- });
-
- return descriptor;
- }
-
- public static SearchDescriptor Sort(this SearchDescriptor descriptor, IEnumerable sorts) where T : class
- {
- var searchRequest = descriptor as ISearchRequest;
-
- foreach (var sort in sorts)
- {
- if (searchRequest.Sort == null)
- searchRequest.Sort = new List();
-
- searchRequest.Sort.Add(sort);
- }
-
- return descriptor;
- }
-}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Foundatio.Parsers.ElasticQueries.csproj b/src/Foundatio.Parsers.ElasticQueries/Foundatio.Parsers.ElasticQueries.csproj
index ab406572..4d1bc5b4 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Foundatio.Parsers.ElasticQueries.csproj
+++ b/src/Foundatio.Parsers.ElasticQueries/Foundatio.Parsers.ElasticQueries.csproj
@@ -1,8 +1,8 @@
-
+
+
-
diff --git a/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineAggregationsVisitor.cs b/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineAggregationsVisitor.cs
index ba7985d4..fd0ee869 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineAggregationsVisitor.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineAggregationsVisitor.cs
@@ -2,11 +2,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Aggregations;
+using Elastic.Clients.Elasticsearch.Core.Search;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Visitors;
@@ -20,7 +22,7 @@ public override async Task VisitAsync(GroupNode node, IQueryVisitorContext conte
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
var container = await GetParentContainerAsync(node, context);
- var termsAggregation = container as ITermsAggregation;
+ var termsAggregation = container.Value as TermsAggregation;
foreach (var child in node.Children.OfType())
{
@@ -49,11 +51,11 @@ public override async Task VisitAsync(GroupNode node, IQueryVisitorContext conte
if (!String.IsNullOrEmpty(termNode.Term) && Int32.TryParse(termNode.UnescapedTerm, out int parsedMinCount))
minCount = parsedMinCount;
- termsAggregation.MinimumDocumentCount = minCount;
+ termsAggregation.MinDocCount = minCount;
}
}
- if (termNode != null && container is ITopHitsAggregation topHitsAggregation)
+ if (termNode != null && container.Value is TopHitsAggregation topHitsAggregation)
{
var filter = node.GetSourceFilter(() => new SourceFilter());
if (termNode.Field == "@exclude")
@@ -70,10 +72,10 @@ public override async Task VisitAsync(GroupNode node, IQueryVisitorContext conte
else
filter.Includes.And(termNode.UnescapedTerm);
}
- topHitsAggregation.Source = filter;
+ topHitsAggregation.Source = new SourceConfig(filter);
}
- if (termNode != null && container is IDateHistogramAggregation dateHistogramAggregation)
+ if (termNode != null && container.Value is DateHistogramAggregation dateHistogramAggregation)
{
if (termNode.Field == "@missing")
{
@@ -92,24 +94,15 @@ public override async Task VisitAsync(GroupNode node, IQueryVisitorContext conte
continue;
}
- if (container is BucketAggregationBase bucketContainer)
+ if (container.Value.IsBucketAggregation())
{
- if (bucketContainer.Aggregations == null)
- bucketContainer.Aggregations = new AggregationDictionary();
-
- bucketContainer.Aggregations[((IAggregation)aggregation).Name] = (AggregationContainer)aggregation;
+ container.Aggregations.Add(aggregation);
}
- if (termsAggregation != null && (child.Prefix == "-" || child.Prefix == "+"))
+ if (termsAggregation != null && child.Prefix is "-" or "+")
{
- if (termsAggregation.Order == null)
- termsAggregation.Order = new List();
-
- termsAggregation.Order.Add(new TermsOrder
- {
- Key = ((IAggregation)aggregation).Name,
- Order = child.Prefix == "-" ? SortOrder.Descending : SortOrder.Ascending
- });
+ termsAggregation.Order ??= new List>();
+ termsAggregation.Order.Add(new KeyValuePair(aggregation.Name, child.Prefix == "-" ? SortOrder.Desc : SortOrder.Asc));
}
}
@@ -117,9 +110,9 @@ public override async Task VisitAsync(GroupNode node, IQueryVisitorContext conte
node.SetAggregation(container);
}
- private async Task GetParentContainerAsync(IQueryNode node, IQueryVisitorContext context)
+ private async Task GetParentContainerAsync(IQueryNode node, IQueryVisitorContext context)
{
- AggregationBase container = null;
+ AggregationMap container = null;
var currentNode = node;
while (container == null && currentNode != null)
{
@@ -141,7 +134,7 @@ private async Task GetParentContainerAsync(IQueryNode node, IQu
if (container == null)
{
- container = new ChildrenAggregation(null, null);
+ container = new AggregationMap(null, null);
currentNode.SetAggregation(container);
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineQueriesVisitor.cs b/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineQueriesVisitor.cs
index 390b5609..12de4c9c 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineQueriesVisitor.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Visitors/CombineQueriesVisitor.cs
@@ -1,11 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Visitors;
@@ -24,10 +24,9 @@ public override async Task VisitAsync(GroupNode node, IQueryVisitorContext conte
if (context is not IElasticQueryVisitorContext elasticContext)
throw new ArgumentException("Context must be of type IElasticQueryVisitorContext", nameof(context));
- QueryBase query = await node.GetQueryAsync(() => node.GetDefaultQueryAsync(context)).ConfigureAwait(false);
- QueryBase container = query;
- var nested = query as NestedQuery;
- if (nested != null && node.Parent != null)
+ Query query = await node.GetQueryAsync(() => node.GetDefaultQueryAsync(context)).ConfigureAwait(false);
+ Query container = query;
+ if (query.TryGet(out var nested) && node.Parent != null)
container = null;
foreach (var child in node.Children.OfType())
diff --git a/src/Foundatio.Parsers.ElasticQueries/Visitors/GeoVisitor.cs b/src/Foundatio.Parsers.ElasticQueries/Visitors/GeoVisitor.cs
index e754808b..576529af 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Visitors/GeoVisitor.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Visitors/GeoVisitor.cs
@@ -1,10 +1,11 @@
using System;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.LuceneQueries;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Visitors;
@@ -23,7 +24,7 @@ public override async Task VisitAsync(TermNode node, IQueryVisitorContext contex
return;
string location = _resolveGeoLocation != null ? await _resolveGeoLocation(node.Term).ConfigureAwait(false) ?? node.Term : node.Term;
- var query = new GeoDistanceQuery { Field = node.Field, Location = location, Distance = node.Proximity ?? Distance.Miles(10) };
+ var query = new GeoDistanceQuery { Field = node.Field, Location = location, Distance = node.Proximity ?? "10mi" };
node.SetQuery(query);
}
@@ -32,7 +33,7 @@ public override void Visit(TermRangeNode node, IQueryVisitorContext context)
if (context is not IElasticQueryVisitorContext elasticContext || !elasticContext.MappingResolver.IsGeoPropertyType(node.Field))
return;
- var box = new BoundingBox { TopLeft = node.Min, BottomRight = node.Max };
+ var box = GeoBounds.TopLeftBottomRight(new TopLeftBottomRightGeoBounds { TopLeft = node.Min, BottomRight = node.Max });
var query = new GeoBoundingBoxQuery { BoundingBox = box, Field = node.Field };
node.SetQuery(query);
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Visitors/GetSortFieldsVisitor.cs b/src/Foundatio.Parsers.ElasticQueries/Visitors/GetSortFieldsVisitor.cs
index 6fb2f28b..e10a54c9 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Visitors/GetSortFieldsVisitor.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Visitors/GetSortFieldsVisitor.cs
@@ -1,16 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Visitors;
-public class GetSortFieldsVisitor : QueryNodeVisitorWithResultBase>
+public class GetSortFieldsVisitor : QueryNodeVisitorWithResultBase>
{
- private readonly List _fields = new();
+ private readonly List _fields = new();
public override void Visit(TermNode node, IQueryVisitorContext context)
{
@@ -18,24 +18,29 @@ public override void Visit(TermNode node, IQueryVisitorContext context)
return;
var sort = node.GetSort(() => node.GetDefaultSort(context));
- if (sort.SortKey == null)
+ // TODO: SortKey https://github.com/elastic/elasticsearch-net/issues/8335
+ // if (sort.SortKey == null)
+ // return;
+
+ object additionalPropertyName = sort.GetType().GetProperty("AdditionalPropertyName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(sort);
+ if (additionalPropertyName is null)
return;
_fields.Add(sort);
}
- public override async Task> AcceptAsync(IQueryNode node, IQueryVisitorContext context)
+ public override async Task> AcceptAsync(IQueryNode node, IQueryVisitorContext context)
{
await node.AcceptAsync(this, context).ConfigureAwait(false);
return _fields;
}
- public static Task> RunAsync(IQueryNode node, IQueryVisitorContext context = null)
+ public static Task> RunAsync(IQueryNode node, IQueryVisitorContext context = null)
{
return new GetSortFieldsVisitor().AcceptAsync(node, context);
}
- public static IEnumerable Run(IQueryNode node, IQueryVisitorContext context = null)
+ public static ICollection Run(IQueryNode node, IQueryVisitorContext context = null)
{
return RunAsync(node, context).GetAwaiter().GetResult();
}
diff --git a/src/Foundatio.Parsers.ElasticQueries/Visitors/NestedVisitor.cs b/src/Foundatio.Parsers.ElasticQueries/Visitors/NestedVisitor.cs
index 95feaf04..037414ad 100644
--- a/src/Foundatio.Parsers.ElasticQueries/Visitors/NestedVisitor.cs
+++ b/src/Foundatio.Parsers.ElasticQueries/Visitors/NestedVisitor.cs
@@ -1,10 +1,10 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch.QueryDsl;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
-using Nest;
namespace Foundatio.Parsers.ElasticQueries.Visitors;
diff --git a/tests/Foundatio.Parsers.ElasticQueries.Tests/AggregationParserTests.cs b/tests/Foundatio.Parsers.ElasticQueries.Tests/AggregationParserTests.cs
index d8d9ac89..9c5e2e3e 100644
--- a/tests/Foundatio.Parsers.ElasticQueries.Tests/AggregationParserTests.cs
+++ b/tests/Foundatio.Parsers.ElasticQueries.Tests/AggregationParserTests.cs
@@ -2,13 +2,16 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Elastic.Clients.Elasticsearch;
+using Elastic.Clients.Elasticsearch.Aggregations;
+using Elastic.Clients.Elasticsearch.Core.Search;
+using Elastic.Clients.Elasticsearch.Mapping;
using Foundatio.Parsers.ElasticQueries.Extensions;
using Foundatio.Parsers.ElasticQueries.Visitors;
using Foundatio.Parsers.LuceneQueries;
using Foundatio.Parsers.LuceneQueries.Extensions;
using Foundatio.Parsers.LuceneQueries.Visitors;
using Microsoft.Extensions.Logging;
-using Nest;
using Xunit;
using Xunit.Abstractions;
@@ -21,44 +24,106 @@ public AggregationParserTests(ITestOutputHelper output, ElasticsearchFixture fix
[Fact]
public async Task ProcessSingleAggregationAsync()
{
- string index = CreateRandomIndex(d => d.Dynamic().Properties(p => p.GeoPoint(g => g.Name(f => f.Field3))));
+ string index = await CreateRandomIndexAsync(d => d.Dynamic(DynamicMapping.True).Properties(p => p.GeoPoint(g => g.Field3)));
await Client.IndexManyAsync([
- new MyType { Field1 = "value1", Field4 = 1, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)), Field2 = "field2" },
- new MyType { Field1 = "value2", Field4 = 2, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4)) },
- new MyType { Field1 = "value3", Field4 = 3, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3)) },
- new MyType { Field1 = "value4", Field4 = 4, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2)) },
- new MyType { Field1 = "value5", Field4 = 5, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1)) }
+ new MyType
+ {
+ Field1 = "value1",
+ Field4 = 1,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)),
+ Field2 = "field2"
+ },
+ new MyType
+ {
+ Field1 = "value2",
+ Field4 = 2,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4))
+ },
+ new MyType
+ {
+ Field1 = "value3",
+ Field4 = 3,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3))
+ },
+ new MyType
+ {
+ Field1 = "value4",
+ Field4 = 4,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2))
+ },
+ new MyType
+ {
+ Field1 = "value5",
+ Field4 = 5,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1))
+ }
], index);
await Client.Indices.RefreshAsync(index);
var processor = new ElasticQueryParser(c => c.SetLoggerFactory(Log).UseMappings(Client, index).UseGeo(_ => "51.5032520,-0.1278990"));
var aggregations = await processor.BuildAggregationsAsync("min:field4");
- var actualResponse = Client.Search(d => d.Index(index).Aggregations(aggregations));
+ var actualResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(aggregations));
string actualRequest = actualResponse.GetRequest();
_logger.LogInformation("Actual: {Request}", actualRequest);
- var expectedResponse = Client.Search(d => d.Index(index).Aggregations(a => a
- .Min("min_field4", c => c.Field("field4").Meta(m => m.Add("@field_type", "long")))));
+ var expectedResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(a => a
+ .Add("min_field4", a1 => a1.Min(c => c.Field("field4")).Meta(m => m.Add("@field_type", "long")))));
string expectedRequest = expectedResponse.GetRequest();
_logger.LogInformation("Expected: {Request}", expectedRequest);
Assert.Equal(expectedRequest, actualRequest);
- Assert.True(actualResponse.IsValid);
- Assert.True(expectedResponse.IsValid);
+ Assert.True(actualResponse.IsValidResponse);
+ Assert.True(expectedResponse.IsValidResponse);
Assert.Equal(expectedResponse.Total, actualResponse.Total);
}
[Fact]
public async Task ProcessSingleAggregationWithAliasAsync()
{
- string index = CreateRandomIndex(d => d.Dynamic().Properties(p => p.GeoPoint(g => g.Name(f => f.Field3))));
+ string index = await CreateRandomIndexAsync(d => d.Dynamic(DynamicMapping.True).Properties(p => p.GeoPoint(g => g.Field3)));
await Client.IndexManyAsync([
- new MyType { Field1 = "value1", Field4 = 1, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)), Field2 = "field2" },
- new MyType { Field1 = "value2", Field4 = 2, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4)) },
- new MyType { Field1 = "value3", Field4 = 3, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3)) },
- new MyType { Field1 = "value4", Field4 = 4, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2)) },
- new MyType { Field1 = "value5", Field4 = 5, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1)) }
+ new MyType
+ {
+ Field1 = "value1",
+ Field4 = 1,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)),
+ Field2 = "field2"
+ },
+ new MyType
+ {
+ Field1 = "value2",
+ Field4 = 2,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4))
+ },
+ new MyType
+ {
+ Field1 = "value3",
+ Field4 = 3,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3))
+ },
+ new MyType
+ {
+ Field1 = "value4",
+ Field4 = 4,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2))
+ },
+ new MyType
+ {
+ Field1 = "value5",
+ Field4 = 5,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1))
+ }
], index);
await Client.Indices.RefreshAsync(index);
@@ -66,35 +131,66 @@ await Client.IndexManyAsync([
var processor = new ElasticQueryParser(c => c.SetLoggerFactory(Log).UseMappings(Client, index).UseFieldMap(fieldMap).UseGeo(_ => "51.5032520,-0.1278990"));
var aggregations = await processor.BuildAggregationsAsync("min:heynow");
- var actualResponse = Client.Search(d => d.Index(index).Aggregations(aggregations));
+ var actualResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(aggregations));
string actualRequest = actualResponse.GetRequest();
_logger.LogInformation("Actual: {Request}", actualRequest);
- var expectedResponse = Client.Search(d => d.Index(index).Aggregations(a => a
- .Min("min_heynow", c => c.Field("field4").Meta(m => m.Add("@field_type", "long")))));
+ var expectedResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(a => a
+ .Add("min_heynow", a1 => a1.Min(c => c.Field(cf => cf.Field4)).Meta(m => m.Add("@field_type", "long")))));
string expectedRequest = expectedResponse.GetRequest();
_logger.LogInformation("Expected: {Request}", expectedRequest);
Assert.Equal(expectedRequest, actualRequest);
- Assert.True(actualResponse.IsValid);
- Assert.True(expectedResponse.IsValid);
+ Assert.True(actualResponse.IsValidResponse);
+ Assert.True(expectedResponse.IsValidResponse);
Assert.Equal(expectedResponse.Total, actualResponse.Total);
}
[Fact]
public async Task ProcessAnalyzedAggregationWithAliasAsync()
{
- string index = CreateRandomIndex(d => d.Dynamic().Properties(p => p
- .Text(f => f.Name(n => n.Field1)
- .Fields(k => k.Keyword(m => m.Name("keyword"))))
- .FieldAlias(f => f.Name("heynow").Path(k => k.Field1))
- .GeoPoint(g => g.Name(f => f.Field3))));
+ string index = await CreateRandomIndexAsync(d => d.Dynamic(DynamicMapping.True).Properties(p => p
+ .Text(f => f.Field1, o => o
+ .Fields(k => k.Keyword("keyword")))
+ .FieldAlias("heynow", o => o.Path(k => k.Field1))
+ .GeoPoint(g => g.Field3)));
await Client.IndexManyAsync([
- new MyType { Field1 = "value1", Field4 = 1, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)), Field2 = "field2" },
- new MyType { Field1 = "value2", Field4 = 2, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4)) },
- new MyType { Field1 = "value3", Field4 = 3, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3)) },
- new MyType { Field1 = "value4", Field4 = 4, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2)) },
- new MyType { Field1 = "value5", Field4 = 5, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1)) }
+ new MyType
+ {
+ Field1 = "value1",
+ Field4 = 1,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)),
+ Field2 = "field2"
+ },
+ new MyType
+ {
+ Field1 = "value2",
+ Field4 = 2,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4))
+ },
+ new MyType
+ {
+ Field1 = "value3",
+ Field4 = 3,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3))
+ },
+ new MyType
+ {
+ Field1 = "value4",
+ Field4 = 4,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2))
+ },
+ new MyType
+ {
+ Field1 = "value5",
+ Field4 = 5,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1))
+ }
], index);
await Client.Indices.RefreshAsync(index);
@@ -102,72 +198,106 @@ await Client.IndexManyAsync([
var processor = new ElasticQueryParser(c => c.SetLoggerFactory(Log).UseMappings(Client, index).UseFieldMap(fieldMap));
var aggregations = await processor.BuildAggregationsAsync("terms:heynow");
- var actualResponse = Client.Search(d => d.Index(index).Aggregations(aggregations));
+ var actualResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(aggregations));
string actualRequest = actualResponse.GetRequest();
_logger.LogInformation("Actual: {Request}", actualRequest);
- var expectedResponse = Client.Search(d => d.Index(index).Aggregations(a => a
- .Terms("terms_heynow", c => c.Field("field1.keyword").Meta(m => m.Add("@field_type", "text")))));
+ var expectedResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(a => a
+ .Add("terms_heynow", a1 => a1.Terms(c => c.Field("field1.keyword")).Meta(m => m.Add("@field_type", "text")))));
string expectedRequest = expectedResponse.GetRequest();
_logger.LogInformation("Expected: {Request}", expectedRequest);
Assert.Equal(expectedRequest, actualRequest);
- Assert.True(actualResponse.IsValid);
- Assert.True(expectedResponse.IsValid);
+ Assert.True(actualResponse.IsValidResponse);
+ Assert.True(expectedResponse.IsValidResponse);
Assert.Equal(expectedResponse.Total, actualResponse.Total);
}
[Fact]
public async Task ProcessAggregationsAsync()
{
- string index = CreateRandomIndex(d => d.Dynamic().Properties(p => p.GeoPoint(g => g.Name(f => f.Field3))));
+ string index = await CreateRandomIndexAsync(d => d.Dynamic(DynamicMapping.True).Properties(p => p.GeoPoint(g => g.Field3)));
await Client.IndexManyAsync([
- new MyType { Field1 = "value1", Field4 = 1, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)), Field2 = "field2" },
- new MyType { Field1 = "value2", Field4 = 2, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4)) },
- new MyType { Field1 = "value3", Field4 = 3, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3)) },
- new MyType { Field1 = "value4", Field4 = 4, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2)) },
- new MyType { Field1 = "value5", Field4 = 5, Field3 = "51.5032520,-0.1278990", Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1)) }
+ new MyType
+ {
+ Field1 = "value1",
+ Field4 = 1,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)),
+ Field2 = "field2"
+ },
+ new MyType
+ {
+ Field1 = "value2",
+ Field4 = 2,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(4))
+ },
+ new MyType
+ {
+ Field1 = "value3",
+ Field4 = 3,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(3))
+ },
+ new MyType
+ {
+ Field1 = "value4",
+ Field4 = 4,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(2))
+ },
+ new MyType
+ {
+ Field1 = "value5",
+ Field4 = 5,
+ Field3 = "51.5032520,-0.1278990",
+ Field5 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(1))
+ }
], index);
await Client.Indices.RefreshAsync(index);
var processor = new ElasticQueryParser(c => c.SetLoggerFactory(Log).UseMappings(Client, index).UseGeo(_ => "51.5032520,-0.1278990"));
var aggregations = await processor.BuildAggregationsAsync("min:field4 max:field4 avg:field4 sum:field4 percentiles:field4~50,100 cardinality:field4 missing:field2 date:field5 histogram:field4 geogrid:field3 terms:field1");
- var actualResponse = Client.Search(d => d.Index(index).Aggregations(aggregations));
+ var actualResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(aggregations));
string actualRequest = actualResponse.GetRequest();
_logger.LogInformation("Actual: {Request}", actualRequest);
- var expectedResponse = Client.Search(d => d.Index(index).Aggregations(a => a
- .GeoHash("geogrid_field3", h => h.Field("field3").GeoHashPrecision(GeoHashPrecision.Precision1)
- .Aggregations(a1 => a1.Average("avg_lat", s => s.Script(ss => ss.Source("doc['field3'].lat"))).Average("avg_lon", s => s.Script(ss => ss.Source("doc['field3'].lon")))))
- .Terms("terms_field1", t => t.Field("field1.keyword").Meta(m => m.Add("@field_type", "text")))
- .Histogram("histogram_field4", h => h.Field("field4").Interval(50).MinimumDocumentCount(0))
- .DateHistogram("date_field5", d1 => d1.Field("field5").CalendarInterval(DateInterval.Day).Format("date_optional_time").MinimumDocumentCount(0))
- .Missing("missing_field2", t => t.Field("field2.keyword"))
- .Cardinality("cardinality_field4", c => c.Field("field4"))
- .Percentiles("percentiles_field4", c => c.Field("field4").Percents(50, 100))
- .Sum("sum_field4", c => c.Field("field4").Meta(m => m.Add("@field_type", "long")))
- .Average("avg_field4", c => c.Field("field4").Meta(m => m.Add("@field_type", "long")))
- .Max("max_field4", c => c.Field("field4").Meta(m => m.Add("@field_type", "long")))
- .Min("min_field4", c => c.Field("field4").Meta(m => m.Add("@field_type", "long")))));
+ var expectedResponse = await Client.SearchAsync(d => d.Index(index).Aggregations(a => a
+ .Add("geogrid_field3", a1 => a1.GeohashGrid(h => h.Field(f => f.Field3).Precision(new GeohashPrecision(1)))
+ .Aggregations(a2 => a2
+ .Add("avg_lat", a3 => a3.Avg(s => s.Script(ss => ss.Source("doc['field3'].lat"))))
+ .Add("avg_lon", a3 => a3.Avg(s => s.Script(ss => ss.Source("doc['field3'].lon"))))
+ ))
+ .Add("terms_field1", a1 => a1.Terms(t => t.Field("field1.keyword")).Meta(m => m.Add("@field_type", "text")))
+ .Add("histogram_field4", a1 => a1.Histogram(h => h.Field(f => f.Field4).Interval(50).MinDocCount(0)))
+ .Add("date_field5", a1 => a1.DateHistogram(d1 => d1.Field(f => f.Field5).CalendarInterval(CalendarInterval.Day).Format("date_optional_time").MinDocCount(0)))
+ .Add("missing_field2", a1 => a1.Missing(t => t.Field("field2.keyword")))
+ .Add("cardinality_field4", a1 => a1.Cardinality(c => c.Field(f => f.Field4)))
+ .Add("percentiles_field4", a1 => a1.Percentiles(c => c.Field(f => f.Field4).Percents([50, 100])))
+ .Add("sum_field4", a1 => a1.Sum(c => c.Field(f => f.Field4)).Meta(m => m.Add("@field_type", "long")))
+ .Add("avg_field4", a1 => a1.Avg(c => c.Field(f => f.Field4)).Meta(m => m.Add("@field_type", "long")))
+ .Add("max_field4", a1 => a1.Max(c => c.Field(f => f.Field4)).Meta(m => m.Add("@field_type", "long")))
+ .Add("min_field4", a1 => a1.Min(c => c.Field(f => f.Field4)).Meta(m => m.Add("@field_type", "long")))));
string expectedRequest = expectedResponse.GetRequest();
_logger.LogInformation("Expected: {Request}", expectedRequest);
Assert.Equal(expectedRequest, actualRequest);
- Assert.True(actualResponse.IsValid);
- Assert.True(expectedResponse.IsValid);
+ Assert.True(actualResponse.IsValidResponse);
+ Assert.True(expectedResponse.IsValidResponse);
Assert.Equal(expectedResponse.Total, actualResponse.Total);
}
[Fact]
public async Task ProcessNestedAggregationsWithAliasesAsync()
{
- string index = CreateRandomIndex(d => d.Dynamic().Properties(p => p
- .GeoPoint(g => g.Name(f => f.Field3))
- .Object>(o1 => o1.Name(f1 => f1.Data).Properties(p1 => p1
- .Object