From 6252a06b72e2be06111ecb0839af2c8460eb9c54 Mon Sep 17 00:00:00 2001 From: oneRain Date: Mon, 16 Sep 2019 16:46:12 +0800 Subject: [PATCH] =?UTF-8?q?*=20QueryTest.cs:=20chore:=20=E5=9C=A8=20AVQuer?= =?UTF-8?q?y=20=E4=B8=AD=E6=8F=90=E5=8F=96=20QueryCondition=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=EF=BC=8C=E5=AE=9E=E7=8E=B0=20Where=20=20=20=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E7=BB=84=E5=90=88=EF=BC=9B=E5=85=B1=E4=BA=AB=E7=BB=99?= =?UTF-8?q?=20AVIMCOnversationQuery=20=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * AVQuery2.cs: * AVQuery.cs: * QueryCondition.cs: * IQueryCondition.cs: * QueryEqualCondition.cs: * QueryRelatedCondition.cs: * QueryCompositionalCondition.cs: * AVObjectController.cs: --- Storage/Storage.Test/QueryTest.cs | 51 ++- Storage/Storage/AVQuery2.cs | 97 ----- .../Object/Controller/AVObjectController.cs | 4 +- .../Storage/Internal/Query/IQueryCondition.cs | 5 + .../Query/QueryCompositionalCondition.cs | 47 +++ .../Storage/Internal/Query/QueryCondition.cs | 36 +- .../Internal/Query/QueryEqualCondition.cs | 27 ++ .../Internal/Query/QueryRelatedCondition.cs | 30 ++ Storage/Storage/Public/AVQuery.cs | 364 ++++-------------- 9 files changed, 252 insertions(+), 409 deletions(-) delete mode 100644 Storage/Storage/AVQuery2.cs create mode 100644 Storage/Storage/Internal/Query/IQueryCondition.cs create mode 100644 Storage/Storage/Internal/Query/QueryCompositionalCondition.cs create mode 100644 Storage/Storage/Internal/Query/QueryEqualCondition.cs create mode 100644 Storage/Storage/Internal/Query/QueryRelatedCondition.cs diff --git a/Storage/Storage.Test/QueryTest.cs b/Storage/Storage.Test/QueryTest.cs index c4f3abb..5b7cb8d 100644 --- a/Storage/Storage.Test/QueryTest.cs +++ b/Storage/Storage.Test/QueryTest.cs @@ -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("Foo"); - query.WhereGreaterThanOrEqualTo("a", 100); - query.WhereLessThanOrEqualTo("a", 100); - //query.WhereEqualTo("content", "hello"); + var query = new AVQuery("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 query = new AVQuery("Tag"); + query.WhereRelatedTo(todo, "tags"); + IEnumerable results = await query.FindAsync(); + foreach (AVObject tag in results) { + TestContext.Out.WriteLine(tag.ObjectId); + } + } + + [Test] + public void Where() { + AVQuery q1 = new AVQuery(); + q1.WhereEqualTo("aa", "bb"); + AVQuery q2 = new AVQuery(); + q2.WhereEqualTo("cc", "dd"); + q2.WhereEqualTo("ee", "ff"); + List> queryList = new List> { + q1, q2 + }; + AVQuery query = AVQuery.Or(queryList); + IDictionary obj = query.BuildWhere(); + TestContext.Out.WriteLine(JsonConvert.SerializeObject(obj)); + + AVQuery q3 = new AVQuery(); + q3.WhereEqualTo("xx", "yy"); + IDictionary q3Obj = q3.BuildWhere(); + TestContext.Out.WriteLine(JsonConvert.SerializeObject(q3Obj)); + + AVQuery q4 = new AVQuery(); + q4.WhereEqualTo("aaa", "bbb"); + q4.WhereEqualTo("ccc", "ddd"); + IDictionary q4Obj = q4.BuildWhere(); + TestContext.Out.WriteLine(JsonConvert.SerializeObject(q4Obj)); + + AVQuery q5 = new AVQuery(); + q5.WhereEqualTo("aaa", "bbb"); + q5.WhereEqualTo("aaa", "ccc"); + IDictionary q5Obj = q5.BuildWhere(); + TestContext.Out.WriteLine(JsonConvert.SerializeObject(q5Obj)); + } } } diff --git a/Storage/Storage/AVQuery2.cs b/Storage/Storage/AVQuery2.cs deleted file mode 100644 index 81f5a53..0000000 --- a/Storage/Storage/AVQuery2.cs +++ /dev/null @@ -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 where T : AVObject { - public string ClassName { - get; set; - } - - internal string Op { - get; set; - } - - /// - /// { key: { op: value } } - /// - public Dictionary Where { - get; set; - } - - internal ReadOnlyCollection OrderBy { - get; set; - } - - internal ReadOnlyCollection Includes { - get; set; - } - - internal ReadOnlyCollection 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(); - } - - public static AVQuery2 And(IEnumerable> queries) { - AVQuery2 combination = new AVQuery2(); - if (queries != null) { - foreach (AVQuery2 query in queries) { - query.BuildWhere(); - } - } - return combination; - } - - #region where - - public AVQuery2 WhereEqualTo(string key, object value) { - AddCondition(key, "$eq", value); - return this; - } - - #endregion - - IDictionary BuildWhere() { - if (Where.Count == 0) { - return new Dictionary(); - } - if (Where.Count == 1) { - return Where.Values.First().ToDictionary(); - } - List> conditions = new List>(); - foreach (QueryCondition condition in Where.Values) { - conditions.Add(condition.ToDictionary()); - } - return new Dictionary { - { Op, conditions } - }; - } - - void AddCondition(string key, string op, object value) { - QueryCondition cond = new QueryCondition { - Key = key, - Op = op, - Value = value - }; - Where[cond.GetHashCode()] = cond; - } - } -} diff --git a/Storage/Storage/Internal/Object/Controller/AVObjectController.cs b/Storage/Storage/Internal/Object/Controller/AVObjectController.cs index fd6c144..af0eae4 100644 --- a/Storage/Storage/Internal/Object/Controller/AVObjectController.cs +++ b/Storage/Storage/Internal/Object/Controller/AVObjectController.cs @@ -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); diff --git a/Storage/Storage/Internal/Query/IQueryCondition.cs b/Storage/Storage/Internal/Query/IQueryCondition.cs new file mode 100644 index 0000000..c3edd2b --- /dev/null +++ b/Storage/Storage/Internal/Query/IQueryCondition.cs @@ -0,0 +1,5 @@ +using System; +namespace LeanCloud.Storage.Internal { + public interface IQueryCondition : IEquatable, IJsonConvertible { + } +} diff --git a/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs b/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs new file mode 100644 index 0000000..3783d3d --- /dev/null +++ b/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs @@ -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 conditions; + readonly string composition; + + internal QueryCompositionalCondition(string composition = AND) { + conditions = new List(); + this.composition = composition; + } + + public bool Equals(IQueryCondition other) { + return false; + } + + public IDictionary ToJSON() { + if (conditions == null || conditions.Count == 0) { + return null; + } + if (conditions.Count == 1) { + return conditions[0].ToJSON(); + } + List list = new List(); + foreach (IQueryCondition cond in conditions) { + list.Add(cond.ToJSON()); + } + return new Dictionary { + { composition, list } + }; + } + + internal void AddCondition(IQueryCondition condition) { + if (condition == null) { + return; + } + // 组合查询的 Key 为 null + conditions.RemoveAll(cond => { + return cond.Equals(condition); + }); + conditions.Add(condition); + } + } +} diff --git a/Storage/Storage/Internal/Query/QueryCondition.cs b/Storage/Storage/Internal/Query/QueryCondition.cs index 9938c02..80b45b3 100644 --- a/Storage/Storage/Internal/Query/QueryCondition.cs +++ b/Storage/Storage/Internal/Query/QueryCondition.cs @@ -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 { - /// - /// 查询条件类 - /// - public class QueryCondition : IEquatable { + 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 ToDictionary() { + public IDictionary ToJSON() { return new Dictionary { - { - Key, new Dictionary { - { Op, PointerOrLocalIdEncoder.Instance.Encode(Value) } - } - } + { Key, new Dictionary { + { Op, Value } + } } }; } } diff --git a/Storage/Storage/Internal/Query/QueryEqualCondition.cs b/Storage/Storage/Internal/Query/QueryEqualCondition.cs new file mode 100644 index 0000000..96ff1b4 --- /dev/null +++ b/Storage/Storage/Internal/Query/QueryEqualCondition.cs @@ -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 ToJSON() { + return new Dictionary { + { key, PointerOrLocalIdEncoder.Instance.Encode(value) } + }; + } + } +} diff --git a/Storage/Storage/Internal/Query/QueryRelatedCondition.cs b/Storage/Storage/Internal/Query/QueryRelatedCondition.cs new file mode 100644 index 0000000..b39cb22 --- /dev/null +++ b/Storage/Storage/Internal/Query/QueryRelatedCondition.cs @@ -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 ToJSON() { + return new Dictionary { + { "$relatedTo", new Dictionary { + { "object", PointerOrLocalIdEncoder.Instance.Encode(parent) }, + { "key", key } + } } + }; + } + } +} diff --git a/Storage/Storage/Public/AVQuery.cs b/Storage/Storage/Public/AVQuery.cs index 749e3d5..e552e24 100644 --- a/Storage/Storage/Public/AVQuery.cs +++ b/Storage/Storage/Public/AVQuery.cs @@ -29,7 +29,8 @@ namespace LeanCloud { } } - internal IDictionary where; + internal QueryCompositionalCondition condition; + internal ReadOnlyCollection orderBy; internal ReadOnlyCollection includes; internal ReadOnlyCollection selectedKeys; @@ -58,148 +59,41 @@ namespace LeanCloud { throw new ArgumentNullException(nameof(className)); } ClassName = className; - } - - protected AVQuery(AVQuery source, - IDictionary where = null, - IEnumerable replacementOrderBy = null, - IEnumerable thenBy = null, - int? skip = null, - int? limit = null, - IEnumerable includes = null, - IEnumerable selectedKeys = null, - string redirectClassNameForKey = null) { - - 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(newWhere); - } - - if (replacementOrderBy != null) { - this.orderBy = new ReadOnlyCollection(replacementOrderBy.ToList()); - } - - if (thenBy != null) { - if (this.orderBy == null) { - throw new ArgumentException("You must call OrderBy before calling ThenBy."); - } - var newOrderBy = new List(this.orderBy); - newOrderBy.AddRange(thenBy); - this.orderBy = new ReadOnlyCollection(newOrderBy); - } - - // Remove duplicates. - if (this.orderBy != null) { - var newOrderBy = new HashSet(this.orderBy); - this.orderBy = new ReadOnlyCollection(newOrderBy.ToList()); - } - - if (skip != null) { - this.skip = (this.skip ?? 0) + skip; - } - - if (limit != null) { - this.limit = limit; - } - - if (includes != null) { - var newIncludes = MergeIncludes(includes); - this.includes = new ReadOnlyCollection(newIncludes.ToList()); - } - - if (selectedKeys != null) { - var newSelectedKeys = MergeSelectedKeys(selectedKeys); - this.selectedKeys = new ReadOnlyCollection(newSelectedKeys.ToList()); - } - - if (redirectClassNameForKey != null) { - this.redirectClassNameForKey = redirectClassNameForKey; - } - } - - HashSet MergeIncludes(IEnumerable otherIncludes) { - if (includes == null) { - return new HashSet(otherIncludes); - } - var newIncludes = new HashSet(includes); - foreach (var item in otherIncludes) { - newIncludes.Add(item); - } - return newIncludes; - } - - HashSet MergeSelectedKeys(IEnumerable otherSelectedKeys) { - if (selectedKeys == null) { - return new HashSet(otherSelectedKeys); - } - var newSelectedKeys = new HashSet(selectedKeys); - foreach (var item in otherSelectedKeys) { - newSelectedKeys.Add(item); - } - return newSelectedKeys; - } - - public static AVQuery Or(IEnumerable> queries) { - string className = null; - var orValue = new List>(); - // We need to cast it to non-generic IEnumerable because of AOT-limitation - var nonGenericQueries = (IEnumerable)queries; - foreach (var obj in nonGenericQueries) { - var q = (AVQuery)obj; - if (className != null && q.ClassName != className) { - throw new ArgumentException("All of the queries in an or query must be on the same class."); - } - className = q.ClassName; - var parameters = q.BuildParameters(); - if (parameters.Count == 0) { - continue; - } - 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); - } - return new AVQuery(new AVQuery(className), new Dictionary { - { "$or", orValue } - }); + condition = new QueryCompositionalCondition(); } public static AVQuery And(IEnumerable> queries) { + AVQuery composition = new AVQuery(); string className = null; - var andValue = new List>(); - // We need to cast it to non-generic IEnumerable because of AOT-limitation - var nonGenericQueries = (IEnumerable)queries; - foreach (var obj in nonGenericQueries) { - var q = (AVQuery)obj; - if (className != null && q.ClassName != className) { - throw new ArgumentException("All of the queries in an or query must be on the same class."); + if (queries != null) { + foreach (AVQuery 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); } - return new AVQuery(new AVQuery(className), new Dictionary { - { "$and", andValue } - }); + composition.ClassName = className; + return composition; + } + + public static AVQuery Or(IEnumerable> queries) { + AVQuery composition = new AVQuery { + condition = new QueryCompositionalCondition(QueryCompositionalCondition.OR) + }; + string className = null; + if (queries != null) { + foreach (AVQuery 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> FindAsync(CancellationToken cancellationToken = default) { @@ -229,7 +123,9 @@ namespace LeanCloud { public virtual async Task GetAsync(string objectId, CancellationToken cancellationToken) { AVQuery singleItemQuery = new AVQuery(ClassName) .WhereEqualTo("objectId", objectId); - singleItemQuery = new AVQuery(singleItemQuery, includes: this.includes, selectedKeys: this.selectedKeys, limit: 1); + 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 { /// CQL 语句 /// CancellationToken /// 返回符合条件的对象集合 - public static Task> DoCloudQueryAsync(string cql, CancellationToken cancellationToken) { + public static Task> DoCloudQueryAsync(string cql, CancellationToken cancellationToken = default) { var queryString = $"cloudQuery?cql={Uri.EscapeDataString(cql)}"; return RebuildObjectFromCloudQueryResult(queryString); } - /// - /// 执行 CQL 查询 - /// - /// - /// - public static Task> DoCloudQueryAsync(string cql) { - return DoCloudQueryAsync(cql, CancellationToken.None); - } - /// /// 执行 CQL 查询 /// @@ -292,35 +179,6 @@ namespace LeanCloud { #endregion - IDictionary MergeWhereClauses(IDictionary otherWhere) { - if (where == null) { - where = otherWhere; - return where; - } - var newWhere = new Dictionary(where); - foreach (var pair in otherWhere) { - var condition = pair.Value as IDictionary; - if (newWhere.ContainsKey(pair.Key)) { - var oldCondition = newWhere[pair.Key] as IDictionary; - if (oldCondition == null || condition == null) { - throw new ArgumentException("More than one where clause for the given key provided."); - } - var newCondition = new Dictionary(oldCondition); - foreach (var conditionPair in condition) { - if (newCondition.ContainsKey(conditionPair.Key)) { - throw new ArgumentException("More than one condition for the given key provided."); - } - newCondition[conditionPair.Key] = conditionPair.Value; - } - newWhere[pair.Key] = newCondition; - } else { - newWhere[pair.Key] = pair.Value; - } - } - where = newWhere; - return where; - } - /// /// 构建查询字符串 /// @@ -328,8 +186,8 @@ namespace LeanCloud { /// public IDictionary BuildParameters(bool includeClassName = false) { Dictionary result = new Dictionary(); - 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; 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 WhereContainedIn(string key, IEnumerable values) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$in", values.ToList() } } } - }); + AddCondition(key, "$in", values.ToList()); return this; } public AVQuery WhereContainsAll(string key, IEnumerable values) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$all", values.ToList() } } } - }); + AddCondition(key, "$all", values.ToList()); return this; } public AVQuery WhereContains(string key, string substring) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$regex", RegexQuote(substring) } } } - }); + AddCondition(key, "$regex", RegexQuote(substring)); return this; } public AVQuery WhereDoesNotExist(string key) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$exists", false } } } - }); + AddCondition(key, "$exists", false); return this; } public AVQuery WhereDoesNotMatchQuery(string key, AVQuery query) where TOther : AVObject { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$notInQuery", query.BuildParameters(true) } } } - }); + AddCondition(key, "$notInQuery", query.BuildParameters(true)); return this; } public AVQuery WhereEndsWith(string key, string suffix) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$regex", RegexQuote(suffix) + "$" } } } - }); + AddCondition(key, "$regex", RegexQuote(suffix) + "$"); return this; } public AVQuery WhereEqualTo(string key, object value) { - MergeWhereClauses(new Dictionary { - { key, value } - }); + AddCondition(new QueryEqualCondition(key, value)); return this; } public AVQuery WhereSizeEqualTo(string key, uint size) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$size", size } } } - }); + AddCondition(key, "$size", size); return this; } public AVQuery WhereExists(string key) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$exists", true } } } - }); + AddCondition(key, "$exists", true); return this; } public AVQuery WhereGreaterThan(string key, object value) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$gt", value } } } - }); + AddCondition(key, "$gt", value); return this; } public AVQuery WhereGreaterThanOrEqualTo(string key, object value) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$gte", value } } } - }); + AddCondition(key, "$gte", value); return this; } public AVQuery WhereLessThan(string key, object value) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$lt", value } } } - }); + AddCondition(key, "$lt", value); return this; } public AVQuery WhereLessThanOrEqualTo(string key, object value) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$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 { - { 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 { - { key, new Dictionary{ { "$select", parameters } } } - }); + AddCondition(key, "$select", parameters); return this; } @@ -573,78 +402,52 @@ namespace LeanCloud { { "query", query.BuildParameters(true)}, { "key", keyInQuery} }; - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$dontSelect", parameters } } } - }); + AddCondition(key, "$dontSelect", parameters); return this; } public AVQuery WhereMatchesQuery(string key, AVQuery query) where TOther : AVObject { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$inQuery", query.BuildParameters(true) } } } - }); + AddCondition(key, "$inQuery", query.BuildParameters(true)); return this; } public AVQuery WhereNear(string key, AVGeoPoint point) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$nearSphere", point } } } - }); + AddCondition(key, "$nearSphere", point); return this; } public AVQuery WhereNotContainedIn(string key, IEnumerable values) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$nin", values.ToList() } } } - }); + AddCondition(key, "$nin", values.ToList()); return this; } public AVQuery WhereNotEqualTo(string key, object value) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$ne", value } } } - }); + AddCondition(key, "$ne", value); return this; } public AVQuery WhereStartsWith(string key, string suffix) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$regex", "^" + RegexQuote(suffix) } } } - }); + AddCondition(key, "$regex", "^" + RegexQuote(suffix)); return this; } public AVQuery WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$within", - new Dictionary { - { "$box", new[] {southwest, northeast}} - } } } } - }); + Dictionary value = new Dictionary { + { "$box", new[] { southwest, northeast } } + }; + AddCondition(key, "$within", value); return this; } public AVQuery WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$nearSphere", point } } } - }); - MergeWhereClauses(new Dictionary { - { key, new Dictionary{ { "$maxDistance", maxDistance.Radians } } } - }); + AddCondition(key, "$nearSphere", point); + AddCondition(key, "$maxDistance", maxDistance.Radians); return this; } - internal AVQuery WhereRelatedTo(AVObject parent, string key) { - MergeWhereClauses(new Dictionary { - { - "$relatedTo", - new Dictionary { - { "object", parent }, - { "key", key } - } - } - }); + public AVQuery 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 EncodeRegex(Regex regex, string modifiers) { - var options = GetRegexOptions(regex, modifiers); - var dict = new Dictionary(); - dict["$regex"] = regex.ToString(); - if (!string.IsNullOrEmpty(options)) { - dict["$options"] = options; - } - return dict; + public IDictionary BuildWhere() { + IDictionary 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); } } }