Skip to content
30 changes: 22 additions & 8 deletions docs/common-options/date-math/date-math-expressions.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,28 @@ anchor will be an actual `DateTime`, even after a serialization/deserialization
[source,csharp]
----
var date = new DateTime(2015, 05, 05);
Expect("2015-05-05T00:00:00")
.WhenSerializing<Nest.DateMath>(date)
.AssertSubject(dateMath => ((IDateMath)dateMath)
.Anchor.Match(
d => d.Should().Be(date),
s => s.Should().BeNull()
)
);
----

will serialize to

[source,javascript]
----
"2015-05-05T00:00:00"
----

When the `DateTime` is local or UTC, the time zone information is included.
For example, for a UTC `DateTime`

[source,csharp]
----
var utcDate = new DateTime(2015, 05, 05, 0, 0, 0, DateTimeKind.Utc);
----

will serialize to

[source,javascript]
----
"2015-05-05T00:00:00Z"
----

==== Complex expressions
Expand Down
2 changes: 1 addition & 1 deletion src/CodeGeneration/DocGenerator/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public static string RemoveNumberOfLeadingTabsOrSpacesAfterNewline(this string i

public static string[] SplitOnNewLines(this string input, StringSplitOptions options) => input.Split(new[] { "\r\n", "\n" }, options);

public static bool TryGetJsonForAnonymousType(this string anonymousTypeString, out string json)
public static bool TryGetJsonForExpressionSyntax(this string anonymousTypeString, out string json)
{
json = null;

Expand Down
7 changes: 3 additions & 4 deletions src/CodeGeneration/DocGenerator/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ public static bool TryGetJsonForSyntaxNode(this SyntaxNode node, out string json
json = null;

// find the first anonymous object or new object expression
var creationExpressionSyntax = node.DescendantNodes()
.FirstOrDefault(n => n is AnonymousObjectCreationExpressionSyntax || n is ObjectCreationExpressionSyntax);
var syntax = node.DescendantNodes()
.FirstOrDefault(n => n is AnonymousObjectCreationExpressionSyntax || n is ObjectCreationExpressionSyntax || n is LiteralExpressionSyntax);

return creationExpressionSyntax != null &&
creationExpressionSyntax.ToFullString().TryGetJsonForAnonymousType(out json);
return syntax != null && syntax.ToFullString().TryGetJsonForExpressionSyntax(out json);
}

/// <summary>
Expand Down
32 changes: 17 additions & 15 deletions src/Elasticsearch.Net/Connection/Content/RequestDataContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ namespace Elasticsearch.Net
internal class RequestDataContent : HttpContent
{
private readonly RequestData _requestData;
private readonly Func<PostData, CompleteTaskOnCloseStream, RequestDataContent, TransportContext, Task> _onStreamAvailable;

private readonly Func<RequestData, CompleteTaskOnCloseStream, RequestDataContent, TransportContext, Task> _onStreamAvailable;

public RequestDataContent(RequestData requestData)
{
Expand All @@ -35,12 +34,17 @@ public RequestDataContent(RequestData requestData)
if (requestData.HttpCompression)
Headers.ContentEncoding.Add("gzip");

Task OnStreamAvailable(PostData data, Stream stream, HttpContent content, TransportContext context)
Task OnStreamAvailable(RequestData data, Stream stream, HttpContent content, TransportContext context)
{
if (data.HttpCompression)
stream = new GZipStream(stream, CompressionMode.Compress, false);

using(stream)
data.Write(stream, requestData.ConnectionSettings);
data.PostData.Write(stream, data.ConnectionSettings);

return Task.CompletedTask;
}

_onStreamAvailable = OnStreamAvailable;
}
public RequestDataContent(RequestData requestData, CancellationToken token)
Expand All @@ -50,11 +54,15 @@ public RequestDataContent(RequestData requestData, CancellationToken token)
if (requestData.HttpCompression)
Headers.ContentEncoding.Add("gzip");

async Task OnStreamAvailable(PostData data, Stream stream, HttpContent content, TransportContext context)
async Task OnStreamAvailable(RequestData data, Stream stream, HttpContent content, TransportContext context)
{
if (data.HttpCompression)
stream = new GZipStream(stream, CompressionMode.Compress, false);

using (stream)
await data.WriteAsync(stream, requestData.ConnectionSettings, token).ConfigureAwait(false);
await data.PostData.WriteAsync(stream, data.ConnectionSettings, token).ConfigureAwait(false);
}

_onStreamAvailable = OnStreamAvailable;
}

Expand All @@ -69,16 +77,9 @@ async Task OnStreamAvailable(PostData data, Stream stream, HttpContent content,
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is passed as task result.")]
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
{

var data = _requestData.PostData;
if (data == null) return;

var serializeToStreamTask = new TaskCompletionSource<bool>();

if (_requestData.HttpCompression)
stream = new GZipStream(stream, CompressionMode.Compress, false);
var wrappedStream = new CompleteTaskOnCloseStream(stream, serializeToStreamTask);
await _onStreamAvailable(data, wrappedStream, this, context).ConfigureAwait(false);
await _onStreamAvailable(_requestData, wrappedStream, this, context).ConfigureAwait(false);
await serializeToStreamTask.Task.ConfigureAwait(false);
}

Expand Down Expand Up @@ -111,7 +112,6 @@ protected override void Dispose(bool disposing)
base.Dispose();
}


public override void Close() => _serializeToStreamTask.TrySetResult(true);
}

Expand Down Expand Up @@ -193,6 +193,8 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As
public override void EndWrite(IAsyncResult asyncResult) => _innerStream.EndWrite(asyncResult);

public override void WriteByte(byte value) => _innerStream.WriteByte(value);

public override void Close() => _innerStream.Close();
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/Elasticsearch.Net/Connection/HttpConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public virtual TResponse Request<TResponse>(RequestData requestData)
try
{
var requestMessage = CreateHttpRequestMessage(requestData);
SetContent(requestMessage, requestData);

if (requestData.PostData != null)
SetContent(requestMessage, requestData);

using(requestMessage?.Content ?? (IDisposable)Stream.Null)
using (var d = DiagnosticSource.Diagnose<RequestData, int?>(DiagnosticSources.HttpConnection.SendAndReceiveHeaders, requestData))
{
Expand Down Expand Up @@ -107,8 +110,11 @@ public virtual async Task<TResponse> RequestAsync<TResponse>(RequestData request
try
{
var requestMessage = CreateHttpRequestMessage(requestData);
SetAsyncContent(requestMessage, requestData, cancellationToken);
using(requestMessage?.Content ?? (IDisposable)Stream.Null)

if (requestData.PostData != null)
SetAsyncContent(requestMessage, requestData, cancellationToken);

using(requestMessage?.Content ?? (IDisposable)Stream.Null)
using (var d = DiagnosticSource.Diagnose<RequestData, int?>(DiagnosticSources.HttpConnection.SendAndReceiveHeaders, requestData))
{
responseMessage = await client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
Expand Down
64 changes: 64 additions & 0 deletions src/Elasticsearch.Net/Extensions/StringBuilderCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Text;

namespace Elasticsearch.Net.Extensions
{
/// <summary>Provide a cached reusable instance of stringbuilder per thread.</summary>
internal static class StringBuilderCache
{
private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity

// The value 360 was chosen in discussion with performance experts as a compromise between using
// as little memory per thread as possible and still covering a large part of short-lived
// StringBuilder creations on the startup path of VS designers.
private const int MaxBuilderSize = 360;

// WARNING: We allow diagnostic tools to directly inspect this member (t_cachedInstance).
// See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
// Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
// Get in touch with the diagnostics team if you have questions.
[ThreadStatic]
private static StringBuilder _cachedInstance;

/// <summary>Get a StringBuilder for the specified capacity.</summary>
/// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
public static StringBuilder Acquire(int capacity = DefaultCapacity)
{
if (capacity <= MaxBuilderSize)
{
var sb = _cachedInstance;
if (sb != null)
{
// Avoid stringbuilder block fragmentation by getting a new StringBuilder
// when the requested size is larger than the current capacity
if (capacity <= sb.Capacity)
{
_cachedInstance = null;
sb.Clear();
return sb;
}
}
}

return new StringBuilder(capacity);
}

/// <summary>Place the specified builder in the cache if it is not too big.</summary>
public static void Release(StringBuilder sb)
{
if (sb.Capacity <= MaxBuilderSize) _cachedInstance = sb;
}

/// <summary>ToString() the stringbuilder, Release it to the cache, and return the resulting string.</summary>
public static string GetStringAndRelease(StringBuilder sb)
{
var result = sb.ToString();
Release(sb);
return result;
}
}
}
2 changes: 2 additions & 0 deletions src/Nest/Aggregations/AggregateDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ public TermsAggregate<TKey> Terms<TKey>(string key)

public MultiBucketAggregate<KeyedBucket<string>> GeoHash(string key) => GetMultiKeyedBucketAggregate<string>(key);

public MultiBucketAggregate<KeyedBucket<string>> GeoTile(string key) => GetMultiKeyedBucketAggregate<string>(key);

public MultiBucketAggregate<KeyedBucket<string>> AdjacencyMatrix(string key) => GetMultiKeyedBucketAggregate<string>(key);

public MultiBucketAggregate<RangeBucket> Range(string key) => GetMultiBucketAggregate<RangeBucket>(key);
Expand Down
12 changes: 12 additions & 0 deletions src/Nest/Aggregations/AggregationContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ public interface IAggregationContainer
[DataMember(Name = "geohash_grid")]
IGeoHashGridAggregation GeoHash { get; set; }

[DataMember(Name = "geotile_grid")]
IGeoTileGridAggregation GeoTile { get; set; }

[DataMember(Name = "global")]
IGlobalAggregation Global { get; set; }

Expand Down Expand Up @@ -300,6 +303,8 @@ public class AggregationContainer : IAggregationContainer

public IGeoHashGridAggregation GeoHash { get; set; }

public IGeoTileGridAggregation GeoTile { get; set; }

public IGlobalAggregation Global { get; set; }

public IHistogramAggregation Histogram { get; set; }
Expand Down Expand Up @@ -439,6 +444,8 @@ public class AggregationContainerDescriptor<T> : DescriptorBase<AggregationConta

IGeoHashGridAggregation IAggregationContainer.GeoHash { get; set; }

IGeoTileGridAggregation IAggregationContainer.GeoTile { get; set; }

IGlobalAggregation IAggregationContainer.Global { get; set; }

IHistogramAggregation IAggregationContainer.Histogram { get; set; }
Expand Down Expand Up @@ -565,6 +572,11 @@ Func<GeoHashGridAggregationDescriptor<T>, IGeoHashGridAggregation> selector
) =>
_SetInnerAggregation(name, selector, (a, d) => a.GeoHash = d);

public AggregationContainerDescriptor<T> GeoTile(string name,
Func<GeoTileGridAggregationDescriptor<T>, IGeoTileGridAggregation> selector
) =>
_SetInnerAggregation(name, selector, (a, d) => a.GeoTile = d);

public AggregationContainerDescriptor<T> GeoBounds(string name,
Func<GeoBoundsAggregationDescriptor<T>, IGeoBoundsAggregation> selector
) =>
Expand Down
87 changes: 87 additions & 0 deletions src/Nest/Aggregations/Bucket/GeoTileGrid/GeoTileGridAggregation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using Elasticsearch.Net.Utf8Json;

namespace Nest
{
[InterfaceDataContract]
[ReadAs(typeof(GeoTileGridAggregation))]
public interface IGeoTileGridAggregation : IBucketAggregation
{
/// <summary>
/// The name of the field indexed with GeoPoints.
/// </summary>
[DataMember(Name ="field")]
Field Field { get; set; }

/// <summary>
/// The zoom of the key used to define cells/buckets in the results.
/// </summary>
[DataMember(Name ="precision")]
GeoTilePrecision? Precision { get; set; }

/// <summary>
/// To allow for more accurate counting of the top cells returned in the final result the aggregation.
/// </summary>
[DataMember(Name ="shard_size")]
int? ShardSize { get; set; }

/// <summary>
/// The maximum number of geohash buckets to return.
/// </summary>
[DataMember(Name ="size")]
int? Size { get; set; }
}

public class GeoTileGridAggregation : BucketAggregationBase, IGeoTileGridAggregation
{
internal GeoTileGridAggregation() { }

public GeoTileGridAggregation(string name) : base(name) { }

/// <inheritdoc />
public Field Field { get; set; }

/// <inheritdoc />
public GeoTilePrecision? Precision { get; set; }

/// <inheritdoc />
public int? ShardSize { get; set; }

/// <inheritdoc />
public int? Size { get; set; }

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

public class GeoTileGridAggregationDescriptor<T>
: BucketAggregationDescriptorBase<GeoTileGridAggregationDescriptor<T>, IGeoTileGridAggregation, T>
, IGeoTileGridAggregation
where T : class
{
Field IGeoTileGridAggregation.Field { get; set; }

GeoTilePrecision? IGeoTileGridAggregation.Precision { get; set; }

int? IGeoTileGridAggregation.ShardSize { get; set; }

int? IGeoTileGridAggregation.Size { get; set; }

/// <inheritdoc cref="IGeoTileGridAggregation.Field" />
public GeoTileGridAggregationDescriptor<T> Field(Field field) => Assign(field, (a, v) => a.Field = v);

/// <inheritdoc cref="IGeoTileGridAggregation.Field" />
public GeoTileGridAggregationDescriptor<T> Field<TValue>(Expression<Func<T, TValue>> field) => Assign(field, (a, v) => a.Field = v);

/// <inheritdoc cref="IGeoTileGridAggregation.Size" />
public GeoTileGridAggregationDescriptor<T> Size(int? size) => Assign(size, (a, v) => a.Size = v);

/// <inheritdoc cref="IGeoTileGridAggregation.ShardSize" />
public GeoTileGridAggregationDescriptor<T> ShardSize(int? shardSize) => Assign(shardSize, (a, v) => a.ShardSize = v);

/// <inheritdoc cref="IGeoTileGridAggregation.Precision" />
public GeoTileGridAggregationDescriptor<T> Precision(GeoTilePrecision? precision) =>
Assign(precision, (a, v) => a.Precision = v);
}
}
Loading