FQueryParams.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FQueryParams.h"
  17. #import "FValidation.h"
  18. #import "FConstants.h"
  19. #import "FIndex.h"
  20. #import "FPriorityIndex.h"
  21. #import "FUtilities.h"
  22. #import "FNodeFilter.h"
  23. #import "FIndexedFilter.h"
  24. #import "FLimitedFilter.h"
  25. #import "FRangedFilter.h"
  26. #import "FNode.h"
  27. #import "FSnapshotUtilities.h"
  28. @interface FQueryParams ()
  29. @property (nonatomic, readwrite) BOOL limitSet;
  30. @property (nonatomic, readwrite) NSInteger limit;
  31. @property (nonatomic, strong, readwrite) NSString *viewFrom;
  32. /**
  33. * indexStartValue is anything you can store as a priority / value.
  34. */
  35. @property (nonatomic, strong, readwrite) id<FNode> indexStartValue;
  36. @property (nonatomic, strong, readwrite) NSString *indexStartKey;
  37. /**
  38. * indexStartValue is anything you can store as a priority / value.
  39. */
  40. @property (nonatomic, strong, readwrite) id<FNode> indexEndValue;
  41. @property (nonatomic, strong, readwrite) NSString *indexEndKey;
  42. @property (nonatomic, strong, readwrite) id<FIndex> index;
  43. @end
  44. @implementation FQueryParams
  45. + (FQueryParams *) defaultInstance {
  46. static FQueryParams *defaultParams = nil;
  47. static dispatch_once_t defaultParamsToken;
  48. dispatch_once(&defaultParamsToken, ^{
  49. defaultParams = [[FQueryParams alloc] init];
  50. });
  51. return defaultParams;
  52. }
  53. - (id)init {
  54. self = [super init];
  55. if (self) {
  56. self->_limitSet = NO;
  57. self->_limit = 0;
  58. self->_viewFrom = nil;
  59. self->_indexStartValue = nil;
  60. self->_indexStartKey = nil;
  61. self->_indexEndValue = nil;
  62. self->_indexEndKey = nil;
  63. self->_index = [FPriorityIndex priorityIndex];
  64. }
  65. return self;
  66. }
  67. /**
  68. * Only valid if hasStart is true
  69. */
  70. - (id) indexStartValue {
  71. NSAssert([self hasStart], @"Only valid if start has been set");
  72. return _indexStartValue;
  73. }
  74. /**
  75. * Only valid if hasStart is true.
  76. * @return The starting key name for the range defined by these query parameters
  77. */
  78. - (NSString *) indexStartKey {
  79. NSAssert([self hasStart], @"Only valid if start has been set");
  80. if (_indexStartKey == nil) {
  81. return [FUtilities minName];
  82. } else {
  83. return _indexStartKey;
  84. }
  85. }
  86. /**
  87. * Only valid if hasEnd is true.
  88. */
  89. - (id) indexEndValue {
  90. NSAssert([self hasEnd], @"Only valid if end has been set");
  91. return _indexEndValue;
  92. }
  93. /**
  94. * Only valid if hasEnd is true.
  95. * @return The end key name for the range defined by these query parameters
  96. */
  97. - (NSString *) indexEndKey {
  98. NSAssert([self hasEnd], @"Only valid if end has been set");
  99. if (_indexEndKey == nil) {
  100. return [FUtilities maxName];
  101. } else {
  102. return _indexEndKey;
  103. }
  104. }
  105. /**
  106. * @return true if a limit has been set and has been explicitly anchored
  107. */
  108. - (BOOL) hasAnchoredLimit {
  109. return self.limitSet && self.viewFrom != nil;
  110. }
  111. /**
  112. * Only valid to call if limitSet returns true
  113. */
  114. - (NSInteger) limit {
  115. NSAssert(self.limitSet, @"Only valid if limit has been set");
  116. return _limit;
  117. }
  118. - (BOOL)hasStart {
  119. return self->_indexStartValue != nil;
  120. }
  121. - (BOOL)hasEnd {
  122. return self->_indexEndValue != nil;
  123. }
  124. - (id) copyWithZone:(NSZone *)zone {
  125. // Immutable
  126. return self;
  127. }
  128. - (id) mutableCopy {
  129. FQueryParams* other = [[[self class] alloc] init];
  130. // Maybe need to do extra copying here
  131. other->_limitSet = _limitSet;
  132. other->_limit = _limit;
  133. other->_indexStartValue = _indexStartValue;
  134. other->_indexStartKey = _indexStartKey;
  135. other->_indexEndValue = _indexEndValue;
  136. other->_indexEndKey = _indexEndKey;
  137. other->_viewFrom = _viewFrom;
  138. other->_index = _index;
  139. return other;
  140. }
  141. - (FQueryParams *) limitTo:(NSInteger)newLimit {
  142. FQueryParams *newParams = [self mutableCopy];
  143. newParams->_limitSet = YES;
  144. newParams->_limit = newLimit;
  145. newParams->_viewFrom = nil;
  146. return newParams;
  147. }
  148. - (FQueryParams *) limitToFirst:(NSInteger)newLimit {
  149. FQueryParams *newParams = [self mutableCopy];
  150. newParams->_limitSet = YES;
  151. newParams->_limit = newLimit;
  152. newParams->_viewFrom = kFQPViewFromLeft;
  153. return newParams;
  154. }
  155. - (FQueryParams *) limitToLast:(NSInteger)newLimit {
  156. FQueryParams *newParams = [self mutableCopy];
  157. newParams->_limitSet = YES;
  158. newParams->_limit = newLimit;
  159. newParams->_viewFrom = kFQPViewFromRight;
  160. return newParams;
  161. }
  162. - (FQueryParams *) startAt:(id<FNode>)indexValue childKey:(NSString *)key {
  163. NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
  164. FQueryParams *newParams = [self mutableCopy];
  165. newParams->_indexStartValue = indexValue;
  166. newParams->_indexStartKey = key;
  167. return newParams;
  168. }
  169. - (FQueryParams *) startAt:(id<FNode>)indexValue {
  170. return [self startAt:indexValue childKey:nil];
  171. }
  172. - (FQueryParams *) endAt:(id<FNode>)indexValue childKey:(NSString *)key {
  173. NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
  174. FQueryParams *newParams = [self mutableCopy];
  175. newParams->_indexEndValue = indexValue;
  176. newParams->_indexEndKey = key;
  177. return newParams;
  178. }
  179. - (FQueryParams *) endAt:(id<FNode>)indexValue {
  180. return [self endAt:indexValue childKey:nil];
  181. }
  182. - (FQueryParams *) orderBy:(id)newIndex {
  183. FQueryParams *newParams = [self mutableCopy];
  184. newParams->_index = newIndex;
  185. return newParams;
  186. }
  187. - (NSDictionary *) wireProtocolParams {
  188. NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
  189. if ([self hasStart]) {
  190. [dict setObject:[self.indexStartValue valForExport:YES] forKey:kFQPIndexStartValue];
  191. // Don't use property as it will be [MIN-NAME]
  192. if (self->_indexStartKey != nil) {
  193. [dict setObject:self->_indexStartKey forKey:kFQPIndexStartName];
  194. }
  195. }
  196. if ([self hasEnd]) {
  197. [dict setObject:[self.indexEndValue valForExport:YES] forKey:kFQPIndexEndValue];
  198. // Don't use property as it will be [MAX-NAME]
  199. if (self->_indexEndKey != nil) {
  200. [dict setObject:self->_indexEndKey forKey:kFQPIndexEndName];
  201. }
  202. }
  203. if (self.limitSet) {
  204. [dict setObject:[NSNumber numberWithInteger:self.limit] forKey:kFQPLimit];
  205. NSString *vf = self.viewFrom;
  206. if (vf == nil) {
  207. // limit() rather than limitToFirst or limitToLast was called.
  208. // This means that only one of startSet or endSet is true. Use them
  209. // to calculate which side of the view to anchor to. If neither is set,
  210. // Anchor to end
  211. if ([self hasStart]) {
  212. vf = kFQPViewFromLeft;
  213. } else {
  214. vf = kFQPViewFromRight;
  215. }
  216. }
  217. [dict setObject:vf forKey:kFQPViewFrom];
  218. }
  219. // For now, priority index is the default, so we only specify if it's some other index.
  220. if (![self.index isEqual:[FPriorityIndex priorityIndex]]) {
  221. [dict setObject:[self.index queryDefinition] forKey:kFQPIndex];
  222. }
  223. return dict;
  224. }
  225. + (FQueryParams *)fromQueryObject:(NSDictionary *)dict {
  226. if (dict.count == 0) {
  227. return [FQueryParams defaultInstance];
  228. }
  229. FQueryParams *params = [[FQueryParams alloc] init];
  230. if (dict[kFQPLimit] != nil) {
  231. params->_limitSet = YES;
  232. params->_limit = [dict[kFQPLimit] integerValue];
  233. }
  234. if (dict[kFQPIndexStartValue] != nil) {
  235. params->_indexStartValue = [FSnapshotUtilities nodeFrom:dict[kFQPIndexStartValue]];
  236. if (dict[kFQPIndexStartName] != nil) {
  237. params->_indexStartKey = dict[kFQPIndexStartName];
  238. }
  239. }
  240. if (dict[kFQPIndexEndValue] != nil) {
  241. params->_indexEndValue = [FSnapshotUtilities nodeFrom:dict[kFQPIndexEndValue]];
  242. if (dict[kFQPIndexEndName] != nil) {
  243. params->_indexEndKey = dict[kFQPIndexEndName];
  244. }
  245. }
  246. if (dict[kFQPViewFrom] != nil) {
  247. NSString *viewFrom = dict[kFQPViewFrom];
  248. if (![viewFrom isEqualToString:kFQPViewFromLeft] && ![viewFrom isEqualToString:kFQPViewFromRight]) {
  249. [NSException raise:NSInvalidArgumentException format:@"Unknown view from paramter: %@", viewFrom];
  250. }
  251. params->_viewFrom = viewFrom;
  252. }
  253. NSString *index = dict[kFQPIndex];
  254. if (index != nil) {
  255. params->_index = [FIndex indexFromQueryDefinition:index];
  256. }
  257. return params;
  258. }
  259. - (BOOL) isViewFromLeft {
  260. if (self.viewFrom != nil) {
  261. // Not null, we can just check
  262. return [self.viewFrom isEqualToString:kFQPViewFromLeft];
  263. } else {
  264. // If start is set, it's view from left. Otherwise not.
  265. return self.hasStart;
  266. }
  267. }
  268. - (id<FNodeFilter>) nodeFilter {
  269. if (self.loadsAllData) {
  270. return [[FIndexedFilter alloc] initWithIndex:self.index];
  271. } else if (self.limitSet) {
  272. return [[FLimitedFilter alloc] initWithQueryParams:self];
  273. } else {
  274. return [[FRangedFilter alloc] initWithQueryParams:self];
  275. }
  276. }
  277. - (BOOL) isValid {
  278. return !(self.hasStart && self.hasEnd && self.limitSet && !self.hasAnchoredLimit);
  279. }
  280. - (BOOL) loadsAllData {
  281. return !(self.hasStart || self.hasEnd || self.limitSet);
  282. }
  283. - (BOOL) isDefault {
  284. return [self loadsAllData] && [self.index isEqual:[FPriorityIndex priorityIndex]];
  285. }
  286. - (NSString *) description {
  287. return [[self wireProtocolParams] description];
  288. }
  289. - (BOOL) isEqual:(id)obj {
  290. if (self == obj) {
  291. return YES;
  292. }
  293. if (![obj isKindOfClass:[self class]]) {
  294. return NO;
  295. }
  296. FQueryParams *other = (FQueryParams *)obj;
  297. if (self->_limitSet != other->_limitSet) return NO;
  298. if (self->_limit != other->_limit) return NO;
  299. if ((self->_index != other->_index) && ![self->_index isEqual:other->_index]) return NO;
  300. if ((self->_indexStartKey != other->_indexStartKey) && ![self->_indexStartKey isEqualToString:other->_indexStartKey]) return NO;
  301. if ((self->_indexStartValue != other->_indexStartValue) && ![self->_indexStartValue isEqual:other->_indexStartValue]) return NO;
  302. if ((self->_indexEndKey != other->_indexEndKey) && ![self->_indexEndKey isEqualToString:other->_indexEndKey]) return NO;
  303. if ((self->_indexEndValue != other->_indexEndValue) && ![self->_indexEndValue isEqual:other->_indexEndValue]) return NO;
  304. if ([self isViewFromLeft] != [other isViewFromLeft]) return NO;
  305. return YES;
  306. }
  307. - (NSUInteger) hash {
  308. NSUInteger result = _limitSet ? _limit : 0;
  309. result = 31 * result + ([self isViewFromLeft] ? 1231 : 1237);
  310. result = 31 * result + [_indexStartKey hash];
  311. result = 31 * result + [_indexStartValue hash];
  312. result = 31 * result + [_indexEndKey hash];
  313. result = 31 * result + [_indexEndValue hash];
  314. result = 31 * result + [_index hash];
  315. return result;
  316. }
  317. @end