From 3f57be22a23b48ccf7744738703c64ccb0ca146d Mon Sep 17 00:00:00 2001 From: oneRain Date: Tue, 17 Sep 2019 11:31:40 +0800 Subject: [PATCH] =?UTF-8?q?*=20AVQueryController.cs:=20chore:=20=E7=AE=80?= =?UTF-8?q?=E5=8C=96=20AVQuery=20=E9=80=BB=E8=BE=91=EF=BC=8C=E5=B0=86?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6=E9=80=BB=E8=BE=91=E8=BD=AC?= =?UTF-8?q?=E7=A7=BB=E8=87=B3=20=20=20QueryCondition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * AVQuery.cs: * IQueryCondition.cs: * QueryEqualCondition.cs: * AVQueryExtensions.cs: * QueryCompositionalCondition.cs: --- .../Query/Controller/AVQueryController.cs | 11 +- .../Storage/Internal/Query/IQueryCondition.cs | 7 +- .../Query/QueryCompositionalCondition.cs | 212 +++++++++++++++--- .../Internal/Query/QueryEqualCondition.cs | 2 +- .../Internal/Utilities/AVQueryExtensions.cs | 24 -- Storage/Storage/Public/AVQuery.cs | 121 ++++------ 6 files changed, 230 insertions(+), 147 deletions(-) delete mode 100644 Storage/Storage/Internal/Utilities/AVQueryExtensions.cs diff --git a/Storage/Storage/Internal/Query/Controller/AVQueryController.cs b/Storage/Storage/Internal/Query/Controller/AVQueryController.cs index a1e1306..d5a0e82 100644 --- a/Storage/Storage/Internal/Query/Controller/AVQueryController.cs +++ b/Storage/Storage/Internal/Query/Controller/AVQueryController.cs @@ -7,16 +7,13 @@ using System.Threading.Tasks; namespace LeanCloud.Storage.Internal { public class AVQueryController { - public async Task> FindAsync(AVQuery query, AVUser user, - CancellationToken cancellationToken) where T : AVObject { + public async Task> FindAsync(AVQuery query, CancellationToken cancellationToken) where T : AVObject { IList items = await FindAsync>(query.Path, query.BuildParameters(), "results", cancellationToken); return from item in items select AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance); } - public async Task CountAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject { + public async Task CountAsync(AVQuery query, CancellationToken cancellationToken) where T : AVObject { var parameters = query.BuildParameters(); parameters["limit"] = 0; parameters["count"] = 1; @@ -24,9 +21,7 @@ namespace LeanCloud.Storage.Internal { return Convert.ToInt32(ret); } - public async Task FirstAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject { + public async Task FirstAsync(AVQuery query, CancellationToken cancellationToken) where T : AVObject { var parameters = query.BuildParameters(); parameters["limit"] = 1; IList items = await FindAsync>(query.Path, query.BuildParameters(), "results", cancellationToken); diff --git a/Storage/Storage/Internal/Query/IQueryCondition.cs b/Storage/Storage/Internal/Query/IQueryCondition.cs index c3edd2b..ae5a84e 100644 --- a/Storage/Storage/Internal/Query/IQueryCondition.cs +++ b/Storage/Storage/Internal/Query/IQueryCondition.cs @@ -1,5 +1,10 @@ using System; namespace LeanCloud.Storage.Internal { - public interface IQueryCondition : IEquatable, IJsonConvertible { + /// + /// 查询条件接口 + /// IEquatable 用于比对(替换)相同的查询条件 + /// IJsonConvertible 用于生成序列化 Dictionary + /// + internal interface IQueryCondition : IEquatable, IJsonConvertible { } } diff --git a/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs b/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs index f1d997f..7ccb991 100644 --- a/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs +++ b/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Text.RegularExpressions; namespace LeanCloud.Storage.Internal { internal class QueryCompositionalCondition : IQueryCondition { @@ -11,10 +11,9 @@ namespace LeanCloud.Storage.Internal { readonly List conditions; readonly string composition; - internal ReadOnlyCollection orderBy; + internal List orderBy; internal HashSet includes; internal HashSet selectedKeys; - internal string redirectClassNameForKey; internal int skip; internal int limit; @@ -25,6 +24,8 @@ namespace LeanCloud.Storage.Internal { limit = 30; } + #region IQueryCondition + public bool Equals(IQueryCondition other) { return false; } @@ -45,41 +46,149 @@ namespace LeanCloud.Storage.Internal { }; } - /// - /// 构建查询字符串 - /// - /// - public IDictionary BuildParameters(string className) { - Dictionary result = new Dictionary(); - if (conditions != null) { - result["where"] = ToJSON(); - } - if (orderBy != null) { - result["order"] = string.Join(",", orderBy.ToArray()); - } - if (includes != null) { - result["include"] = string.Join(",", includes.ToArray()); - } - if (selectedKeys != null) { - result["keys"] = string.Join(",", selectedKeys.ToArray()); - } - if (!string.IsNullOrEmpty(className)) { - result["className"] = className; - } - if (redirectClassNameForKey != null) { - result["redirectClassNameForKey"] = redirectClassNameForKey; - } - result["skip"] = skip; - result["limit"] = limit; - return result; + #endregion + + #region where + + public void WhereContainedIn(string key, IEnumerable values) { + AddCondition(key, "$in", values.ToList()); } + public void WhereContainsAll(string key, IEnumerable values) { + AddCondition(key, "$all", values.ToList()); + } + + public void WhereContains(string key, string substring) { + AddCondition(key, "$regex", RegexQuote(substring)); + } + + public void WhereDoesNotExist(string key) { + AddCondition(key, "$exists", false); + } + + public void WhereDoesNotMatchQuery(string key, AVQuery query) where T : AVObject { + AddCondition(key, "$notInQuery", query.BuildParameters(query.ClassName)); + } + + public void WhereEndsWith(string key, string suffix) { + AddCondition(key, "$regex", RegexQuote(suffix) + "$"); + } + + public void WhereEqualTo(string key, object value) { + AddCondition(new QueryEqualCondition(key, value)); + } + + public void WhereSizeEqualTo(string key, uint size) { + AddCondition(key, "$size", size); + } + + public void WhereExists(string key) { + AddCondition(key, "$exists", true); + } + + public void WhereGreaterThan(string key, object value) { + AddCondition(key, "$gt", value); + } + + public void WhereGreaterThanOrEqualTo(string key, object value) { + AddCondition(key, "$gte", value); + } + + public void WhereLessThan(string key, object value) { + AddCondition(key, "$lt", value); + } + + public void WhereLessThanOrEqualTo(string key, object value) { + AddCondition(key, "$lte", value); + } + + public void 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."); + } + AddCondition(key, "$regex", regex.ToString()); + AddCondition(key, "options", modifiers); + } + + public void WhereMatches(string key, Regex regex) { + WhereMatches(key, regex, null); + } + + public void WhereMatches(string key, string pattern, string modifiers) { + WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); + } + + public void WhereMatches(string key, string pattern) { + WhereMatches(key, pattern, null); + } + + public void WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where T : AVObject { + var parameters = new Dictionary { + { "query", query.BuildParameters(query.ClassName)}, + { "key", keyInQuery} + }; + AddCondition(key, "$select", parameters); + } + + public void WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where T : AVObject { + var parameters = new Dictionary { + { "query", query.BuildParameters(query.ClassName)}, + { "key", keyInQuery} + }; + AddCondition(key, "$dontSelect", parameters); + } + + public void WhereMatchesQuery(string key, AVQuery query) where T : AVObject { + AddCondition(key, "$inQuery", query.BuildParameters(query.ClassName)); + } + + public void WhereNear(string key, AVGeoPoint point) { + AddCondition(key, "$nearSphere", point); + } + + public void WhereNotContainedIn(string key, IEnumerable values) { + AddCondition(key, "$nin", values.ToList()); + } + + public void WhereNotEqualTo(string key, object value) { + AddCondition(key, "$ne", value); + } + + public void WhereStartsWith(string key, string suffix) { + AddCondition(key, "$regex", "^" + RegexQuote(suffix)); + } + + public void WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { + Dictionary value = new Dictionary { + { "$box", new[] { southwest, northeast } } + }; + AddCondition(key, "$within", value); + } + + public void WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { + AddCondition(key, "$nearSphere", point); + AddCondition(key, "$maxDistance", maxDistance.Radians); + } + + public void WhereRelatedTo(AVObject parent, string key) { + AddCondition(new QueryRelatedCondition(parent, key)); + } + + #endregion + internal void OrderBy(string key) { - orderBy = new ReadOnlyCollection(new List { key }); + if (orderBy == null) { + orderBy = new List(); + } + orderBy.Add(key); } internal void OrderByDescending(string key) { - orderBy = new ReadOnlyCollection(new List { "-" + key }); + if (orderBy == null) { + orderBy = new List(); + } + orderBy.Add($"-{key}"); } internal void Include(string key) { @@ -112,6 +221,15 @@ namespace LeanCloud.Storage.Internal { limit = count; } + internal void AddCondition(string key, string op, object value) { + QueryOperationCondition cond = new QueryOperationCondition { + Key = key, + Op = op, + Value = value + }; + AddCondition(cond); + } + internal void AddCondition(IQueryCondition condition) { if (condition == null) { return; @@ -122,5 +240,35 @@ namespace LeanCloud.Storage.Internal { }); conditions.Add(condition); } + + /// + /// 构建查询字符串 + /// + /// + internal IDictionary BuildParameters(string className) { + Dictionary result = new Dictionary(); + if (conditions != null) { + result["where"] = ToJSON(); + } + if (orderBy != null) { + result["order"] = string.Join(",", orderBy.ToArray()); + } + if (includes != null) { + result["include"] = string.Join(",", includes.ToArray()); + } + if (selectedKeys != null) { + result["keys"] = string.Join(",", selectedKeys.ToArray()); + } + if (!string.IsNullOrEmpty(className)) { + result["className"] = className; + } + result["skip"] = skip; + result["limit"] = limit; + return result; + } + + string RegexQuote(string input) { + return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; + } } } diff --git a/Storage/Storage/Internal/Query/QueryEqualCondition.cs b/Storage/Storage/Internal/Query/QueryEqualCondition.cs index 96ff1b4..27a1d3b 100644 --- a/Storage/Storage/Internal/Query/QueryEqualCondition.cs +++ b/Storage/Storage/Internal/Query/QueryEqualCondition.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; namespace LeanCloud.Storage.Internal { - public class QueryEqualCondition : IQueryCondition { + internal class QueryEqualCondition : IQueryCondition { string key; object value; diff --git a/Storage/Storage/Internal/Utilities/AVQueryExtensions.cs b/Storage/Storage/Internal/Utilities/AVQueryExtensions.cs deleted file mode 100644 index 4e4dd59..0000000 --- a/Storage/Storage/Internal/Utilities/AVQueryExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVQueryExtensions { - public static string GetClassName(this AVQuery query) where T: AVObject { - return query.ClassName; - } - - public static object GetConstraint(this AVQuery query, string key) where T : AVObject { - return query.GetConstraint(key); - } - } -} diff --git a/Storage/Storage/Public/AVQuery.cs b/Storage/Storage/Public/AVQuery.cs index 459db52..3d2dc91 100644 --- a/Storage/Storage/Public/AVQuery.cs +++ b/Storage/Storage/Public/AVQuery.cs @@ -30,20 +30,14 @@ namespace LeanCloud { internal QueryCompositionalCondition condition; - internal static AVQueryController QueryController { + static AVQueryController QueryController { get { return AVPlugins.Instance.QueryController; } } - internal static ObjectSubclassingController SubclassingController { - get { - return AVPlugins.Instance.SubclassingController; - } - } - public AVQuery() - : this(SubclassingController.GetClassName(typeof(T))) { + : this(AVPlugins.Instance.SubclassingController.GetClassName(typeof(T))) { } public AVQuery(string className) { @@ -54,6 +48,8 @@ namespace LeanCloud { condition = new QueryCompositionalCondition(); } + #region Composition + public static AVQuery And(IEnumerable> queries) { AVQuery composition = new AVQuery(); string className = null; @@ -62,7 +58,7 @@ namespace LeanCloud { 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); + composition.condition.AddCondition(query.condition); className = query.ClassName; } } @@ -80,7 +76,7 @@ namespace LeanCloud { 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); + composition.condition.AddCondition(query.condition); className = query.ClassName; } } @@ -88,14 +84,16 @@ namespace LeanCloud { return composition; } + #endregion + public virtual async Task> FindAsync(CancellationToken cancellationToken = default) { - IEnumerable states = await QueryController.FindAsync(this, AVUser.CurrentUser, cancellationToken); + IEnumerable states = await QueryController.FindAsync(this, cancellationToken); return (from state in states select AVObject.FromState(state, ClassName)); } public virtual async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) { - IObjectState state = await QueryController.FirstAsync(this, AVUser.CurrentUser, cancellationToken); + IObjectState state = await QueryController.FirstAsync(this, cancellationToken); return state == null ? default : AVObject.FromState(state, ClassName); } @@ -109,7 +107,7 @@ namespace LeanCloud { } public virtual Task CountAsync(CancellationToken cancellationToken = default) { - return QueryController.CountAsync(this, AVUser.CurrentUser, cancellationToken); + return QueryController.CountAsync(this, cancellationToken); } public virtual async Task GetAsync(string objectId, CancellationToken cancellationToken) { @@ -224,78 +222,72 @@ namespace LeanCloud { #region Where public AVQuery WhereContainedIn(string key, IEnumerable values) { - AddCondition(key, "$in", values.ToList()); + condition.WhereContainedIn(key, values); return this; } public AVQuery WhereContainsAll(string key, IEnumerable values) { - AddCondition(key, "$all", values.ToList()); + condition.WhereContainsAll(key, values); return this; } public AVQuery WhereContains(string key, string substring) { - AddCondition(key, "$regex", RegexQuote(substring)); + condition.WhereContains(key, substring); return this; } public AVQuery WhereDoesNotExist(string key) { - AddCondition(key, "$exists", false); + condition.WhereDoesNotExist(key); return this; } - public AVQuery WhereDoesNotMatchQuery(string key, AVQuery query) - where TOther : AVObject { - AddCondition(key, "$notInQuery", query.BuildParameters(query.ClassName)); + public AVQuery WhereDoesNotMatchQuery(string key, AVQuery query) where TOther : AVObject { + condition.WhereDoesNotMatchQuery(key, query); return this; } public AVQuery WhereEndsWith(string key, string suffix) { - AddCondition(key, "$regex", RegexQuote(suffix) + "$"); + condition.WhereEndsWith(key, suffix); return this; } public AVQuery WhereEqualTo(string key, object value) { - AddCondition(new QueryEqualCondition(key, value)); + condition.WhereEqualTo(key, value); return this; } public AVQuery WhereSizeEqualTo(string key, uint size) { - AddCondition(key, "$size", size); + condition.WhereSizeEqualTo(key, size); return this; } public AVQuery WhereExists(string key) { - AddCondition(key, "$exists", true); + condition.WhereExists(key); return this; } public AVQuery WhereGreaterThan(string key, object value) { - AddCondition(key, "$gt", value); + condition.WhereGreaterThan(key, value); return this; } public AVQuery WhereGreaterThanOrEqualTo(string key, object value) { - AddCondition(key, "$gte", value); + condition.WhereGreaterThanOrEqualTo(key, value); return this; } public AVQuery WhereLessThan(string key, object value) { - AddCondition(key, "$lt", value); + condition.WhereLessThan(key, value); return this; } public AVQuery WhereLessThanOrEqualTo(string key, object value) { - AddCondition(key, "$lte", value); + condition.WhereLessThanOrEqualTo(key, value); return this; } public AVQuery 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."); - } - AddCondition(key, "$regex", regex.ToString()); - AddCondition(key, "options", modifiers); + condition.WhereMatches(key, regex, modifiers); return this; } @@ -311,68 +303,53 @@ namespace LeanCloud { return WhereMatches(key, pattern, null); } - public AVQuery WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) - where TOther : AVObject { - var parameters = new Dictionary { - { "query", query.BuildParameters(query.ClassName)}, - { "key", keyInQuery} - }; - AddCondition(key, "$select", parameters); + public AVQuery WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where TOther : AVObject { + condition.WhereMatchesKeyInQuery(key, keyInQuery, query); return this; } - public AVQuery WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) - where TOther : AVObject { - var parameters = new Dictionary { - { "query", query.BuildParameters(query.ClassName)}, - { "key", keyInQuery} - }; - AddCondition(key, "$dontSelect", parameters); + public AVQuery WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where TOther : AVObject { + condition.WhereDoesNotMatchesKeyInQuery(key, keyInQuery, query); return this; } - public AVQuery WhereMatchesQuery(string key, AVQuery query) - where TOther : AVObject { - AddCondition(key, "$inQuery", query.BuildParameters(query.ClassName)); + public AVQuery WhereMatchesQuery(string key, AVQuery query) where TOther : AVObject { + condition.WhereMatchesQuery(key, query); return this; } public AVQuery WhereNear(string key, AVGeoPoint point) { - AddCondition(key, "$nearSphere", point); + condition.WhereNear(key, point); return this; } public AVQuery WhereNotContainedIn(string key, IEnumerable values) { - AddCondition(key, "$nin", values.ToList()); + condition.WhereNotContainedIn(key, values); return this; } public AVQuery WhereNotEqualTo(string key, object value) { - AddCondition(key, "$ne", value); + condition.WhereNotEqualTo(key, value); return this; } public AVQuery WhereStartsWith(string key, string suffix) { - AddCondition(key, "$regex", "^" + RegexQuote(suffix)); + condition.WhereStartsWith(key, suffix); return this; } public AVQuery WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { - Dictionary value = new Dictionary { - { "$box", new[] { southwest, northeast } } - }; - AddCondition(key, "$within", value); + condition.WhereWithinGeoBox(key, southwest, northeast); return this; } public AVQuery WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { - AddCondition(key, "$nearSphere", point); - AddCondition(key, "$maxDistance", maxDistance.Radians); + condition.WhereWithinDistance(key, point, maxDistance); return this; } public AVQuery WhereRelatedTo(AVObject parent, string key) { - AddCondition(new QueryRelatedCondition(parent, key)); + condition.WhereRelatedTo(parent, key); return this; } @@ -382,26 +359,8 @@ namespace LeanCloud { return condition.BuildParameters(className); } - private string RegexQuote(string input) { - return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; - } - public IDictionary BuildWhere() { - IDictionary where = condition.ToJSON(); - return where; - } - - 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); + return condition.ToJSON(); } } }