2019-09-16 18:25:05 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Collections.Generic;
|
2019-09-17 11:31:40 +08:00
|
|
|
|
using System.Text.RegularExpressions;
|
2019-09-16 16:46:12 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2019-09-17 11:31:40 +08:00
|
|
|
|
internal List<string> orderBy;
|
2019-09-16 18:25:05 +08:00
|
|
|
|
internal HashSet<string> includes;
|
|
|
|
|
|
internal HashSet<string> selectedKeys;
|
|
|
|
|
|
internal int skip;
|
|
|
|
|
|
internal int limit;
|
|
|
|
|
|
|
2019-09-16 16:46:12 +08:00
|
|
|
|
internal QueryCompositionalCondition(string composition = AND) {
|
|
|
|
|
|
conditions = new List<IQueryCondition>();
|
|
|
|
|
|
this.composition = composition;
|
2019-09-16 18:25:05 +08:00
|
|
|
|
skip = 0;
|
|
|
|
|
|
limit = 30;
|
2019-09-16 16:46:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-17 11:31:40 +08:00
|
|
|
|
#region IQueryCondition
|
|
|
|
|
|
|
2019-09-16 16:46:12 +08:00
|
|
|
|
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 }
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-17 11:31:40 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region where
|
|
|
|
|
|
|
|
|
|
|
|
public void WhereContainedIn<T>(string key, IEnumerable<T> values) {
|
|
|
|
|
|
AddCondition(key, "$in", values.ToList());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void WhereContainsAll<T>(string key, IEnumerable<T> 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<T>(string key, AVQuery<T> 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.");
|
2019-09-16 18:25:05 +08:00
|
|
|
|
}
|
2019-09-17 11:31:40 +08:00
|
|
|
|
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<T>(string key, string keyInQuery, AVQuery<T> query) where T : AVObject {
|
|
|
|
|
|
var parameters = new Dictionary<string, object> {
|
|
|
|
|
|
{ "query", query.BuildParameters(query.ClassName)},
|
|
|
|
|
|
{ "key", keyInQuery}
|
|
|
|
|
|
};
|
|
|
|
|
|
AddCondition(key, "$select", parameters);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void WhereDoesNotMatchesKeyInQuery<T>(string key, string keyInQuery, AVQuery<T> query) where T : AVObject {
|
|
|
|
|
|
var parameters = new Dictionary<string, object> {
|
|
|
|
|
|
{ "query", query.BuildParameters(query.ClassName)},
|
|
|
|
|
|
{ "key", keyInQuery}
|
|
|
|
|
|
};
|
|
|
|
|
|
AddCondition(key, "$dontSelect", parameters);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void WhereMatchesQuery<T>(string key, AVQuery<T> 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<T>(string key, IEnumerable<T> values) {
|
|
|
|
|
|
AddCondition(key, "$nin", values.ToList());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void WhereNotEqualTo(string key, object value) {
|
|
|
|
|
|
AddCondition(key, "$ne", value);
|
2019-09-16 18:25:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-17 11:31:40 +08:00
|
|
|
|
public void WhereStartsWith(string key, string suffix) {
|
|
|
|
|
|
AddCondition(key, "$regex", "^" + RegexQuote(suffix));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) {
|
|
|
|
|
|
Dictionary<string, object> value = new Dictionary<string, object> {
|
|
|
|
|
|
{ "$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
|
|
|
|
|
|
|
2019-09-16 18:25:05 +08:00
|
|
|
|
internal void OrderBy(string key) {
|
2019-09-17 11:31:40 +08:00
|
|
|
|
if (orderBy == null) {
|
|
|
|
|
|
orderBy = new List<string>();
|
|
|
|
|
|
}
|
|
|
|
|
|
orderBy.Add(key);
|
2019-09-16 18:25:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void OrderByDescending(string key) {
|
2019-09-17 11:31:40 +08:00
|
|
|
|
if (orderBy == null) {
|
|
|
|
|
|
orderBy = new List<string>();
|
|
|
|
|
|
}
|
|
|
|
|
|
orderBy.Add($"-{key}");
|
2019-09-16 18:25:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void Include(string key) {
|
|
|
|
|
|
if (includes == null) {
|
|
|
|
|
|
includes = new HashSet<string>();
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
includes.Add(key);
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
AVClient.PrintLog(e.Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void Select(string key) {
|
|
|
|
|
|
if (selectedKeys == null) {
|
|
|
|
|
|
selectedKeys = new HashSet<string>();
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
selectedKeys.Add(key);
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
AVClient.PrintLog(e.Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void Skip(int count) {
|
|
|
|
|
|
skip = count;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void Limit(int count) {
|
|
|
|
|
|
limit = count;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-17 11:31:40 +08:00
|
|
|
|
internal void AddCondition(string key, string op, object value) {
|
|
|
|
|
|
QueryOperationCondition cond = new QueryOperationCondition {
|
|
|
|
|
|
Key = key,
|
|
|
|
|
|
Op = op,
|
|
|
|
|
|
Value = value
|
|
|
|
|
|
};
|
|
|
|
|
|
AddCondition(cond);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-16 16:46:12 +08:00
|
|
|
|
internal void AddCondition(IQueryCondition condition) {
|
|
|
|
|
|
if (condition == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 组合查询的 Key 为 null
|
|
|
|
|
|
conditions.RemoveAll(cond => {
|
|
|
|
|
|
return cond.Equals(condition);
|
|
|
|
|
|
});
|
|
|
|
|
|
conditions.Add(condition);
|
|
|
|
|
|
}
|
2019-09-17 11:31:40 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构建查询字符串
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
internal IDictionary<string, object> BuildParameters(string className) {
|
|
|
|
|
|
Dictionary<string, object> result = new Dictionary<string, object>();
|
|
|
|
|
|
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";
|
|
|
|
|
|
}
|
2019-09-16 16:46:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|