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

Add support for weighted average aggregation #3417

Merged
merged 4 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions src/Nest/Aggregations/AggregateDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ internal static string[] TypedKeyTokens(string key)

public ValueAggregate SerialDifferencing(string key) => this.TryGet<ValueAggregate>(key);

public ValueAggregate WeightedAverage(string key) => this.TryGet<ValueAggregate>(key);

public KeyedValueAggregate MaxBucket(string key) => this.TryGet<KeyedValueAggregate>(key);

public KeyedValueAggregate MinBucket(string key) => this.TryGet<KeyedValueAggregate>(key);
Expand Down
11 changes: 11 additions & 0 deletions src/Nest/Aggregations/AggregationContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ public interface IAggregationContainer
[JsonProperty("composite")]
ICompositeAggregation Composite { get; set; }

[JsonProperty("weighted_avg")]
IWeightedAverageAggregation WeightedAverage { get; set; }

[JsonProperty("aggs")]
AggregationDictionary Aggregations { get; set; }

Expand Down Expand Up @@ -315,6 +318,8 @@ public class AggregationContainer : IAggregationContainer

public ICompositeAggregation Composite { get; set; }

public IWeightedAverageAggregation WeightedAverage { get; set; }

public AggregationDictionary Aggregations { get; set; }

public static implicit operator AggregationContainer(AggregationBase aggregator)
Expand Down Expand Up @@ -450,6 +455,8 @@ public class AggregationContainerDescriptor<T> : DescriptorBase<AggregationConta

ICompositeAggregation IAggregationContainer.Composite { get; set; }

IWeightedAverageAggregation IAggregationContainer.WeightedAverage { get; set; }

public AggregationContainerDescriptor<T> Average(string name,
Func<AverageAggregationDescriptor<T>, IAverageAggregation> selector) =>
_SetInnerAggregation(name, selector, (a, d) => a.Average = d);
Expand Down Expand Up @@ -646,6 +653,10 @@ public AggregationContainerDescriptor<T> Composite(string name,
Func<CompositeAggregationDescriptor<T>, ICompositeAggregation> selector) =>
_SetInnerAggregation(name, selector, (a, d) => a.Composite = d);

public AggregationContainerDescriptor<T> WeightedAverage(string name,
Func<WeightedAverageAggregationDescriptor<T>, IWeightedAverageAggregation> selector) =>
_SetInnerAggregation(name, selector, (a, d) => a.WeightedAverage = d);

/// <summary>
/// Fluent methods do not assign to properties on `this` directly but on IAggregationContainers inside `this.Aggregations[string, IContainer]
/// </summary>
Expand Down
5 changes: 1 addition & 4 deletions src/Nest/Aggregations/Metric/MetricAggregation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ public abstract class MetricAggregationBase : AggregationBase, IMetricAggregatio
{
internal MetricAggregationBase() { }

protected MetricAggregationBase(string name, Field field) : base(name)
{
this.Field = field;
}
protected MetricAggregationBase(string name, Field field) : base(name) => this.Field = field;

public Field Field { get; set; }
public virtual IScript Script { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Newtonsoft.Json;

namespace Nest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
[ContractJsonConverter(typeof(AggregationJsonConverter<WeightedAverageAggregation>))]
public interface IWeightedAverageAggregation : IAggregation
{
/// <summary> The configuration for the field or script that provides the values </summary>
[JsonProperty("value")]
IWeightedAverageValue Value { get; set; }
/// <summary> The configuration for the field or script that provides the weights /// </summary>
[JsonProperty("weight")]
IWeightedAverageValue Weight { get; set; }
/// <summary> The optional numeric response formatter </summary>
[JsonProperty("format")]
string Format { get; set; }
/// <summary> A hint about the values for pure scripts or unmapped fields </summary>
[JsonProperty("value_type")]
// TODO map as on server enum ?
// https://github.com/elastic/elasticsearch/blob/master/server/src/main/java/org/elasticsearch/search/aggregations/support/ValueType.java
string ValueType { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weighted Avg Aggregation is not marked as beta or experimental, so mapping as an enum might be better, as it would help guide users as to which values are supported.

}

public class WeightedAverageAggregation : AggregationBase, IWeightedAverageAggregation
{
internal WeightedAverageAggregation() { }
public WeightedAverageAggregation(string name) : base(name) { }

internal override void WrapInContainer(AggregationContainer c) => c.WeightedAverage = this;

/// <inheritdoc cref="IWeightedAverageAggregation.Value"/>>
public IWeightedAverageValue Value { get; set; }
/// <inheritdoc cref="IWeightedAverageAggregation.Weight"/>>
public IWeightedAverageValue Weight { get; set; }
/// <inheritdoc cref="IWeightedAverageAggregation.Format"/>>
public string Format { get; set; }
/// <inheritdoc cref="IWeightedAverageAggregation.ValueType"/>>
public string ValueType { get; set; }
}

public class WeightedAverageAggregationDescriptor<T>
: DescriptorBase<WeightedAverageAggregationDescriptor<T>, IWeightedAverageAggregation>
, IWeightedAverageAggregation
where T : class
{
IWeightedAverageValue IWeightedAverageAggregation.Value { get; set; }
IWeightedAverageValue IWeightedAverageAggregation.Weight { get; set; }
string IWeightedAverageAggregation.Format { get; set; }
string IWeightedAverageAggregation.ValueType { get; set; }

string IAggregation.Name { get; set; }
IDictionary<string, object> IAggregation.Meta { get; set; }
/// <inheritdoc cref="IAggregation.Meta"/>>
public WeightedAverageAggregationDescriptor<T> Meta(Func<FluentDictionary<string, object>, FluentDictionary<string, object>> selector) =>
Assign(a => a.Meta = selector?.Invoke(new FluentDictionary<string, object>()));

/// <inheritdoc cref="IWeightedAverageAggregation.Value"/>>
public WeightedAverageAggregationDescriptor<T> Value(Func<WeightedAverageValueDescriptor<T>, IWeightedAverageValue> selector) =>
Assign(a => a.Value = selector?.Invoke(new WeightedAverageValueDescriptor<T>()));

/// <inheritdoc cref="IWeightedAverageAggregation.Weight"/>>
public WeightedAverageAggregationDescriptor<T> Weight(Func<WeightedAverageValueDescriptor<T>, IWeightedAverageValue> selector) =>
Assign(a => a.Weight = selector?.Invoke(new WeightedAverageValueDescriptor<T>()));

/// <inheritdoc cref="IWeightedAverageAggregation.Format"/>>
public WeightedAverageAggregationDescriptor<T> Format(string format) => Assign(a => a.Format = format);

/// <inheritdoc cref="IWeightedAverageAggregation.ValueType"/>>
public WeightedAverageAggregationDescriptor<T> ValueType(string valueType) => Assign(a => a.ValueType = valueType);


}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Linq.Expressions;
using Newtonsoft.Json;

namespace Nest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
[ContractJsonConverter(typeof(ReadAsTypeJsonConverter<WeightedAverageValue>))]
public interface IWeightedAverageValue
{
[JsonProperty("field")]
Field Field { get; set; }

[JsonProperty("script")]
IScript Script { get; set; }

[JsonProperty("missing")]
double? Missing { get; set; }
}

public class WeightedAverageValue : IWeightedAverageValue
{
internal WeightedAverageValue() { }
public WeightedAverageValue(Field field) => this.Field = field;
public WeightedAverageValue(IScript script) => this.Script = script;

public Field Field { get; set; }
public virtual IScript Script { get; set; }
public double? Missing { get; set; }
}

public class WeightedAverageValueDescriptor<T> : DescriptorBase<WeightedAverageValueDescriptor<T>, IWeightedAverageValue>
, IWeightedAverageValue
where T : class
{
Field IWeightedAverageValue.Field { get; set; }
IScript IWeightedAverageValue.Script { get; set; }
double? IWeightedAverageValue.Missing { get; set; }

public WeightedAverageValueDescriptor<T> Field(Field field) => Assign(a => a.Field = field);

public WeightedAverageValueDescriptor<T> Field(Expression<Func<T, object>> field) => Assign(a => a.Field = field);

public virtual WeightedAverageValueDescriptor<T> Script(string script) => Assign(a => a.Script = (InlineScript)script);

public virtual WeightedAverageValueDescriptor<T> Script(Func<ScriptDescriptor, IScript> scriptSelector) =>
Assign(a => a.Script = scriptSelector?.Invoke(new ScriptDescriptor()));

public WeightedAverageValueDescriptor<T> Missing(double? missing) => Assign(a => a.Missing = missing);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using FluentAssertions;
using Nest;
using Tests.Framework.Integration;
using static Nest.Infer;
using Tests.Core.Extensions;
using Tests.Core.ManagedElasticsearch.Clusters;
using Tests.Domain;

namespace Tests.Aggregations.Metric.WeightedAverage
{
public class WeightedAverageAggregationUsageTests : AggregationUsageTestBase
{
public WeightedAverageAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }

private const string AggregationName = "weighted_avg_commits";

protected override object AggregationJson => new
{
weighted_avg_commits = new
{
weighted_avg = new
{
value = new
{
field = "numberOfCommits",
missing = 0.0
},
weight = new
{
field = "numberOfContributors"
}
}
}
};

protected override Func<AggregationContainerDescriptor<Project>, IAggregationContainer> FluentAggs => a => a
.WeightedAverage(AggregationName, avg => avg
.Value(v => v.Field(p => p.NumberOfCommits).Missing(0))
.Weight(v => v.Field(p => p.NumberOfContributors))
);

protected override AggregationDictionary InitializerAggs =>
new WeightedAverageAggregation(AggregationName)
{
Value = new WeightedAverageValue(Field<Project>(p => p.NumberOfCommits))
{
Missing = 0
},
Weight = new WeightedAverageValue(Field<Project>(p => p.NumberOfContributors))
};

protected override void ExpectResponse(ISearchResponse<Project> response)
{
response.ShouldBeValid();
var commitsAvg = response.Aggregations.WeightedAverage(AggregationName);
commitsAvg.Should().NotBeNull();
commitsAvg.Value.Should().BeGreaterThan(0);
}
}
}