FSTQuery.mm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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. #include <vector>
  22. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  23. #import "Firestore/Source/Model/FSTDocument.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/nan_filter.h"
  28. #include "Firestore/core/src/firebase/firestore/core/null_filter.h"
  29. #include "Firestore/core/src/firebase/firestore/core/query.h"
  30. #include "Firestore/core/src/firebase/firestore/core/relation_filter.h"
  31. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  32. #include "Firestore/core/src/firebase/firestore/model/field_path.h"
  33. #include "Firestore/core/src/firebase/firestore/model/field_value.h"
  34. #include "Firestore/core/src/firebase/firestore/model/resource_path.h"
  35. #include "Firestore/core/src/firebase/firestore/objc/objc_compatibility.h"
  36. #include "Firestore/core/src/firebase/firestore/util/comparison.h"
  37. #include "Firestore/core/src/firebase/firestore/util/equality.h"
  38. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  39. #include "Firestore/core/src/firebase/firestore/util/hashing.h"
  40. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  41. #include "absl/algorithm/container.h"
  42. namespace core = firebase::firestore::core;
  43. namespace objc = firebase::firestore::objc;
  44. namespace util = firebase::firestore::util;
  45. using firebase::firestore::api::ThrowInvalidArgument;
  46. using firebase::firestore::core::Filter;
  47. using firebase::firestore::core::Query;
  48. using firebase::firestore::model::Document;
  49. using firebase::firestore::model::DocumentComparator;
  50. using firebase::firestore::model::DocumentKey;
  51. using firebase::firestore::model::FieldPath;
  52. using firebase::firestore::model::FieldValue;
  53. using firebase::firestore::model::ResourcePath;
  54. using firebase::firestore::util::ComparisonResult;
  55. NS_ASSUME_NONNULL_BEGIN
  56. #pragma mark - FSTSortOrder
  57. @interface FSTSortOrder () {
  58. /** The field to sort by. */
  59. FieldPath _field;
  60. }
  61. /** Creates a new sort order with the given field and direction. */
  62. - (instancetype)initWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending;
  63. - (NSString *)canonicalID;
  64. @end
  65. @implementation FSTSortOrder
  66. #pragma mark - Constructor methods
  67. + (instancetype)sortOrderWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending {
  68. return [[FSTSortOrder alloc] initWithFieldPath:std::move(fieldPath) ascending:ascending];
  69. }
  70. - (instancetype)initWithFieldPath:(FieldPath)fieldPath ascending:(BOOL)ascending {
  71. self = [super init];
  72. if (self) {
  73. _field = std::move(fieldPath);
  74. _ascending = ascending;
  75. }
  76. return self;
  77. }
  78. - (const FieldPath &)field {
  79. return _field;
  80. }
  81. #pragma mark - Public methods
  82. - (ComparisonResult)compareDocument:(FSTDocument *)document1 toDocument:(FSTDocument *)document2 {
  83. ComparisonResult result;
  84. if (_field == FieldPath::KeyFieldPath()) {
  85. result = util::Compare(document1.key, document2.key);
  86. } else {
  87. absl::optional<FieldValue> value1 = [document1 fieldForPath:self.field];
  88. absl::optional<FieldValue> value2 = [document2 fieldForPath:self.field];
  89. HARD_ASSERT(value1.has_value() && value2.has_value(),
  90. "Trying to compare documents on fields that don't exist.");
  91. result = value1->CompareTo(*value2);
  92. }
  93. if (!self.isAscending) {
  94. result = util::ReverseOrder(result);
  95. }
  96. return result;
  97. }
  98. - (NSString *)canonicalID {
  99. return [NSString stringWithFormat:@"%s%@", _field.CanonicalString().c_str(),
  100. self.isAscending ? @"asc" : @"desc"];
  101. }
  102. - (BOOL)isEqualToSortOrder:(FSTSortOrder *)other {
  103. return _field == other->_field && self.isAscending == other.isAscending;
  104. }
  105. #pragma mark - NSObject methods
  106. - (NSString *)description {
  107. return [NSString stringWithFormat:@"<FSTSortOrder: path:%s dir:%@>",
  108. _field.CanonicalString().c_str(),
  109. self.ascending ? @"asc" : @"desc"];
  110. }
  111. - (BOOL)isEqual:(NSObject *)other {
  112. if (self == other) {
  113. return YES;
  114. }
  115. if (![other isKindOfClass:[FSTSortOrder class]]) {
  116. return NO;
  117. }
  118. return [self isEqualToSortOrder:(FSTSortOrder *)other];
  119. }
  120. - (NSUInteger)hash {
  121. return [self.canonicalID hash];
  122. }
  123. - (instancetype)copyWithZone:(nullable NSZone *)zone {
  124. return self;
  125. }
  126. @end
  127. #pragma mark - FSTBound
  128. @implementation FSTBound {
  129. std::vector<FieldValue> _position;
  130. }
  131. - (instancetype)initWithPosition:(std::vector<FieldValue>)position isBefore:(bool)isBefore {
  132. if (self = [super init]) {
  133. _position = std::move(position);
  134. _before = isBefore;
  135. }
  136. return self;
  137. }
  138. + (instancetype)boundWithPosition:(std::vector<FieldValue>)position isBefore:(bool)isBefore {
  139. return [[FSTBound alloc] initWithPosition:position isBefore:isBefore];
  140. }
  141. - (NSString *)canonicalString {
  142. // TODO(b/29183165): Make this collision robust.
  143. NSMutableString *string = [NSMutableString string];
  144. if (self.isBefore) {
  145. [string appendString:@"b:"];
  146. } else {
  147. [string appendString:@"a:"];
  148. }
  149. for (const FieldValue &component : _position) {
  150. [string appendFormat:@"%s", component.ToString().c_str()];
  151. }
  152. return string;
  153. }
  154. - (bool)sortsBeforeDocument:(FSTDocument *)document
  155. usingSortOrder:(NSArray<FSTSortOrder *> *)sortOrder {
  156. HARD_ASSERT(_position.size() <= sortOrder.count,
  157. "FSTIndexPosition has more components than provided sort order.");
  158. ComparisonResult result = ComparisonResult::Same;
  159. for (size_t idx = 0; idx < _position.size(); ++idx) {
  160. const FieldValue &fieldValue = _position[idx];
  161. FSTSortOrder *sortOrderComponent = sortOrder[idx];
  162. ComparisonResult comparison;
  163. if (sortOrderComponent.field == FieldPath::KeyFieldPath()) {
  164. HARD_ASSERT(fieldValue.type() == FieldValue::Type::Reference,
  165. "FSTBound has a non-key value where the key path is being used %s",
  166. fieldValue.ToString());
  167. const auto &ref = fieldValue.reference_value();
  168. comparison = ref.key().CompareTo(document.key);
  169. } else {
  170. absl::optional<FieldValue> docValue = [document fieldForPath:sortOrderComponent.field];
  171. HARD_ASSERT(docValue.has_value(),
  172. "Field should exist since document matched the orderBy already.");
  173. comparison = fieldValue.CompareTo(*docValue);
  174. }
  175. if (!sortOrderComponent.isAscending) {
  176. comparison = util::ReverseOrder(comparison);
  177. }
  178. if (!util::Same(comparison)) {
  179. result = comparison;
  180. break;
  181. }
  182. }
  183. return self.isBefore ? result <= ComparisonResult::Same : result < ComparisonResult::Same;
  184. }
  185. #pragma mark - NSObject methods
  186. - (NSString *)description {
  187. return
  188. [NSString stringWithFormat:@"<FSTBound: position:%s before:%@>",
  189. util::ToString(_position).c_str(), self.isBefore ? @"YES" : @"NO"];
  190. }
  191. - (BOOL)isEqual:(NSObject *)other {
  192. if (self == other) {
  193. return YES;
  194. }
  195. if (![other isKindOfClass:[FSTBound class]]) {
  196. return NO;
  197. }
  198. FSTBound *otherBound = (FSTBound *)other;
  199. return _position == otherBound->_position && self.isBefore == otherBound.isBefore;
  200. }
  201. - (NSUInteger)hash {
  202. return util::Hash(self.position, self.isBefore);
  203. }
  204. - (instancetype)copyWithZone:(nullable NSZone *)zone {
  205. return self;
  206. }
  207. @end
  208. #pragma mark - FSTQuery
  209. @interface FSTQuery () {
  210. // Cached value of the canonicalID property.
  211. NSString *_canonicalID;
  212. // The C++ implementation of this query to which FSTQuery delegates.
  213. Query _query;
  214. }
  215. /** A list of fields given to sort by. This does not include the implicit key sort at the end. */
  216. @property(nonatomic, strong, readonly) NSArray<FSTSortOrder *> *explicitSortOrders;
  217. /** The memoized list of sort orders */
  218. @property(nonatomic, nullable, strong, readwrite) NSArray<FSTSortOrder *> *memoizedSortOrders;
  219. @end
  220. @implementation FSTQuery
  221. #pragma mark - Constructors
  222. + (instancetype)queryWithPath:(ResourcePath)path {
  223. return [FSTQuery queryWithPath:std::move(path) collectionGroup:nullptr];
  224. }
  225. + (instancetype)queryWithPath:(ResourcePath)path
  226. collectionGroup:(std::shared_ptr<const std::string>)collectionGroup {
  227. return [[self alloc] initWithQuery:Query(std::move(path), std::move(collectionGroup))
  228. orderBy:@[]
  229. limit:Query::kNoLimit
  230. startAt:nil
  231. endAt:nil];
  232. }
  233. - (instancetype)initWithQuery:(core::Query)query
  234. orderBy:(NSArray<FSTSortOrder *> *)sortOrders
  235. limit:(int32_t)limit
  236. startAt:(nullable FSTBound *)startAtBound
  237. endAt:(nullable FSTBound *)endAtBound {
  238. if (self = [super init]) {
  239. _query = std::move(query);
  240. _explicitSortOrders = sortOrders;
  241. _limit = limit;
  242. _startAt = startAtBound;
  243. _endAt = endAtBound;
  244. }
  245. return self;
  246. }
  247. #pragma mark - NSObject methods
  248. - (NSString *)description {
  249. return [NSString stringWithFormat:@"<FSTQuery: canonicalID:%@>", self.canonicalID];
  250. }
  251. - (BOOL)isEqual:(id)object {
  252. if (self == object) {
  253. return YES;
  254. }
  255. if (![object isKindOfClass:[FSTQuery class]]) {
  256. return NO;
  257. }
  258. return [self isEqualToQuery:(FSTQuery *)object];
  259. }
  260. - (NSUInteger)hash {
  261. return [self.canonicalID hash];
  262. }
  263. - (instancetype)copyWithZone:(nullable NSZone *)zone {
  264. return self;
  265. }
  266. #pragma mark - Public methods
  267. - (const Query::FilterList &)filters {
  268. return _query.filters();
  269. }
  270. - (NSArray *)sortOrders {
  271. if (self.memoizedSortOrders == nil) {
  272. const FieldPath *inequalityField = [self inequalityFilterField];
  273. const FieldPath *firstSortOrderField = [self firstSortOrderField];
  274. if (inequalityField && !firstSortOrderField) {
  275. // In order to implicitly add key ordering, we must also add the inequality filter field for
  276. // it to be a valid query. Note that the default inequality field and key ordering is
  277. // ascending.
  278. if (inequalityField->IsKeyFieldPath()) {
  279. self.memoizedSortOrders = @[ [FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath()
  280. ascending:YES] ];
  281. } else {
  282. self.memoizedSortOrders = @[
  283. [FSTSortOrder sortOrderWithFieldPath:*inequalityField ascending:YES],
  284. [FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath() ascending:YES]
  285. ];
  286. }
  287. } else {
  288. HARD_ASSERT(!inequalityField || *inequalityField == *firstSortOrderField,
  289. "First orderBy %s should match inequality field %s.",
  290. firstSortOrderField->CanonicalString(), inequalityField->CanonicalString());
  291. __block BOOL foundKeyOrder = NO;
  292. NSMutableArray *result = [NSMutableArray array];
  293. for (FSTSortOrder *sortOrder in self.explicitSortOrders) {
  294. [result addObject:sortOrder];
  295. if (sortOrder.field == FieldPath::KeyFieldPath()) {
  296. foundKeyOrder = YES;
  297. }
  298. }
  299. if (!foundKeyOrder) {
  300. // The direction of the implicit key ordering always matches the direction of the last
  301. // explicit sort order
  302. BOOL lastIsAscending =
  303. self.explicitSortOrders.count > 0 ? self.explicitSortOrders.lastObject.ascending : YES;
  304. [result addObject:[FSTSortOrder sortOrderWithFieldPath:FieldPath::KeyFieldPath()
  305. ascending:lastIsAscending]];
  306. }
  307. self.memoizedSortOrders = result;
  308. }
  309. }
  310. return self.memoizedSortOrders;
  311. }
  312. - (instancetype)queryByAddingFilter:(std::shared_ptr<Filter>)filter {
  313. return [[FSTQuery alloc] initWithQuery:_query.Filter(std::move(filter))
  314. orderBy:self.explicitSortOrders
  315. limit:self.limit
  316. startAt:self.startAt
  317. endAt:self.endAt];
  318. }
  319. - (instancetype)queryByAddingSortOrder:(FSTSortOrder *)sortOrder {
  320. HARD_ASSERT(![self isDocumentQuery], "No ordering is allowed for a document query.");
  321. // TODO(klimt): Validate that the same key isn't added twice.
  322. return [[FSTQuery alloc] initWithQuery:_query
  323. orderBy:[self.explicitSortOrders arrayByAddingObject:sortOrder]
  324. limit:self.limit
  325. startAt:self.startAt
  326. endAt:self.endAt];
  327. }
  328. - (instancetype)queryBySettingLimit:(int32_t)limit {
  329. return [[FSTQuery alloc] initWithQuery:_query
  330. orderBy:self.explicitSortOrders
  331. limit:limit
  332. startAt:self.startAt
  333. endAt:self.endAt];
  334. }
  335. - (instancetype)queryByAddingStartAt:(FSTBound *)bound {
  336. return [[FSTQuery alloc] initWithQuery:_query
  337. orderBy:self.explicitSortOrders
  338. limit:self.limit
  339. startAt:bound
  340. endAt:self.endAt];
  341. }
  342. - (instancetype)queryByAddingEndAt:(FSTBound *)bound {
  343. return [[FSTQuery alloc] initWithQuery:_query
  344. orderBy:self.explicitSortOrders
  345. limit:self.limit
  346. startAt:self.startAt
  347. endAt:bound];
  348. }
  349. - (instancetype)collectionQueryAtPath:(ResourcePath)path {
  350. return [[FSTQuery alloc] initWithQuery:_query.AsCollectionQueryAtPath(std::move(path))
  351. orderBy:self.explicitSortOrders
  352. limit:self.limit
  353. startAt:self.startAt
  354. endAt:self.endAt];
  355. }
  356. - (BOOL)isDocumentQuery {
  357. return _query.IsDocumentQuery();
  358. }
  359. - (BOOL)isCollectionGroupQuery {
  360. return self.collectionGroup != nil;
  361. }
  362. - (BOOL)matchesDocument:(FSTDocument *)document {
  363. return [self pathAndCollectionGroupMatchDocument:document] &&
  364. [self orderByMatchesDocument:document] && [self filtersMatchDocument:document] &&
  365. [self boundsMatchDocument:document];
  366. }
  367. - (DocumentComparator)comparator {
  368. NSArray<FSTSortOrder *> *sortOrders = self.sortOrders;
  369. return DocumentComparator([sortOrders](id document1, id document2) {
  370. bool didCompareOnKeyField = false;
  371. for (FSTSortOrder *orderBy in sortOrders) {
  372. ComparisonResult comp = [orderBy compareDocument:document1 toDocument:document2];
  373. if (!util::Same(comp)) return comp;
  374. didCompareOnKeyField = didCompareOnKeyField || orderBy.field == FieldPath::KeyFieldPath();
  375. }
  376. HARD_ASSERT(didCompareOnKeyField, "sortOrder of query did not include key ordering");
  377. return ComparisonResult::Same;
  378. });
  379. }
  380. - (nullable const FieldPath *)inequalityFilterField {
  381. return _query.InequalityFilterField();
  382. }
  383. - (BOOL)hasArrayContainsFilter {
  384. return _query.HasArrayContainsFilter();
  385. }
  386. - (nullable const FieldPath *)firstSortOrderField {
  387. if (self.explicitSortOrders.count > 0) {
  388. return &self.explicitSortOrders.firstObject.field;
  389. }
  390. return nullptr;
  391. }
  392. /** The base path of the query. */
  393. - (const ResourcePath &)path {
  394. return _query.path();
  395. }
  396. - (const std::shared_ptr<const std::string> &)collectionGroup {
  397. return _query.collection_group();
  398. }
  399. #pragma mark - Private properties
  400. - (NSString *)canonicalID {
  401. if (_canonicalID) {
  402. return _canonicalID;
  403. }
  404. NSMutableString *canonicalID = [NSMutableString string];
  405. [canonicalID appendFormat:@"%s", self.path.CanonicalString().c_str()];
  406. if (self.collectionGroup) {
  407. [canonicalID appendFormat:@"|cg:%s", self.collectionGroup->c_str()];
  408. }
  409. // Add filters.
  410. [canonicalID appendString:@"|f:"];
  411. for (const auto &filter : self.filters) {
  412. [canonicalID appendFormat:@"%s", filter->CanonicalId().c_str()];
  413. }
  414. // Add order by.
  415. [canonicalID appendString:@"|ob:"];
  416. for (FSTSortOrder *orderBy in self.sortOrders) {
  417. [canonicalID appendString:orderBy.canonicalID];
  418. }
  419. // Add limit.
  420. if (self.limit != Query::kNoLimit) {
  421. [canonicalID appendFormat:@"|l:%ld", (long)self.limit];
  422. }
  423. if (self.startAt) {
  424. [canonicalID appendFormat:@"|lb:%@", self.startAt.canonicalString];
  425. }
  426. if (self.endAt) {
  427. [canonicalID appendFormat:@"|ub:%@", self.endAt.canonicalString];
  428. }
  429. _canonicalID = canonicalID;
  430. return canonicalID;
  431. }
  432. #pragma mark - Private methods
  433. - (BOOL)isEqualToQuery:(FSTQuery *)other {
  434. return _query == other->_query && self.limit == other.limit &&
  435. objc::Equals(self.sortOrders, other.sortOrders) &&
  436. objc::Equals(self.startAt, other.startAt) && objc::Equals(self.endAt, other.endAt);
  437. }
  438. /* Returns YES if the document matches the path and collection group for the receiver. */
  439. - (BOOL)pathAndCollectionGroupMatchDocument:(FSTDocument *)document {
  440. const ResourcePath &documentPath = document.key.path();
  441. if (self.collectionGroup) {
  442. // NOTE: self.path is currently always empty since we don't expose Collection Group queries
  443. // rooted at a document path yet.
  444. return document.key.HasCollectionId(*self.collectionGroup) &&
  445. self.path.IsPrefixOf(documentPath);
  446. } else if (DocumentKey::IsDocumentKey(self.path)) {
  447. // Exact match for document queries.
  448. return self.path == documentPath;
  449. } else {
  450. // Shallow ancestor queries by default.
  451. return self.path.IsPrefixOf(documentPath) && self.path.size() == documentPath.size() - 1;
  452. }
  453. }
  454. /**
  455. * A document must have a value for every ordering clause in order to show up in the results.
  456. */
  457. - (BOOL)orderByMatchesDocument:(FSTDocument *)document {
  458. for (FSTSortOrder *orderBy in self.explicitSortOrders) {
  459. const FieldPath &fieldPath = orderBy.field;
  460. // order by key always matches
  461. if (fieldPath != FieldPath::KeyFieldPath() &&
  462. [document fieldForPath:fieldPath] == absl::nullopt) {
  463. return NO;
  464. }
  465. }
  466. return YES;
  467. }
  468. /** Returns YES if the document matches all of the filters in the receiver. */
  469. - (BOOL)filtersMatchDocument:(FSTDocument *)document {
  470. Document converted(document);
  471. for (const auto &filter : self.filters) {
  472. if (!filter->Matches(converted)) {
  473. return NO;
  474. }
  475. }
  476. return YES;
  477. }
  478. - (BOOL)boundsMatchDocument:(FSTDocument *)document {
  479. if (self.startAt && ![self.startAt sortsBeforeDocument:document usingSortOrder:self.sortOrders]) {
  480. return NO;
  481. }
  482. if (self.endAt && [self.endAt sortsBeforeDocument:document usingSortOrder:self.sortOrders]) {
  483. return NO;
  484. }
  485. return YES;
  486. }
  487. @end
  488. NS_ASSUME_NONNULL_END