FQueryParams.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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 "FConstants.h"
  18. #import "FIndex.h"
  19. #import "FIndexedFilter.h"
  20. #import "FLimitedFilter.h"
  21. #import "FNode.h"
  22. #import "FNodeFilter.h"
  23. #import "FPriorityIndex.h"
  24. #import "FRangedFilter.h"
  25. #import "FSnapshotUtilities.h"
  26. #import "FUtilities.h"
  27. #import "FValidation.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]
  191. forKey:kFQPIndexStartValue];
  192. // Don't use property as it will be [MIN-NAME]
  193. if (self->_indexStartKey != nil) {
  194. [dict setObject:self->_indexStartKey forKey:kFQPIndexStartName];
  195. }
  196. }
  197. if ([self hasEnd]) {
  198. [dict setObject:[self.indexEndValue valForExport:YES]
  199. forKey:kFQPIndexEndValue];
  200. // Don't use property as it will be [MAX-NAME]
  201. if (self->_indexEndKey != nil) {
  202. [dict setObject:self->_indexEndKey forKey:kFQPIndexEndName];
  203. }
  204. }
  205. if (self.limitSet) {
  206. [dict setObject:[NSNumber numberWithInteger:self.limit]
  207. forKey:kFQPLimit];
  208. NSString *vf = self.viewFrom;
  209. if (vf == nil) {
  210. // limit() rather than limitToFirst or limitToLast was called.
  211. // This means that only one of startSet or endSet is true. Use them
  212. // to calculate which side of the view to anchor to. If neither is
  213. // set, Anchor to end
  214. if ([self hasStart]) {
  215. vf = kFQPViewFromLeft;
  216. } else {
  217. vf = kFQPViewFromRight;
  218. }
  219. }
  220. [dict setObject:vf forKey:kFQPViewFrom];
  221. }
  222. // For now, priority index is the default, so we only specify if it's some
  223. // other index.
  224. if (![self.index isEqual:[FPriorityIndex priorityIndex]]) {
  225. [dict setObject:[self.index queryDefinition] forKey:kFQPIndex];
  226. }
  227. return dict;
  228. }
  229. + (FQueryParams *)fromQueryObject:(NSDictionary *)dict {
  230. if (dict.count == 0) {
  231. return [FQueryParams defaultInstance];
  232. }
  233. FQueryParams *params = [[FQueryParams alloc] init];
  234. if (dict[kFQPLimit] != nil) {
  235. params->_limitSet = YES;
  236. params->_limit = [dict[kFQPLimit] integerValue];
  237. }
  238. if (dict[kFQPIndexStartValue] != nil) {
  239. params->_indexStartValue =
  240. [FSnapshotUtilities nodeFrom:dict[kFQPIndexStartValue]];
  241. if (dict[kFQPIndexStartName] != nil) {
  242. params->_indexStartKey = dict[kFQPIndexStartName];
  243. }
  244. }
  245. if (dict[kFQPIndexEndValue] != nil) {
  246. params->_indexEndValue =
  247. [FSnapshotUtilities nodeFrom:dict[kFQPIndexEndValue]];
  248. if (dict[kFQPIndexEndName] != nil) {
  249. params->_indexEndKey = dict[kFQPIndexEndName];
  250. }
  251. }
  252. if (dict[kFQPViewFrom] != nil) {
  253. NSString *viewFrom = dict[kFQPViewFrom];
  254. if (![viewFrom isEqualToString:kFQPViewFromLeft] &&
  255. ![viewFrom isEqualToString:kFQPViewFromRight]) {
  256. [NSException raise:NSInvalidArgumentException
  257. format:@"Unknown view from paramter: %@", viewFrom];
  258. }
  259. params->_viewFrom = viewFrom;
  260. }
  261. NSString *index = dict[kFQPIndex];
  262. if (index != nil) {
  263. params->_index = [FIndex indexFromQueryDefinition:index];
  264. }
  265. return params;
  266. }
  267. - (BOOL)isViewFromLeft {
  268. if (self.viewFrom != nil) {
  269. // Not null, we can just check
  270. return [self.viewFrom isEqualToString:kFQPViewFromLeft];
  271. } else {
  272. // If start is set, it's view from left. Otherwise not.
  273. return self.hasStart;
  274. }
  275. }
  276. - (id<FNodeFilter>)nodeFilter {
  277. if (self.loadsAllData) {
  278. return [[FIndexedFilter alloc] initWithIndex:self.index];
  279. } else if (self.limitSet) {
  280. return [[FLimitedFilter alloc] initWithQueryParams:self];
  281. } else {
  282. return [[FRangedFilter alloc] initWithQueryParams:self];
  283. }
  284. }
  285. - (BOOL)isValid {
  286. return !(self.hasStart && self.hasEnd && self.limitSet &&
  287. !self.hasAnchoredLimit);
  288. }
  289. - (BOOL)loadsAllData {
  290. return !(self.hasStart || self.hasEnd || self.limitSet);
  291. }
  292. - (BOOL)isDefault {
  293. return [self loadsAllData] &&
  294. [self.index isEqual:[FPriorityIndex priorityIndex]];
  295. }
  296. - (NSString *)description {
  297. return [[self wireProtocolParams] description];
  298. }
  299. - (BOOL)isEqual:(id)obj {
  300. if (self == obj) {
  301. return YES;
  302. }
  303. if (![obj isKindOfClass:[self class]]) {
  304. return NO;
  305. }
  306. FQueryParams *other = (FQueryParams *)obj;
  307. if (self->_limitSet != other->_limitSet)
  308. return NO;
  309. if (self->_limit != other->_limit)
  310. return NO;
  311. if ((self->_index != other->_index) && !
  312. [self->_index isEqual:other->_index])
  313. return NO;
  314. if ((self->_indexStartKey != other->_indexStartKey) &&
  315. ![self->_indexStartKey isEqualToString:other->_indexStartKey])
  316. return NO;
  317. if ((self->_indexStartValue != other->_indexStartValue) &&
  318. ![self->_indexStartValue isEqual:other->_indexStartValue])
  319. return NO;
  320. if ((self->_indexEndKey != other->_indexEndKey) &&
  321. ![self->_indexEndKey isEqualToString:other->_indexEndKey])
  322. return NO;
  323. if ((self->_indexEndValue != other->_indexEndValue) &&
  324. ![self->_indexEndValue isEqual:other->_indexEndValue])
  325. return NO;
  326. if ([self isViewFromLeft] != [other isViewFromLeft])
  327. return NO;
  328. return YES;
  329. }
  330. - (NSUInteger)hash {
  331. NSUInteger result = _limitSet ? _limit : 0;
  332. result = 31 * result + ([self isViewFromLeft] ? 1231 : 1237);
  333. result = 31 * result + [_indexStartKey hash];
  334. result = 31 * result + [_indexStartValue hash];
  335. result = 31 * result + [_indexEndKey hash];
  336. result = 31 * result + [_indexEndValue hash];
  337. result = 31 * result + [_index hash];
  338. return result;
  339. }
  340. @end