Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using keyword suffix when sort or group properties with Keyword attribute #15

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion LinqToElasticSearch.IntegrationTests/IntegrationTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,91 @@ protected IntegrationTestsBase()
ElasticClient.Indices.Delete(IndexName);
}

ElasticClient.Indices.Create(IndexName, d => d.Settings(descriptor => descriptor).Map(m => m.AutoMap<T>()));
ElasticClient.Indices.Create(IndexName, d => d.Settings(descriptor => descriptor).Map(m =>
{
m.Properties(prop => prop
.Number(sprop =>
{
sprop.Type(NumberType.Integer);
return sprop.Name("age");
}));

m.Properties(prop => prop
.Boolean(sprop => sprop.Name("can")));

m.Properties(prop => prop
.Text(sprop =>
{
sprop.Fields(f =>
f.Keyword(k => k.IgnoreAbove(256).Name("keyword")));
return sprop.Name("countryCode");
}));

m.Properties(prop => prop
.Date(sprop => sprop.Name("date")));

m.Properties(prop => prop
.Date(sprop => sprop.Name("date1")));

m.Properties(prop => prop
.Keyword(sprop => sprop.Name("emails")));

m.Properties(prop => prop
.Number(sprop => sprop.Name("enumNullable")));

m.Properties(prop => prop
.Keyword(sprop => sprop.Name("folderId")));

m.Properties(prop => prop
.Keyword(sprop => sprop.Name("id")));

m.Properties(prop => prop
.Text(sprop =>
{
sprop.Fields(f =>
f.Keyword(k => k.IgnoreAbove(256).Name("keyword")));
return sprop.Name("lastName");
}));

m.Properties(prop => prop
.Text(sprop =>
{
sprop.Fields(f =>
f.Keyword(k => k.IgnoreAbove(256).Name("keyword")));
return sprop
.Name("name");
}));


m.Properties(prop => prop
.Number(sprop =>
{
sprop.Type(NumberType.Integer);
return sprop.Name("sampleTypeProperty");
}));

m.Properties(prop => prop
.Keyword(sprop => sprop.Name("sampleTypePropertyString")));

m.Properties(prop => prop
.Number(sprop =>
{
sprop.Type(NumberType.Long);
return sprop.Name("timeSpan");
}));

m.Properties(prop => prop
.Number(sprop =>
{
sprop.Type(NumberType.Long);
return sprop.Name("timeSpanNullable");
}));

m.Properties(prop => prop
.Keyword(sprop => sprop.Name("typeId")));

return m;
}));

Sut = new ElasticQueryable<T>(ElasticClient, IndexName);

Expand Down
34 changes: 34 additions & 0 deletions LinqToElasticSearch.IntegrationTests/MainFrom/MainFromTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -743,5 +743,39 @@ public void ShouldOptimizeBoolQueries()
// Then
results.Should().HaveCount(30);
}

[Fact]
public void ShouldUseKeywordSuffixWhenOrderByPropertyWithKeywordAttribute()
{
// Given
var samples = Fixture.CreateMany<SampleData>(35).ToList();
Bulk(samples);
ElasticClient.Indices.Refresh();

// When
var result = Sut.OrderBy(x => x.CountryCode).ToList();

// Then
result.Should().HaveCount(35);
}

[Fact]
public void ShouldUseKeywordSuffixWhenGroupByPropertyWithKeywordAttribute()
{
// Given
var samples = Fixture.CreateMany<SampleData>(4).ToList();
samples[0].CountryCode = samples[2].CountryCode;
samples[1].CountryCode = samples[3].CountryCode;

Bulk(samples);
ElasticClient.Indices.Refresh();

// When
var result = Sut.GroupBy(x => x.CountryCode).ToList();

// Then
result.Should().HaveCount(2);
}

}
}
1 change: 1 addition & 0 deletions LinqToElasticSearch.IntegrationTests/SampleData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class SampleData
public TimeSpan? TimeSpanNullable { get; set; }
[Keyword] public IList<string> Emails { get; set; }
[StringEnum] [Keyword] public SampleType SampleTypePropertyString { get; set; }
[Keyword]public int CountryCode { get; set; }
}

public enum SampleType
Expand Down
12 changes: 7 additions & 5 deletions LinqToElasticSearch/ElasticGeneratorQueryModelVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,24 @@ protected override void VisitResultOperators(ObservableCollection<ResultOperator

if (resultOperator is GroupResultOperator groupResultOperator)
{
var members = new List<Tuple<string, Type>>();
var members = new List<Tuple<string, Type, bool>>();

switch (groupResultOperator.KeySelector)
{
case MemberExpression memberExpression:
members.Add(new Tuple<string, Type>(memberExpression.Member.Name, memberExpression.Type));
var isKeyword = memberExpression.Member.CustomAttributes.Any(x => x.AttributeType == typeof(KeywordAttribute));
members.Add(new Tuple<string, Type, bool>(memberExpression.Member.Name, memberExpression.Type, isKeyword));
break;
case NewExpression newExpression:
members.AddRange(newExpression.Arguments
.Cast<MemberExpression>()
.Select(memberExpression => new Tuple<string, Type>(memberExpression.Member.Name, memberExpression.Type)));
.Select(memberExpression => new Tuple<string, Type, bool>(memberExpression.Member.Name, memberExpression.Type, false)));
break;
}

members.ForEach(property =>
{
QueryAggregator.GroupByExpressions.Add(new GroupByProperties(property.Item1, property.Item2));
QueryAggregator.GroupByExpressions.Add(new GroupByProperties(property.Item1, property.Item2, property.Item3));
});
}
}
Expand All @@ -121,7 +122,8 @@ public override void VisitOrderByClause(OrderByClause orderByClause, QueryModel
var direction = orderByClause.Orderings[0].OrderingDirection;
var propertyName = memberExpression.Member.Name;
var type = memberExpression.Type;
QueryAggregator.OrderByExpressions.Add(new OrderProperties(propertyName, type, direction));
var isKeyword = memberExpression.Member.CustomAttributes.Any(x => x.AttributeType == typeof(KeywordAttribute));
QueryAggregator.OrderByExpressions.Add(new OrderProperties(propertyName, type, direction, isKeyword));
}

base.VisitOrderByClause(orderByClause, queryModel, index);
Expand Down
3 changes: 2 additions & 1 deletion LinqToElasticSearch/ElasticQueryExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ private dynamic GenerateKey(CompositeKey ck, Type keyGenerics)
var date = (long) ck.Values.First();
return FormatDateTimeKey(date);
}
return ck.Values.First();

return Convert.ChangeType(ck.Values.First(), keyGenerics);
}

IDictionary<string, object> expando = new ExpandoObject();
Expand Down
12 changes: 8 additions & 4 deletions LinqToElasticSearch/QueryAggregator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,41 @@ public class QueryAggregator
public class OrderProperties
{
public readonly Type PropertyType;
private readonly bool _isKeyword;

public string PropertyName { get; set; }
public OrderingDirection OrderingDirection { get; set; }

public OrderProperties(string propertyName, Type propertyType, OrderingDirection direction)
public OrderProperties(string propertyName, Type propertyType, OrderingDirection direction, bool isKeyword)
{
PropertyType = propertyType;
_isKeyword = isKeyword;
PropertyName = propertyName;
OrderingDirection = direction;
}

public string GetKeywordIfNecessary()
{
return PropertyType.Name.ToLower().Contains("string") ? ".keyword" : "";
return _isKeyword || PropertyType.Name.ToLower().Contains("string") ? ".keyword" : "";
}
}

public class GroupByProperties
{
private readonly bool _isKeyword;
public string PropertyName { get; }
public Type PropertyType { get; set; }

public GroupByProperties(string propertyName, Type propertyType)
public GroupByProperties(string propertyName, Type propertyType, bool isKeyword)
{
_isKeyword = isKeyword;
PropertyName = propertyName;
PropertyType = propertyType;
}

public string GetKeywordIfNecessary()
{
return PropertyType.Name.ToLower().Contains("string") ? ".keyword" : "";
return _isKeyword || PropertyType.Name.ToLower().Contains("string") ? ".keyword" : "";
}
}
}