diff --git a/Storage/Storage.Test/QueryTest.cs b/Storage/Storage.Test/QueryTest.cs index 354646c..41f2c9f 100644 --- a/Storage/Storage.Test/QueryTest.cs +++ b/Storage/Storage.Test/QueryTest.cs @@ -17,7 +17,8 @@ namespace LeanCloudTests { [Test] public async Task TestQuery() { - var query = new AVQuery("Foo").WhereEqualTo("content", "hello"); + var query = new AVQuery("Foo"); + query.WhereEqualTo("content", "hello"); var results = await query.FindAsync(); foreach (var result in results) { TestContext.Out.WriteLine(result.ObjectId); @@ -26,7 +27,8 @@ namespace LeanCloudTests { [Test] public async Task TestQueryCount() { - var query = new AVQuery("Foo").WhereEqualTo("content", "hello, world"); + var query = new AVQuery("Foo"); + query.WhereEqualTo("content", "hello, world"); var count = await query.CountAsync(); Assert.Greater(count, 8); } diff --git a/Storage/Storage/Internal/Query/Controller/AVQueryController.cs b/Storage/Storage/Internal/Query/Controller/AVQueryController.cs index 46b286f..d53c361 100644 --- a/Storage/Storage/Internal/Query/Controller/AVQueryController.cs +++ b/Storage/Storage/Internal/Query/Controller/AVQueryController.cs @@ -22,7 +22,7 @@ namespace LeanCloud.Storage.Internal { string sessionToken = user != null ? user.SessionToken : null; - return FindAsync(query.RelativeUri, query.BuildParameters(), sessionToken, cancellationToken).OnSuccess(t => + return FindAsync(query.Path, query.BuildParameters(), sessionToken, cancellationToken).OnSuccess(t => { var items = t.Result["results"] as IList; @@ -40,7 +40,7 @@ namespace LeanCloud.Storage.Internal parameters["limit"] = 0; parameters["count"] = 1; - return FindAsync(query.RelativeUri, parameters, sessionToken, cancellationToken).OnSuccess(t => + return FindAsync(query.Path, parameters, sessionToken, cancellationToken).OnSuccess(t => { return Convert.ToInt32(t.Result["count"]); }); @@ -54,7 +54,7 @@ namespace LeanCloud.Storage.Internal var parameters = query.BuildParameters(); parameters["limit"] = 1; - return FindAsync(query.RelativeUri, parameters, sessionToken, cancellationToken).OnSuccess(t => + return FindAsync(query.Path, parameters, sessionToken, cancellationToken).OnSuccess(t => { var items = t.Result["results"] as IList; var item = items.FirstOrDefault() as IDictionary; @@ -69,13 +69,13 @@ namespace LeanCloud.Storage.Internal }); } - private Task> FindAsync(string relativeUri, + private Task> FindAsync(string path, IDictionary parameters, string sessionToken, CancellationToken cancellationToken) { var command = new AVCommand { - Path = $"{relativeUri}?{AVClient.BuildQueryString(parameters)}", + Path = $"{path}?{AVClient.BuildQueryString(parameters)}", Method = HttpMethod.Get }; return commandRunner.RunCommandAsync>(command, cancellationToken: cancellationToken).OnSuccess(t => diff --git a/Storage/Storage/Public/AVQuery.cs b/Storage/Storage/Public/AVQuery.cs index 7512c39..e99d33c 100644 --- a/Storage/Storage/Public/AVQuery.cs +++ b/Storage/Storage/Public/AVQuery.cs @@ -1,83 +1,65 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.Threading.Tasks; -using LeanCloud.Storage.Internal; using System.Net.Http; +using System.Text.RegularExpressions; using Newtonsoft.Json; +using LeanCloud.Storage.Internal; namespace LeanCloud { + public class AVQuery where T : AVObject { + public string ClassName { + get; internal set; + } - /// - /// The AVQuery class defines a query that is used to fetch AVObjects. The - /// most common use case is finding all objects that match a query through the - /// method. - /// - /// - /// This sample code fetches all objects of - /// class "MyClass": - /// - /// - /// AVQuery query = new AVQuery("MyClass"); - /// IEnumerable<AVObject> result = await query.FindAsync(); - /// - /// - /// A AVQuery can also be used to retrieve a single object whose id is known, - /// through the method. For example, this sample code - /// fetches an object of class "MyClass" and id myId. - /// - /// - /// AVQuery query = new AVQuery("MyClass"); - /// AVObject result = await query.GetAsync(myId); - /// - /// - /// A AVQuery can also be used to count the number of objects that match the - /// query without retrieving all of those objects. For example, this sample code - /// counts the number of objects of the class "MyClass". - /// - /// - /// AVQuery query = new AVQuery("MyClass"); - /// int count = await query.CountAsync(); - /// - /// - public class AVQuery : AVQueryPair, T>, IAVQuery - where T : AVObject - { - internal static IAVQueryController QueryController - { - get - { + private string path; + public string Path { + get { + if (string.IsNullOrEmpty(path)) { + return $"classes/{Uri.EscapeDataString(ClassName)}"; + } + return path; + } set { + path = value; + } + } + + internal IDictionary where; + internal ReadOnlyCollection orderBy; + internal ReadOnlyCollection includes; + internal ReadOnlyCollection selectedKeys; + internal string redirectClassNameForKey; + internal int? skip; + internal int? limit; + + internal static IAVQueryController QueryController { + get { return AVPlugins.Instance.QueryController; } } - internal static IObjectSubclassingController SubclassingController - { - get - { + internal static IObjectSubclassingController SubclassingController { + get { return AVPlugins.Instance.SubclassingController; } } - /// - /// 调试时可以用来查看最终的发送的查询语句 - /// - private string JsonString - { - get - { - return JsonConvert.SerializeObject(BuildParameters(true)); - } + public AVQuery() + : this(SubclassingController.GetClassName(typeof(T))) { + } + + public AVQuery(string className) { + if (string.IsNullOrEmpty(className)) { + throw new ArgumentNullException(nameof(className)); + } + ClassName = className; } - /// - /// Private constructor for composition of queries. A Source query is required, - /// but the remaining values can be null if they won't be changed in this - /// composition. - /// private AVQuery(AVQuery source, IDictionary where = null, IEnumerable replacementOrderBy = null, @@ -86,64 +68,90 @@ namespace LeanCloud int? limit = null, IEnumerable includes = null, IEnumerable selectedKeys = null, - String redirectClassNameForKey = null) - : base(source, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey) - { + string redirectClassNameForKey = null) { + if (source == null) { + throw new ArgumentNullException(nameof(source)); + } + + ClassName = source.ClassName; + this.where = source.where; + this.orderBy = source.orderBy; + this.skip = source.skip; + this.limit = source.limit; + this.includes = source.includes; + this.selectedKeys = source.selectedKeys; + this.redirectClassNameForKey = source.redirectClassNameForKey; + + if (where != null) { + var newWhere = MergeWhereClauses(where); + this.where = new Dictionary(newWhere); + } + + if (replacementOrderBy != null) { + this.orderBy = new ReadOnlyCollection(replacementOrderBy.ToList()); + } + + if (thenBy != null) { + if (this.orderBy == null) { + throw new ArgumentException("You must call OrderBy before calling ThenBy."); + } + var newOrderBy = new List(this.orderBy); + newOrderBy.AddRange(thenBy); + this.orderBy = new ReadOnlyCollection(newOrderBy); + } + + // Remove duplicates. + if (this.orderBy != null) { + var newOrderBy = new HashSet(this.orderBy); + this.orderBy = new ReadOnlyCollection(newOrderBy.ToList()); + } + + if (skip != null) { + this.skip = (this.skip ?? 0) + skip; + } + + if (limit != null) { + this.limit = limit; + } + + if (includes != null) { + var newIncludes = MergeIncludes(includes); + this.includes = new ReadOnlyCollection(newIncludes.ToList()); + } + + if (selectedKeys != null) { + var newSelectedKeys = MergeSelectedKeys(selectedKeys); + this.selectedKeys = new ReadOnlyCollection(newSelectedKeys.ToList()); + } + + if (redirectClassNameForKey != null) { + this.redirectClassNameForKey = redirectClassNameForKey; + } } - //internal override AVQuery CreateInstance( - // AVQuery source, - // IDictionary where = null, - // IEnumerable replacementOrderBy = null, - // IEnumerable thenBy = null, - // int? skip = null, - // int? limit = null, - // IEnumerable includes = null, - // IEnumerable selectedKeys = null, - // String redirectClassNameForKey = null) - //{ - // return new AVQuery(this, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey); - //} - - public override AVQuery CreateInstance( - IDictionary where = null, - IEnumerable replacementOrderBy = null, - IEnumerable thenBy = null, - int? skip = null, - int? limit = null, - IEnumerable includes = null, - IEnumerable selectedKeys = null, - String redirectClassNameForKey = null) - { - return new AVQuery(this, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey); + HashSet MergeIncludes(IEnumerable otherIncludes) { + if (includes == null) { + return new HashSet(otherIncludes); + } + var newIncludes = new HashSet(includes); + foreach (var item in otherIncludes) { + newIncludes.Add(item); + } + return newIncludes; } - - /// - /// Constructs a query based upon the AVObject subclass used as the generic parameter for the AVQuery. - /// - public AVQuery() - : this(SubclassingController.GetClassName(typeof(T))) - { + HashSet MergeSelectedKeys(IEnumerable otherSelectedKeys) { + if (selectedKeys == null) { + return new HashSet(otherSelectedKeys); + } + var newSelectedKeys = new HashSet(selectedKeys); + foreach (var item in otherSelectedKeys) { + newSelectedKeys.Add(item); + } + return newSelectedKeys; } - /// - /// Constructs a query. A default query with no further parameters will retrieve - /// all s of the provided class. - /// - /// The name of the class to retrieve AVObjects for. - public AVQuery(string className) - : base(className) - { - - } - - /// - /// Constructs a query that is the or of the given queries. - /// - /// The list of AVQueries to 'or' together. - /// A AVQquery that is the 'or' of the passed in queries. public static AVQuery Or(IEnumerable> queries) { string className = null; @@ -153,12 +161,12 @@ namespace LeanCloud foreach (var obj in nonGenericQueries) { var q = (AVQuery)obj; - if (className != null && q.className != className) + if (className != null && q.ClassName != className) { throw new ArgumentException( "All of the queries in an or query must be on the same class."); } - className = q.className; + className = q.ClassName; var parameters = q.BuildParameters(); if (parameters.Count == 0) { @@ -178,12 +186,7 @@ namespace LeanCloud }); } - /// - /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - /// - /// The cancellation token. - /// The list of AVObjects that match this query. - public override Task> FindAsync(CancellationToken cancellationToken) + public Task> FindAsync(CancellationToken cancellationToken = default) { return AVUser.GetCurrentUserAsync().OnSuccess(t => { @@ -196,12 +199,7 @@ namespace LeanCloud }); } - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// The cancellation token. - /// A single AVObject that satisfies this query, or else null. - public override Task FirstOrDefaultAsync(CancellationToken cancellationToken) + public Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) { return AVUser.GetCurrentUserAsync().OnSuccess(t => { @@ -213,13 +211,7 @@ namespace LeanCloud }); } - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// The cancellation token. - /// A single AVObject that satisfies this query. - /// If no results match the query. - public override Task FirstAsync(CancellationToken cancellationToken) + public Task FirstAsync(CancellationToken cancellationToken) { return FirstOrDefaultAsync(cancellationToken).OnSuccess(t => { @@ -232,29 +224,16 @@ namespace LeanCloud }); } - /// - /// Counts the number of objects that match this query. - /// - /// The cancellation token. - /// The number of objects that match this query. - public override Task CountAsync(CancellationToken cancellationToken) + public Task CountAsync(CancellationToken cancellationToken = default) { - return AVUser.GetCurrentUserAsync().OnSuccess(t => - { + return AVUser.GetCurrentUserAsync().OnSuccess(t => { return QueryController.CountAsync(this, t.Result, cancellationToken); }).Unwrap(); } - /// - /// Constructs a AVObject whose id is already known by fetching data - /// from the server. - /// - /// ObjectId of the AVObject to fetch. - /// The cancellation token. - /// The AVObject for the given objectId. - public override Task GetAsync(string objectId, CancellationToken cancellationToken) + public Task GetAsync(string objectId, CancellationToken cancellationToken) { - AVQuery singleItemQuery = new AVQuery(className) + AVQuery singleItemQuery = new AVQuery(ClassName) .WhereEqualTo("objectId", objectId); singleItemQuery = new AVQuery(singleItemQuery, includes: this.includes, selectedKeys: this.selectedKeys, limit: 1); return singleItemQuery.FindAsync(cancellationToken).OnSuccess(t => @@ -329,31 +308,384 @@ namespace LeanCloud #endregion + IDictionary MergeWhereClauses(IDictionary otherWhere) { + if (where == null) { + where = otherWhere; + return where; + } + var newWhere = new Dictionary(where); + foreach (var pair in otherWhere) { + var condition = pair.Value as IDictionary; + if (newWhere.ContainsKey(pair.Key)) { + var oldCondition = newWhere[pair.Key] as IDictionary; + if (oldCondition == null || condition == null) { + throw new ArgumentException("More than one where clause for the given key provided."); + } + var newCondition = new Dictionary(oldCondition); + foreach (var conditionPair in condition) { + if (newCondition.ContainsKey(conditionPair.Key)) { + throw new ArgumentException("More than one condition for the given key provided."); + } + newCondition[conditionPair.Key] = conditionPair.Value; + } + newWhere[pair.Key] = newCondition; + } else { + newWhere[pair.Key] = pair.Value; + } + } + where = newWhere; + return where; + } + + /// + /// 构建查询字符串 + /// + /// 是否包含 ClassName + /// + public IDictionary BuildParameters(bool includeClassName = false) { + Dictionary result = new Dictionary(); + if (where != null) { + result["where"] = PointerOrLocalIdEncoder.Instance.Encode(where); + } + if (orderBy != null) { + result["order"] = string.Join(",", orderBy.ToArray()); + } + if (skip != null) { + result["skip"] = skip.Value; + } + if (limit != null) { + result["limit"] = limit.Value; + } + if (includes != null) { + result["include"] = string.Join(",", includes.ToArray()); + } + if (selectedKeys != null) { + result["keys"] = string.Join(",", selectedKeys.ToArray()); + } + if (includeClassName) { + result["className"] = ClassName; + } + if (redirectClassNameForKey != null) { + result["redirectClassNameForKey"] = redirectClassNameForKey; + } + return result; + } + /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false - public override bool Equals(object obj) - { - if (obj == null || !(obj is AVQuery)) - { + public override bool Equals(object obj) { + if (obj == null || !(obj is AVQuery)) { return false; } var other = obj as AVQuery; - return Object.Equals(this.className, other.ClassName) && + return ClassName.Equals(other.ClassName) && this.where.CollectionsEqual(other.where) && this.orderBy.CollectionsEqual(other.orderBy) && this.includes.CollectionsEqual(other.includes) && this.selectedKeys.CollectionsEqual(other.selectedKeys) && - Object.Equals(this.skip, other.skip) && - Object.Equals(this.limit, other.limit); + object.Equals(this.skip, other.skip) && + object.Equals(this.limit, other.limit); } - public override int GetHashCode() - { + public override int GetHashCode() { return base.GetHashCode(); } + + #region Order By + + public AVQuery OrderBy(string key) { + orderBy = new ReadOnlyCollection(new List { key }); + return this; + } + + public AVQuery OrderByDescending(string key) { + orderBy = new ReadOnlyCollection(new List { "-" + key }); + return this; + } + + public AVQuery ThenBy(string key) { + if (orderBy == null) { + throw new ArgumentException("You must call OrderBy before calling ThenBy"); + } + List newOrderBy = orderBy.ToList(); + newOrderBy.Add(key); + orderBy = new ReadOnlyCollection(newOrderBy); + return this; + } + + public AVQuery ThenByDescending(string key) { + if (orderBy == null) { + throw new ArgumentException("You must call OrderBy before calling ThenBy"); + } + List newOrderBy = orderBy.ToList(); + newOrderBy.Add($"-{key}"); + orderBy = new ReadOnlyCollection(newOrderBy); + return this; + } + + #endregion + + public AVQuery Include(string key) { + includes = new ReadOnlyCollection(new List { key }); + return this; + } + + public AVQuery Select(string key) { + selectedKeys = new ReadOnlyCollection(new List { key }); + return this; + } + + public AVQuery Skip(int count) { + skip = count; + return this; + } + + public AVQuery Limit(int count) { + limit = count; + return this; + } + + internal AVQuery RedirectClassName(string key) { + redirectClassNameForKey = key; + return this; + } + + #region Where + + public AVQuery WhereContainedIn(string key, IEnumerable values) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$in", values.ToList() } } } + }); + return this; + } + + public AVQuery WhereContainsAll(string key, IEnumerable values) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$all", values.ToList() } } } + }); + return this; + } + + public AVQuery WhereContains(string key, string substring) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$regex", RegexQuote(substring) } } } + }); + return this; + } + + public AVQuery WhereDoesNotExist(string key) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$exists", false } } } + }); + return this; + } + + public AVQuery WhereDoesNotMatchQuery(string key, AVQuery query) + where TOther : AVObject { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$notInQuery", query.BuildParameters(true) } } } + }); + return this; + } + + public AVQuery WhereEndsWith(string key, string suffix) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$regex", RegexQuote(suffix) + "$" } } } + }); + return this; + } + + public AVQuery WhereEqualTo(string key, object value) { + MergeWhereClauses(new Dictionary { + { key, value } + }); + return this; + } + + public AVQuery WhereSizeEqualTo(string key, uint size) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$size", size } } } + }); + return this; + } + + public AVQuery WhereExists(string key) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$exists", true } } } + }); + return this; + } + + public AVQuery WhereGreaterThan(string key, object value) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$gt", value } } } + }); + return this; + } + + public AVQuery WhereGreaterThanOrEqualTo(string key, object value) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$gte", value } } } + }); + return this; + } + + public AVQuery WhereLessThan(string key, object value) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$lt", value } } } + }); + return this; + } + + public AVQuery WhereLessThanOrEqualTo(string key, object value) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$lte", value } } } + }); + return this; + } + + public AVQuery WhereMatches(string key, Regex regex, string modifiers) { + if (!regex.Options.HasFlag(RegexOptions.ECMAScript)) { + throw new ArgumentException( + "Only ECMAScript-compatible regexes are supported. Please use the ECMAScript RegexOptions flag when creating your regex."); + } + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { key, EncodeRegex(regex, modifiers) } } } + }); + return this; + } + + public AVQuery WhereMatches(string key, Regex regex) { + return WhereMatches(key, regex, null); + } + + public AVQuery WhereMatches(string key, string pattern, string modifiers = null) { + return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); + } + + public AVQuery WhereMatches(string key, string pattern) { + return WhereMatches(key, pattern, null); + } + + public AVQuery WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) + where TOther : AVObject { + var parameters = new Dictionary { + { "query", query.BuildParameters(true)}, + { "key", keyInQuery} + }; + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$select", parameters } } } + }); + return this; + } + + public AVQuery WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) + where TOther : AVObject { + var parameters = new Dictionary { + { "query", query.BuildParameters(true)}, + { "key", keyInQuery} + }; + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$dontSelect", parameters } } } + }); + return this; + } + + public AVQuery WhereMatchesQuery(string key, AVQuery query) + where TOther : AVObject { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$inQuery", query.BuildParameters(true) } } } + }); + return this; + } + + public AVQuery WhereNear(string key, AVGeoPoint point) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$nearSphere", point } } } + }); + return this; + } + + public AVQuery WhereNotContainedIn(string key, IEnumerable values) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$nin", values.ToList() } } } + }); + return this; + } + + public AVQuery WhereNotEqualTo(string key, object value) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$ne", value } } } + }); + return this; + } + + public AVQuery WhereStartsWith(string key, string suffix) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$regex", "^" + RegexQuote(suffix) } } } + }); + return this; + } + + public AVQuery WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$within", + new Dictionary { + { "$box", new[] {southwest, northeast}} + } } } } + }); + return this; + } + + public AVQuery WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$nearSphere", point } } } + }); + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$maxDistance", maxDistance.Radians } } } + }); + return this; + } + + internal AVQuery WhereRelatedTo(AVObject parent, string key) { + MergeWhereClauses(new Dictionary { + { key, new Dictionary{ { "$relatedTo", new Dictionary { + { "object", parent}, + { "key", key} + } } } } + }); + return this; + } + + #endregion + + private string RegexQuote(string input) { + return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; + } + + private IDictionary EncodeRegex(Regex regex, string modifiers) { + var options = GetRegexOptions(regex, modifiers); + var dict = new Dictionary(); + dict["$regex"] = regex.ToString(); + if (!string.IsNullOrEmpty(options)) { + dict["$options"] = options; + } + return dict; + } + + private string GetRegexOptions(Regex regex, string modifiers) { + string result = modifiers ?? ""; + if (regex.Options.HasFlag(RegexOptions.IgnoreCase) && !modifiers.Contains("i")) { + result += "i"; + } + if (regex.Options.HasFlag(RegexOptions.Multiline) && !modifiers.Contains("m")) { + result += "m"; + } + return result; + } } } diff --git a/Storage/Storage/Public/AVUser.cs b/Storage/Storage/Public/AVUser.cs index d2fb783..9e2992b 100644 --- a/Storage/Storage/Public/AVUser.cs +++ b/Storage/Storage/Public/AVUser.cs @@ -322,7 +322,7 @@ namespace LeanCloud public AVQuery GetFollowerQuery() { AVQuery query = new AVQuery { - RelativeUri = string.Format("users/{0}/followers", this.ObjectId) + Path = $"users/{ObjectId}/followers" }; return query; } @@ -333,8 +333,9 @@ namespace LeanCloud /// public AVQuery GetFolloweeQuery() { - AVQuery query = new AVQuery(); - query.RelativeUri = string.Format("users/{0}/followees", this.ObjectId); + AVQuery query = new AVQuery { + Path = $"users/{ObjectId}/followees" + }; return query; } @@ -344,8 +345,9 @@ namespace LeanCloud /// public AVQuery GetFollowersAndFolloweesQuery() { - AVQuery query = new AVQuery(); - query.RelativeUri = string.Format("users/{0}/followersAndFollowees", this.ObjectId); + AVQuery query = new AVQuery { + Path = $"users/{ObjectId}/followersAndFollowees" + }; return query; } @@ -355,7 +357,7 @@ namespace LeanCloud /// public Task> GetFollowersAsync() { - return this.GetFollowerQuery().FindAsync(); + return GetFollowerQuery().FindAsync(CancellationToken.None); } /// @@ -364,7 +366,7 @@ namespace LeanCloud /// public Task> GetFolloweesAsync() { - return this.GetFolloweeQuery().FindAsync(); + return GetFolloweeQuery().FindAsync(CancellationToken.None); } diff --git a/Storage/Storage/Public/IAVQuery.cs b/Storage/Storage/Public/IAVQuery.cs index a4ee243..35b64e7 100644 --- a/Storage/Storage/Public/IAVQuery.cs +++ b/Storage/Storage/Public/IAVQuery.cs @@ -1097,972 +1097,4 @@ namespace LeanCloud return dict; } } - - //public abstract class AVQueryBase : IAVQueryTuple - // where T : IAVObject - //{ - // protected readonly string className; - // protected readonly Dictionary where; - // protected readonly ReadOnlyCollection orderBy; - // protected readonly ReadOnlyCollection includes; - // protected readonly ReadOnlyCollection selectedKeys; - // protected readonly String redirectClassNameForKey; - // protected readonly int? skip; - // protected readonly int? limit; - - // internal string ClassName { get { return className; } } - - // private string relativeUri; - // internal string RelativeUri - // { - // get - // { - // string rtn = string.Empty; - // if (string.IsNullOrEmpty(relativeUri)) - // { - // rtn = "classes/" + Uri.EscapeDataString(this.className); - // } - // else - // { - // rtn = relativeUri; - // } - // return rtn; - // } - // set - // { - // relativeUri = value; - // } - // } - // public Dictionary Condition - // { - // get { return this.where; } - // } - - // protected AVQueryBase() - // { - - // } - - // internal abstract S CreateInstance(AVQueryBase source, - // IDictionary where = null, - // IEnumerable replacementOrderBy = null, - // IEnumerable thenBy = null, - // int? skip = null, - // int? limit = null, - // IEnumerable includes = null, - // IEnumerable selectedKeys = null, - // String redirectClassNameForKey = null); - - // /// - // /// Private constructor for composition of queries. A Source query is required, - // /// but the remaining values can be null if they won't be changed in this - // /// composition. - // /// - // protected AVQueryBase(AVQueryBase source, - // IDictionary where = null, - // IEnumerable replacementOrderBy = null, - // IEnumerable thenBy = null, - // int? skip = null, - // int? limit = null, - // IEnumerable includes = null, - // IEnumerable selectedKeys = null, - // String redirectClassNameForKey = null) - // { - // if (source == null) - // { - // throw new ArgumentNullException("Source"); - // } - - // className = source.className; - // this.where = source.where; - // this.orderBy = source.orderBy; - // this.skip = source.skip; - // this.limit = source.limit; - // this.includes = source.includes; - // this.selectedKeys = source.selectedKeys; - // this.redirectClassNameForKey = source.redirectClassNameForKey; - - // if (where != null) - // { - // var newWhere = MergeWhereClauses(where); - // this.where = new Dictionary(newWhere); - // } - - // if (replacementOrderBy != null) - // { - // this.orderBy = new ReadOnlyCollection(replacementOrderBy.ToList()); - // } - - // if (thenBy != null) - // { - // if (this.orderBy == null) - // { - // throw new ArgumentException("You must call OrderBy before calling ThenBy."); - // } - // var newOrderBy = new List(this.orderBy); - // newOrderBy.AddRange(thenBy); - // this.orderBy = new ReadOnlyCollection(newOrderBy); - // } - - // // Remove duplicates. - // if (this.orderBy != null) - // { - // var newOrderBy = new HashSet(this.orderBy); - // this.orderBy = new ReadOnlyCollection(newOrderBy.ToList()); - // } - - // if (skip != null) - // { - // this.skip = (this.skip ?? 0) + skip; - // } - - // if (limit != null) - // { - // this.limit = limit; - // } - - // if (includes != null) - // { - // var newIncludes = MergeIncludes(includes); - // this.includes = new ReadOnlyCollection(newIncludes.ToList()); - // } - - // if (selectedKeys != null) - // { - // var newSelectedKeys = MergeSelectedKeys(selectedKeys); - // this.selectedKeys = new ReadOnlyCollection(newSelectedKeys.ToList()); - // } - - // if (redirectClassNameForKey != null) - // { - // this.redirectClassNameForKey = redirectClassNameForKey; - // } - // } - - // public AVQueryBase(string className) - // { - // if (string.IsNullOrEmpty(className)) - // { - // throw new ArgumentNullException("className", "Must specify a AVObject class name when creating a AVQuery."); - // } - // this.className = className; - // } - - // private HashSet MergeIncludes(IEnumerable includes) - // { - // if (this.includes == null) - // { - // return new HashSet(includes); - // } - // var newIncludes = new HashSet(this.includes); - // foreach (var item in includes) - // { - // newIncludes.Add(item); - // } - // return newIncludes; - // } - - // private HashSet MergeSelectedKeys(IEnumerable selectedKeys) - // { - // if (this.selectedKeys == null) - // { - // return new HashSet(selectedKeys); - // } - // var newSelectedKeys = new HashSet(this.selectedKeys); - // foreach (var item in selectedKeys) - // { - // newSelectedKeys.Add(item); - // } - // return newSelectedKeys; - // } - - // private IDictionary MergeWhereClauses(IDictionary where) - // { - // return MergeWhere(this.where, where); - // } - - // public virtual IDictionary MergeWhere(IDictionary primary, IDictionary secondary) - // { - // if (secondary == null) - // { - // return primary; - // } - // var newWhere = new Dictionary(primary); - // foreach (var pair in secondary) - // { - // var condition = pair.Value as IDictionary; - // if (newWhere.ContainsKey(pair.Key)) - // { - // var oldCondition = newWhere[pair.Key] as IDictionary; - // if (oldCondition == null || condition == null) - // { - // throw new ArgumentException("More than one where clause for the given key provided."); - // } - // var newCondition = new Dictionary(oldCondition); - // foreach (var conditionPair in condition) - // { - // if (newCondition.ContainsKey(conditionPair.Key)) - // { - // throw new ArgumentException("More than one condition for the given key provided."); - // } - // newCondition[conditionPair.Key] = conditionPair.Value; - // } - // newWhere[pair.Key] = newCondition; - // } - // else - // { - // newWhere[pair.Key] = pair.Value; - // } - // } - // return newWhere; - // } - - // ///// - // ///// Constructs a query that is the or of the given queries. - // ///// - // ///// The list of AVQueries to 'or' together. - // ///// A AVQquery that is the 'or' of the passed in queries. - // //public static AVQuery Or(IEnumerable> queries) - // //{ - // // string className = null; - // // var orValue = new List>(); - // // // We need to cast it to non-generic IEnumerable because of AOT-limitation - // // var nonGenericQueries = (IEnumerable)queries; - // // foreach (var obj in nonGenericQueries) - // // { - // // var q = (AVQuery)obj; - // // if (className != null && q.className != className) - // // { - // // throw new ArgumentException( - // // "All of the queries in an or query must be on the same class."); - // // } - // // className = q.className; - // // var parameters = q.BuildParameters(); - // // if (parameters.Count == 0) - // // { - // // continue; - // // } - // // object where; - // // if (!parameters.TryGetValue("where", out where) || parameters.Count > 1) - // // { - // // throw new ArgumentException( - // // "None of the queries in an or query can have non-filtering clauses"); - // // } - // // orValue.Add(where as IDictionary); - // // } - // // return new AVQuery(new AVQuery(className), - // // where: new Dictionary { - // // {"$or", orValue} - // // }); - // //} - - // #region Order By - - // /// - // /// Sorts the results in ascending order by the given key. - // /// This will override any existing ordering for the query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S OrderBy(string key) - // { - // return CreateInstance( replacementOrderBy: new List { key }); - // } - - // /// - // /// Sorts the results in descending order by the given key. - // /// This will override any existing ordering for the query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S OrderByDescending(string key) - // { - // return CreateInstance( replacementOrderBy: new List { "-" + key }); - // } - - // /// - // /// Sorts the results in ascending order by the given key, after previous - // /// ordering has been applied. - // /// - // /// This method can only be called if there is already an - // /// or - // /// on this query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S ThenBy(string key) - // { - // return CreateInstance( thenBy: new List { key }); - // } - - // /// - // /// Sorts the results in descending order by the given key, after previous - // /// ordering has been applied. - // /// - // /// This method can only be called if there is already an - // /// or on this query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S ThenByDescending(string key) - // { - // return CreateInstance( thenBy: new List { "-" + key }); - // } - - // #endregion - - // /// - // /// Include nested AVObjects for the provided key. You can use dot notation - // /// to specify which fields in the included objects should also be fetched. - // /// - // /// The key that should be included. - // /// A new query with the additional constraint. - // public virtual S Include(string key) - // { - // return CreateInstance( includes: new List { key }); - // } - - // /// - // /// Restrict the fields of returned AVObjects to only include the provided key. - // /// If this is called multiple times, then all of the keys specified in each of - // /// the calls will be included. - // /// - // /// The key that should be included. - // /// A new query with the additional constraint. - // public virtual S Select(string key) - // { - // return CreateInstance( selectedKeys: new List { key }); - // } - - // /// - // /// Skips a number of results before returning. This is useful for pagination - // /// of large queries. Chaining multiple skips together will cause more results - // /// to be skipped. - // /// - // /// The number of results to skip. - // /// A new query with the additional constraint. - // public virtual S Skip(int count) - // { - // return CreateInstance( skip: count); - // } - - // /// - // /// Controls the maximum number of results that are returned. Setting a negative - // /// limit denotes retrieval without a limit. Chaining multiple limits - // /// results in the last limit specified being used. The default limit is - // /// 100, with a maximum of 1000 results being returned at a time. - // /// - // /// The maximum number of results to return. - // /// A new query with the additional constraint. - // public virtual S Limit(int count) - // { - // return CreateInstance( limit: count); - // } - - // internal virtual S RedirectClassName(String key) - // { - // return CreateInstance( redirectClassNameForKey: key); - // } - - // #region Where - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// contained in the provided list of values. - // /// - // /// The key to check. - // /// The values that will match. - // /// A new query with the additional constraint. - // public virtual S WhereContainedIn(string key, IEnumerable values) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$in", values.ToList()}}} - // }); - // } - - // /// - // /// Add a constraint to the querey that requires a particular key's value to be - // /// a list containing all of the elements in the provided list of values. - // /// - // /// The key to check. - // /// The values that will match. - // /// A new query with the additional constraint. - // public virtual S WhereContainsAll(string key, IEnumerable values) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$all", values.ToList()}}} - // }); - // } - - // /// - // /// Adds a constraint for finding string values that contain a provided string. - // /// This will be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The substring that the value must contain. - // /// A new query with the additional constraint. - // public virtual S WhereContains(string key, string substring) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$regex", RegexQuote(substring)}}} - // }); - // } - - // /// - // /// Adds a constraint for finding objects that do not contain a given key. - // /// - // /// The key that should not exist. - // /// A new query with the additional constraint. - // public virtual S WhereDoesNotExist(string key) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$exists", false}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires that a particular key's value - // /// does not match another AVQuery. This only works on keys whose values are - // /// AVObjects or lists of AVObjects. - // /// - // /// The key to check. - // /// The query that the value should not match. - // /// A new query with the additional constraint. - // public virtual S WhereDoesNotMatchQuery(string key, AVQuery query) - // where TOther : AVObject - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$notInQuery", query.BuildParameters(true)}}} - // }); - // } - - // /// - // /// Adds a constraint for finding string values that end with a provided string. - // /// This will be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The substring that the value must end with. - // /// A new query with the additional constraint. - // public virtual S WhereEndsWith(string key, string suffix) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$regex", RegexQuote(suffix) + "$"}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// equal to the provided value. - // /// - // /// The key to check. - // /// The value that the AVObject must contain. - // /// A new query with the additional constraint. - // public virtual S WhereEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary { - // { key, value} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's size to be - // /// equal to the provided size. - // /// - // /// The size equal to. - // /// The key to check. - // /// The value that the size must be. - // /// A new query with the additional constraint. - // public virtual S WhereSizeEqualTo(string key, uint size) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$size", size}}} - // }); - // } - - // /// - // /// Adds a constraint for finding objects that contain a given key. - // /// - // /// The key that should exist. - // /// A new query with the additional constraint. - // public virtual S WhereExists(string key) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$exists", true}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// greater than the provided value. - // /// - // /// The key to check. - // /// The value that provides a lower bound. - // /// A new query with the additional constraint. - // public virtual S WhereGreaterThan(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$gt", value}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// greater or equal to than the provided value. - // /// - // /// The key to check. - // /// The value that provides a lower bound. - // /// A new query with the additional constraint. - // public virtual S WhereGreaterThanOrEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$gte", value}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// less than the provided value. - // /// - // /// The key to check. - // /// The value that provides an upper bound. - // /// A new query with the additional constraint. - // public virtual S WhereLessThan(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$lt", value}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// less than or equal to the provided value. - // /// - // /// The key to check. - // /// The value that provides a lower bound. - // /// A new query with the additional constraint. - // public virtual S WhereLessThanOrEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$lte", value}}} - // }); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The regular expression pattern to match. The Regex must - // /// have the options flag set. - // /// Any of the following supported PCRE modifiers: - // /// i - Case insensitive search - // /// m Search across multiple lines of input - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, Regex regex, string modifiers) - // { - // if (!regex.Options.HasFlag(RegexOptions.ECMAScript)) - // { - // throw new ArgumentException( - // "Only ECMAScript-compatible regexes are supported. Please use the ECMAScript RegexOptions flag when creating your regex."); - // } - // return CreateInstance( where: new Dictionary { - // { key, EncodeRegex(regex, modifiers)} - // }); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The regular expression pattern to match. The Regex must - // /// have the options flag set. - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, Regex regex) - // { - // return WhereMatches(key, regex, null); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The PCRE regular expression pattern to match. - // /// Any of the following supported PCRE modifiers: - // /// i - Case insensitive search - // /// m Search across multiple lines of input - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, string pattern, string modifiers = null) - // { - // return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The PCRE regular expression pattern to match. - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, string pattern) - // { - // return WhereMatches(key, pattern, null); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value - // /// to match a value for a key in the results of another AVQuery. - // /// - // /// The key whose value is being checked. - // /// The key in the objects from the subquery to look in. - // /// The subquery to run - // /// A new query with the additional constraint. - // public virtual S WhereMatchesKeyInQuery(string key, - // string keyInQuery, - // AVQuery query) where TOther : AVObject - // { - // var parameters = new Dictionary { - // { "query", query.BuildParameters(true)}, - // { "key", keyInQuery} - // }; - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$select", parameters}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value - // /// does not match any value for a key in the results of another AVQuery. - // /// - // /// The key whose value is being checked. - // /// The key in the objects from the subquery to look in. - // /// The subquery to run - // /// A new query with the additional constraint. - // public virtual S WhereDoesNotMatchesKeyInQuery(string key, - // string keyInQuery, - // AVQuery query) where TOther : AVObject - // { - // var parameters = new Dictionary { - // { "query", query.BuildParameters(true)}, - // { "key", keyInQuery} - // }; - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$dontSelect", parameters}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires that a particular key's value - // /// matches another AVQuery. This only works on keys whose values are - // /// AVObjects or lists of AVObjects. - // /// - // /// The key to check. - // /// The query that the value should match. - // /// A new query with the additional constraint. - // public virtual S WhereMatchesQuery(string key, AVQuery query) - // where TOther : AVObject - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$inQuery", query.BuildParameters(true)}}} - // }); - // } - - // /// - // /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint - // /// values are near the given point. - // /// - // /// The key that the AVGeoPoint is stored in. - // /// The reference AVGeoPoint. - // /// A new query with the additional constraint. - // public virtual S WhereNear(string key, AVGeoPoint point) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$nearSphere", point}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// contained in the provided list of values. - // /// - // /// The key to check. - // /// The values that will match. - // /// A new query with the additional constraint. - // public virtual S WhereNotContainedIn(string key, IEnumerable values) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$nin", values.ToList()}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value not - // /// to be equal to the provided value. - // /// - // /// The key to check. - // /// The value that that must not be equalled. - // /// A new query with the additional constraint. - // public virtual S WhereNotEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$ne", value}}} - // }); - // } - - // /// - // /// Adds a constraint for finding string values that start with the provided string. - // /// This query will use the backend index, so it will be fast even with large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The substring that the value must start with. - // /// A new query with the additional constraint. - // public virtual S WhereStartsWith(string key, string suffix) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$regex", "^" + RegexQuote(suffix)}}} - // }); - // } - - // /// - // /// Add a constraint to the query that requires a particular key's coordinates to be - // /// contained within a given rectangular geographic bounding box. - // /// - // /// The key to be constrained. - // /// The lower-left inclusive corner of the box. - // /// The upper-right inclusive corner of the box. - // /// A new query with the additional constraint. - // public virtual S WhereWithinGeoBox(string key, - // AVGeoPoint southwest, - // AVGeoPoint northeast) - // { - - // return this.CreateInstance( where: new Dictionary - // { - // { - // key, - // new Dictionary - // { - // { - // "$within", - // new Dictionary { - // { "$box", new[] {southwest, northeast}} - // } - // } - // } - // } - // }); - // } - - // /// - // /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint - // /// values are near the given point and within the maximum distance given. - // /// - // /// The key that the AVGeoPoint is stored in. - // /// The reference AVGeoPoint. - // /// The maximum distance (in radians) of results to return. - // /// A new query with the additional constraint. - // public virtual S WhereWithinDistance( - // string key, AVGeoPoint point, AVGeoDistance maxDistance) - // { - // var nearWhere = new Dictionary { - // { key, new Dictionary{{"$nearSphere", point}}} - // }; - // var mergedWhere = MergeWhere(nearWhere, new Dictionary { - // { key, new Dictionary{{"$maxDistance", maxDistance.Radians}}} - // }); - // return CreateInstance( where: mergedWhere); - // } - - // internal virtual S WhereRelatedTo(AVObject parent, string key) - // { - // return CreateInstance( where: new Dictionary { - // { - // "$relatedTo", - // new Dictionary { - // { "object", parent}, - // { "key", key} - // } - // } - // }); - // } - - // #endregion - - // /// - // /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - // /// - // /// The list of AVObjects that match this query. - // public virtual Task> FindAsync() - // { - // return FindAsync(CancellationToken.None); - // } - - // /// - // /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - // /// - // /// The cancellation token. - // /// The list of AVObjects that match this query. - // public abstract Task> FindAsync(CancellationToken cancellationToken); - - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// A single AVObject that satisfies this query, or else null. - // public virtual Task FirstOrDefaultAsync() - // { - // return FirstOrDefaultAsync(CancellationToken.None); - // } - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// The cancellation token. - // /// A single AVObject that satisfies this query, or else null. - // public abstract Task FirstOrDefaultAsync(CancellationToken cancellationToken); - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// A single AVObject that satisfies this query. - // /// If no results match the query. - // public virtual Task FirstAsync() - // { - // return FirstAsync(CancellationToken.None); - // } - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// The cancellation token. - // /// A single AVObject that satisfies this query. - // /// If no results match the query. - // public abstract Task FirstAsync(CancellationToken cancellationToken); - - // /// - // /// Counts the number of objects that match this query. - // /// - // /// The number of objects that match this query. - // public virtual Task CountAsync() - // { - // return CountAsync(CancellationToken.None); - // } - - // /// - // /// Counts the number of objects that match this query. - // /// - // /// The cancellation token. - // /// The number of objects that match this query. - // public abstract Task CountAsync(CancellationToken cancellationToken); - - // /// - // /// Constructs a AVObject whose id is already known by fetching data - // /// from the server. - // /// - // /// ObjectId of the AVObject to fetch. - // /// The AVObject for the given objectId. - // public virtual Task GetAsync(string objectId) - // { - // return GetAsync(objectId, CancellationToken.None); - // } - - // /// - // /// Constructs a AVObject whose id is already known by fetching data - // /// from the server. - // /// - // /// ObjectId of the AVObject to fetch. - // /// The cancellation token. - // /// The AVObject for the given objectId. - // public abstract Task GetAsync(string objectId, CancellationToken cancellationToken); - - // internal object GetConstraint(string key) - // { - // return where == null ? null : where.GetOrDefault(key, null); - // } - - // /// - // /// 构建查询字符串 - // /// - // /// 是否包含 ClassName - // /// - // public IDictionary BuildParameters(bool includeClassName = false) - // { - // Dictionary result = new Dictionary(); - // if (where != null) - // { - // result["where"] = PointerOrLocalIdEncoder.Instance.Encode(where); - // } - // if (orderBy != null) - // { - // result["order"] = string.Join(",", orderBy.ToArray()); - // } - // if (skip != null) - // { - // result["skip"] = skip.Value; - // } - // if (limit != null) - // { - // result["limit"] = limit.Value; - // } - // if (includes != null) - // { - // result["include"] = string.Join(",", includes.ToArray()); - // } - // if (selectedKeys != null) - // { - // result["keys"] = string.Join(",", selectedKeys.ToArray()); - // } - // if (includeClassName) - // { - // result["className"] = className; - // } - // if (redirectClassNameForKey != null) - // { - // result["redirectClassNameForKey"] = redirectClassNameForKey; - // } - // return result; - // } - - // private string RegexQuote(string input) - // { - // return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; - // } - - // private string GetRegexOptions(Regex regex, string modifiers) - // { - // string result = modifiers ?? ""; - // if (regex.Options.HasFlag(RegexOptions.IgnoreCase) && !modifiers.Contains("i")) - // { - // result += "i"; - // } - // if (regex.Options.HasFlag(RegexOptions.Multiline) && !modifiers.Contains("m")) - // { - // result += "m"; - // } - // return result; - // } - - // private IDictionary EncodeRegex(Regex regex, string modifiers) - // { - // var options = GetRegexOptions(regex, modifiers); - // var dict = new Dictionary(); - // dict["$regex"] = regex.ToString(); - // if (!string.IsNullOrEmpty(options)) - // { - // dict["$options"] = options; - // } - // return dict; - // } - - // /// - // /// Serves as the default hash function. - // /// - // /// A hash code for the current object. - // public override int GetHashCode() - // { - // // TODO (richardross): Implement this. - // return 0; - // } - //} }