Skip to content

Commit 9243aaf

Browse files
Merge pull request #33 from Unity3dAzure/iss25
Query support
2 parents 7be3703 + ff5cd30 commit 9243aaf

31 files changed

+1768
-265
lines changed

Assets/AppServices/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ For game developers looking to use Azure App Services (previously Mobile Service
99
3. Create an Azure App Service [Mobile App](https://portal.azure.com)
1010
* Create a Table (using Easy Tables) for app data.
1111

12-
## Unity 5 leaderboard demo
13-
[App Services Demo project](https://github.com/Unity3dAzure/AppServicesDemo) will run inside UnityEditor on Mac or Windows. (The demo project has got everything bundled in and does not require any additional assets to work.)
14-
For detailed instructions read my developer blog on how to [use Azure App Services with Unity project](http://www.deadlyfingers.net/azure/azure-app-services-for-unity3d/).
12+
## Azure App Services Demos for Unity 5
13+
Try the [Azure App Services Demos](https://github.com/Unity3dAzure/AppServicesDemo) project for Unity 5 on Mac / Windows. (The demo project has got everything already bundled in and does not require any additional assets to work. Just wire it up with your [Azure App Service](https://portal.azure.com) and run it right inside the Unity Editor.)
14+
For detailed instructions read my developer blog on [how to setup Azure App Services and Unity demo project](http://www.deadlyfingers.net/azure/azure-app-services-for-unity3d/).
1515

1616
## Supported Features
1717
### MobileServiceClient
@@ -40,9 +40,9 @@ Lookup | Get an item’s data using id property.
4040
void Update<T>(T item, Action<IRestResponse<T>> callback = null) where T : new();
4141
void Delete<T>(string id, Action<IRestResponse<T>> callback = null) where T : new();
4242
void Query<T>(CustomQuery query, Action<IRestResponse<List<T>>> callback = null) where T : new();
43+
void Query<T>(CustomQuery query, Action<IRestResponse<T>> callback = null) where T : INestedResults, new();
4344
void Lookup<T>(string id, Action<IRestResponse<T>> callback = null) where T : new();
4445

45-
4646
## Sample usage
4747
```
4848
using UnityEngine;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.ComponentModel;
3+
using Pathfinding.Serialization.JsonFx;
4+
5+
namespace Unity3dAzure.AppServices
6+
{
7+
[Serializable]
8+
[CLSCompliant(false)]
9+
public abstract class DataModel : IDataModel
10+
{
11+
public string id { get; internal set; }
12+
13+
// system properties
14+
[JsonIgnore] public DateTime createdAt { get; private set; }
15+
[JsonIgnore] public DateTime updatedAt { get; private set; }
16+
[JsonIgnore] public string version { get; private set; }
17+
[JsonIgnore] public bool deleted { get; private set; }
18+
19+
// `$inlinecount=allpages` property
20+
[EditorBrowsable(EditorBrowsableState.Never)]
21+
[JsonIgnore] public uint ROW_NUMBER { get; private set; }
22+
public uint GetIndex()
23+
{
24+
return ROW_NUMBER;
25+
}
26+
27+
public string GetId()
28+
{
29+
return id;
30+
}
31+
32+
public void SetId(string id)
33+
{
34+
this.id = id;
35+
}
36+
37+
public override string ToString ()
38+
{
39+
return string.Format ("id:{0}, createdAt:{1}, updatedAt:{2}, version:{3}, deleted:{4}, index:{5}", id, createdAt, updatedAt, version, deleted, GetIndex());
40+
}
41+
}
42+
}

Assets/AppServices/model/optional/IAzureDataModel.cs renamed to Assets/AppServices/model/base/IDataModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace Unity3dAzure.AppServices
22
{
3-
public interface IAzureDataModel
3+
public interface IDataModel
44
{
55
string GetId();
66
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System;
4+
using RestSharp;
5+
6+
namespace Unity3dAzure.AppServices
7+
{
8+
/// <summary>
9+
/// Interface to support table Query with `$inlinecount=allpages`
10+
/// </summary>
11+
public interface INestedResults
12+
{
13+
}
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Unity3dAzure.AppServices
5+
{
6+
/// <summary>
7+
/// Wrap your data model with this object to call the table Query with `$inlinecount=allpages` param.
8+
/// </summary>
9+
[CLSCompliant(false)]
10+
[Serializable]
11+
public class NestedResults<T> : INestedResults
12+
{
13+
public uint count { get; set; }
14+
public List<T> results { get; set; }
15+
}
16+
}

Assets/AppServices/model/optional/AzureDataModel.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

Assets/AppServices/table/IAzureMobileServiceTable.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public interface IAzureMobileServiceTable
4141
/// https://msdn.microsoft.com/en-us/library/azure/jj677199.aspx
4242
/// </summary>
4343
void Query<T>(CustomQuery query, Action<IRestResponse<List<T>>> callback = null) where T : new();
44+
45+
/// <summary>
46+
/// Returns a 'count' and nested list of 'results' (appends `$inlinecount=allpages` parameter to the query)
47+
/// </summary>
48+
void Query<T>(CustomQuery query, Action<IRestResponse<T>> callback = null) where T : INestedResults, new();
4449

4550
/// <summary>
4651
/// Get an item's data using id property.

Assets/AppServices/table/MobileServiceTable.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,20 @@ public override string ToString()
4646
{
4747
string uri = string.Format("{0}{1}{2}", URI_TABLES, _name, query);
4848
ZumoRequest request = new ZumoRequest(_client, uri, Method.GET);
49-
Debug.Log( "Query Request: " + uri );
50-
_client.ExecuteAsync<List<T>>(request, callback);
49+
Debug.Log( "Query Request: " + uri +" Query:"+ query );
50+
_client.ExecuteAsync<List<T>> (request, callback);
5151
}
52+
53+
public void Query<T>(CustomQuery query, Action<IRestResponse<T>> callback = null) where T : INestedResults, new()
54+
{
55+
string queryResults = query.ToString ();
56+
string q = queryResults.Length > 0 ? "&" : "?";
57+
queryResults += string.Format("{0}$inlinecount=allpages", q);
58+
string uri = string.Format("{0}{1}{2}", URI_TABLES, _name, queryResults);
59+
ZumoRequest request = new ZumoRequest(_client, uri, Method.GET);
60+
Debug.Log( "Query Request: " + uri +" Query with inlinecount:"+ queryResults );
61+
_client.ExecuteAsync<T> (request, callback);
62+
}
5263

5364
public void Update<T>(T item, Action<IRestResponse<T>> callback = null) where T : new()
5465
{

Assets/AppServices/table/query/CustomQuery.cs

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,46 @@
22
using System.Text;
33
using UnityEngine;
44

5+
/// <summary>
6+
/// Query records operation https://msdn.microsoft.com/en-us/library/azure/jj677199.aspx
7+
/// There is a maximum of 50 records returned in a query - use top and skip params to return additional pages of results.
8+
/// NB: `$inlinecount` (which returns count of all items without paging applied) is not set here as it changes the data model and the way the REST decode callback works makes it intangible to decode.
9+
/// Rather the '$inlinecount=allpages' param is automically set when using the table's Query method and wrapping your data model with the NestedResults object wrapper.
10+
/// </summary>
511
namespace Unity3dAzure.AppServices
612
{
13+
[Flags]
14+
public enum MobileServiceSystemProperty
15+
{
16+
nil = 0x0,
17+
createdAt = 0x1,
18+
updatedAt = 0x2,
19+
version = 0x4,
20+
deleted = 0x8
21+
}
22+
723
[CLSCompliant(false)]
824
public class CustomQuery
925
{
26+
// query option parameters defined by the Open Data Protocol (OData)
1027
private string _filter;
1128
private string _orderBy;
12-
private UInt32 _top;
29+
private uint _top;
30+
private uint _skip;
31+
private string _select;
32+
// other params
33+
private MobileServiceSystemProperty _systemProperties;
34+
private bool _includeDeleted;
1335

14-
public CustomQuery(string filter, string orderBy=null, UInt32 top=0)
36+
public CustomQuery(string filter, string orderBy=null, uint top=0, uint skip=0, string select=null, MobileServiceSystemProperty systemProperties=MobileServiceSystemProperty.nil, bool includeDeleted=false)
1537
{
16-
_filter = filter; // return only rows that satisty the specified filter
17-
_orderBy = orderBy; // sort column by 'desc' or 'asc' order
18-
_top = top; // return the top n entities for any query,
38+
_filter = filter; // return only rows that satisty the specified filter predicate
39+
_orderBy = orderBy; // sort column by one or more columns: order can be specified in 'desc' or 'asc' order ('asc' is default)
40+
_top = top; // return the top n entities for any query
41+
_skip = skip; // the n of records to skip (used for paging results)
42+
_select = select; // defines new projection of data by specifying the columns
43+
_systemProperties = systemProperties; // list of system properties to be included in the response
44+
_includeDeleted = includeDeleted; // if table has soft delete enabled then deleted records will be included in the results
1945
}
2046

2147
public static CustomQuery OrderBy(string orderBy) {
@@ -26,24 +52,59 @@ public override string ToString()
2652
{
2753
string queryString = "";
2854
string q = "?";
29-
if (!string.IsNullOrEmpty(_filter)){
30-
queryString += string.Format("{0}$filter=({1})", q, encode(_filter));
55+
if (!string.IsNullOrEmpty(_filter)) {
56+
queryString += string.Format("{0}$filter=({1})", q, _filter);
3157
q = "&";
3258
}
33-
if (!string.IsNullOrEmpty(_orderBy)){
34-
queryString += string.Format("{0}$orderby={1}", q, encode(_orderBy));
59+
if (!string.IsNullOrEmpty(_orderBy)) {
60+
queryString += string.Format("{0}$orderby={1}", q, _orderBy);
3561
q = "&";
3662
}
3763
if (_top > 0) {
3864
queryString += string.Format("{0}$top={1}", q, _top.ToString());
65+
q = "&";
66+
}
67+
if (_skip > 0) {
68+
queryString += string.Format("{0}$skip={1}", q, _skip.ToString());
69+
q = "&";
70+
}
71+
if (!string.IsNullOrEmpty(_select)) {
72+
queryString += string.Format("{0}$select={1}", q, _select);
73+
q = "&";
74+
}
75+
if (_systemProperties!=MobileServiceSystemProperty.nil) {
76+
// NB: setting __systemproperties param doesn't seem to do anything different as these properties are all included by default, but we can append values to the 'select' param.
77+
if (!string.IsNullOrEmpty (_select)) {
78+
queryString += string.Format (",{0}", SystemPropertiesValues (_systemProperties));
79+
}
80+
queryString += string.Format("{0}__systemproperties={1}", q, SystemPropertiesValues(_systemProperties));
81+
q = "&";
3982
}
40-
return queryString;
83+
if (_includeDeleted) {
84+
queryString += string.Format("{0}__includeDeleted=true", q);
85+
}
86+
return EscapeURL(queryString);
4187
}
4288

43-
private string encode(string url)
89+
private string EscapeURL(string query)
4490
{
45-
return WWW.EscapeURL(url.Replace("+", "%20")); // NB: replace space with '%20' and not '+'
91+
string q = WWW.EscapeURL (query);
92+
StringBuilder sb = new StringBuilder (q);
93+
sb.Replace ("+", "%20"); // NB: replace space with '%20' instead of '+'
94+
// keep query params: ?&=$
95+
sb.Replace ("%3f", "?");
96+
sb.Replace ("%26", "&");
97+
sb.Replace ("%3d", "=");
98+
sb.Replace ("%24", "$");
99+
return sb.ToString();
46100
}
47101

102+
private string SystemPropertiesValues(MobileServiceSystemProperty systemProperties)
103+
{
104+
if (systemProperties == MobileServiceSystemProperty.nil) {
105+
return "";
106+
}
107+
return systemProperties.ToString().Replace(", ","%2C"); // remove spaces from string and escape comma
108+
}
48109
}
49110
}

Assets/Prefabs/InventoryCellPrefab.prefab

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ GameObject:
3737
serializedVersion: 4
3838
m_Component:
3939
- 224: {fileID: 224000010748435472}
40-
- 114: {fileID: 114000010894356006}
40+
- 114: {fileID: 114000012629349416}
4141
m_Layer: 5
4242
m_Name: InventoryCellPrefab
4343
m_TagString: Untagged
@@ -96,21 +96,6 @@ GameObject:
9696
m_NavMeshLayer: 0
9797
m_StaticEditorFlags: 0
9898
m_IsActive: 1
99-
--- !u!114 &114000010894356006
100-
MonoBehaviour:
101-
m_ObjectHideFlags: 1
102-
m_PrefabParentObject: {fileID: 0}
103-
m_PrefabInternal: {fileID: 100100000}
104-
m_GameObject: {fileID: 1000012312357288}
105-
m_Enabled: 1
106-
m_EditorHideFlags: 0
107-
m_Script: {fileID: 11500000, guid: 833090a17290e4c3f83e448026c0ccf4, type: 3}
108-
m_Name:
109-
m_EditorClassIdentifier:
110-
Icon: {fileID: 114000013534120506}
111-
Name: {fileID: 114000013939998824}
112-
Amount: {fileID: 114000011077488226}
113-
Btn: {fileID: 114000011147225522}
11499
--- !u!114 &114000011077488226
115100
MonoBehaviour:
116101
m_ObjectHideFlags: 1
@@ -196,6 +181,21 @@ MonoBehaviour:
196181
m_CallState: 2
197182
m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0,
198183
Culture=neutral, PublicKeyToken=null
184+
--- !u!114 &114000012629349416
185+
MonoBehaviour:
186+
m_ObjectHideFlags: 1
187+
m_PrefabParentObject: {fileID: 0}
188+
m_PrefabInternal: {fileID: 100100000}
189+
m_GameObject: {fileID: 1000012312357288}
190+
m_Enabled: 1
191+
m_EditorHideFlags: 0
192+
m_Script: {fileID: 11500000, guid: 177d64a8e263e4ca990b0b5b61fd989a, type: 3}
193+
m_Name:
194+
m_EditorClassIdentifier:
195+
Icon: {fileID: 114000013534120506}
196+
Name: {fileID: 114000013939998824}
197+
Amount: {fileID: 114000011077488226}
198+
Btn: {fileID: 114000011147225522}
199199
--- !u!114 &114000013534120506
200200
MonoBehaviour:
201201
m_ObjectHideFlags: 1

0 commit comments

Comments
 (0)