Skip to content
Merged
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
21 changes: 19 additions & 2 deletions src/Nest/DSL/Aggregations/AggregationDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public interface IAggregationContainer
[JsonProperty("percentile_ranks")]
IPercentileRanksAggregaor PercentileRanks { get; set; }

[JsonProperty("top_hits")]
ITopHitsAggregator TopHits { get; set; }

[JsonProperty("aggs")]
[JsonConverter(typeof(DictionaryKeysAreNotPropertyNamesJsonConverter))]
IDictionary<string, IAggregationContainer> Aggregations { get; set; }
Expand Down Expand Up @@ -111,14 +114,14 @@ public class AggregationContainer : IAggregationContainer
private ISignificantTermsAggregator _significantTerms;
private IPercentileRanksAggregaor _percentileRanks;

private ITopHitsAggregator _topHits;
public IAverageAggregator Average { get; set; }
public IValueCountAggregator ValueCount { get; set; }
public IMaxAggregator Max { get; set; }
public IMinAggregator Min { get; set; }
public IStatsAggregator Stats { get; set; }
public ISumAggregator Sum { get; set; }
public IExtendedStatsAggregator ExtendedStats { get; set; }

public IDateHistogramAggregator DateHistogram
{
get { return _dateHistogram; }
Expand Down Expand Up @@ -227,6 +230,12 @@ public IPercentileRanksAggregaor PercentileRanks
set { _percentileRanks = value; }
}

public ITopHitsAggregator TopHits
{
get { return _topHits; }
set { _topHits = value; }
}

private void LiftAggregations(IBucketAggregator bucket)
{
if (bucket == null) return;
Expand Down Expand Up @@ -290,7 +299,9 @@ public class AggregationDescriptor<T> : IAggregationContainer
IPercentileRanksAggregaor IAggregationContainer.PercentileRanks { get;set; }

ITermsAggregator IAggregationContainer.Terms { get; set; }


ITopHitsAggregator IAggregationContainer.TopHits { get; set; }

public AggregationDescriptor<T> Average(string name, Func<AverageAggregationDescriptor<T>, AverageAggregationDescriptor<T>> selector)
{
return _SetInnerAggregation(name, selector, (a, d) => a.Average = d);
Expand Down Expand Up @@ -429,6 +440,12 @@ public AggregationDescriptor<T> ValueCount(string name,
return _SetInnerAggregation(name, selector, (a, d) => a.ValueCount = d);
}

public AggregationDescriptor<T> TopHits(string name,
Func<TopHitsAggregationDescriptor<T>, TopHitsAggregationDescriptor<T>> selector)
{
return _SetInnerAggregation(name, selector, (a, d) => a.TopHits = d);
}

private AggregationDescriptor<T> _SetInnerAggregation<TAggregation>(
string key,
Func<TAggregation, TAggregation> selector
Expand Down
91 changes: 91 additions & 0 deletions src/Nest/DSL/Aggregations/TopHitsAggregationDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using Nest.Resolvers.Converters;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
[JsonConverter(typeof(ReadAsTypeConverter<TopHitsAggregator>))]
public interface ITopHitsAggregator : IMetricAggregator
{
[JsonProperty("from")]
int? From { get; set; }

[JsonProperty("size")]
int? Size { get; set; }

[JsonProperty("sort")]
[JsonConverter(typeof(SortCollectionConverter))]
IList<KeyValuePair<PropertyPathMarker, ISort>> Sort { get; set; }

[JsonProperty("_source")]
ISourceFilter Source { get; set; }
}

public class TopHitsAggregator : MetricAggregator, ITopHitsAggregator
{
public int? From { get; set; }
public int? Size { get; set; }
public IList<KeyValuePair<PropertyPathMarker, ISort>> Sort { get; set; }
public ISourceFilter Source { get; set; }
}

public class TopHitsAggregationDescriptor<T>
: MetricAggregationBaseDescriptor<TopHitsAggregationDescriptor<T>, T>, ITopHitsAggregator
where T : class
{
ITopHitsAggregator Self { get { return this; } }

int? ITopHitsAggregator.From { get; set; }

int? ITopHitsAggregator.Size { get; set; }

IList<KeyValuePair<PropertyPathMarker, ISort>> ITopHitsAggregator.Sort { get; set; }

ISourceFilter ITopHitsAggregator.Source { get; set; }

public TopHitsAggregationDescriptor<T> From(int from)
{
this.Self.From = from;
return this;
}

public TopHitsAggregationDescriptor<T> Size(int size)
{
this.Self.Size = size;
return this;
}

public TopHitsAggregationDescriptor<T> Sort(Func<SortFieldDescriptor<T>, IFieldSort> sortSelector)
{
sortSelector.ThrowIfNull("sortSelector");

if (Self.Sort == null)
Self.Sort = new List<KeyValuePair<PropertyPathMarker, ISort>>();

var descriptor = sortSelector(new SortFieldDescriptor<T>());
this.Self.Sort.Add(new KeyValuePair<PropertyPathMarker, ISort>(descriptor.Field, descriptor));

return this;
}

public TopHitsAggregationDescriptor<T> Source(bool include = true)
{
if (!include)
this.Self.Source = new SourceFilter { Exclude = new PropertyPathMarker[] { "*" } };
else
this.Self.Source = null;

return this;
}

public TopHitsAggregationDescriptor<T> Source(Func<SearchSourceDescriptor<T>, SearchSourceDescriptor<T>> sourceSelector)
{
this.Self.Source = sourceSelector(new SearchSourceDescriptor<T>());
return this;
}
}
}
5 changes: 5 additions & 0 deletions src/Nest/Domain/Aggregations/AggregationsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public PercentilesMetric PercentilesRank(string key)
return this.TryGet<PercentilesMetric>(key);
}

public TopHitsMetric TopHitsMetric(string key)
{
return this.TryGet<TopHitsMetric>(key);
}

public SingleBucket Global(string key)
{
return this.TryGet<SingleBucket>(key);
Expand Down
41 changes: 41 additions & 0 deletions src/Nest/Domain/Aggregations/TopHitsMetric.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Nest
{
public class TopHitsMetric : IMetricAggregation
{
private IEnumerable<JObject> _hits;

public TopHitsMetric()
{
}

internal TopHitsMetric(IEnumerable<JObject> hits)
{
_hits = hits;
}

public long Total { get; set; }
public double? MaxScore { get; set; }

public IEnumerable<Hit<T>> Hits<T>(JsonSerializer serializer = null) where T : class
{
if (serializer != null)
return _hits.Select(h => h.ToObject<Hit<T>>(serializer));
return _hits.Select(h => h.ToObject<Hit<T>>());
}

public IEnumerable<T> Documents<T>(JsonSerializer serializer = null) where T : class
{
return this.Hits<T>(serializer).Select(h => h.Source);
}


}

}
6 changes: 5 additions & 1 deletion src/Nest/Domain/Hit/Hit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ public class Hit<T> : IHit<T>
public T Source { get; internal set; }
[JsonProperty(PropertyName = "_index")]
public string Index { get; internal set; }

//TODO in NEST 2.0 make the property itself double?
[JsonProperty(PropertyName = "_score")]
public double Score { get; internal set; }
internal double? _score { get; set; }
public double Score { get { return _score.GetValueOrDefault(0); } }

[JsonProperty(PropertyName = "_type")]
public string Type { get; internal set; }
[JsonProperty(PropertyName = "_version")]
Expand Down
2 changes: 2 additions & 0 deletions src/Nest/Nest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
<Compile Include="Domain\Aggregations\AggregationsHelper.cs" />
<Compile Include="Domain\Aggregations\Bucket.cs" />
<Compile Include="Domain\Aggregations\BucketAggregationBase.cs" />
<Compile Include="Domain\Aggregations\TopHitsMetric.cs" />
<Compile Include="Domain\Aggregations\GeoBoundsMetric.cs" />
<Compile Include="Domain\Aggregations\HistogramItem.cs" />
<Compile Include="Domain\Aggregations\ExtendedStatsMetric.cs" />
Expand Down Expand Up @@ -211,6 +212,7 @@
<Compile Include="Domain\Stats\PluginStats.cs" />
<Compile Include="Domain\Stats\SegmentsStats.cs" />
<Compile Include="DSL\Aggregations\PercentileRanksAggregationDescriptor.cs" />
<Compile Include="DSL\Aggregations\TopHitsAggregationDescriptor.cs" />
<Compile Include="DSL\Aggregations\GeoBoundsAggregationDescriptor.cs" />
<Compile Include="DSL\Aggregations\ReverseNestedAggregationDescriptor.cs" />
<Compile Include="DSL\CatIndicesDescriptor.cs" />
Expand Down
33 changes: 24 additions & 9 deletions src/Nest/Resolvers/Converters/Aggregations/AggregationConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,27 @@ private IAggregation ReadAggregation(JsonReader reader, JsonSerializer serialize
return GetSingleBucketAggregation(reader, serializer);
case "bounds":
return GetGeoBoundsMetricAggregation(reader, serializer);
case "hits":
return GetHitsAggregation(reader, serializer);
default:
return null;

}
}

private IAggregation GetHitsAggregation(JsonReader reader, JsonSerializer serializer)
{
reader.Read();
var o = JObject.Load(reader);
if (o == null)
return null;

var total = o["total"].ToObject<long>();
var maxScore = o["max_score"].ToObject<double?>();
var hits = o["hits"].Children().OfType<JObject>().Select(s=>s);
return new TopHitsMetric(hits) { Total = total, MaxScore = maxScore };
}

private IAggregation GetGeoBoundsMetricAggregation(JsonReader reader, JsonSerializer serializer)
{
reader.Read();
Expand Down Expand Up @@ -369,15 +384,15 @@ public IAggregation GetRangeAggregation(JsonReader reader, JsonSerializer serial
break;
}
}
var bucket = new RangeItem
{
Key = key,
From = fromDouble,
To = toDouble,
DocCount = docCount.GetValueOrDefault(),
FromAsString = fromAsString,
ToAsString = toAsString
};
var bucket = new RangeItem
{
Key = key,
From = fromDouble,
To = toDouble,
DocCount = docCount.GetValueOrDefault(),
FromAsString = fromAsString,
ToAsString = toAsString
};

bucket.Aggregations = this.GetNestedAggregations(reader, serializer);
return bucket;
Expand Down
2 changes: 1 addition & 1 deletion src/Nest/Resolvers/Converters/ConcreteTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ internal static Type GetConcreteTypeUsingSelector<T>(
hitDynamic.Fields = sel;
hitDynamic.Source = d._source;
hitDynamic.Index = d._index;
hitDynamic.Score = (d._score is double) ? d._score : default(double);
hitDynamic._score = (d._score is double) ? d._score : default(double);
hitDynamic.Type = d._type;
hitDynamic.Version = d._version;
hitDynamic.Id = d._id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Elasticsearch.Net;
using FluentAssertions;
using Nest.Tests.MockData.Domain;
using NUnit.Framework;
Expand Down Expand Up @@ -162,5 +163,46 @@ public void GeoBounds()
geoBoundsMetric.Bounds.BottomRight.Lat.Should().NotBe(0);
geoBoundsMetric.Bounds.BottomRight.Lon.Should().NotBe(0);
}

[Test]
public void TopHits()
{
var results = this.Client.Search<ElasticsearchProject>(s => s
.Size(0)
.Aggregations(a => a
.Terms("top-countries", t => t
.Field(p => p.Country)
.Size(3)
.Aggregations(aa => aa
.TopHits("top-country-hits", th => th
.Sort(sort => sort
.OnField(p => p.StartedOn)
.Order(SortOrder.Descending)
)
.Source(src => src
.Include(p => p.Name)
)
.Size(1)
)
)
)
)
);

results.IsValid.Should().BeTrue();

var topCountries = results.Aggs.Terms("top-countries").Items;
foreach(var topCountry in topCountries)
{
var topHits = topCountry.TopHitsMetric("top-country-hits");
topHits.Should().NotBeNull();
topHits.Total.Should().BeGreaterThan(0);
var hits = topHits.Hits<ElasticsearchProject>();
hits.Should().NotBeEmpty().And.NotContain(h=> h.Id.IsNullOrEmpty() || h.Index.IsNullOrEmpty());
topHits.Documents<ElasticsearchProject>().Should().NotBeEmpty();

}

}
}
}