Skip to content

This change improves the usability around Fields #619

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

Merged
merged 5 commits into from
Apr 23, 2014
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
27 changes: 19 additions & 8 deletions src/Nest/Domain/FieldSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,31 @@ public interface IFieldSelection<out T>
/// As of elasticsearch fields are always returned as an array. except for internal metadata values such as routing.
/// </summary>
/// <typeparam name="K">The type to return the value as, remember that if your field is a string K should be string[]</typeparam>
K FieldValue<K>(string path);
K FieldValues<K>(string path);

K[] FieldValue<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
K[] FieldValues<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
where TBindTo : class;

IDictionary<string, object> FieldValuesDictionary { get; set; }
}

public class FieldSelection<T> : IFieldSelection<T>
{
private ElasticInferrer Infer { get; set; }
public FieldSelection(IConnectionSettingsValues settings, IDictionary<string, object> values = null)
public FieldSelection(IConnectionSettingsValues settings, IDictionary<string, object> valuesDictionary = null)
{
this.Infer = new ElasticInferrer(settings);
this.FieldValues = values;
((IFieldSelection<T>)this).FieldValuesDictionary = valuesDictionary;
}

[JsonConverter(typeof(DictionaryKeysAreNotPropertyNamesJsonConverter))]
public IDictionary<string, object> FieldValues { get; internal set; }
IDictionary<string, object> IFieldSelection<T>.FieldValuesDictionary { get; set; }

/// <summary>
/// As of elasticsearch fields are always returned as an array. except for internal metadata values such as routing.
/// </summary>
/// <typeparam name="K">The type to return the value as, remember that if your field is a string K should be string[]</typeparam>
public K FieldValue<K>(string path)
public K FieldValues<K>(string path)
{
return this.FieldArray<K>(path);
}
Expand All @@ -49,21 +50,31 @@ public K FieldValue<K>(string path)
/// As of elasticsearch fields are always returned as an array.
/// except for internal metadata values such as routing.
/// </summary>
public K[] FieldValue<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
public K[] FieldValues<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
where TBindTo : class
{
var path = this.Infer.PropertyPath(objectPath);
return this.FieldArray<K[]>(path);
}

/// <summary>
/// As of elasticsearch fields are always returned as an array.
/// except for internal metadata values such as routing.
/// </summary>
public K[] FieldValues<K>(Expression<Func<T, K>> objectPath)
{
var path = this.Infer.PropertyPath(objectPath);
return this.FieldArray<K[]>(path);
}

/// <summary>
/// As of elasticsearch fields are always returned as an array. except for internal metadata values such as routing.
/// </summary>
/// <typeparam name="K">The type to return the value as, remember that if your field is a string K should be string[]</typeparam>
private K FieldArray<K>(string path)
{
object o;
if (FieldValues.TryGetValue(path, out o))
if (((IFieldSelection<T>)this).FieldValuesDictionary.TryGetValue(path, out o))
{
var t = typeof(K);
if (o is JArray && t.GetInterfaces().Contains(typeof(IEnumerable)))
Expand Down
17 changes: 14 additions & 3 deletions src/Nest/Domain/Responses/BaseResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,28 @@ public BaseResponse()
public virtual bool IsValid { get; internal set; }
public IElasticsearchResponse ConnectionStatus { get; internal set; }
public ElasticInferrer _infer;

protected IConnectionSettingsValues Settings
{
get
{
if (this.ConnectionStatus == null)
return null;

var settings = this.ConnectionStatus.Settings as IConnectionSettingsValues;
return settings;
}
}


public ElasticInferrer Infer
{
get
{
if (this._infer != null)
return this._infer;
if (this.ConnectionStatus == null)
return null;

var settings = this.ConnectionStatus.Settings as IConnectionSettingsValues;
var settings = this.Settings;
if (settings == null)
return null;
this._infer = new ElasticInferrer(settings);
Expand Down
4 changes: 2 additions & 2 deletions src/Nest/Domain/Responses/GetResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ public K[] FieldValue<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
where TBindTo : class
{
if (this.Fields == null) return default(K[]);
return this.Fields.FieldValue<TBindTo,K>(objectPath);
return this.Fields.FieldValues<TBindTo,K>(objectPath);
}

public K FieldValue<K>(string path)
{
if (this.Fields == null) return default(K);
return this.Fields.FieldValue<K>(path);
return this.Fields.FieldValues<K>(path);
}

}
Expand Down
71 changes: 38 additions & 33 deletions src/Nest/Domain/Responses/SearchResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Nest.Domain;
using Nest.Resolvers.Converters;
using Newtonsoft.Json;
using System.Linq.Expressions;
Expand All @@ -20,8 +21,21 @@ public interface ISearchResponse<T> : IResponse where T : class
string ScrollId { get; }
long Total { get; }
double MaxScore { get; }
/// <summary>
/// Returns a view on the documents inside the hits that are returned.
/// <para>NOTE: if you use Fields() on the search descriptor .Documents will be empty use
/// .Fields instead or try the 'source filtering' feature introduced in Elasticsearch 1.0
/// using .Source() on the search descriptor to get Documents of type T with only certain parts selected
/// </para>
/// </summary>
IEnumerable<T> Documents { get; }
IEnumerable<IHit<T>> Hits { get; }

/// <summary>
/// Will return the field selections inside the hits when the search descriptor specified .Fields.
/// Otherwise this will always be an empty collection.
/// </summary>
IEnumerable<FieldSelection<T>> FieldSelections { get; }
HighlightDocumentDictionary Highlights { get; }
F Facet<F>(Expression<Func<T, object>> expression) where F : class, IFacet;
F Facet<F>(string fieldName) where F : class, IFacet;
Expand Down Expand Up @@ -54,7 +68,7 @@ public SearchResponse()
public IDictionary<string, IAggregation> Aggregations { get; internal set; }

private AggregationsHelper _agg = null;

[JsonIgnore]
public AggregationsHelper Aggs
{
get { return _agg ?? (_agg = new AggregationsHelper(this.Aggregations)); }
Expand All @@ -72,55 +86,46 @@ public AggregationsHelper Aggs
[JsonProperty(PropertyName = "_scroll_id")]
public string ScrollId { get; internal set; }

public long Total
{
get
{
if (this.HitsMetaData == null)
{
return 0;
}
return this.HitsMetaData.Total;
}
}
[JsonIgnore]
public long Total { get { return this.HitsMetaData == null ? 0 : this.HitsMetaData.Total; } }

public double MaxScore
{
get
{
if (this.HitsMetaData == null)
{
return 0;
}
return this.HitsMetaData.MaxScore;
}
}
[JsonIgnore]
public double MaxScore { get { return this.HitsMetaData == null ? 0 : this.HitsMetaData.MaxScore; } }

private IList<T> _documents;
/// <inheritdoc />
[JsonIgnore]
public IEnumerable<T> Documents
{
get
{
if (this.HitsMetaData != null && this._documents == null)
this._documents = this.HitsMetaData.Hits.Select(h => h.Source).ToList();
return this._documents ?? Enumerable.Empty<T>();
return this._documents ?? (this._documents = this.Hits
.Select(h => h.Source)
.Where(d => d != null)
.ToList());
}
}

[JsonIgnore]
public IEnumerable<IHit<T>> Hits
{
get
{
if (this.HitsMetaData != null)
{
return this.HitsMetaData.Hits;
}
get { return this.HitsMetaData != null ? (IEnumerable<IHit<T>>) this.HitsMetaData.Hits : new List<Hit<T>>(); }
}

return new List<Hit<T>>();
/// <inheritdoc />
[JsonIgnore]
public IEnumerable<FieldSelection<T>> FieldSelections
{
get
{
return this.Hits
.Select(h => h.Fields)
.Where(f=>f != null)
.Select(f => new FieldSelection<T>(this.Settings, f.FieldValuesDictionary));
}
}


public F Facet<F>(Expression<Func<T, object>> expression) where F : class, IFacet
{
var fieldName = this.Infer.PropertyPath(expression);
Expand Down
2 changes: 1 addition & 1 deletion src/Nest/Resolvers/Converters/MultiGetHitConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private static void CreateMultiHit<T>(MultiHitTuple tuple, JsonSerializer serial
var source = tuple.Hit["fields"];
if (source != null)
{
f.FieldValues = serializer.Deserialize<Dictionary<string, object>>( source.CreateReader());
((IFieldSelection<T>)f).FieldValuesDictionary = serializer.Deserialize<Dictionary<string, object>>( source.CreateReader());
hit.FieldSelection = f;
}

Expand Down
6 changes: 4 additions & 2 deletions src/Nest/Resolvers/IndexNameResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ public string GetIndexForType(Type type)

if (defaultIndices == null)
return this._connectionSettings.DefaultIndex;
if (defaultIndices.ContainsKey(type) && !string.IsNullOrWhiteSpace(defaultIndices[type]))
return defaultIndices[type];

string value;
if (defaultIndices.TryGetValue(type, out value) && !string.IsNullOrWhiteSpace(value))
return value;
return this._connectionSettings.DefaultIndex;
}

Expand Down
7 changes: 3 additions & 4 deletions src/Tests/Nest.Tests.Integration/Core/Get/GetFullTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ public void GetUsingDescriptorWithTypeAndFields()

result.Source.Should().BeNull();
result.Fields.Should().NotBeNull();
result.Fields.FieldValues.Should().NotBeNull().And.HaveCount(4);
result.Fields.FieldValue<ElasticsearchProject, string>(p => p.Name).Should().BeEquivalentTo(new [] {"pyelasticsearch"});
result.Fields.FieldValue<ElasticsearchProject, int>(p => p.Id).Should().BeEquivalentTo( new []{1});
result.Fields.FieldValue<ElasticsearchProject, string>(p => p.DoubleValue).Should().NotBeEquivalentTo(new [] {default(double) });
result.Fields.FieldValues<ElasticsearchProject, string>(p => p.Name).Should().BeEquivalentTo(new [] {"pyelasticsearch"});
result.Fields.FieldValues<ElasticsearchProject, int>(p => p.Id).Should().BeEquivalentTo( new []{1});
result.Fields.FieldValues<ElasticsearchProject, string>(p => p.DoubleValue).Should().NotBeEquivalentTo(new [] {default(double) });

}

Expand Down
12 changes: 6 additions & 6 deletions src/Tests/Nest.Tests.Integration/Core/Get/GetMultiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ public void GetMultiWithMetaData()

var fieldSelection = personHit.FieldSelection;
fieldSelection.Should().NotBeNull();
fieldSelection.FieldValue<Person, int>(p=>p.Id).Should().BeEquivalentTo(new []{authorId});
fieldSelection.FieldValue<Person, string>(p => p.FirstName)
fieldSelection.FieldValues<Person, int>(p=>p.Id).Should().BeEquivalentTo(new []{authorId});
fieldSelection.FieldValues<Person, string>(p => p.FirstName)
.Should().NotBeEmpty();

}
Expand Down Expand Up @@ -122,15 +122,15 @@ public void GetMultiWithMetaDataUsingCleanApi()
//personHit.FieldSelection would work too
var personFieldSelection = result.GetFieldSelection<Person>(authorId);
personFieldSelection.Should().NotBeNull();
personFieldSelection.FieldValue<Person, int>(p => p.Id).Should().BeEquivalentTo(new []{authorId});
personFieldSelection.FieldValue<Person, string>(p => p.FirstName)
personFieldSelection.FieldValues<Person, int>(p => p.Id).Should().BeEquivalentTo(new []{authorId});
personFieldSelection.FieldValues<Person, string>(p => p.FirstName)
.Should().NotBeEmpty();

var projectFieldSelection = result.GetFieldSelection<ElasticsearchProject>(projectId);
projectFieldSelection.Should().NotBeNull();
projectFieldSelection.FieldValue<ElasticsearchProject, int>(p => p.Id)
projectFieldSelection.FieldValues<ElasticsearchProject, int>(p => p.Id)
.Should().BeEquivalentTo(new []{projectId});
projectFieldSelection.FieldValue<ElasticsearchProject, string>(p => p.Followers.First().FirstName)
projectFieldSelection.FieldValues<ElasticsearchProject, string>(p => p.Followers.First().FirstName)
.Should().NotBeEmpty();

}
Expand Down
4 changes: 2 additions & 2 deletions src/Tests/Nest.Tests.Integration/Core/Get/GetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public void GetWithFieldsDeep()
).Fields;

Assert.NotNull(fieldSelection);
var name = fieldSelection.FieldValue<ElasticsearchProject, string>(f => f.Name);
var name = fieldSelection.FieldValues<ElasticsearchProject, string>(f => f.Name);
Assert.IsNotEmpty(name);
var list = fieldSelection.FieldValue<ElasticsearchProject, string>(f=>f.Followers.First().FirstName);
var list = fieldSelection.FieldValues<ElasticsearchProject, string>(f=>f.Followers.First().FirstName);
Assert.NotNull(list);
Assert.IsNotEmpty(list);

Expand Down
Loading