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 { /// /// 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 { return AVPlugins.Instance.QueryController; } } internal static IObjectSubclassingController SubclassingController { get { return AVPlugins.Instance.SubclassingController; } } /// /// 调试时可以用来查看最终的发送的查询语句 /// private string JsonString { get { return AVClient.SerializeJsonString(this.BuildParameters(true)); } } /// /// 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, IEnumerable thenBy = null, int? skip = null, int? limit = null, IEnumerable includes = null, IEnumerable selectedKeys = null, string redirectClassNameForKey = null) : base(source, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, 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); } /// /// Constructs a query based upon the AVObject subclass used as the generic parameter for the AVQuery. /// public AVQuery() : this(SubclassingController.GetClassName(typeof(T))) { } /// /// 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; 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} }); } /// /// 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) { return AVUser.GetCurrentUserAsync().OnSuccess(t => { return QueryController.FindAsync(this, t.Result, cancellationToken); }).Unwrap().OnSuccess(t => { IEnumerable states = t.Result; return (from state in states select AVObject.FromState(state, ClassName)); }); } /// /// 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) { return AVUser.GetCurrentUserAsync().OnSuccess(t => { return QueryController.FirstAsync(this, t.Result, cancellationToken); }).Unwrap().OnSuccess(t => { IObjectState state = t.Result; return state == null ? default(T) : AVObject.FromState(state, ClassName); }); } /// /// 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) { return FirstOrDefaultAsync(cancellationToken).OnSuccess(t => { if (t.Result == null) { throw new AVException(AVException.ErrorCode.ObjectNotFound, "No results matched the query."); } return t.Result; }); } /// /// 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) { 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) { 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 => { var result = t.Result.FirstOrDefault(); if (result == null) { throw new AVException(AVException.ErrorCode.ObjectNotFound, "Object with the given objectId not found."); } return result; }); } #region CQL /// /// 执行 CQL 查询 /// /// CQL 语句 /// CancellationToken /// 返回符合条件的对象集合 public static Task> DoCloudQueryAsync(string cql, CancellationToken cancellationToken) { var queryString = string.Format("cloudQuery?cql={0}", Uri.EscapeDataString(cql)); return rebuildObjectFromCloudQueryResult(queryString); } /// /// 执行 CQL 查询 /// /// /// public static Task> DoCloudQueryAsync(string cql) { return DoCloudQueryAsync(cql, CancellationToken.None); } /// /// 执行 CQL 查询 /// /// 带有占位符的模板 cql 语句 /// 占位符对应的参数数组 /// public static Task> DoCloudQueryAsync(string cqlTeamplate, params object[] pvalues) { string queryStringTemplate = "cloudQuery?cql={0}&pvalues={1}"; string pSrting = Json.Encode(pvalues); string queryString = string.Format(queryStringTemplate, Uri.EscapeDataString(cqlTeamplate), Uri.EscapeDataString(pSrting)); return rebuildObjectFromCloudQueryResult(queryString); } internal static Task> rebuildObjectFromCloudQueryResult(string queryString) { var command = new AVCommand(queryString, method: "GET", sessionToken: AVUser.CurrentSessionToken, data: null); return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: CancellationToken.None).OnSuccess(t => { var items = t.Result.Item2["results"] as IList; var className = t.Result.Item2["className"].ToString(); IEnumerable states = (from item in items select AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance)); return (from state in states select AVObject.FromState(state, className)); }); } #endregion /// /// 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)) { return false; } var other = obj as AVQuery; return Object.Equals(this.className, 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); } public override int GetHashCode() { return base.GetHashCode(); } } }