Skip to content

Commit

Permalink
Refactor TypedStatsResult and add unit test
Browse files Browse the repository at this point in the history
 - TypedStatsResult was changed to an interface so that the setter can be overridden.
 - TypedStatsResultDouble is no longer needed.
 - Add some unit tests.
  • Loading branch information
semrekkers committed May 2, 2019
1 parent b916d57 commit 12f8b9b
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 279 deletions.
67 changes: 44 additions & 23 deletions SolrNet.Tests/Resources/partialResponseWithStats.xml
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- https://cwiki.apache.org/confluence/display/solr/The+Stats+Component#TheStatsComponent-Example.1 -->
<lst name="stats">
<lst name="stats_fields">
<lst name="instock_prices">
<double name="min">0.0</double>
<double name="max">2199.0</double>
<long name="count">16</long>
<long name="missing">16</long>
<double name="sum">5251.270030975342</double>
<double name="sumOfSquares">6038619.175900028</double>
<double name="mean">328.20437693595886</double>
<double name="stddev">536.3536996709846</double>
<lst name="facets"/>
<lst name="stats_fields">
<lst name="instock_prices">
<double name="min">0.0</double>
<double name="max">2199.0</double>
<long name="count">16</long>
<long name="missing">16</long>
<double name="sum">5251.270030975342</double>
<double name="sumOfSquares">6038619.175900028</double>
<double name="mean">328.20437693595886</double>
<double name="stddev">536.3536996709846</double>
<lst name="facets"/>
</lst>
<lst name="all_prices">
<double name="min">0.0</double>
<double name="max">2199.0</double>
<long name="count">12</long>
<long name="missing">5</long>
<double name="sum">4089.880027770996</double>
<double name="sumOfSquares">5385249.921747174</double>
<double name="mean">340.823335647583</double>
<double name="stddev">602.3683083752779</double>
<lst name="facets"/>
</lst>
<lst name="dateField">
<date name="min">2013-12-20T00:00:00Z</date>
<date name="max">2013-12-23T00:00:00Z</date>
<long name="count">2</long>
<long name="missing">70096</long>
<date name="sum">2057-12-10T23:59:59.999Z</date>
<date name="mean">2013-12-21T11:59:59.999Z</date>
<double name="sumOfSquares">-1.6000595974543114E19</double>
<double name="stddev">NaN</double>
<lst name="facets"/>
</lst>
<lst name="stringField">
<str name="min">Test</str>
<str name="max">Test string</str>
<long name="count">277</long>
<long name="missing">0</long>
</lst>
<lst name="nullField">
<null name="min"/>
<null name="max"/>
</lst>
</lst>
<lst name="all_prices">
<double name="min">0.0</double>
<double name="max">2199.0</double>
<long name="count">12</long>
<long name="missing">5</long>
<double name="sum">4089.880027770996</double>
<double name="sumOfSquares">5385249.921747174</double>
<double name="mean">340.823335647583</double>
<double name="stddev">602.3683083752779</double>
<lst name="facets"/>
</lst>
</lst>
</lst>
27 changes: 27 additions & 0 deletions SolrNet.Tests/SolrQueryResultsParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,33 @@ public void ParseStatsResults2()
Assert.Equal(2199, all.Max);
}

[Fact]
public void ParseTypedStatsResult()
{
var xml = EmbeddedResource.GetEmbeddedXml(GetType(), "Resources.partialResponseWithStats.xml");
var parser = new StatsResponseParser<Product>();
var stats = parser.ParseStats(xml.Root, "stats_fields");

Assert.NotNull(stats);
Assert.Contains("stringField", stats.Keys);
Assert.Contains("dateField", stats.Keys);
Assert.Contains("nullField", stats.Keys);

var stringField = stats["stringField"].GetTyped<string>();
Assert.Equal("Test", stringField.Min);
Assert.Equal("Test string", stringField.Max);

var dateField = stats["dateField"].GetTyped<DateTimeOffset>();
Assert.Equal(DateTimeOffset.Parse("2013-12-20T00:00:00Z"), dateField.Min);
Assert.Equal(DateTimeOffset.Parse("2013-12-23T00:00:00Z"), dateField.Max);
Assert.Equal(DateTimeOffset.Parse("2057-12-10T23:59:59.999Z"), dateField.Sum);
Assert.Equal(DateTimeOffset.Parse("2013-12-21T11:59:59.999Z"), dateField.Mean);

Assert.Null(stats["nullField"].GetTyped<string>().Min);
Assert.Null(stats["nullField"].GetTyped<DateTimeOffset?>().Min);
Assert.Equal(default(DateTimeOffset), stats["nullField"].GetTyped<DateTimeOffset>().Min);
}

[Fact]
public void ParseFacetDateResults()
{
Expand Down
38 changes: 38 additions & 0 deletions SolrNet/ITypedStatsResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace SolrNet
{
/// <summary>
/// ITypedStatsResult of a field.
/// </summary>
public interface ITypedStatsResult<out T>
{
/// <summary>
/// Minimum value
/// </summary>
T Min { get; }

/// <summary>
/// Maximum value
/// </summary>
T Max { get; }

/// <summary>
/// Sum of all values
/// </summary>
T Sum { get; }

/// <summary>
/// Sum of all values squared (useful for stddev)
/// </summary>
T SumOfSquares { get; }

/// <summary>
/// The average (v1+v2...+vN)/N
/// </summary>
T Mean { get; }

/// <summary>
/// Standard deviation
/// </summary>
T StdDev { get; }
}
}
23 changes: 14 additions & 9 deletions SolrNet/Impl/ResponseParsers/StatsResponseParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,26 +83,31 @@ public StatsResult ParseStatsNode(XElement node)
{
var stringValues = new TypedStatsResultString();
var r = new StatsResult(stringValues);
foreach (var statNode in node.Elements()) {
var name = statNode.Attribute("name").Value;
switch (name) {
foreach (var statNode in node.Elements())
{
var nodeName = statNode.Name.LocalName;
var nameAttr = statNode.Attribute("name").Value;
var value = statNode.Value;
if (nodeName == "null")
value = null;
switch (nameAttr) {
case "min":
stringValues.Min = statNode.Value;
stringValues.Min = value;
break;
case "max":
stringValues.Max = statNode.Value;
stringValues.Max = value;
break;
case "sum":
stringValues.Sum = statNode.Value;
stringValues.Sum = value;
break;
case "sumOfSquares":
stringValues.SumOfSquares = statNode.Value;
stringValues.SumOfSquares = value;
break;
case "mean":
stringValues.Mean = statNode.Value;
stringValues.Mean = value;
break;
case "stddev":
stringValues.StdDev = statNode.Value;
stringValues.StdDev = value;
break;
case "count":
r.Count = Convert.ToInt64( statNode.Value, CultureInfo.InvariantCulture );
Expand Down
84 changes: 62 additions & 22 deletions SolrNet/StatsResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
// limitations under the License.
#endregion

using System;
using System.Collections.Generic;
using static SolrNet.Impl.ResponseParsers.StatsResponseParser<double>;

namespace SolrNet {
/// <summary>
Expand All @@ -23,34 +25,58 @@ namespace SolrNet {
/// </summary>
public class StatsResult
{
private readonly TypedStatsResult<string> stringValues;
private readonly TypedStatsResult<double> doubleValues;
private readonly ITypedStatsResult<string> stringValues;

private double min;
private double max;
private double sum;
private double sumOfSquares;
private double mean;
private double stdDev;

/// <summary>
/// Minimum value
/// </summary>
[Obsolete("Deprecated, please use `GetTyped<double>().Min` instead.")]
public double Min
{
get => doubleValues.Min;
set => doubleValues.Min = value;
get
{
if (min == default(double))
min = GetDoubleValue(stringValues.Min);
return min;
}
set => min = value;
}

/// <summary>
/// Maximum value
/// </summary>
[Obsolete("Deprecated, please use `GetTyped<double>().Max` instead.")]
public double Max
{
get => doubleValues.Max;
set => doubleValues.Max = value;
get
{
if (max == default(double))
max = GetDoubleValue(stringValues.Max);
return max;
}
set => max = value;
}

/// <summary>
/// Sum of all values
/// </summary>
[Obsolete("Deprecated, please use `GetTyped<double>().Sum` instead.")]
public double Sum
{
get => doubleValues.Sum;
set => doubleValues.Sum = value;
get
{
if (sum == default(double))
sum = GetDoubleValue(stringValues.Sum);
return sum;
}
set => sum = value;
}

/// <summary>
Expand All @@ -66,28 +92,46 @@ public double Sum
/// <summary>
/// Sum of all values squared (useful for stddev)
/// </summary>
[Obsolete("Deprecated, please use `GetTyped<double>().SumOfSquares` instead.")]
public double SumOfSquares
{
get => doubleValues.SumOfSquares;
set => doubleValues.SumOfSquares = value;
get
{
if (sumOfSquares == default(double))
sumOfSquares = GetDoubleValue(stringValues.SumOfSquares);
return sumOfSquares;
}
set => sumOfSquares = value;
}

/// <summary>
/// The average (v1+v2...+vN)/N
/// </summary>
[Obsolete("Deprecated, please use `GetTyped<double>().Mean` instead.")]
public double Mean
{
get => doubleValues.Mean;
set => doubleValues.Mean = value;
get
{
if (mean == default(double))
mean = GetDoubleValue(stringValues.Mean);
return mean;
}
set => mean = value;
}

/// <summary>
/// Standard deviation
/// </summary>
[Obsolete("Deprecated, please use `GetTyped<double>().StdDev` instead.")]
public double StdDev
{
get => doubleValues.StdDev;
set => doubleValues.StdDev = value;
get
{
if (stdDev == default(double))
stdDev = GetDoubleValue(stringValues.StdDev);
return stdDev;
}
set => stdDev = value;
}

/// <summary>
Expand All @@ -113,27 +157,23 @@ public double StdDev
/// Returns a `TypedStatsResult`.
///
/// In case T is string, the instance's `TypedStatsResultString` is returned.
/// In case T is double, the instance's `TypedStatsResultDouble` is returned.
/// Otherwise a `TypedStatsResultCast` is returned.
/// </summary>
/// <typeparam name="T">The type to cast the `StatsResult` to.</typeparam>
public TypedStatsResult<T> GetTyped<T>()
/// <typeparam name="T">The type to cast the `StatsResult` properties to.</typeparam>
public ITypedStatsResult<T> GetTyped<T>()
{
var type = typeof(T);
if (type == typeof(string))
return stringValues as TypedStatsResult<T>;
if (type == typeof(double))
return doubleValues as TypedStatsResult<T>;
return stringValues as ITypedStatsResult<T>;
return new TypedStatsResultCast<T>(stringValues);
}

/// <summary>
/// Stats results
/// </summary>
public StatsResult(TypedStatsResult<string> stringValues)
public StatsResult(ITypedStatsResult<string> stringValues)
{
this.stringValues = stringValues;
doubleValues = new TypedStatsResultDouble(stringValues);
FacetResults = new Dictionary<string, Dictionary<string, StatsResult>>();
}
}
Expand Down
45 changes: 0 additions & 45 deletions SolrNet/TypedStatsResult.cs

This file was deleted.

Loading

0 comments on commit 12f8b9b

Please sign in to comment.