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