using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using LeanCloud.Storage.Internal; namespace LeanCloud { /// /// Query 对象的基础接口 /// public interface IAVQuery { } /// /// LeanCloud 存储对象的接触接口 /// public interface IAVObject { } public abstract class AVQueryBase : IAVQuery where T : IAVObject { internal string className; internal Dictionary where; internal ReadOnlyCollection orderBy; internal ReadOnlyCollection includes; internal ReadOnlyCollection selectedKeys; internal String redirectClassNameForKey; internal int? skip; internal int? limit; /// /// 构建查询字符串 /// /// 是否包含 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; } public virtual Dictionary Where { get { return this.where; } set { this.where = value; } } 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; } } public abstract class AVQueryPair where S : IAVQuery 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 AVQueryPair() { } public abstract S 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); /// /// 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 AVQueryPair(AVQueryPair 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 AVQueryPair(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; } if (primary == null) { return secondary; } 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 AVeQquery that is the 'or' of the passed in queries. public static Q Or(IEnumerable queries) where Q : AVQueryBase where O : IAVObject { string className = null; var orValue = new List>(); // We need to cast it to non-generic IEnumerable because of AOT-limitation var nonGenericQueries = (IEnumerable)queries; Q current = null; foreach (var obj in nonGenericQueries) { var q = (Q)obj; current = q; 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); } current.Where = new Dictionary() { {"$or", orValue} }; return current; } #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; } } //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; // } //} }