FSTQuery.mm 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  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 "Firestore/Source/Core/FSTQuery.h"
  17. #include <limits>
  18. #include <memory>
  19. #include <string>
  20. #include <utility>
  21. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  22. #import "Firestore/Source/Model/FSTDocument.h"
  23. #import "Firestore/Source/Model/FSTFieldValue.h"
  24. #import "Firestore/Source/Util/FSTClasses.h"
  25. #include "Firestore/core/src/firebase/firestore/api/input_validation.h"
  26. #include "Firestore/core/src/firebase/firestore/core/filter.h"
  27. #include "Firestore/core/src/firebase/firestore/core/query.h"
  28. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  29. #include "Firestore/core/src/firebase/firestore/model/field_path.h"
  30. #include "Firestore/core/src/firebase/firestore/model/field_value.h"
  31. #include "Firestore/core/src/firebase/firestore/model/resource_path.h"
  32. #include "Firestore/core/src/firebase/firestore/objc/objc_compatibility.h"
  33. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  34. #include "Firestore/core/src/firebase/firestore/util/hashing.h"
  35. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  36. namespace core = firebase::firestore::core;
  37. namespace objc = firebase::firestore::objc;
  38. namespace util = firebase::firestore::util;
  39. using firebase::firestore::api::ThrowInvalidArgument;
  40. using firebase::firestore::core::Filter;
  41. using firebase::firestore::core::Query;
  42. using firebase::firestore::model::DocumentComparator;
  43. using firebase::firestore::model::DocumentKey;
  44. using firebase::firestore::model::FieldPath;
  45. using firebase::firestore::model::FieldValue;
  46. using firebase::firestore::model::ResourcePath;
  47. using firebase::firestore::util::ComparisonResult;
  48. NS_ASSUME_NONNULL_BEGIN
  49. #pragma mark - Filter::Operator functions
  50. NSString *FSTStringFromQueryRelationOperator(Filter::Operator filterOperator) {
  51. switch (filterOperator) {
  52. case Filter::Operator::LessThan:
  53. return @"<";
  54. case Filter::Operator::LessThanOrEqual:
  55. return @"<=";
  56. case Filter::Operator::Equal:
  57. return @"==";
  58. case Filter::Operator::GreaterThanOrEqual:
  59. return @">=";
  60. case Filter::Operator::GreaterThan:
  61. return @">";
  62. case Filter::Operator::ArrayContains:
  63. return @"array_contains";
  64. default:
  65. HARD_FAIL("Unknown Filter::Operator %s", filterOperator);
  66. }
  67. }
  68. @implementation FSTFilter
  69. + (instancetype)filterWithField:(const FieldPath &)field
  70. filterOperator:(Filter::Operator)op
  71. value:(FSTFieldValue *)value {
  72. if (value.type == FieldValue::Type::Null) {
  73. if (op != Filter::Operator::Equal) {
  74. ThrowInvalidArgument("Invalid Query. Nil and NSNull only support equality comparisons.");
  75. }
  76. return [[FSTNullFilter alloc] initWithField:field];
  77. } else if (value.isNAN) {
  78. if (op != Filter::Operator::Equal) {
  79. ThrowInvalidArgument("Invalid Query. NaN only supports equality comparisons.");
  80. }
  81. return [[FSTNanFilter alloc] initWithField:field];
  82. } else {
  83. return [[FSTRelationFilter alloc] initWithField:field filterOperator:op value:value];
  84. }
  85. }
  86. - (const FieldPath &)field {
  87. @throw FSTAbstractMethodException(); // NOLINT
  88. }
  89. - (BOOL)matchesDocument:(FSTDocument *)document {
  90. @throw FSTAbstractMethodException(); // NOLINT
  91. }
  92. - (NSString *)canonicalID {
  93. @throw FSTAbstractMethodException(); // NOLINT
  94. }
  95. @end
  96. #pragma mark - FSTRelationFilter
  97. @interface FSTRelationFilter () {
  98. /** The left hand side of the relation. A path into a document field. */
  99. firebase::firestore::model::FieldPath _field;
  100. }
  101. /**
  102. * Initializes the receiver relation filter.
  103. *
  104. * @param field A path to a field in the document to filter on. The LHS of the expression.
  105. * @param filterOperator The binary operator to apply.
  106. * @param value A constant value to compare @a field to. The RHS of the expression.
  107. */
  108. - (instancetype)initWithField:(FieldPath)field
  109. filterOperator:(Filter::Operator)filterOperator
  110. value:(FSTFieldValue *)value NS_DESIGNATED_INITIALIZER;
  111. /** Returns YES if @a document matches the receiver's constraint. */
  112. - (BOOL)matchesDocument:(FSTDocument *)document;
  113. /**
  114. * A canonical string identifying the filter. Two different instances of equivalent filters will
  115. * return the same canonicalID.
  116. */
  117. - (NSString *)canonicalID;
  118. @end
  119. @implementation FSTRelationFilter
  120. #pragma mark - Constructor methods
  121. - (instancetype)initWithField:(FieldPath)field
  122. filterOperator:(Filter::Operator)filterOperator
  123. value:(FSTFieldValue *)value {
  124. self = [super init];
  125. if (self) {
  126. _field = std::move(field);
  127. _filterOperator = filterOperator;
  128. _value = value;
  129. }
  130. return self;
  131. }
  132. #pragma mark - Public Methods
  133. - (BOOL)isInequality {
  134. return self.filterOperator != Filter::Operator::Equal &&
  135. self.filterOperator != Filter::Operator::ArrayContains;
  136. }
  137. - (const firebase::firestore::model::FieldPath &)field {
  138. return _field;
  139. }
  140. #pragma mark - NSObject methods
  141. - (NSString *)description {
  142. return [NSString stringWithFormat:@"%s %@ %@", _field.CanonicalString().c_str(),
  143. FSTStringFromQueryRelationOperator(self.filterOperator),
  144. self.value];
  145. }
  146. - (BOOL)isEqual:(id)other {
  147. if (self == other) {
  148. return YES;
  149. }
  150. if (![other isKindOfClass:[FSTRelationFilter class]]) {
  151. return NO;
  152. }
  153. return [self isEqualToFilter:(FSTRelationFilter *)other];
  154. }
  155. #pragma mark - Private methods
  156. - (BOOL)matchesDocument:(FSTDocument *)document {
  157. if (_field.IsKeyFieldPath()) {
  158. HARD_ASSERT(self.value.type == FieldValue::Type::Reference,
  159. "Comparing on key, but filter value not a FSTReferenceValue.");
  160. HARD_ASSERT(self.filterOperator != Filter::Operator::ArrayContains,
  161. "arrayContains queries don't make sense on document keys.");
  162. FSTReferenceValue *refValue = (FSTReferenceValue *)self.value;
  163. NSComparisonResult comparison = util::WrapCompare(document.key, refValue.value.key);
  164. return [self matchesComparison:comparison];
  165. } else {
  166. return [self matchesValue:[document fieldForPath:self.field]];
  167. }
  168. }
  169. - (NSString *)canonicalID {
  170. // TODO(b/37283291): This should be collision robust and avoid relying on |description| methods.
  171. return [NSString stringWithFormat:@"%s%@%@", _field.CanonicalString().c_str(),
  172. FSTStringFromQueryRelationOperator(self.filterOperator),
  173. [self.value value]];
  174. }
  175. - (BOOL)isEqualToFilter:(FSTRelationFilter *)other {
  176. if (self.filterOperator != other.filterOperator) {
  177. return NO;
  178. }
  179. if (_field != other.field) {
  180. return NO;
  181. }
  182. if (![self.value isEqual:other.value]) {
  183. return NO;
  184. }
  185. return YES;
  186. }
  187. /** Returns YES if receiver is true with the given value as its LHS. */
  188. - (BOOL)matchesValue:(FSTFieldValue *)other {
  189. if (self.filterOperator == Filter::Operator::ArrayContains) {
  190. if ([other isMemberOfClass:[FSTArrayValue class]]) {
  191. FSTArrayValue *arrayValue = (FSTArrayValue *)other;
  192. return [arrayValue.internalValue containsObject:self.value];
  193. } else {
  194. return false;
  195. }
  196. } else {
  197. // Only perform comparison queries on types with matching backend order (such as double and
  198. // int).
  199. return self.value.typeOrder == other.typeOrder &&
  200. [self matchesComparison:[other compare:self.value]];
  201. }
  202. }
  203. - (BOOL)matchesComparison:(NSComparisonResult)comparison {
  204. switch (self.filterOperator) {
  205. case Filter::Operator::LessThan:
  206. return comparison == NSOrderedAscending;
  207. case Filter::Operator::LessThanOrEqual:
  208. return comparison == NSOrderedAscending || comparison == NSOrderedSame;
  209. case Filter::Operator::Equal:
  210. return comparison == NSOrderedSame;
  211. case Filter::Operator::GreaterThanOrEqual:
  212. return comparison == NSOrderedDescending || comparison == NSOrderedSame;
  213. case Filter::Operator::GreaterThan:
  214. return comparison == NSOrderedDescending;
  215. default:
  216. HARD_FAIL("Unknown operator: %s", self.filterOperator);
  217. }
  218. }
  219. @end
  220. #pragma mark - FSTNullFilter
  221. @interface FSTNullFilter () {
  222. FieldPath _field;
  223. }
  224. @end
  225. @implementation FSTNullFilter
  226. - (instancetype)initWithField:(FieldPath)field {
  227. if (self = [super init]) {
  228. _field = std::move(field);
  229. }
  230. return self;
  231. }
  232. - (BOOL)matchesDocument:(FSTDocument *)document {
  233. FSTFieldValue *fieldValue = [document fieldForPath:self.field];
  234. return fieldValue != nil && fieldValue.type == FieldValue::Type::Null;
  235. }
  236. - (NSString *)canonicalID {
  237. return [NSString stringWithFormat:@"%s IS NULL", _field.CanonicalString().c_str()];
  238. }
  239. - (const firebase::firestore::model::FieldPath &)field {
  240. return _field;
  241. }
  242. - (NSString *)description {
  243. return [self canonicalID];
  244. }
  245. - (BOOL)isEqual:(id)other {
  246. if (other == self) return YES;
  247. if (![[other class] isEqual:[self class]]) return NO;
  248. return _field == ((FSTNullFilter *)other)->_field;
  249. }
  250. - (NSUInteger)hash {
  251. return util::Hash(_field);
  252. }
  253. @end
  254. #pragma mark - FSTNanFilter
  255. @interface FSTNanFilter () {
  256. FieldPath _field;
  257. }
  258. @end
  259. @implementation FSTNanFilter
  260. - (instancetype)initWithField:(FieldPath)field {
  261. if (self = [super init]) {
  262. _field = std::move(field);
  263. }
  264. return self;
  265. }
  266. - (BOOL)matchesDocument:(FSTDocument *)document {
  267. FSTFieldValue *fieldValue = [document fieldForPath:self.field];
  268. return fieldValue != nil && fieldValue.isNAN;
  269. }
  270. - (NSString *)canonicalID {
  271. return [NSString stringWithFormat:@"%s IS NaN", _field.CanonicalString().c_str()];
  272. }
  273. - (const firebase::firestore::model::FieldPath &)field {
  274. return _field;
  275. }
  276. - (NSString *)description {
  277. return [self canonicalID];
  278. }
  279. - (BOOL)isEqual:(id)other {
  280. if (other == self) return YES;
  281. if (![[other class] isEqual:[self class]]) return NO;
  282. return _field == ((FSTNanFilter *)other)->_field;
  283. }
  284. - (NSUInteger)hash {
  285. return util::Hash(_field);
  286. }
  287. @end
  288. #pragma mark - FSTSortOrder
  289. @interface FSTSortOrder () {
  290. /** The field to sort by. */
  291. firebase::firestore::model::FieldPath _field;
  292. }
  293. /** Creates a new sort order with the given field and direction. */
  294. - (instancetype)initWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending;
  295. - (NSString *)canonicalID;
  296. @end
  297. @implementation FSTSortOrder
  298. #pragma mark - Constructor methods
  299. + (instancetype)sortOrderWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending {
  300. return [[FSTSortOrder alloc] initWithFieldPath:std::move(fieldPath) ascending:ascending];
  301. }
  302. - (instancetype)initWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending {
  303. self = [super init];
  304. if (self) {
  305. _field = std::move(fieldPath);
  306. _ascending = ascending;
  307. }
  308. return self;
  309. }
  310. - (const firebase::firestore::model::FieldPath &)field {
  311. return _field;
  312. }
  313. #pragma mark - Public methods
  314. - (ComparisonResult)compareDocument:(FSTDocument *)document1 toDocument:(FSTDocument *)document2 {
  315. ComparisonResult result;
  316. if (_field == FieldPath::KeyFieldPath()) {
  317. result = util::Compare(document1.key, document2.key);
  318. } else {
  319. FSTFieldValue *value1 = [document1 fieldForPath:self.field];
  320. FSTFieldValue *value2 = [document2 fieldForPath:self.field];
  321. HARD_ASSERT(value1 != nil && value2 != nil,
  322. "Trying to compare documents on fields that don't exist.");
  323. result = util::MakeComparisonResult([value1 compare:value2]);
  324. }
  325. if (!self.isAscending) {
  326. result = util::ReverseOrder(result);
  327. }
  328. return result;
  329. }
  330. - (NSString *)canonicalID {
  331. return [NSString stringWithFormat:@"%s%@", _field.CanonicalString().c_str(),
  332. self.isAscending ? @"asc" : @"desc"];
  333. }
  334. - (BOOL)isEqualToSortOrder:(FSTSortOrder *)other {
  335. return _field == other->_field && self.isAscending == other.isAscending;
  336. }
  337. #pragma mark - NSObject methods
  338. - (NSString *)description {
  339. return [NSString stringWithFormat:@"<FSTSortOrder: path:%s dir:%@>",
  340. _field.CanonicalString().c_str(),
  341. self.ascending ? @"asc" : @"desc"];
  342. }
  343. - (BOOL)isEqual:(NSObject *)other {
  344. if (self == other) {
  345. return YES;
  346. }
  347. if (![other isKindOfClass:[FSTSortOrder class]]) {
  348. return NO;
  349. }
  350. return [self isEqualToSortOrder:(FSTSortOrder *)other];
  351. }
  352. - (NSUInteger)hash {
  353. return [self.canonicalID hash];
  354. }
  355. - (instancetype)copyWithZone:(nullable NSZone *)zone {
  356. return self;
  357. }
  358. @end
  359. #pragma mark - FSTBound
  360. @implementation FSTBound
  361. - (instancetype)initWithPosition:(NSArray<FSTFieldValue *> *)position isBefore:(BOOL)isBefore {
  362. if (self = [super init]) {
  363. _position = position;
  364. _before = isBefore;
  365. }
  366. return self;
  367. }
  368. + (instancetype)boundWithPosition:(NSArray<FSTFieldValue *> *)position isBefore:(BOOL)isBefore {
  369. return [[FSTBound alloc] initWithPosition:position isBefore:isBefore];
  370. }
  371. - (NSString *)canonicalString {
  372. // TODO(b/29183165): Make this collision robust.
  373. NSMutableString *string = [NSMutableString string];
  374. if (self.isBefore) {
  375. [string appendString:@"b:"];
  376. } else {
  377. [string appendString:@"a:"];
  378. }
  379. for (FSTFieldValue *component in self.position) {
  380. [string appendFormat:@"%@", component];
  381. }
  382. return string;
  383. }
  384. - (BOOL)sortsBeforeDocument:(FSTDocument *)document
  385. usingSortOrder:(NSArray<FSTSortOrder *> *)sortOrder {
  386. HARD_ASSERT(self.position.count <= sortOrder.count,
  387. "FSTIndexPosition has more components than provided sort order.");
  388. __block ComparisonResult result = ComparisonResult::Same;
  389. [self.position enumerateObjectsUsingBlock:^(FSTFieldValue *fieldValue, NSUInteger idx,
  390. BOOL *stop) {
  391. FSTSortOrder *sortOrderComponent = sortOrder[idx];
  392. ComparisonResult comparison;
  393. if (sortOrderComponent.field == FieldPath::KeyFieldPath()) {
  394. HARD_ASSERT(fieldValue.type == FieldValue::Type::Reference,
  395. "FSTBound has a non-key value where the key path is being used %s", fieldValue);
  396. FSTReferenceValue *refValue = (FSTReferenceValue *)fieldValue;
  397. comparison = util::Compare(refValue.value.key, document.key);
  398. } else {
  399. FSTFieldValue *docValue = [document fieldForPath:sortOrderComponent.field];
  400. HARD_ASSERT(docValue != nil,
  401. "Field should exist since document matched the orderBy already.");
  402. comparison = util::MakeComparisonResult([fieldValue compare:docValue]);
  403. }
  404. if (!sortOrderComponent.isAscending) {
  405. comparison = util::ReverseOrder(comparison);
  406. }
  407. if (!util::Same(comparison)) {
  408. result = comparison;
  409. *stop = YES;
  410. }
  411. }];
  412. return self.isBefore ? result <= ComparisonResult::Same : result < ComparisonResult::Same;
  413. }
  414. #pragma mark - NSObject methods
  415. - (NSString *)description {
  416. return [NSString stringWithFormat:@"<FSTBound: position:%@ before:%@>", self.position,
  417. self.isBefore ? @"YES" : @"NO"];
  418. }
  419. - (BOOL)isEqual:(NSObject *)other {
  420. if (self == other) {
  421. return YES;
  422. }
  423. if (![other isKindOfClass:[FSTBound class]]) {
  424. return NO;
  425. }
  426. FSTBound *otherBound = (FSTBound *)other;
  427. return [self.position isEqualToArray:otherBound.position] && self.isBefore == otherBound.isBefore;
  428. }
  429. - (NSUInteger)hash {
  430. return 31 * self.position.hash + (self.isBefore ? 0 : 1);
  431. }
  432. - (instancetype)copyWithZone:(nullable NSZone *)zone {
  433. return self;
  434. }
  435. @end
  436. #pragma mark - FSTQuery
  437. @interface FSTQuery () {
  438. // Cached value of the canonicalID property.
  439. NSString *_canonicalID;
  440. /** The base path of the query. */
  441. ResourcePath _path;
  442. }
  443. /** A list of fields given to sort by. This does not include the implicit key sort at the end. */
  444. @property(nonatomic, strong, readonly) NSArray<FSTSortOrder *> *explicitSortOrders;
  445. /** The memoized list of sort orders */
  446. @property(nonatomic, nullable, strong, readwrite) NSArray<FSTSortOrder *> *memoizedSortOrders;
  447. @end
  448. @implementation FSTQuery
  449. #pragma mark - Constructors
  450. + (instancetype)queryWithPath:(ResourcePath)path {
  451. return [FSTQuery queryWithPath:std::move(path) collectionGroup:nil];
  452. }
  453. + (instancetype)queryWithPath:(ResourcePath)path
  454. collectionGroup:(nullable NSString *)collectionGroup {
  455. return [[FSTQuery alloc] initWithPath:std::move(path)
  456. collectionGroup:collectionGroup
  457. filterBy:@[]
  458. orderBy:@[]
  459. limit:Query::kNoLimit
  460. startAt:nil
  461. endAt:nil];
  462. }
  463. - (instancetype)initWithPath:(ResourcePath)path
  464. collectionGroup:(nullable NSString *)collectionGroup
  465. filterBy:(NSArray<FSTFilter *> *)filters
  466. orderBy:(NSArray<FSTSortOrder *> *)sortOrders
  467. limit:(int32_t)limit
  468. startAt:(nullable FSTBound *)startAtBound
  469. endAt:(nullable FSTBound *)endAtBound {
  470. if (self = [super init]) {
  471. _path = std::move(path);
  472. _collectionGroup = collectionGroup;
  473. _filters = filters;
  474. _explicitSortOrders = sortOrders;
  475. _limit = limit;
  476. _startAt = startAtBound;
  477. _endAt = endAtBound;
  478. }
  479. return self;
  480. }
  481. #pragma mark - NSObject methods
  482. - (NSString *)description {
  483. return [NSString stringWithFormat:@"<FSTQuery: canonicalID:%@>", self.canonicalID];
  484. }
  485. - (BOOL)isEqual:(id)object {
  486. if (self == object) {
  487. return YES;
  488. }
  489. if (![object isKindOfClass:[FSTQuery class]]) {
  490. return NO;
  491. }
  492. return [self isEqualToQuery:(FSTQuery *)object];
  493. }
  494. - (NSUInteger)hash {
  495. return [self.canonicalID hash];
  496. }
  497. - (instancetype)copyWithZone:(nullable NSZone *)zone {
  498. return self;
  499. }
  500. #pragma mark - Public methods
  501. - (NSArray *)sortOrders {
  502. if (self.memoizedSortOrders == nil) {
  503. const FieldPath *inequalityField = [self inequalityFilterField];
  504. const FieldPath *firstSortOrderField = [self firstSortOrderField];
  505. if (inequalityField && !firstSortOrderField) {
  506. // In order to implicitly add key ordering, we must also add the inequality filter field for
  507. // it to be a valid query. Note that the default inequality field and key ordering is
  508. // ascending.
  509. if (inequalityField->IsKeyFieldPath()) {
  510. self.memoizedSortOrders = @[ [FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath()
  511. ascending:YES] ];
  512. } else {
  513. self.memoizedSortOrders = @[
  514. [FSTSortOrder sortOrderWithFieldPath:*inequalityField ascending:YES],
  515. [FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath() ascending:YES]
  516. ];
  517. }
  518. } else {
  519. HARD_ASSERT(!inequalityField || *inequalityField == *firstSortOrderField,
  520. "First orderBy %s should match inequality field %s.",
  521. firstSortOrderField->CanonicalString(), inequalityField->CanonicalString());
  522. __block BOOL foundKeyOrder = NO;
  523. NSMutableArray *result = [NSMutableArray array];
  524. for (FSTSortOrder *sortOrder in self.explicitSortOrders) {
  525. [result addObject:sortOrder];
  526. if (sortOrder.field == FieldPath::KeyFieldPath()) {
  527. foundKeyOrder = YES;
  528. }
  529. }
  530. if (!foundKeyOrder) {
  531. // The direction of the implicit key ordering always matches the direction of the last
  532. // explicit sort order
  533. BOOL lastIsAscending =
  534. self.explicitSortOrders.count > 0 ? self.explicitSortOrders.lastObject.ascending : YES;
  535. [result addObject:[FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath()
  536. ascending:lastIsAscending]];
  537. }
  538. self.memoizedSortOrders = result;
  539. }
  540. }
  541. return self.memoizedSortOrders;
  542. }
  543. - (instancetype)queryByAddingFilter:(FSTFilter *)filter {
  544. HARD_ASSERT(![self isDocumentQuery], "No filtering allowed for document query");
  545. const FieldPath *newInequalityField = nullptr;
  546. if ([filter isKindOfClass:[FSTRelationFilter class]] &&
  547. [((FSTRelationFilter *)filter) isInequality]) {
  548. newInequalityField = &filter.field;
  549. }
  550. const FieldPath *queryInequalityField = [self inequalityFilterField];
  551. HARD_ASSERT(
  552. !queryInequalityField || !newInequalityField || *queryInequalityField == *newInequalityField,
  553. "Query must only have one inequality field.");
  554. return [[FSTQuery alloc] initWithPath:self.path
  555. collectionGroup:self.collectionGroup
  556. filterBy:[self.filters arrayByAddingObject:filter]
  557. orderBy:self.explicitSortOrders
  558. limit:self.limit
  559. startAt:self.startAt
  560. endAt:self.endAt];
  561. }
  562. - (instancetype)queryByAddingSortOrder:(FSTSortOrder *)sortOrder {
  563. HARD_ASSERT(![self isDocumentQuery], "No ordering is allowed for a document query.");
  564. // TODO(klimt): Validate that the same key isn't added twice.
  565. return [[FSTQuery alloc] initWithPath:self.path
  566. collectionGroup:self.collectionGroup
  567. filterBy:self.filters
  568. orderBy:[self.explicitSortOrders arrayByAddingObject:sortOrder]
  569. limit:self.limit
  570. startAt:self.startAt
  571. endAt:self.endAt];
  572. }
  573. - (instancetype)queryBySettingLimit:(int32_t)limit {
  574. return [[FSTQuery alloc] initWithPath:self.path
  575. collectionGroup:self.collectionGroup
  576. filterBy:self.filters
  577. orderBy:self.explicitSortOrders
  578. limit:limit
  579. startAt:self.startAt
  580. endAt:self.endAt];
  581. }
  582. - (instancetype)queryByAddingStartAt:(FSTBound *)bound {
  583. return [[FSTQuery alloc] initWithPath:self.path
  584. collectionGroup:self.collectionGroup
  585. filterBy:self.filters
  586. orderBy:self.explicitSortOrders
  587. limit:self.limit
  588. startAt:bound
  589. endAt:self.endAt];
  590. }
  591. - (instancetype)queryByAddingEndAt:(FSTBound *)bound {
  592. return [[FSTQuery alloc] initWithPath:self.path
  593. collectionGroup:self.collectionGroup
  594. filterBy:self.filters
  595. orderBy:self.explicitSortOrders
  596. limit:self.limit
  597. startAt:self.startAt
  598. endAt:bound];
  599. }
  600. - (instancetype)collectionQueryAtPath:(firebase::firestore::model::ResourcePath)path {
  601. return [[FSTQuery alloc] initWithPath:path
  602. collectionGroup:nil
  603. filterBy:self.filters
  604. orderBy:self.explicitSortOrders
  605. limit:self.limit
  606. startAt:self.startAt
  607. endAt:self.endAt];
  608. }
  609. - (BOOL)isDocumentQuery {
  610. return DocumentKey::IsDocumentKey(_path) && !self.collectionGroup && self.filters.count == 0;
  611. }
  612. - (BOOL)isCollectionGroupQuery {
  613. return self.collectionGroup != nil;
  614. }
  615. - (BOOL)matchesDocument:(FSTDocument *)document {
  616. return [self pathAndCollectionGroupMatchDocument:document] &&
  617. [self orderByMatchesDocument:document] && [self filtersMatchDocument:document] &&
  618. [self boundsMatchDocument:document];
  619. }
  620. - (DocumentComparator)comparator {
  621. NSArray<FSTSortOrder *> *sortOrders = self.sortOrders;
  622. return DocumentComparator([sortOrders](id document1, id document2) {
  623. bool didCompareOnKeyField = false;
  624. for (FSTSortOrder *orderBy in sortOrders) {
  625. ComparisonResult comp = [orderBy compareDocument:document1 toDocument:document2];
  626. if (!util::Same(comp)) return comp;
  627. didCompareOnKeyField = didCompareOnKeyField || orderBy.field == FieldPath::KeyFieldPath();
  628. }
  629. HARD_ASSERT(didCompareOnKeyField, "sortOrder of query did not include key ordering");
  630. return ComparisonResult::Same;
  631. });
  632. }
  633. - (nullable const FieldPath *)inequalityFilterField {
  634. for (FSTFilter *filter in self.filters) {
  635. if ([filter isKindOfClass:[FSTRelationFilter class]] &&
  636. ((FSTRelationFilter *)filter).isInequality) {
  637. return &filter.field;
  638. }
  639. }
  640. return nullptr;
  641. }
  642. - (BOOL)hasArrayContainsFilter {
  643. for (FSTFilter *filter in self.filters) {
  644. if ([filter isKindOfClass:[FSTRelationFilter class]] &&
  645. ((FSTRelationFilter *)filter).filterOperator == Filter::Operator::ArrayContains) {
  646. return YES;
  647. }
  648. }
  649. return NO;
  650. }
  651. - (nullable const FieldPath *)firstSortOrderField {
  652. if (self.explicitSortOrders.count > 0) {
  653. return &self.explicitSortOrders.firstObject.field;
  654. }
  655. return nullptr;
  656. }
  657. /** The base path of the query. */
  658. - (const firebase::firestore::model::ResourcePath &)path {
  659. return _path;
  660. }
  661. #pragma mark - Private properties
  662. - (NSString *)canonicalID {
  663. if (_canonicalID) {
  664. return _canonicalID;
  665. }
  666. NSMutableString *canonicalID = [NSMutableString string];
  667. [canonicalID appendFormat:@"%s", _path.CanonicalString().c_str()];
  668. if (self.collectionGroup) {
  669. [canonicalID appendFormat:@"|cg:%@", self.collectionGroup];
  670. }
  671. // Add filters.
  672. [canonicalID appendString:@"|f:"];
  673. for (FSTFilter *predicate in self.filters) {
  674. [canonicalID appendFormat:@"%@", [predicate canonicalID]];
  675. }
  676. // Add order by.
  677. [canonicalID appendString:@"|ob:"];
  678. for (FSTSortOrder *orderBy in self.sortOrders) {
  679. [canonicalID appendString:orderBy.canonicalID];
  680. }
  681. // Add limit.
  682. if (self.limit != Query::kNoLimit) {
  683. [canonicalID appendFormat:@"|l:%ld", (long)self.limit];
  684. }
  685. if (self.startAt) {
  686. [canonicalID appendFormat:@"|lb:%@", self.startAt.canonicalString];
  687. }
  688. if (self.endAt) {
  689. [canonicalID appendFormat:@"|ub:%@", self.endAt.canonicalString];
  690. }
  691. _canonicalID = canonicalID;
  692. return canonicalID;
  693. }
  694. #pragma mark - Private methods
  695. - (BOOL)isEqualToQuery:(FSTQuery *)other {
  696. return self.path == other.path && objc::Equals(self.collectionGroup, other.collectionGroup) &&
  697. self.limit == other.limit && objc::Equals(self.filters, other.filters) &&
  698. objc::Equals(self.sortOrders, other.sortOrders) &&
  699. objc::Equals(self.startAt, other.startAt) && objc::Equals(self.endAt, other.endAt);
  700. }
  701. /* Returns YES if the document matches the path and collection group for the receiver. */
  702. - (BOOL)pathAndCollectionGroupMatchDocument:(FSTDocument *)document {
  703. const ResourcePath &documentPath = document.key.path();
  704. if (self.collectionGroup) {
  705. // NOTE: self.path is currently always empty since we don't expose Collection Group queries
  706. // rooted at a document path yet.
  707. return document.key.HasCollectionId(util::MakeString(self.collectionGroup)) &&
  708. self.path.IsPrefixOf(documentPath);
  709. } else if (DocumentKey::IsDocumentKey(_path)) {
  710. // Exact match for document queries.
  711. return self.path == documentPath;
  712. } else {
  713. // Shallow ancestor queries by default.
  714. return self.path.IsPrefixOf(documentPath) && _path.size() == documentPath.size() - 1;
  715. }
  716. }
  717. /**
  718. * A document must have a value for every ordering clause in order to show up in the results.
  719. */
  720. - (BOOL)orderByMatchesDocument:(FSTDocument *)document {
  721. for (FSTSortOrder *orderBy in self.explicitSortOrders) {
  722. const FieldPath &fieldPath = orderBy.field;
  723. // order by key always matches
  724. if (fieldPath != FieldPath::KeyFieldPath() && [document fieldForPath:fieldPath] == nil) {
  725. return NO;
  726. }
  727. }
  728. return YES;
  729. }
  730. /** Returns YES if the document matches all of the filters in the receiver. */
  731. - (BOOL)filtersMatchDocument:(FSTDocument *)document {
  732. for (FSTFilter *filter in self.filters) {
  733. if (![filter matchesDocument:document]) {
  734. return NO;
  735. }
  736. }
  737. return YES;
  738. }
  739. - (BOOL)boundsMatchDocument:(FSTDocument *)document {
  740. if (self.startAt && ![self.startAt sortsBeforeDocument:document usingSortOrder:self.sortOrders]) {
  741. return NO;
  742. }
  743. if (self.endAt && [self.endAt sortsBeforeDocument:document usingSortOrder:self.sortOrders]) {
  744. return NO;
  745. }
  746. return YES;
  747. }
  748. @end
  749. NS_ASSUME_NONNULL_END