chore: 简单支持链式查询
parent
4e1139a73b
commit
572bf3d060
|
@ -17,7 +17,8 @@ namespace LeanCloudTests {
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestQuery() {
|
public async Task TestQuery() {
|
||||||
var query = new AVQuery<AVObject>("Foo").WhereEqualTo("content", "hello");
|
var query = new AVQuery<AVObject>("Foo");
|
||||||
|
query.WhereEqualTo("content", "hello");
|
||||||
var results = await query.FindAsync();
|
var results = await query.FindAsync();
|
||||||
foreach (var result in results) {
|
foreach (var result in results) {
|
||||||
TestContext.Out.WriteLine(result.ObjectId);
|
TestContext.Out.WriteLine(result.ObjectId);
|
||||||
|
@ -26,7 +27,8 @@ namespace LeanCloudTests {
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestQueryCount() {
|
public async Task TestQueryCount() {
|
||||||
var query = new AVQuery<AVObject>("Foo").WhereEqualTo("content", "hello, world");
|
var query = new AVQuery<AVObject>("Foo");
|
||||||
|
query.WhereEqualTo("content", "hello, world");
|
||||||
var count = await query.CountAsync();
|
var count = await query.CountAsync();
|
||||||
Assert.Greater(count, 8);
|
Assert.Greater(count, 8);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
{
|
{
|
||||||
string sessionToken = user != null ? user.SessionToken : null;
|
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<object>;
|
var items = t.Result["results"] as IList<object>;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
parameters["limit"] = 0;
|
parameters["limit"] = 0;
|
||||||
parameters["count"] = 1;
|
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"]);
|
return Convert.ToInt32(t.Result["count"]);
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
var parameters = query.BuildParameters();
|
var parameters = query.BuildParameters();
|
||||||
parameters["limit"] = 1;
|
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<object>;
|
var items = t.Result["results"] as IList<object>;
|
||||||
var item = items.FirstOrDefault() as IDictionary<string, object>;
|
var item = items.FirstOrDefault() as IDictionary<string, object>;
|
||||||
|
@ -69,13 +69,13 @@ namespace LeanCloud.Storage.Internal
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<IDictionary<string, object>> FindAsync(string relativeUri,
|
private Task<IDictionary<string, object>> FindAsync(string path,
|
||||||
IDictionary<string, object> parameters,
|
IDictionary<string, object> parameters,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"{relativeUri}?{AVClient.BuildQueryString(parameters)}",
|
Path = $"{path}?{AVClient.BuildQueryString(parameters)}",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
|
|
|
@ -1,83 +1,65 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using LeanCloud.Storage.Internal;
|
||||||
|
|
||||||
namespace LeanCloud
|
namespace LeanCloud
|
||||||
{
|
{
|
||||||
|
public class AVQuery<T> where T : AVObject {
|
||||||
|
public string ClassName {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
private string path;
|
||||||
/// The AVQuery class defines a query that is used to fetch AVObjects. The
|
public string Path {
|
||||||
/// most common use case is finding all objects that match a query through the
|
get {
|
||||||
/// <see cref="FindAsync()"/> method.
|
if (string.IsNullOrEmpty(path)) {
|
||||||
/// </summary>
|
return $"classes/{Uri.EscapeDataString(ClassName)}";
|
||||||
/// <example>
|
}
|
||||||
/// This sample code fetches all objects of
|
return path;
|
||||||
/// class <c>"MyClass"</c>:
|
} set {
|
||||||
///
|
path = value;
|
||||||
/// <code>
|
}
|
||||||
/// AVQuery query = new AVQuery("MyClass");
|
}
|
||||||
/// IEnumerable<AVObject> result = await query.FindAsync();
|
|
||||||
/// </code>
|
internal IDictionary<string, object> where;
|
||||||
///
|
internal ReadOnlyCollection<string> orderBy;
|
||||||
/// A AVQuery can also be used to retrieve a single object whose id is known,
|
internal ReadOnlyCollection<string> includes;
|
||||||
/// through the <see cref="GetAsync(string)"/> method. For example, this sample code
|
internal ReadOnlyCollection<string> selectedKeys;
|
||||||
/// fetches an object of class <c>"MyClass"</c> and id <c>myId</c>.
|
internal string redirectClassNameForKey;
|
||||||
///
|
internal int? skip;
|
||||||
/// <code>
|
internal int? limit;
|
||||||
/// AVQuery query = new AVQuery("MyClass");
|
|
||||||
/// AVObject result = await query.GetAsync(myId);
|
internal static IAVQueryController QueryController {
|
||||||
/// </code>
|
get {
|
||||||
///
|
|
||||||
/// 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 <c>"MyClass"</c>.
|
|
||||||
///
|
|
||||||
/// <code>
|
|
||||||
/// AVQuery query = new AVQuery("MyClass");
|
|
||||||
/// int count = await query.CountAsync();
|
|
||||||
/// </code>
|
|
||||||
/// </example>
|
|
||||||
public class AVQuery<T> : AVQueryPair<AVQuery<T>, T>, IAVQuery
|
|
||||||
where T : AVObject
|
|
||||||
{
|
|
||||||
internal static IAVQueryController QueryController
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return AVPlugins.Instance.QueryController;
|
return AVPlugins.Instance.QueryController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IObjectSubclassingController SubclassingController
|
internal static IObjectSubclassingController SubclassingController {
|
||||||
{
|
get {
|
||||||
get
|
|
||||||
{
|
|
||||||
return AVPlugins.Instance.SubclassingController;
|
return AVPlugins.Instance.SubclassingController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public AVQuery()
|
||||||
/// 调试时可以用来查看最终的发送的查询语句
|
: this(SubclassingController.GetClassName(typeof(T))) {
|
||||||
/// </summary>
|
}
|
||||||
private string JsonString
|
|
||||||
{
|
public AVQuery(string className) {
|
||||||
get
|
if (string.IsNullOrEmpty(className)) {
|
||||||
{
|
throw new ArgumentNullException(nameof(className));
|
||||||
return JsonConvert.SerializeObject(BuildParameters(true));
|
}
|
||||||
}
|
ClassName = className;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
private AVQuery(AVQuery<T> source,
|
private AVQuery(AVQuery<T> source,
|
||||||
IDictionary<string, object> where = null,
|
IDictionary<string, object> where = null,
|
||||||
IEnumerable<string> replacementOrderBy = null,
|
IEnumerable<string> replacementOrderBy = null,
|
||||||
|
@ -86,64 +68,90 @@ namespace LeanCloud
|
||||||
int? limit = null,
|
int? limit = null,
|
||||||
IEnumerable<string> includes = null,
|
IEnumerable<string> includes = null,
|
||||||
IEnumerable<string> selectedKeys = null,
|
IEnumerable<string> selectedKeys = null,
|
||||||
String redirectClassNameForKey = null)
|
string redirectClassNameForKey = null) {
|
||||||
: base(source, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
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<string, object>(newWhere);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (replacementOrderBy != null) {
|
||||||
|
this.orderBy = new ReadOnlyCollection<string>(replacementOrderBy.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thenBy != null) {
|
||||||
|
if (this.orderBy == null) {
|
||||||
|
throw new ArgumentException("You must call OrderBy before calling ThenBy.");
|
||||||
|
}
|
||||||
|
var newOrderBy = new List<string>(this.orderBy);
|
||||||
|
newOrderBy.AddRange(thenBy);
|
||||||
|
this.orderBy = new ReadOnlyCollection<string>(newOrderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicates.
|
||||||
|
if (this.orderBy != null) {
|
||||||
|
var newOrderBy = new HashSet<string>(this.orderBy);
|
||||||
|
this.orderBy = new ReadOnlyCollection<string>(newOrderBy.ToList<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
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<string>(newIncludes.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedKeys != null) {
|
||||||
|
var newSelectedKeys = MergeSelectedKeys(selectedKeys);
|
||||||
|
this.selectedKeys = new ReadOnlyCollection<string>(newSelectedKeys.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirectClassNameForKey != null) {
|
||||||
|
this.redirectClassNameForKey = redirectClassNameForKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//internal override AVQuery<T> CreateInstance(
|
HashSet<string> MergeIncludes(IEnumerable<string> otherIncludes) {
|
||||||
// AVQuery<T> source,
|
if (includes == null) {
|
||||||
// IDictionary<string, object> where = null,
|
return new HashSet<string>(otherIncludes);
|
||||||
// IEnumerable<string> replacementOrderBy = null,
|
}
|
||||||
// IEnumerable<string> thenBy = null,
|
var newIncludes = new HashSet<string>(includes);
|
||||||
// int? skip = null,
|
foreach (var item in otherIncludes) {
|
||||||
// int? limit = null,
|
newIncludes.Add(item);
|
||||||
// IEnumerable<string> includes = null,
|
}
|
||||||
// IEnumerable<string> selectedKeys = null,
|
return newIncludes;
|
||||||
// String redirectClassNameForKey = null)
|
|
||||||
//{
|
|
||||||
// return new AVQuery<T>(this, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey);
|
|
||||||
//}
|
|
||||||
|
|
||||||
public override AVQuery<T> CreateInstance(
|
|
||||||
IDictionary<string, object> where = null,
|
|
||||||
IEnumerable<string> replacementOrderBy = null,
|
|
||||||
IEnumerable<string> thenBy = null,
|
|
||||||
int? skip = null,
|
|
||||||
int? limit = null,
|
|
||||||
IEnumerable<string> includes = null,
|
|
||||||
IEnumerable<string> selectedKeys = null,
|
|
||||||
String redirectClassNameForKey = null)
|
|
||||||
{
|
|
||||||
return new AVQuery<T>(this, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashSet<string> MergeSelectedKeys(IEnumerable<string> otherSelectedKeys) {
|
||||||
/// <summary>
|
if (selectedKeys == null) {
|
||||||
/// Constructs a query based upon the AVObject subclass used as the generic parameter for the AVQuery.
|
return new HashSet<string>(otherSelectedKeys);
|
||||||
/// </summary>
|
}
|
||||||
public AVQuery()
|
var newSelectedKeys = new HashSet<string>(selectedKeys);
|
||||||
: this(SubclassingController.GetClassName(typeof(T)))
|
foreach (var item in otherSelectedKeys) {
|
||||||
{
|
newSelectedKeys.Add(item);
|
||||||
|
}
|
||||||
|
return newSelectedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a query. A default query with no further parameters will retrieve
|
|
||||||
/// all <see cref="AVObject"/>s of the provided class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="className">The name of the class to retrieve AVObjects for.</param>
|
|
||||||
public AVQuery(string className)
|
|
||||||
: base(className)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a query that is the or of the given queries.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="queries">The list of AVQueries to 'or' together.</param>
|
|
||||||
/// <returns>A AVQquery that is the 'or' of the passed in queries.</returns>
|
|
||||||
public static AVQuery<T> Or(IEnumerable<AVQuery<T>> queries)
|
public static AVQuery<T> Or(IEnumerable<AVQuery<T>> queries)
|
||||||
{
|
{
|
||||||
string className = null;
|
string className = null;
|
||||||
|
@ -153,12 +161,12 @@ namespace LeanCloud
|
||||||
foreach (var obj in nonGenericQueries)
|
foreach (var obj in nonGenericQueries)
|
||||||
{
|
{
|
||||||
var q = (AVQuery<T>)obj;
|
var q = (AVQuery<T>)obj;
|
||||||
if (className != null && q.className != className)
|
if (className != null && q.ClassName != className)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"All of the queries in an or query must be on the same class.");
|
"All of the queries in an or query must be on the same class.");
|
||||||
}
|
}
|
||||||
className = q.className;
|
className = q.ClassName;
|
||||||
var parameters = q.BuildParameters();
|
var parameters = q.BuildParameters();
|
||||||
if (parameters.Count == 0)
|
if (parameters.Count == 0)
|
||||||
{
|
{
|
||||||
|
@ -178,12 +186,7 @@ namespace LeanCloud
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Task<IEnumerable<T>> FindAsync(CancellationToken cancellationToken = default)
|
||||||
/// Retrieves a list of AVObjects that satisfy this query from LeanCloud.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>The list of AVObjects that match this query.</returns>
|
|
||||||
public override Task<IEnumerable<T>> FindAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
return AVUser.GetCurrentUserAsync().OnSuccess(t =>
|
return AVUser.GetCurrentUserAsync().OnSuccess(t =>
|
||||||
{
|
{
|
||||||
|
@ -196,12 +199,7 @@ namespace LeanCloud
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Task<T> FirstOrDefaultAsync(CancellationToken cancellationToken = default)
|
||||||
/// Retrieves at most one AVObject that satisfies this query.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>A single AVObject that satisfies this query, or else null.</returns>
|
|
||||||
public override Task<T> FirstOrDefaultAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
return AVUser.GetCurrentUserAsync().OnSuccess(t =>
|
return AVUser.GetCurrentUserAsync().OnSuccess(t =>
|
||||||
{
|
{
|
||||||
|
@ -213,13 +211,7 @@ namespace LeanCloud
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Task<T> FirstAsync(CancellationToken cancellationToken)
|
||||||
/// Retrieves at most one AVObject that satisfies this query.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>A single AVObject that satisfies this query.</returns>
|
|
||||||
/// <exception cref="AVException">If no results match the query.</exception>
|
|
||||||
public override Task<T> FirstAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
return FirstOrDefaultAsync(cancellationToken).OnSuccess(t =>
|
return FirstOrDefaultAsync(cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
|
@ -232,29 +224,16 @@ namespace LeanCloud
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Task<int> CountAsync(CancellationToken cancellationToken = default)
|
||||||
/// Counts the number of objects that match this query.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>The number of objects that match this query.</returns>
|
|
||||||
public override Task<int> CountAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
return AVUser.GetCurrentUserAsync().OnSuccess(t =>
|
return AVUser.GetCurrentUserAsync().OnSuccess(t => {
|
||||||
{
|
|
||||||
return QueryController.CountAsync<T>(this, t.Result, cancellationToken);
|
return QueryController.CountAsync<T>(this, t.Result, cancellationToken);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Task<T> GetAsync(string objectId, CancellationToken cancellationToken)
|
||||||
/// Constructs a AVObject whose id is already known by fetching data
|
|
||||||
/// from the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="objectId">ObjectId of the AVObject to fetch.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>The AVObject for the given objectId.</returns>
|
|
||||||
public override Task<T> GetAsync(string objectId, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
AVQuery<T> singleItemQuery = new AVQuery<T>(className)
|
AVQuery<T> singleItemQuery = new AVQuery<T>(ClassName)
|
||||||
.WhereEqualTo("objectId", objectId);
|
.WhereEqualTo("objectId", objectId);
|
||||||
singleItemQuery = new AVQuery<T>(singleItemQuery, includes: this.includes, selectedKeys: this.selectedKeys, limit: 1);
|
singleItemQuery = new AVQuery<T>(singleItemQuery, includes: this.includes, selectedKeys: this.selectedKeys, limit: 1);
|
||||||
return singleItemQuery.FindAsync(cancellationToken).OnSuccess(t =>
|
return singleItemQuery.FindAsync(cancellationToken).OnSuccess(t =>
|
||||||
|
@ -329,31 +308,384 @@ namespace LeanCloud
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
IDictionary<string, object> MergeWhereClauses(IDictionary<string, object> otherWhere) {
|
||||||
|
if (where == null) {
|
||||||
|
where = otherWhere;
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
var newWhere = new Dictionary<string, object>(where);
|
||||||
|
foreach (var pair in otherWhere) {
|
||||||
|
var condition = pair.Value as IDictionary<string, object>;
|
||||||
|
if (newWhere.ContainsKey(pair.Key)) {
|
||||||
|
var oldCondition = newWhere[pair.Key] as IDictionary<string, object>;
|
||||||
|
if (oldCondition == null || condition == null) {
|
||||||
|
throw new ArgumentException("More than one where clause for the given key provided.");
|
||||||
|
}
|
||||||
|
var newCondition = new Dictionary<string, object>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建查询字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="includeClassName">是否包含 ClassName </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IDictionary<string, object> BuildParameters(bool includeClassName = false) {
|
||||||
|
Dictionary<string, object> result = new Dictionary<string, object>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current object.
|
/// Determines whether the specified object is equal to the current object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current object.</param>
|
/// <param name="obj">The object to compare with the current object.</param>
|
||||||
/// <returns><c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c></returns>
|
/// <returns><c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c></returns>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj) {
|
||||||
{
|
if (obj == null || !(obj is AVQuery<T>)) {
|
||||||
if (obj == null || !(obj is AVQuery<T>))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var other = obj as AVQuery<T>;
|
var other = obj as AVQuery<T>;
|
||||||
return Object.Equals(this.className, other.ClassName) &&
|
return ClassName.Equals(other.ClassName) &&
|
||||||
this.where.CollectionsEqual(other.where) &&
|
this.where.CollectionsEqual(other.where) &&
|
||||||
this.orderBy.CollectionsEqual(other.orderBy) &&
|
this.orderBy.CollectionsEqual(other.orderBy) &&
|
||||||
this.includes.CollectionsEqual(other.includes) &&
|
this.includes.CollectionsEqual(other.includes) &&
|
||||||
this.selectedKeys.CollectionsEqual(other.selectedKeys) &&
|
this.selectedKeys.CollectionsEqual(other.selectedKeys) &&
|
||||||
Object.Equals(this.skip, other.skip) &&
|
object.Equals(this.skip, other.skip) &&
|
||||||
Object.Equals(this.limit, other.limit);
|
object.Equals(this.limit, other.limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode() {
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
return base.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Order By
|
||||||
|
|
||||||
|
public AVQuery<T> OrderBy(string key) {
|
||||||
|
orderBy = new ReadOnlyCollection<string>(new List<string> { key });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> OrderByDescending(string key) {
|
||||||
|
orderBy = new ReadOnlyCollection<string>(new List<string> { "-" + key });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> ThenBy(string key) {
|
||||||
|
if (orderBy == null) {
|
||||||
|
throw new ArgumentException("You must call OrderBy before calling ThenBy");
|
||||||
|
}
|
||||||
|
List<string> newOrderBy = orderBy.ToList();
|
||||||
|
newOrderBy.Add(key);
|
||||||
|
orderBy = new ReadOnlyCollection<string>(newOrderBy);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> ThenByDescending(string key) {
|
||||||
|
if (orderBy == null) {
|
||||||
|
throw new ArgumentException("You must call OrderBy before calling ThenBy");
|
||||||
|
}
|
||||||
|
List<string> newOrderBy = orderBy.ToList();
|
||||||
|
newOrderBy.Add($"-{key}");
|
||||||
|
orderBy = new ReadOnlyCollection<string>(newOrderBy);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public AVQuery<T> Include(string key) {
|
||||||
|
includes = new ReadOnlyCollection<string>(new List<string> { key });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> Select(string key) {
|
||||||
|
selectedKeys = new ReadOnlyCollection<string>(new List<string> { key });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> Skip(int count) {
|
||||||
|
skip = count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> Limit(int count) {
|
||||||
|
limit = count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal AVQuery<T> RedirectClassName(string key) {
|
||||||
|
redirectClassNameForKey = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Where
|
||||||
|
|
||||||
|
public AVQuery<T> WhereContainedIn<TIn>(string key, IEnumerable<TIn> values) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$in", values.ToList() } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereContainsAll<TIn>(string key, IEnumerable<TIn> values) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$all", values.ToList() } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereContains(string key, string substring) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$regex", RegexQuote(substring) } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereDoesNotExist(string key) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$exists", false } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereDoesNotMatchQuery<TOther>(string key, AVQuery<TOther> query)
|
||||||
|
where TOther : AVObject {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$notInQuery", query.BuildParameters(true) } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereEndsWith(string key, string suffix) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$regex", RegexQuote(suffix) + "$" } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereEqualTo(string key, object value) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, value }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereSizeEqualTo(string key, uint size) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$size", size } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereExists(string key) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$exists", true } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereGreaterThan(string key, object value) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$gt", value } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereGreaterThanOrEqualTo(string key, object value) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$gte", value } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereLessThan(string key, object value) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$lt", value } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereLessThanOrEqualTo(string key, object value) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$lte", value } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> 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<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { key, EncodeRegex(regex, modifiers) } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereMatches(string key, Regex regex) {
|
||||||
|
return WhereMatches(key, regex, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereMatches(string key, string pattern, string modifiers = null) {
|
||||||
|
return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereMatches(string key, string pattern) {
|
||||||
|
return WhereMatches(key, pattern, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereMatchesKeyInQuery<TOther>(string key, string keyInQuery, AVQuery<TOther> query)
|
||||||
|
where TOther : AVObject {
|
||||||
|
var parameters = new Dictionary<string, object> {
|
||||||
|
{ "query", query.BuildParameters(true)},
|
||||||
|
{ "key", keyInQuery}
|
||||||
|
};
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$select", parameters } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereDoesNotMatchesKeyInQuery<TOther>(string key, string keyInQuery, AVQuery<TOther> query)
|
||||||
|
where TOther : AVObject {
|
||||||
|
var parameters = new Dictionary<string, object> {
|
||||||
|
{ "query", query.BuildParameters(true)},
|
||||||
|
{ "key", keyInQuery}
|
||||||
|
};
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$dontSelect", parameters } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereMatchesQuery<TOther>(string key, AVQuery<TOther> query)
|
||||||
|
where TOther : AVObject {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$inQuery", query.BuildParameters(true) } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereNear(string key, AVGeoPoint point) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$nearSphere", point } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereNotContainedIn<TIn>(string key, IEnumerable<TIn> values) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$nin", values.ToList() } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereNotEqualTo(string key, object value) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$ne", value } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereStartsWith(string key, string suffix) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$regex", "^" + RegexQuote(suffix) } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$within",
|
||||||
|
new Dictionary<string, object> {
|
||||||
|
{ "$box", new[] {southwest, northeast}}
|
||||||
|
} } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVQuery<T> WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$nearSphere", point } } }
|
||||||
|
});
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$maxDistance", maxDistance.Radians } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal AVQuery<T> WhereRelatedTo(AVObject parent, string key) {
|
||||||
|
MergeWhereClauses(new Dictionary<string, object> {
|
||||||
|
{ key, new Dictionary<string, object>{ { "$relatedTo", new Dictionary<string, object> {
|
||||||
|
{ "object", parent},
|
||||||
|
{ "key", key}
|
||||||
|
} } } }
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private string RegexQuote(string input) {
|
||||||
|
return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E";
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<string, object> EncodeRegex(Regex regex, string modifiers) {
|
||||||
|
var options = GetRegexOptions(regex, modifiers);
|
||||||
|
var dict = new Dictionary<string, object>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,7 +322,7 @@ namespace LeanCloud
|
||||||
public AVQuery<AVUser> GetFollowerQuery()
|
public AVQuery<AVUser> GetFollowerQuery()
|
||||||
{
|
{
|
||||||
AVQuery<AVUser> query = new AVQuery<AVUser> {
|
AVQuery<AVUser> query = new AVQuery<AVUser> {
|
||||||
RelativeUri = string.Format("users/{0}/followers", this.ObjectId)
|
Path = $"users/{ObjectId}/followers"
|
||||||
};
|
};
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
@ -333,8 +333,9 @@ namespace LeanCloud
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public AVQuery<AVUser> GetFolloweeQuery()
|
public AVQuery<AVUser> GetFolloweeQuery()
|
||||||
{
|
{
|
||||||
AVQuery<AVUser> query = new AVQuery<AVUser>();
|
AVQuery<AVUser> query = new AVQuery<AVUser> {
|
||||||
query.RelativeUri = string.Format("users/{0}/followees", this.ObjectId);
|
Path = $"users/{ObjectId}/followees"
|
||||||
|
};
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,8 +345,9 @@ namespace LeanCloud
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public AVQuery<AVUser> GetFollowersAndFolloweesQuery()
|
public AVQuery<AVUser> GetFollowersAndFolloweesQuery()
|
||||||
{
|
{
|
||||||
AVQuery<AVUser> query = new AVQuery<AVUser>();
|
AVQuery<AVUser> query = new AVQuery<AVUser> {
|
||||||
query.RelativeUri = string.Format("users/{0}/followersAndFollowees", this.ObjectId);
|
Path = $"users/{ObjectId}/followersAndFollowees"
|
||||||
|
};
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +357,7 @@ namespace LeanCloud
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<IEnumerable<AVUser>> GetFollowersAsync()
|
public Task<IEnumerable<AVUser>> GetFollowersAsync()
|
||||||
{
|
{
|
||||||
return this.GetFollowerQuery().FindAsync();
|
return GetFollowerQuery().FindAsync(CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -364,7 +366,7 @@ namespace LeanCloud
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<IEnumerable<AVUser>> GetFolloweesAsync()
|
public Task<IEnumerable<AVUser>> GetFolloweesAsync()
|
||||||
{
|
{
|
||||||
return this.GetFolloweeQuery().FindAsync();
|
return GetFolloweeQuery().FindAsync(CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1097,972 +1097,4 @@ namespace LeanCloud
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//public abstract class AVQueryBase<S, T> : IAVQueryTuple<S, T>
|
|
||||||
// where T : IAVObject
|
|
||||||
//{
|
|
||||||
// protected readonly string className;
|
|
||||||
// protected readonly Dictionary<string, object> where;
|
|
||||||
// protected readonly ReadOnlyCollection<string> orderBy;
|
|
||||||
// protected readonly ReadOnlyCollection<string> includes;
|
|
||||||
// protected readonly ReadOnlyCollection<string> 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<string, object> Condition
|
|
||||||
// {
|
|
||||||
// get { return this.where; }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected AVQueryBase()
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal abstract S CreateInstance(AVQueryBase<S, T> source,
|
|
||||||
// IDictionary<string, object> where = null,
|
|
||||||
// IEnumerable<string> replacementOrderBy = null,
|
|
||||||
// IEnumerable<string> thenBy = null,
|
|
||||||
// int? skip = null,
|
|
||||||
// int? limit = null,
|
|
||||||
// IEnumerable<string> includes = null,
|
|
||||||
// IEnumerable<string> selectedKeys = null,
|
|
||||||
// String redirectClassNameForKey = null);
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// protected AVQueryBase(AVQueryBase<S, T> source,
|
|
||||||
// IDictionary<string, object> where = null,
|
|
||||||
// IEnumerable<string> replacementOrderBy = null,
|
|
||||||
// IEnumerable<string> thenBy = null,
|
|
||||||
// int? skip = null,
|
|
||||||
// int? limit = null,
|
|
||||||
// IEnumerable<string> includes = null,
|
|
||||||
// IEnumerable<string> 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<string, object>(newWhere);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (replacementOrderBy != null)
|
|
||||||
// {
|
|
||||||
// this.orderBy = new ReadOnlyCollection<string>(replacementOrderBy.ToList());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (thenBy != null)
|
|
||||||
// {
|
|
||||||
// if (this.orderBy == null)
|
|
||||||
// {
|
|
||||||
// throw new ArgumentException("You must call OrderBy before calling ThenBy.");
|
|
||||||
// }
|
|
||||||
// var newOrderBy = new List<string>(this.orderBy);
|
|
||||||
// newOrderBy.AddRange(thenBy);
|
|
||||||
// this.orderBy = new ReadOnlyCollection<string>(newOrderBy);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Remove duplicates.
|
|
||||||
// if (this.orderBy != null)
|
|
||||||
// {
|
|
||||||
// var newOrderBy = new HashSet<string>(this.orderBy);
|
|
||||||
// this.orderBy = new ReadOnlyCollection<string>(newOrderBy.ToList<string>());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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<string>(newIncludes.ToList());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (selectedKeys != null)
|
|
||||||
// {
|
|
||||||
// var newSelectedKeys = MergeSelectedKeys(selectedKeys);
|
|
||||||
// this.selectedKeys = new ReadOnlyCollection<string>(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<string> MergeIncludes(IEnumerable<string> includes)
|
|
||||||
// {
|
|
||||||
// if (this.includes == null)
|
|
||||||
// {
|
|
||||||
// return new HashSet<string>(includes);
|
|
||||||
// }
|
|
||||||
// var newIncludes = new HashSet<string>(this.includes);
|
|
||||||
// foreach (var item in includes)
|
|
||||||
// {
|
|
||||||
// newIncludes.Add(item);
|
|
||||||
// }
|
|
||||||
// return newIncludes;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private HashSet<String> MergeSelectedKeys(IEnumerable<String> selectedKeys)
|
|
||||||
// {
|
|
||||||
// if (this.selectedKeys == null)
|
|
||||||
// {
|
|
||||||
// return new HashSet<string>(selectedKeys);
|
|
||||||
// }
|
|
||||||
// var newSelectedKeys = new HashSet<String>(this.selectedKeys);
|
|
||||||
// foreach (var item in selectedKeys)
|
|
||||||
// {
|
|
||||||
// newSelectedKeys.Add(item);
|
|
||||||
// }
|
|
||||||
// return newSelectedKeys;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private IDictionary<string, object> MergeWhereClauses(IDictionary<string, object> where)
|
|
||||||
// {
|
|
||||||
// return MergeWhere(this.where, where);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public virtual IDictionary<string, object> MergeWhere(IDictionary<string, object> primary, IDictionary<string, object> secondary)
|
|
||||||
// {
|
|
||||||
// if (secondary == null)
|
|
||||||
// {
|
|
||||||
// return primary;
|
|
||||||
// }
|
|
||||||
// var newWhere = new Dictionary<string, object>(primary);
|
|
||||||
// foreach (var pair in secondary)
|
|
||||||
// {
|
|
||||||
// var condition = pair.Value as IDictionary<string, object>;
|
|
||||||
// if (newWhere.ContainsKey(pair.Key))
|
|
||||||
// {
|
|
||||||
// var oldCondition = newWhere[pair.Key] as IDictionary<string, object>;
|
|
||||||
// if (oldCondition == null || condition == null)
|
|
||||||
// {
|
|
||||||
// throw new ArgumentException("More than one where clause for the given key provided.");
|
|
||||||
// }
|
|
||||||
// var newCondition = new Dictionary<string, object>(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;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ///// <summary>
|
|
||||||
// ///// Constructs a query that is the or of the given queries.
|
|
||||||
// ///// </summary>
|
|
||||||
// ///// <param name="queries">The list of AVQueries to 'or' together.</param>
|
|
||||||
// ///// <returns>A AVQquery that is the 'or' of the passed in queries.</returns>
|
|
||||||
// //public static AVQuery<T> Or(IEnumerable<AVQuery<T>> queries)
|
|
||||||
// //{
|
|
||||||
// // string className = null;
|
|
||||||
// // var orValue = new List<IDictionary<string, object>>();
|
|
||||||
// // // 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<T>)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<string, object>);
|
|
||||||
// // }
|
|
||||||
// // return new AVQuery<T>(new AVQuery<T>(className),
|
|
||||||
// // where: new Dictionary<string, object> {
|
|
||||||
// // {"$or", orValue}
|
|
||||||
// // });
|
|
||||||
// //}
|
|
||||||
|
|
||||||
// #region Order By
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Sorts the results in ascending order by the given key.
|
|
||||||
// /// This will override any existing ordering for the query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to order by.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S OrderBy(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( replacementOrderBy: new List<string> { key });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Sorts the results in descending order by the given key.
|
|
||||||
// /// This will override any existing ordering for the query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to order by.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S OrderByDescending(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( replacementOrderBy: new List<string> { "-" + key });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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 <see cref="OrderBy"/>
|
|
||||||
// /// or <see cref="OrderByDescending"/>
|
|
||||||
// /// on this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to order by.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S ThenBy(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( thenBy: new List<string> { key });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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 <see cref="OrderBy"/>
|
|
||||||
// /// or <see cref="OrderByDescending"/> on this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to order by.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S ThenByDescending(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( thenBy: new List<string> { "-" + key });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Include nested AVObjects for the provided key. You can use dot notation
|
|
||||||
// /// to specify which fields in the included objects should also be fetched.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that should be included.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S Include(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( includes: new List<string> { key });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that should be included.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S Select(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( selectedKeys: new List<string> { key });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="count">The number of results to skip.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S Skip(int count)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( skip: count);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="count">The maximum number of results to return.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S Limit(int count)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( limit: count);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal virtual S RedirectClassName(String key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( redirectClassNameForKey: key);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #region Where
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// contained in the provided list of values.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="values">The values that will match.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereContainedIn<TIn>(string key, IEnumerable<TIn> values)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$in", values.ToList()}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="values">The values that will match.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereContainsAll<TIn>(string key, IEnumerable<TIn> values)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$all", values.ToList()}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint for finding string values that contain a provided string.
|
|
||||||
// /// This will be slow for large data sets.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="substring">The substring that the value must contain.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereContains(string key, string substring)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$regex", RegexQuote(substring)}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint for finding objects that do not contain a given key.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that should not exist.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereDoesNotExist(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object>{
|
|
||||||
// { key, new Dictionary<string, object>{{"$exists", false}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="query">The query that the value should not match.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereDoesNotMatchQuery<TOther>(string key, AVQuery<TOther> query)
|
|
||||||
// where TOther : AVObject
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$notInQuery", query.BuildParameters(true)}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint for finding string values that end with a provided string.
|
|
||||||
// /// This will be slow for large data sets.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="suffix">The substring that the value must end with.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereEndsWith(string key, string suffix)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$regex", RegexQuote(suffix) + "$"}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// equal to the provided value.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="value">The value that the AVObject must contain.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereEqualTo(string key, object value)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, value}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's size to be
|
|
||||||
// /// equal to the provided size.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <returns>The size equal to.</returns>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="size">The value that the size must be.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereSizeEqualTo(string key, uint size)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, uint>{{"$size", size}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint for finding objects that contain a given key.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that should exist.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereExists(string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object>{
|
|
||||||
// { key, new Dictionary<string, object>{{"$exists", true}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// greater than the provided value.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="value">The value that provides a lower bound.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereGreaterThan(string key, object value)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object>{
|
|
||||||
// { key, new Dictionary<string, object>{{"$gt", value}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// greater or equal to than the provided value.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="value">The value that provides a lower bound.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereGreaterThanOrEqualTo(string key, object value)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object>{
|
|
||||||
// { key, new Dictionary<string, object>{{"$gte", value}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// less than the provided value.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="value">The value that provides an upper bound.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereLessThan(string key, object value)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object>{
|
|
||||||
// { key, new Dictionary<string, object>{{"$lt", value}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// less than or equal to the provided value.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="value">The value that provides a lower bound.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereLessThanOrEqualTo(string key, object value)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object>{
|
|
||||||
// { key, new Dictionary<string, object>{{"$lte", value}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a regular expression constraint for finding string values that match the provided
|
|
||||||
// /// regular expression. This may be slow for large data sets.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="regex">The regular expression pattern to match. The Regex must
|
|
||||||
// /// have the <see cref="RegexOptions.ECMAScript"/> options flag set.</param>
|
|
||||||
// /// <param name="modifiers">Any of the following supported PCRE modifiers:
|
|
||||||
// /// <code>i</code> - Case insensitive search
|
|
||||||
// /// <code>m</code> Search across multiple lines of input</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// 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<string, object> {
|
|
||||||
// { key, EncodeRegex(regex, modifiers)}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a regular expression constraint for finding string values that match the provided
|
|
||||||
// /// regular expression. This may be slow for large data sets.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="regex">The regular expression pattern to match. The Regex must
|
|
||||||
// /// have the <see cref="RegexOptions.ECMAScript"/> options flag set.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereMatches(string key, Regex regex)
|
|
||||||
// {
|
|
||||||
// return WhereMatches(key, regex, null);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a regular expression constraint for finding string values that match the provided
|
|
||||||
// /// regular expression. This may be slow for large data sets.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="pattern">The PCRE regular expression pattern to match.</param>
|
|
||||||
// /// <param name="modifiers">Any of the following supported PCRE modifiers:
|
|
||||||
// /// <code>i</code> - Case insensitive search
|
|
||||||
// /// <code>m</code> Search across multiple lines of input</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereMatches(string key, string pattern, string modifiers = null)
|
|
||||||
// {
|
|
||||||
// return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a regular expression constraint for finding string values that match the provided
|
|
||||||
// /// regular expression. This may be slow for large data sets.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="pattern">The PCRE regular expression pattern to match.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereMatches(string key, string pattern)
|
|
||||||
// {
|
|
||||||
// return WhereMatches(key, pattern, null);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key whose value is being checked.</param>
|
|
||||||
// /// <param name="keyInQuery">The key in the objects from the subquery to look in.</param>
|
|
||||||
// /// <param name="query">The subquery to run</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereMatchesKeyInQuery<TOther>(string key,
|
|
||||||
// string keyInQuery,
|
|
||||||
// AVQuery<TOther> query) where TOther : AVObject
|
|
||||||
// {
|
|
||||||
// var parameters = new Dictionary<string, object> {
|
|
||||||
// { "query", query.BuildParameters(true)},
|
|
||||||
// { "key", keyInQuery}
|
|
||||||
// };
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$select", parameters}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key whose value is being checked.</param>
|
|
||||||
// /// <param name="keyInQuery">The key in the objects from the subquery to look in.</param>
|
|
||||||
// /// <param name="query">The subquery to run</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereDoesNotMatchesKeyInQuery<TOther>(string key,
|
|
||||||
// string keyInQuery,
|
|
||||||
// AVQuery<TOther> query) where TOther : AVObject
|
|
||||||
// {
|
|
||||||
// var parameters = new Dictionary<string, object> {
|
|
||||||
// { "query", query.BuildParameters(true)},
|
|
||||||
// { "key", keyInQuery}
|
|
||||||
// };
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$dontSelect", parameters}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="query">The query that the value should match.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereMatchesQuery<TOther>(string key, AVQuery<TOther> query)
|
|
||||||
// where TOther : AVObject
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$inQuery", query.BuildParameters(true)}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint
|
|
||||||
// /// values are near the given point.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the AVGeoPoint is stored in.</param>
|
|
||||||
// /// <param name="point">The reference AVGeoPoint.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereNear(string key, AVGeoPoint point)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$nearSphere", point}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value to be
|
|
||||||
// /// contained in the provided list of values.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="values">The values that will match.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereNotContainedIn<TIn>(string key, IEnumerable<TIn> values)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$nin", values.ToList()}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a constraint to the query that requires a particular key's value not
|
|
||||||
// /// to be equal to the provided value.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to check.</param>
|
|
||||||
// /// <param name="value">The value that that must not be equalled.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereNotEqualTo(string key, object value)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$ne", value}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 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.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the string to match is stored in.</param>
|
|
||||||
// /// <param name="suffix">The substring that the value must start with.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereStartsWith(string key, string suffix)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$regex", "^" + RegexQuote(suffix)}}}
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Add a constraint to the query that requires a particular key's coordinates to be
|
|
||||||
// /// contained within a given rectangular geographic bounding box.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key to be constrained.</param>
|
|
||||||
// /// <param name="southwest">The lower-left inclusive corner of the box.</param>
|
|
||||||
// /// <param name="northeast">The upper-right inclusive corner of the box.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereWithinGeoBox(string key,
|
|
||||||
// AVGeoPoint southwest,
|
|
||||||
// AVGeoPoint northeast)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// return this.CreateInstance( where: new Dictionary<string, object>
|
|
||||||
// {
|
|
||||||
// {
|
|
||||||
// key,
|
|
||||||
// new Dictionary<string, object>
|
|
||||||
// {
|
|
||||||
// {
|
|
||||||
// "$within",
|
|
||||||
// new Dictionary<string, object> {
|
|
||||||
// { "$box", new[] {southwest, northeast}}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint
|
|
||||||
// /// values are near the given point and within the maximum distance given.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="key">The key that the AVGeoPoint is stored in.</param>
|
|
||||||
// /// <param name="point">The reference AVGeoPoint.</param>
|
|
||||||
// /// <param name="maxDistance">The maximum distance (in radians) of results to return.</param>
|
|
||||||
// /// <returns>A new query with the additional constraint.</returns>
|
|
||||||
// public virtual S WhereWithinDistance(
|
|
||||||
// string key, AVGeoPoint point, AVGeoDistance maxDistance)
|
|
||||||
// {
|
|
||||||
// var nearWhere = new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$nearSphere", point}}}
|
|
||||||
// };
|
|
||||||
// var mergedWhere = MergeWhere(nearWhere, new Dictionary<string, object> {
|
|
||||||
// { key, new Dictionary<string, object>{{"$maxDistance", maxDistance.Radians}}}
|
|
||||||
// });
|
|
||||||
// return CreateInstance( where: mergedWhere);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal virtual S WhereRelatedTo(AVObject parent, string key)
|
|
||||||
// {
|
|
||||||
// return CreateInstance( where: new Dictionary<string, object> {
|
|
||||||
// {
|
|
||||||
// "$relatedTo",
|
|
||||||
// new Dictionary<string, object> {
|
|
||||||
// { "object", parent},
|
|
||||||
// { "key", key}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Retrieves a list of AVObjects that satisfy this query from LeanCloud.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <returns>The list of AVObjects that match this query.</returns>
|
|
||||||
// public virtual Task<IEnumerable<T>> FindAsync()
|
|
||||||
// {
|
|
||||||
// return FindAsync(CancellationToken.None);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Retrieves a list of AVObjects that satisfy this query from LeanCloud.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
// /// <returns>The list of AVObjects that match this query.</returns>
|
|
||||||
// public abstract Task<IEnumerable<T>> FindAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Retrieves at most one AVObject that satisfies this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <returns>A single AVObject that satisfies this query, or else null.</returns>
|
|
||||||
// public virtual Task<T> FirstOrDefaultAsync()
|
|
||||||
// {
|
|
||||||
// return FirstOrDefaultAsync(CancellationToken.None);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Retrieves at most one AVObject that satisfies this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
// /// <returns>A single AVObject that satisfies this query, or else null.</returns>
|
|
||||||
// public abstract Task<T> FirstOrDefaultAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Retrieves at most one AVObject that satisfies this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <returns>A single AVObject that satisfies this query.</returns>
|
|
||||||
// /// <exception cref="AVException">If no results match the query.</exception>
|
|
||||||
// public virtual Task<T> FirstAsync()
|
|
||||||
// {
|
|
||||||
// return FirstAsync(CancellationToken.None);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Retrieves at most one AVObject that satisfies this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
// /// <returns>A single AVObject that satisfies this query.</returns>
|
|
||||||
// /// <exception cref="AVException">If no results match the query.</exception>
|
|
||||||
// public abstract Task<T> FirstAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Counts the number of objects that match this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <returns>The number of objects that match this query.</returns>
|
|
||||||
// public virtual Task<int> CountAsync()
|
|
||||||
// {
|
|
||||||
// return CountAsync(CancellationToken.None);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Counts the number of objects that match this query.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
// /// <returns>The number of objects that match this query.</returns>
|
|
||||||
// public abstract Task<int> CountAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Constructs a AVObject whose id is already known by fetching data
|
|
||||||
// /// from the server.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="objectId">ObjectId of the AVObject to fetch.</param>
|
|
||||||
// /// <returns>The AVObject for the given objectId.</returns>
|
|
||||||
// public virtual Task<T> GetAsync(string objectId)
|
|
||||||
// {
|
|
||||||
// return GetAsync(objectId, CancellationToken.None);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Constructs a AVObject whose id is already known by fetching data
|
|
||||||
// /// from the server.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="objectId">ObjectId of the AVObject to fetch.</param>
|
|
||||||
// /// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
// /// <returns>The AVObject for the given objectId.</returns>
|
|
||||||
// public abstract Task<T> GetAsync(string objectId, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
// internal object GetConstraint(string key)
|
|
||||||
// {
|
|
||||||
// return where == null ? null : where.GetOrDefault(key, null);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 构建查询字符串
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="includeClassName">是否包含 ClassName </param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public IDictionary<string, object> BuildParameters(bool includeClassName = false)
|
|
||||||
// {
|
|
||||||
// Dictionary<string, object> result = new Dictionary<string, object>();
|
|
||||||
// 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<string, object> EncodeRegex(Regex regex, string modifiers)
|
|
||||||
// {
|
|
||||||
// var options = GetRegexOptions(regex, modifiers);
|
|
||||||
// var dict = new Dictionary<string, object>();
|
|
||||||
// dict["$regex"] = regex.ToString();
|
|
||||||
// if (!string.IsNullOrEmpty(options))
|
|
||||||
// {
|
|
||||||
// dict["$options"] = options;
|
|
||||||
// }
|
|
||||||
// return dict;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Serves as the default hash function.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <returns>A hash code for the current object.</returns>
|
|
||||||
// public override int GetHashCode()
|
|
||||||
// {
|
|
||||||
// // TODO (richardross): Implement this.
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue