Permalink
Browse files

Add Skip and Take methods to Query

  • Loading branch information...
1 parent ae677c3 commit 8ce0fdd6705da686000050d41c7d06cadad5a89c @Valve Valve committed Mar 5, 2014
View
2 Igooana.Demo/MainWindow.xaml.cs
@@ -31,7 +31,7 @@ public partial class MainWindow : Window {
var query = Query
.For(74167085, DateTime.Now.AddDays(-31), DateTime.Now)
.WithMetrics(Metric.PageTracking.Pageviews + Metric.PageTracking.AvgTimeOnPage + Metric.Session.TimeOnSite)
- .WithDimensions(Dimension.Time.DayOfWeek);
+ .WithDimensions(Dimension.Time.DayOfWeek).Skip(2).Take(3);
var result = await api.Execute(query);
}
catch (ConnectionException ex) {
View
12 Igooana/Extensions/StringExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
@@ -27,5 +28,16 @@ internal static class StringExtensions {
// should be much faster than regex
return Array.TrueForAll<Char>(input.ToCharArray(), x => Char.IsDigit(x));
}
+
+ /// <summary>
+ /// Converts CamelCased strings to dash-separated.
+ /// The input will be split by capitals, downcased and joined with a dash.
+ /// This method handles unicode as well.
+ /// </summary>
+ /// <param name="input">input string to process</param>
+ /// <returns>Dasherized string</returns>
+ internal static string Dasherize(this string input) {
+ return String.Join("-", Regex.Matches(input, @"\p{Lu}\p{Ll}*").Cast<Match>().Select(x => x.Value.ToLowerInvariant()));
+ }
}
}
View
6 Igooana/GaParam.cs
@@ -0,0 +1,6 @@
+using System;
+
+namespace Igooana {
+ [AttributeUsage(AttributeTargets.Property)]
+ public class GaParam : Attribute { }
+}
View
2 Igooana/Igooana.csproj
@@ -52,6 +52,7 @@
<Compile Include="Dimensions\Visitor.cs" />
<Compile Include="Ensure.cs" />
<Compile Include="Extensions\JsonExtensions.cs" />
+ <Compile Include="GaParam.cs" />
<Compile Include="Json\Management\JsonResponse.cs" />
<Compile Include="Json\JsonResponse.cs" />
<Compile Include="Json\ResponseColumn.cs" />
@@ -77,6 +78,7 @@
<Compile Include="Metric.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Query.cs" />
+ <Compile Include="QueryOptions.cs" />
<Compile Include="Response.cs" />
</ItemGroup>
<ItemGroup>
View
35 Igooana/Query.cs
@@ -9,13 +9,15 @@ public class Query {
private readonly DateTime dt2;
private readonly Metric metric;
private readonly Dimension dimension;
+ private readonly QueryOptions options;
- private Query(int profileId, DateTime dt1, DateTime dt2, Metric metric = null, Dimension dimension = null) {
+ private Query(int profileId, DateTime dt1, DateTime dt2, Metric metric = null, Dimension dimension = null, QueryOptions options = null) {
this.profileId = profileId;
this.dt1 = dt1;
this.dt2 = dt2;
this.metric = metric ?? Metric.Empty;
this.dimension = dimension ?? Dimension.Empty;
+ this.options = options ?? QueryOptions.Empty;
}
public static Query For(int profileId, DateTime dt1, DateTime dt2) {
@@ -30,6 +32,33 @@ public class Query {
return new Query(profileId, dt1, dt2, metric, this.dimension + dimension);
}
+ /// <summary>
+ /// Maximum number of rows to include in the response.
+ /// You can use this in combination with Skip to retrieve a subset of elements,
+ /// or use it alone to restrict the number of returned elements, starting with the first.
+ /// If Take is not called, the query returns the default maximum of 1000 rows.
+ /// It can also return fewer rows than requested, if there aren't as many dimension segments as you expect.
+ /// For instance, there are fewer than 300 possible values for Dimension.GeoNetwork.Country,
+ /// so when segmenting only by country, you can't get more than 300 rows, even if you call Take with a higher value.
+ /// </summary>
+ /// <param name="rows">Maximum number of rows to include in the response</param>
+ /// <returns>New instance of Query with applied option</returns>
+ public Query Take(int rows) {
+ return new Query(profileId, dt1, dt2, metric, dimension, options + new QueryOptions { MaxResults = rows });
+ }
+
+ /// <summary>
+ /// If not supplied, the starting index is 1.
+ /// (Result indexes are 1-based. That is, the first row is row 1, not row 0.)
+ /// Use this parameter as a pagination mechanism along with the Take parameter or for situations when total results count exceeds 10,000
+ /// and you want to retrieve rows indexed at 10,001 and beyond.
+ /// </summary>
+ /// <param name="rows">Starting index of rows to include in the response</param>
+ /// <returns>New instance of Query with applied option</returns>
+ public Query Skip(int rows) {
+ return new Query(profileId, dt1, dt2, metric, dimension, options + new QueryOptions { StartIndex = rows });
+ }
+
public override string ToString() {
string dateFormat = "yyyy-MM-dd";
@@ -41,6 +70,10 @@ public class Query {
if (!dimension.IsEmpty) {
builder.AppendFormat("&dimensions={0}", dimension);
}
+ if (!options.IsEmpty) {
+ builder.AppendFormat("&{0}", options);
+ }
+
return builder.ToString();
}
}
View
56 Igooana/QueryOptions.cs
@@ -0,0 +1,56 @@
+using Igooana.Extensions;
+using System;
+using System.Linq;
+using System.Reflection;
+namespace Igooana {
+ internal class QueryOptions {
+ internal static readonly QueryOptions Empty = new QueryOptions();
+
+ internal QueryOptions() { }
+
+ /// <summary>
+ /// Maximum number of rows to include in this response.
+ /// </summary>
+ [GaParam]
+ public int MaxResults { get; set; }
+
+ /// <summary>
+ /// The first row of data to retrieve, starting at 1. Use this parameter as a pagination mechanism along with the MaxResults parameter.
+ /// </summary>
+ [GaParam]
+ public int StartIndex { get; set; }
+
+ public static QueryOptions operator +(QueryOptions o1, QueryOptions o2) {
+ Ensure.ArgumentNotNull(o1, "o1");
+ Ensure.ArgumentNotNull(o2, "o2");
+ return o1.Add(o2);
+ }
+
+ public QueryOptions Add(QueryOptions other) {
+ if (object.ReferenceEquals(this, other)) {
+ throw new InvalidOperationException("Addition must use two different QueryOption instances");
+ }
+ return new QueryOptions {
+ MaxResults = MaxResults == default(int) ? other.MaxResults : MaxResults,
+ StartIndex = StartIndex == default(int) ? other.StartIndex : StartIndex
+ };
+ }
+
+ public bool IsEmpty {
+ get {
+ return MaxResults == default(int) && StartIndex == default(int);
+ }
+ }
+ public override string ToString() {
+ if (IsEmpty) {
+ return "";
+ }
+ var urlParams = GetType()
+ .GetTypeInfo()
+ .DeclaredProperties
+ .Where(x => x.IsDefined(typeof(GaParam)))
+ .Select(x => "{0}={1}".FormatWith(x.Name.Dasherize(), x.GetValue(this)));
+ return String.Join("&", urlParams);
+ }
+ }
+}

0 comments on commit 8ce0fdd

Please sign in to comment.