* QueryTest.cs: chore: 在 AVQuery 中提取 QueryCondition 模块,实现 Where
条件组合;共享给 AVIMCOnversationQuery 使用 * AVQuery2.cs: * AVQuery.cs: * QueryCondition.cs: * IQueryCondition.cs: * QueryEqualCondition.cs: * QueryRelatedCondition.cs: * QueryCompositionalCondition.cs: * AVObjectController.cs:
parent
6c70b459d5
commit
6252a06b72
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using LeanCloud;
|
||||
|
||||
namespace LeanCloudTests {
|
||||
|
@ -13,10 +14,9 @@ namespace LeanCloudTests {
|
|||
|
||||
[Test]
|
||||
public async Task BasicQuery() {
|
||||
var query = new AVQuery<AVObject>("Foo");
|
||||
query.WhereGreaterThanOrEqualTo("a", 100);
|
||||
query.WhereLessThanOrEqualTo("a", 100);
|
||||
//query.WhereEqualTo("content", "hello");
|
||||
var query = new AVQuery<AVObject>("Account");
|
||||
query.WhereGreaterThanOrEqualTo("balance", 100);
|
||||
query.WhereLessThanOrEqualTo("balance", 100);
|
||||
var results = await query.FindAsync();
|
||||
foreach (var result in results) {
|
||||
TestContext.Out.WriteLine(result.ObjectId);
|
||||
|
@ -75,5 +75,48 @@ namespace LeanCloudTests {
|
|||
TestContext.Out.WriteLine(result.ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Related() {
|
||||
AVObject todo = AVObject.CreateWithoutData("Todo", "5d71f798d5de2b006c0136bc");
|
||||
AVQuery<AVObject> query = new AVQuery<AVObject>("Tag");
|
||||
query.WhereRelatedTo(todo, "tags");
|
||||
IEnumerable<AVObject> results = await query.FindAsync();
|
||||
foreach (AVObject tag in results) {
|
||||
TestContext.Out.WriteLine(tag.ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Where() {
|
||||
AVQuery<AVObject> q1 = new AVQuery<AVObject>();
|
||||
q1.WhereEqualTo("aa", "bb");
|
||||
AVQuery<AVObject> q2 = new AVQuery<AVObject>();
|
||||
q2.WhereEqualTo("cc", "dd");
|
||||
q2.WhereEqualTo("ee", "ff");
|
||||
List<AVQuery<AVObject>> queryList = new List<AVQuery<AVObject>> {
|
||||
q1, q2
|
||||
};
|
||||
AVQuery<AVObject> query = AVQuery<AVObject>.Or(queryList);
|
||||
IDictionary<string, object> obj = query.BuildWhere();
|
||||
TestContext.Out.WriteLine(JsonConvert.SerializeObject(obj));
|
||||
|
||||
AVQuery<AVObject> q3 = new AVQuery<AVObject>();
|
||||
q3.WhereEqualTo("xx", "yy");
|
||||
IDictionary<string, object> q3Obj = q3.BuildWhere();
|
||||
TestContext.Out.WriteLine(JsonConvert.SerializeObject(q3Obj));
|
||||
|
||||
AVQuery<AVObject> q4 = new AVQuery<AVObject>();
|
||||
q4.WhereEqualTo("aaa", "bbb");
|
||||
q4.WhereEqualTo("ccc", "ddd");
|
||||
IDictionary<string, object> q4Obj = q4.BuildWhere();
|
||||
TestContext.Out.WriteLine(JsonConvert.SerializeObject(q4Obj));
|
||||
|
||||
AVQuery<AVObject> q5 = new AVQuery<AVObject>();
|
||||
q5.WhereEqualTo("aaa", "bbb");
|
||||
q5.WhereEqualTo("aaa", "ccc");
|
||||
IDictionary<string, object> q5Obj = q5.BuildWhere();
|
||||
TestContext.Out.WriteLine(JsonConvert.SerializeObject(q5Obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LeanCloud.Storage.Internal;
|
||||
|
||||
namespace LeanCloud {
|
||||
public class AVQuery2<T> where T : AVObject {
|
||||
public string ClassName {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal string Op {
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// { key: { op: value } }
|
||||
/// </summary>
|
||||
public Dictionary<int, QueryCondition> Where {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal ReadOnlyCollection<string> OrderBy {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal ReadOnlyCollection<string> Includes {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal ReadOnlyCollection<string> SelectedKeys {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal string RedirectClassNameForKey {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal int? Skip {
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal int? Limit {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public AVQuery2() {
|
||||
Op = "$and";
|
||||
Where = new Dictionary<int, QueryCondition>();
|
||||
}
|
||||
|
||||
public static AVQuery2<T> And(IEnumerable<AVQuery2<T>> queries) {
|
||||
AVQuery2<T> combination = new AVQuery2<T>();
|
||||
if (queries != null) {
|
||||
foreach (AVQuery2<T> query in queries) {
|
||||
query.BuildWhere();
|
||||
}
|
||||
}
|
||||
return combination;
|
||||
}
|
||||
|
||||
#region where
|
||||
|
||||
public AVQuery2<T> WhereEqualTo(string key, object value) {
|
||||
AddCondition(key, "$eq", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
IDictionary<string, object> BuildWhere() {
|
||||
if (Where.Count == 0) {
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
if (Where.Count == 1) {
|
||||
return Where.Values.First().ToDictionary();
|
||||
}
|
||||
List<IDictionary<string, object>> conditions = new List<IDictionary<string, object>>();
|
||||
foreach (QueryCondition condition in Where.Values) {
|
||||
conditions.Add(condition.ToDictionary());
|
||||
}
|
||||
return new Dictionary<string, object> {
|
||||
{ Op, conditions }
|
||||
};
|
||||
}
|
||||
|
||||
void AddCondition(string key, string op, object value) {
|
||||
QueryCondition cond = new QueryCondition {
|
||||
Key = key,
|
||||
Op = op,
|
||||
Value = value
|
||||
};
|
||||
Where[cond.GetHashCode()] = cond;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,8 +37,8 @@ namespace LeanCloud.Storage.Internal {
|
|||
args.Add("fetchWhenSave", fetchWhenSave);
|
||||
}
|
||||
// 查询条件
|
||||
if (query != null && query.where != null) {
|
||||
args.Add("where", PointerOrLocalIdEncoder.Instance.Encode(query.where));
|
||||
if (query != null && query.condition != null) {
|
||||
args.Add("where", query.BuildWhere());
|
||||
}
|
||||
if (args.Count > 0) {
|
||||
string encode = AVClient.BuildQueryString(args);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
using System;
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public interface IQueryCondition : IEquatable<IQueryCondition>, IJsonConvertible {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
internal class QueryCompositionalCondition : IQueryCondition {
|
||||
internal const string AND = "$and";
|
||||
internal const string OR = "$or";
|
||||
|
||||
readonly List<IQueryCondition> conditions;
|
||||
readonly string composition;
|
||||
|
||||
internal QueryCompositionalCondition(string composition = AND) {
|
||||
conditions = new List<IQueryCondition>();
|
||||
this.composition = composition;
|
||||
}
|
||||
|
||||
public bool Equals(IQueryCondition other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IDictionary<string, object> ToJSON() {
|
||||
if (conditions == null || conditions.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
if (conditions.Count == 1) {
|
||||
return conditions[0].ToJSON();
|
||||
}
|
||||
List<object> list = new List<object>();
|
||||
foreach (IQueryCondition cond in conditions) {
|
||||
list.Add(cond.ToJSON());
|
||||
}
|
||||
return new Dictionary<string, object> {
|
||||
{ composition, list }
|
||||
};
|
||||
}
|
||||
|
||||
internal void AddCondition(IQueryCondition condition) {
|
||||
if (condition == null) {
|
||||
return;
|
||||
}
|
||||
// 组合查询的 Key 为 null
|
||||
conditions.RemoveAll(cond => {
|
||||
return cond.Equals(condition);
|
||||
});
|
||||
conditions.Add(condition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
/// <summary>
|
||||
/// 查询条件类
|
||||
/// </summary>
|
||||
public class QueryCondition : IEquatable<QueryCondition> {
|
||||
internal class QueryOperationCondition : IQueryCondition {
|
||||
public string Key {
|
||||
get; set;
|
||||
}
|
||||
|
@ -20,25 +14,19 @@ namespace LeanCloud.Storage.Internal {
|
|||
get; set;
|
||||
}
|
||||
|
||||
public bool Equals(QueryCondition other) {
|
||||
return Key == other.Key && Op == other.Op;
|
||||
public bool Equals(IQueryCondition other) {
|
||||
if (other is QueryOperationCondition) {
|
||||
QueryOperationCondition otherCond = other as QueryOperationCondition;
|
||||
return Key == otherCond.Key && Op == otherCond.Op;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is QueryCondition && Equals(obj as QueryCondition);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return Key.GetHashCode() * 31 + Op.GetHashCode();
|
||||
}
|
||||
|
||||
internal IDictionary<string, object> ToDictionary() {
|
||||
public IDictionary<string, object> ToJSON() {
|
||||
return new Dictionary<string, object> {
|
||||
{
|
||||
Key, new Dictionary<string, object> {
|
||||
{ Op, PointerOrLocalIdEncoder.Instance.Encode(Value) }
|
||||
}
|
||||
}
|
||||
{ Key, new Dictionary<string, object> {
|
||||
{ Op, Value }
|
||||
} }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public class QueryEqualCondition : IQueryCondition {
|
||||
string key;
|
||||
object value;
|
||||
|
||||
public QueryEqualCondition(string key, object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public bool Equals(IQueryCondition other) {
|
||||
if (other is QueryEqualCondition) {
|
||||
QueryEqualCondition otherCond = other as QueryEqualCondition;
|
||||
return key == otherCond.key;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IDictionary<string, object> ToJSON() {
|
||||
return new Dictionary<string, object> {
|
||||
{ key, PointerOrLocalIdEncoder.Instance.Encode(value) }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
internal class QueryRelatedCondition : IQueryCondition {
|
||||
AVObject parent;
|
||||
string key;
|
||||
|
||||
public QueryRelatedCondition(AVObject parent, string key) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public bool Equals(IQueryCondition other) {
|
||||
if (other is QueryRelatedCondition) {
|
||||
QueryRelatedCondition otherCond = other as QueryRelatedCondition;
|
||||
return key == otherCond.key;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IDictionary<string, object> ToJSON() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "$relatedTo", new Dictionary<string, object> {
|
||||
{ "object", PointerOrLocalIdEncoder.Instance.Encode(parent) },
|
||||
{ "key", key }
|
||||
} }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,8 @@ namespace LeanCloud {
|
|||
}
|
||||
}
|
||||
|
||||
internal IDictionary<string, object> where;
|
||||
internal QueryCompositionalCondition condition;
|
||||
|
||||
internal ReadOnlyCollection<string> orderBy;
|
||||
internal ReadOnlyCollection<string> includes;
|
||||
internal ReadOnlyCollection<string> selectedKeys;
|
||||
|
@ -58,148 +59,41 @@ namespace LeanCloud {
|
|||
throw new ArgumentNullException(nameof(className));
|
||||
}
|
||||
ClassName = className;
|
||||
}
|
||||
|
||||
protected AVQuery(AVQuery<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(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;
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<string> MergeIncludes(IEnumerable<string> otherIncludes) {
|
||||
if (includes == null) {
|
||||
return new HashSet<string>(otherIncludes);
|
||||
}
|
||||
var newIncludes = new HashSet<string>(includes);
|
||||
foreach (var item in otherIncludes) {
|
||||
newIncludes.Add(item);
|
||||
}
|
||||
return newIncludes;
|
||||
}
|
||||
|
||||
HashSet<string> MergeSelectedKeys(IEnumerable<string> otherSelectedKeys) {
|
||||
if (selectedKeys == null) {
|
||||
return new HashSet<string>(otherSelectedKeys);
|
||||
}
|
||||
var newSelectedKeys = new HashSet<string>(selectedKeys);
|
||||
foreach (var item in otherSelectedKeys) {
|
||||
newSelectedKeys.Add(item);
|
||||
}
|
||||
return newSelectedKeys;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (!parameters.TryGetValue("where", out object 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), new Dictionary<string, object> {
|
||||
{ "$or", orValue }
|
||||
});
|
||||
condition = new QueryCompositionalCondition();
|
||||
}
|
||||
|
||||
public static AVQuery<T> And(IEnumerable<AVQuery<T>> queries) {
|
||||
AVQuery<T> composition = new AVQuery<T>();
|
||||
string className = null;
|
||||
var andValue = 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.");
|
||||
if (queries != null) {
|
||||
foreach (AVQuery<T> query in queries) {
|
||||
if (className != null && className != query.ClassName) {
|
||||
throw new ArgumentException("All of the queries in an or query must be on the same class.");
|
||||
}
|
||||
composition.AddCondition(query.condition);
|
||||
className = query.ClassName;
|
||||
}
|
||||
className = q.ClassName;
|
||||
var parameters = q.BuildParameters();
|
||||
if (parameters.Count == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!parameters.TryGetValue("where", out object where) || parameters.Count > 1) {
|
||||
throw new ArgumentException("None of the queries in an or query can have non-filtering clauses");
|
||||
}
|
||||
andValue.Add(where as IDictionary<string, object>);
|
||||
}
|
||||
return new AVQuery<T>(new AVQuery<T>(className), new Dictionary<string, object> {
|
||||
{ "$and", andValue }
|
||||
});
|
||||
composition.ClassName = className;
|
||||
return composition;
|
||||
}
|
||||
|
||||
public static AVQuery<T> Or(IEnumerable<AVQuery<T>> queries) {
|
||||
AVQuery<T> composition = new AVQuery<T> {
|
||||
condition = new QueryCompositionalCondition(QueryCompositionalCondition.OR)
|
||||
};
|
||||
string className = null;
|
||||
if (queries != null) {
|
||||
foreach (AVQuery<T> query in queries) {
|
||||
if (className != null && className != query.ClassName) {
|
||||
throw new ArgumentException("All of the queries in an or query must be on the same class.");
|
||||
}
|
||||
composition.AddCondition(query.condition);
|
||||
className = query.ClassName;
|
||||
}
|
||||
}
|
||||
composition.ClassName = className;
|
||||
return composition;
|
||||
}
|
||||
|
||||
public virtual async Task<IEnumerable<T>> FindAsync(CancellationToken cancellationToken = default) {
|
||||
|
@ -229,7 +123,9 @@ namespace LeanCloud {
|
|||
public virtual async Task<T> GetAsync(string objectId, CancellationToken cancellationToken) {
|
||||
AVQuery<T> singleItemQuery = new AVQuery<T>(ClassName)
|
||||
.WhereEqualTo("objectId", objectId);
|
||||
singleItemQuery = new AVQuery<T>(singleItemQuery, includes: this.includes, selectedKeys: this.selectedKeys, limit: 1);
|
||||
singleItemQuery.includes = includes;
|
||||
singleItemQuery.selectedKeys = selectedKeys;
|
||||
singleItemQuery.limit = 1;
|
||||
var result = await singleItemQuery.FindAsync(cancellationToken);
|
||||
var first = result.FirstOrDefault();
|
||||
if (first == null) {
|
||||
|
@ -246,20 +142,11 @@ namespace LeanCloud {
|
|||
/// <param name="cql">CQL 语句</param>
|
||||
/// <param name="cancellationToken">CancellationToken</param>
|
||||
/// <returns>返回符合条件的对象集合</returns>
|
||||
public static Task<IEnumerable<T>> DoCloudQueryAsync(string cql, CancellationToken cancellationToken) {
|
||||
public static Task<IEnumerable<T>> DoCloudQueryAsync(string cql, CancellationToken cancellationToken = default) {
|
||||
var queryString = $"cloudQuery?cql={Uri.EscapeDataString(cql)}";
|
||||
return RebuildObjectFromCloudQueryResult(queryString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行 CQL 查询
|
||||
/// </summary>
|
||||
/// <param name="cql"></param>
|
||||
/// <returns></returns>
|
||||
public static Task<IEnumerable<T>> DoCloudQueryAsync(string cql) {
|
||||
return DoCloudQueryAsync(cql, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行 CQL 查询
|
||||
/// </summary>
|
||||
|
@ -292,35 +179,6 @@ namespace LeanCloud {
|
|||
|
||||
#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>
|
||||
|
@ -328,8 +186,8 @@ namespace LeanCloud {
|
|||
/// <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 (condition != null) {
|
||||
result["where"] = condition.ToJSON();
|
||||
}
|
||||
if (orderBy != null) {
|
||||
result["order"] = string.Join(",", orderBy.ToArray());
|
||||
|
@ -367,7 +225,7 @@ namespace LeanCloud {
|
|||
|
||||
var other = obj as AVQuery<T>;
|
||||
return ClassName.Equals(other.ClassName) &&
|
||||
where.CollectionsEqual(other.where) &&
|
||||
condition.Equals(other.condition) &&
|
||||
orderBy.CollectionsEqual(other.orderBy) &&
|
||||
includes.CollectionsEqual(other.includes) &&
|
||||
selectedKeys.CollectionsEqual(other.selectedKeys) &&
|
||||
|
@ -441,94 +299,68 @@ namespace LeanCloud {
|
|||
#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() } } }
|
||||
});
|
||||
AddCondition(key, "$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() } } }
|
||||
});
|
||||
AddCondition(key, "$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) } } }
|
||||
});
|
||||
AddCondition(key, "$regex", RegexQuote(substring));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereDoesNotExist(string key) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$exists", false } } }
|
||||
});
|
||||
AddCondition(key, "$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) } } }
|
||||
});
|
||||
AddCondition(key, "$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) + "$" } } }
|
||||
});
|
||||
AddCondition(key, "$regex", RegexQuote(suffix) + "$");
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereEqualTo(string key, object value) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, value }
|
||||
});
|
||||
AddCondition(new QueryEqualCondition(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereSizeEqualTo(string key, uint size) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$size", size } } }
|
||||
});
|
||||
AddCondition(key, "$size", size);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereExists(string key) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$exists", true } } }
|
||||
});
|
||||
AddCondition(key, "$exists", true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereGreaterThan(string key, object value) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$gt", value } } }
|
||||
});
|
||||
AddCondition(key, "$gt", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereGreaterThanOrEqualTo(string key, object value) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$gte", value } } }
|
||||
});
|
||||
AddCondition(key, "$gte", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereLessThan(string key, object value) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$lt", value } } }
|
||||
});
|
||||
AddCondition(key, "$lt", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AVQuery<T> WhereLessThanOrEqualTo(string key, object value) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$lte", value } } }
|
||||
});
|
||||
AddCondition(key, "$lte", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -537,9 +369,8 @@ namespace LeanCloud {
|
|||
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, EncodeRegex(regex, modifiers) }
|
||||
});
|
||||
AddCondition(key, "$regex", regex.ToString());
|
||||
AddCondition(key, "options", modifiers);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -561,9 +392,7 @@ namespace LeanCloud {
|
|||
{ "query", query.BuildParameters(true)},
|
||||
{ "key", keyInQuery}
|
||||
};
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$select", parameters } } }
|
||||
});
|
||||
AddCondition(key, "$select", parameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -573,78 +402,52 @@ namespace LeanCloud {
|
|||
{ "query", query.BuildParameters(true)},
|
||||
{ "key", keyInQuery}
|
||||
};
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{ key, new Dictionary<string, object>{ { "$dontSelect", parameters } } }
|
||||
});
|
||||
AddCondition(key, "$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) } } }
|
||||
});
|
||||
AddCondition(key, "$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 } } }
|
||||
});
|
||||
AddCondition(key, "$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() } } }
|
||||
});
|
||||
AddCondition(key, "$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 } } }
|
||||
});
|
||||
AddCondition(key, "$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) } } }
|
||||
});
|
||||
AddCondition(key, "$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}}
|
||||
} } } }
|
||||
});
|
||||
Dictionary<string, object> value = new Dictionary<string, object> {
|
||||
{ "$box", new[] { southwest, northeast } }
|
||||
};
|
||||
AddCondition(key, "$within", value);
|
||||
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 } } }
|
||||
});
|
||||
AddCondition(key, "$nearSphere", point);
|
||||
AddCondition(key, "$maxDistance", maxDistance.Radians);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal AVQuery<T> WhereRelatedTo(AVObject parent, string key) {
|
||||
MergeWhereClauses(new Dictionary<string, object> {
|
||||
{
|
||||
"$relatedTo",
|
||||
new Dictionary<string, object> {
|
||||
{ "object", parent },
|
||||
{ "key", key }
|
||||
}
|
||||
}
|
||||
});
|
||||
public AVQuery<T> WhereRelatedTo(AVObject parent, string key) {
|
||||
AddCondition(new QueryRelatedCondition(parent, key));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -654,25 +457,22 @@ namespace LeanCloud {
|
|||
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;
|
||||
public IDictionary<string, object> BuildWhere() {
|
||||
IDictionary<string, object> where = condition.ToJSON();
|
||||
return where;
|
||||
}
|
||||
|
||||
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;
|
||||
void AddCondition(string key, string op, object value) {
|
||||
QueryOperationCondition cond = new QueryOperationCondition {
|
||||
Key = key,
|
||||
Op = op,
|
||||
Value = value
|
||||
};
|
||||
condition.AddCondition(cond);
|
||||
}
|
||||
|
||||
void AddCondition(IQueryCondition cond) {
|
||||
condition.AddCondition(cond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue