| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759 |
- /*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #import "Firestore/Source/Core/FSTQuery.h"
- #import "Firestore/Source/API/FIRFirestore+Internal.h"
- #import "Firestore/Source/Model/FSTDocument.h"
- #import "Firestore/Source/Model/FSTDocumentKey.h"
- #import "Firestore/Source/Model/FSTFieldValue.h"
- #import "Firestore/Source/Model/FSTPath.h"
- #import "Firestore/Source/Util/FSTAssert.h"
- NS_ASSUME_NONNULL_BEGIN
- #pragma mark - FSTRelationFilterOperator functions
- NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOperator) {
- switch (filterOperator) {
- case FSTRelationFilterOperatorLessThan:
- return @"<";
- case FSTRelationFilterOperatorLessThanOrEqual:
- return @"<=";
- case FSTRelationFilterOperatorEqual:
- return @"==";
- case FSTRelationFilterOperatorGreaterThanOrEqual:
- return @">=";
- case FSTRelationFilterOperatorGreaterThan:
- return @">";
- default:
- FSTCFail(@"Unknown FSTRelationFilterOperator %lu", (unsigned long)filterOperator);
- }
- }
- #pragma mark - FSTRelationFilter
- @interface FSTRelationFilter ()
- /**
- * Initializes the receiver relation filter.
- *
- * @param field A path to a field in the document to filter on. The LHS of the expression.
- * @param filterOperator The binary operator to apply.
- * @param value A constant value to compare @a field to. The RHS of the expression.
- */
- - (instancetype)initWithField:(FSTFieldPath *)field
- filterOperator:(FSTRelationFilterOperator)filterOperator
- value:(FSTFieldValue *)value NS_DESIGNATED_INITIALIZER;
- /** Returns YES if @a document matches the receiver's constraint. */
- - (BOOL)matchesDocument:(FSTDocument *)document;
- /**
- * A canonical string identifying the filter. Two different instances of equivalent filters will
- * return the same canonicalID.
- */
- - (NSString *)canonicalID;
- @end
- @implementation FSTRelationFilter
- #pragma mark - Constructor methods
- + (instancetype)filterWithField:(FSTFieldPath *)field
- filterOperator:(FSTRelationFilterOperator)filterOperator
- value:(FSTFieldValue *)value {
- return [[FSTRelationFilter alloc] initWithField:field filterOperator:filterOperator value:value];
- }
- - (instancetype)initWithField:(FSTFieldPath *)field
- filterOperator:(FSTRelationFilterOperator)filterOperator
- value:(FSTFieldValue *)value {
- self = [super init];
- if (self) {
- _field = field;
- _filterOperator = filterOperator;
- _value = value;
- }
- return self;
- }
- #pragma mark - Public Methods
- - (BOOL)isInequality {
- return self.filterOperator != FSTRelationFilterOperatorEqual;
- }
- #pragma mark - NSObject methods
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@ %@ %@", [self.field canonicalString],
- FSTStringFromQueryRelationOperator(self.filterOperator),
- self.value];
- }
- - (BOOL)isEqual:(id)other {
- if (self == other) {
- return YES;
- }
- if (![other isKindOfClass:[FSTRelationFilter class]]) {
- return NO;
- }
- return [self isEqualToFilter:(FSTRelationFilter *)other];
- }
- #pragma mark - Private methods
- - (BOOL)matchesDocument:(FSTDocument *)document {
- if ([self.field isKeyFieldPath]) {
- FSTAssert([self.value isKindOfClass:[FSTReferenceValue class]],
- @"Comparing on key, but filter value not a FSTReferenceValue.");
- FSTReferenceValue *refValue = (FSTReferenceValue *)self.value;
- NSComparisonResult comparison = FSTDocumentKeyComparator(document.key, refValue.value);
- return [self matchesComparison:comparison];
- } else {
- return [self matchesValue:[document fieldForPath:self.field]];
- }
- }
- - (NSString *)canonicalID {
- // TODO(b/37283291): This should be collision robust and avoid relying on |description| methods.
- return [NSString stringWithFormat:@"%@%@%@", [self.field canonicalString],
- FSTStringFromQueryRelationOperator(self.filterOperator),
- [self.value value]];
- }
- - (BOOL)isEqualToFilter:(FSTRelationFilter *)other {
- if (self.filterOperator != other.filterOperator) {
- return NO;
- }
- if (![self.field isEqual:other.field]) {
- return NO;
- }
- if (![self.value isEqual:other.value]) {
- return NO;
- }
- return YES;
- }
- /** Returns YES if receiver is true with the given value as its LHS. */
- - (BOOL)matchesValue:(FSTFieldValue *)other {
- // Only compare types with matching backend order (such as double and int).
- return self.value.typeOrder == other.typeOrder &&
- [self matchesComparison:[other compare:self.value]];
- }
- - (BOOL)matchesComparison:(NSComparisonResult)comparison {
- switch (self.filterOperator) {
- case FSTRelationFilterOperatorLessThan:
- return comparison == NSOrderedAscending;
- case FSTRelationFilterOperatorLessThanOrEqual:
- return comparison == NSOrderedAscending || comparison == NSOrderedSame;
- case FSTRelationFilterOperatorEqual:
- return comparison == NSOrderedSame;
- case FSTRelationFilterOperatorGreaterThanOrEqual:
- return comparison == NSOrderedDescending || comparison == NSOrderedSame;
- case FSTRelationFilterOperatorGreaterThan:
- return comparison == NSOrderedDescending;
- default:
- FSTFail(@"Unknown operator: %ld", (long)self.filterOperator);
- }
- }
- @end
- #pragma mark - FSTNullFilter
- @interface FSTNullFilter ()
- @property(nonatomic, strong, readonly) FSTFieldPath *field;
- @end
- @implementation FSTNullFilter
- - (instancetype)initWithField:(FSTFieldPath *)field {
- if (self = [super init]) {
- _field = field;
- }
- return self;
- }
- - (BOOL)matchesDocument:(FSTDocument *)document {
- FSTFieldValue *fieldValue = [document fieldForPath:self.field];
- return fieldValue != nil && [fieldValue isEqual:[FSTNullValue nullValue]];
- }
- - (NSString *)canonicalID {
- return [NSString stringWithFormat:@"%@ IS NULL", [self.field canonicalString]];
- }
- - (NSString *)description {
- return [self canonicalID];
- }
- - (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
- return [self.field isEqual:((FSTNullFilter *)other).field];
- }
- - (NSUInteger)hash {
- return [self.field hash];
- }
- @end
- #pragma mark - FSTNanFilter
- @interface FSTNanFilter ()
- @property(nonatomic, strong, readonly) FSTFieldPath *field;
- @end
- @implementation FSTNanFilter
- - (instancetype)initWithField:(FSTFieldPath *)field {
- if (self = [super init]) {
- _field = field;
- }
- return self;
- }
- - (BOOL)matchesDocument:(FSTDocument *)document {
- FSTFieldValue *fieldValue = [document fieldForPath:self.field];
- return fieldValue != nil && [fieldValue isEqual:[FSTDoubleValue nanValue]];
- }
- - (NSString *)canonicalID {
- return [NSString stringWithFormat:@"%@ IS NaN", [self.field canonicalString]];
- }
- - (NSString *)description {
- return [self canonicalID];
- }
- - (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
- return [self.field isEqual:((FSTNanFilter *)other).field];
- }
- - (NSUInteger)hash {
- return [self.field hash];
- }
- @end
- #pragma mark - FSTSortOrder
- @interface FSTSortOrder ()
- /** Creates a new sort order with the given field and direction. */
- - (instancetype)initWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending;
- - (NSString *)canonicalID;
- @end
- @implementation FSTSortOrder
- #pragma mark - Constructor methods
- + (instancetype)sortOrderWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending {
- return [[FSTSortOrder alloc] initWithFieldPath:fieldPath ascending:ascending];
- }
- - (instancetype)initWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)ascending {
- self = [super init];
- if (self) {
- _field = fieldPath;
- _ascending = ascending;
- }
- return self;
- }
- #pragma mark - Public methods
- - (NSComparisonResult)compareDocument:(FSTDocument *)document1 toDocument:(FSTDocument *)document2 {
- int modifier = self.isAscending ? 1 : -1;
- if ([self.field isEqual:[FSTFieldPath keyFieldPath]]) {
- return (NSComparisonResult)(modifier * FSTDocumentKeyComparator(document1.key, document2.key));
- } else {
- FSTFieldValue *value1 = [document1 fieldForPath:self.field];
- FSTFieldValue *value2 = [document2 fieldForPath:self.field];
- FSTAssert(value1 != nil && value2 != nil,
- @"Trying to compare documents on fields that don't exist.");
- return modifier * [value1 compare:value2];
- }
- }
- - (NSString *)canonicalID {
- return [NSString
- stringWithFormat:@"%@%@", self.field.canonicalString, self.isAscending ? @"asc" : @"desc"];
- }
- - (BOOL)isEqualToSortOrder:(FSTSortOrder *)other {
- return [self.field isEqual:other.field] && self.isAscending == other.isAscending;
- }
- #pragma mark - NSObject methods
- - (NSString *)description {
- return [NSString stringWithFormat:@"<FSTSortOrder: path:%@ dir:%@>", self.field,
- self.ascending ? @"asc" : @"desc"];
- }
- - (BOOL)isEqual:(NSObject *)other {
- if (self == other) {
- return YES;
- }
- if (![other isKindOfClass:[FSTSortOrder class]]) {
- return NO;
- }
- return [self isEqualToSortOrder:(FSTSortOrder *)other];
- }
- - (NSUInteger)hash {
- return [self.canonicalID hash];
- }
- - (instancetype)copyWithZone:(nullable NSZone *)zone {
- return self;
- }
- @end
- #pragma mark - FSTBound
- @implementation FSTBound
- - (instancetype)initWithPosition:(NSArray<FSTFieldValue *> *)position isBefore:(BOOL)isBefore {
- if (self = [super init]) {
- _position = position;
- _before = isBefore;
- }
- return self;
- }
- + (instancetype)boundWithPosition:(NSArray<FSTFieldValue *> *)position isBefore:(BOOL)isBefore {
- return [[FSTBound alloc] initWithPosition:position isBefore:isBefore];
- }
- - (NSString *)canonicalString {
- // TODO(b/29183165): Make this collision robust.
- NSMutableString *string = [NSMutableString string];
- if (self.isBefore) {
- [string appendString:@"b:"];
- } else {
- [string appendString:@"a:"];
- }
- for (FSTFieldValue *component in self.position) {
- [string appendFormat:@"%@", component];
- }
- return string;
- }
- - (BOOL)sortsBeforeDocument:(FSTDocument *)document
- usingSortOrder:(NSArray<FSTSortOrder *> *)sortOrder {
- FSTAssert(self.position.count <= sortOrder.count,
- @"FSTIndexPosition has more components than provided sort order.");
- __block NSComparisonResult result = NSOrderedSame;
- [self.position enumerateObjectsUsingBlock:^(FSTFieldValue *fieldValue, NSUInteger idx,
- BOOL *stop) {
- FSTSortOrder *sortOrderComponent = sortOrder[idx];
- NSComparisonResult comparison;
- if ([sortOrderComponent.field isEqual:[FSTFieldPath keyFieldPath]]) {
- FSTAssert([fieldValue isKindOfClass:[FSTReferenceValue class]],
- @"FSTBound has a non-key value where the key path is being used %@", fieldValue);
- comparison = [fieldValue.value compare:document.key];
- } else {
- FSTFieldValue *docValue = [document fieldForPath:sortOrderComponent.field];
- FSTAssert(docValue != nil, @"Field should exist since document matched the orderBy already.");
- comparison = [fieldValue compare:docValue];
- }
- if (!sortOrderComponent.isAscending) {
- comparison = comparison * -1;
- }
- if (comparison != 0) {
- result = comparison;
- *stop = YES;
- }
- }];
- return self.isBefore ? result <= NSOrderedSame : result < NSOrderedSame;
- }
- #pragma mark - NSObject methods
- - (NSString *)description {
- return [NSString stringWithFormat:@"<FSTBound: position:%@ before:%@>", self.position,
- self.isBefore ? @"YES" : @"NO"];
- }
- - (BOOL)isEqual:(NSObject *)other {
- if (self == other) {
- return YES;
- }
- if (![other isKindOfClass:[FSTBound class]]) {
- return NO;
- }
- FSTBound *otherBound = (FSTBound *)other;
- return [self.position isEqualToArray:otherBound.position] && self.isBefore == otherBound.isBefore;
- }
- - (NSUInteger)hash {
- return 31 * self.position.hash + (self.isBefore ? 0 : 1);
- }
- - (instancetype)copyWithZone:(nullable NSZone *)zone {
- return self;
- }
- @end
- #pragma mark - FSTQuery
- @interface FSTQuery () {
- // Cached value of the canonicalID property.
- NSString *_canonicalID;
- }
- /**
- * Initializes the receiver with the given query constraints.
- *
- * @param path The base path of the query.
- * @param filters Filters specify which documents to include in the results.
- * @param sortOrders The fields and directions to sort the results.
- * @param limit If not NSNotFound, only this many results will be returned.
- */
- - (instancetype)initWithPath:(FSTResourcePath *)path
- filterBy:(NSArray<id<FSTFilter>> *)filters
- orderBy:(NSArray<FSTSortOrder *> *)sortOrders
- limit:(NSInteger)limit
- startAt:(nullable FSTBound *)startAtBound
- endAt:(nullable FSTBound *)endAtBound NS_DESIGNATED_INITIALIZER;
- /** A list of fields given to sort by. This does not include the implicit key sort at the end. */
- @property(nonatomic, strong, readonly) NSArray<FSTSortOrder *> *explicitSortOrders;
- /** The memoized list of sort orders */
- @property(nonatomic, nullable, strong, readwrite) NSArray<FSTSortOrder *> *memoizedSortOrders;
- @end
- @implementation FSTQuery
- #pragma mark - Constructors
- + (instancetype)queryWithPath:(FSTResourcePath *)path {
- return [[FSTQuery alloc] initWithPath:path
- filterBy:@[]
- orderBy:@[]
- limit:NSNotFound
- startAt:nil
- endAt:nil];
- }
- - (instancetype)initWithPath:(FSTResourcePath *)path
- filterBy:(NSArray<id<FSTFilter>> *)filters
- orderBy:(NSArray<FSTSortOrder *> *)sortOrders
- limit:(NSInteger)limit
- startAt:(nullable FSTBound *)startAtBound
- endAt:(nullable FSTBound *)endAtBound {
- if (self = [super init]) {
- _path = path;
- _filters = filters;
- _explicitSortOrders = sortOrders;
- _limit = limit;
- _startAt = startAtBound;
- _endAt = endAtBound;
- }
- return self;
- }
- #pragma mark - NSObject methods
- - (NSString *)description {
- return [NSString stringWithFormat:@"<FSTQuery: canonicalID:%@>", self.canonicalID];
- }
- - (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
- if (![object isKindOfClass:[FSTQuery class]]) {
- return NO;
- }
- return [self isEqualToQuery:(FSTQuery *)object];
- }
- - (NSUInteger)hash {
- return [self.canonicalID hash];
- }
- - (instancetype)copyWithZone:(nullable NSZone *)zone {
- return self;
- }
- #pragma mark - Public methods
- - (NSArray *)sortOrders {
- if (self.memoizedSortOrders == nil) {
- FSTFieldPath *_Nullable inequalityField = [self inequalityFilterField];
- FSTFieldPath *_Nullable firstSortOrderField = [self firstSortOrderField];
- if (inequalityField && !firstSortOrderField) {
- // In order to implicitly add key ordering, we must also add the inequality filter field for
- // it to be a valid query. Note that the default inequality field and key ordering is
- // ascending.
- if ([inequalityField isKeyFieldPath]) {
- self.memoizedSortOrders =
- @[ [FSTSortOrder sortOrderWithFieldPath:[FSTFieldPath keyFieldPath] ascending:YES] ];
- } else {
- self.memoizedSortOrders = @[
- [FSTSortOrder sortOrderWithFieldPath:inequalityField ascending:YES],
- [FSTSortOrder sortOrderWithFieldPath:[FSTFieldPath keyFieldPath] ascending:YES]
- ];
- }
- } else {
- FSTAssert(!inequalityField || [inequalityField isEqual:firstSortOrderField],
- @"First orderBy %@ should match inequality field %@.", firstSortOrderField,
- inequalityField);
- __block BOOL foundKeyOrder = NO;
- NSMutableArray *result = [NSMutableArray array];
- for (FSTSortOrder *sortOrder in self.explicitSortOrders) {
- [result addObject:sortOrder];
- if ([sortOrder.field isEqual:[FSTFieldPath keyFieldPath]]) {
- foundKeyOrder = YES;
- }
- }
- if (!foundKeyOrder) {
- // The direction of the implicit key ordering always matches the direction of the last
- // explicit sort order
- BOOL lastIsAscending =
- self.explicitSortOrders.count > 0 ? self.explicitSortOrders.lastObject.ascending : YES;
- [result addObject:[FSTSortOrder sortOrderWithFieldPath:[FSTFieldPath keyFieldPath]
- ascending:lastIsAscending]];
- }
- self.memoizedSortOrders = result;
- }
- }
- return self.memoizedSortOrders;
- }
- - (instancetype)queryByAddingFilter:(id<FSTFilter>)filter {
- FSTAssert(![FSTDocumentKey isDocumentKey:self.path], @"No filtering allowed for document query");
- FSTFieldPath *_Nullable newInequalityField = nil;
- if ([filter isKindOfClass:[FSTRelationFilter class]] &&
- [((FSTRelationFilter *)filter)isInequality]) {
- newInequalityField = filter.field;
- }
- FSTFieldPath *_Nullable queryInequalityField = [self inequalityFilterField];
- FSTAssert(!queryInequalityField || !newInequalityField ||
- [queryInequalityField isEqual:newInequalityField],
- @"Query must only have one inequality field.");
- return [[FSTQuery alloc] initWithPath:self.path
- filterBy:[self.filters arrayByAddingObject:filter]
- orderBy:self.explicitSortOrders
- limit:self.limit
- startAt:self.startAt
- endAt:self.endAt];
- }
- - (instancetype)queryByAddingSortOrder:(FSTSortOrder *)sortOrder {
- FSTAssert(![FSTDocumentKey isDocumentKey:self.path],
- @"No ordering is allowed for a document query.");
- // TODO(klimt): Validate that the same key isn't added twice.
- return [[FSTQuery alloc] initWithPath:self.path
- filterBy:self.filters
- orderBy:[self.explicitSortOrders arrayByAddingObject:sortOrder]
- limit:self.limit
- startAt:self.startAt
- endAt:self.endAt];
- }
- - (instancetype)queryBySettingLimit:(NSInteger)limit {
- return [[FSTQuery alloc] initWithPath:self.path
- filterBy:self.filters
- orderBy:self.explicitSortOrders
- limit:limit
- startAt:self.startAt
- endAt:self.endAt];
- }
- - (instancetype)queryByAddingStartAt:(FSTBound *)bound {
- return [[FSTQuery alloc] initWithPath:self.path
- filterBy:self.filters
- orderBy:self.explicitSortOrders
- limit:self.limit
- startAt:bound
- endAt:self.endAt];
- }
- - (instancetype)queryByAddingEndAt:(FSTBound *)bound {
- return [[FSTQuery alloc] initWithPath:self.path
- filterBy:self.filters
- orderBy:self.explicitSortOrders
- limit:self.limit
- startAt:self.startAt
- endAt:bound];
- }
- - (BOOL)isDocumentQuery {
- return [FSTDocumentKey isDocumentKey:self.path] && self.filters.count == 0;
- }
- - (BOOL)matchesDocument:(FSTDocument *)document {
- return [self pathMatchesDocument:document] && [self orderByMatchesDocument:document] &&
- [self filtersMatchDocument:document] && [self boundsMatchDocument:document];
- }
- - (NSComparator)comparator {
- return ^NSComparisonResult(id document1, id document2) {
- BOOL didCompareOnKeyField = NO;
- for (FSTSortOrder *orderBy in self.sortOrders) {
- NSComparisonResult comp = [orderBy compareDocument:document1 toDocument:document2];
- if (comp != NSOrderedSame) {
- return comp;
- }
- didCompareOnKeyField =
- didCompareOnKeyField || [orderBy.field isEqual:[FSTFieldPath keyFieldPath]];
- }
- FSTAssert(didCompareOnKeyField, @"sortOrder of query did not include key ordering");
- return NSOrderedSame;
- };
- }
- - (FSTFieldPath *_Nullable)inequalityFilterField {
- for (id<FSTFilter> filter in self.filters) {
- if ([filter isKindOfClass:[FSTRelationFilter class]] &&
- ((FSTRelationFilter *)filter).filterOperator != FSTRelationFilterOperatorEqual) {
- return filter.field;
- }
- }
- return nil;
- }
- - (FSTFieldPath *_Nullable)firstSortOrderField {
- return self.explicitSortOrders.firstObject.field;
- }
- #pragma mark - Private properties
- - (NSString *)canonicalID {
- if (_canonicalID) {
- return _canonicalID;
- }
- NSMutableString *canonicalID = [[self.path canonicalString] mutableCopy];
- // Add filters.
- [canonicalID appendString:@"|f:"];
- for (id<FSTFilter> predicate in self.filters) {
- [canonicalID appendFormat:@"%@", [predicate canonicalID]];
- }
- // Add order by.
- [canonicalID appendString:@"|ob:"];
- for (FSTSortOrder *orderBy in self.sortOrders) {
- [canonicalID appendString:orderBy.canonicalID];
- }
- // Add limit.
- if (self.limit != NSNotFound) {
- [canonicalID appendFormat:@"|l:%ld", (long)self.limit];
- }
- if (self.startAt) {
- [canonicalID appendFormat:@"|lb:%@", self.startAt.canonicalString];
- }
- if (self.endAt) {
- [canonicalID appendFormat:@"|ub:%@", self.endAt.canonicalString];
- }
- _canonicalID = canonicalID;
- return canonicalID;
- }
- #pragma mark - Private methods
- - (BOOL)isEqualToQuery:(FSTQuery *)other {
- return [self.path isEqual:other.path] && self.limit == other.limit &&
- [self.filters isEqual:other.filters] && [self.sortOrders isEqual:other.sortOrders] &&
- (self.startAt == other.startAt || [self.startAt isEqual:other.startAt]) &&
- (self.endAt == other.endAt || [self.endAt isEqual:other.endAt]);
- }
- /* Returns YES if the document matches the path for the receiver. */
- - (BOOL)pathMatchesDocument:(FSTDocument *)document {
- FSTResourcePath *documentPath = document.key.path;
- if ([FSTDocumentKey isDocumentKey:self.path]) {
- // Exact match for document queries.
- return [self.path isEqual:documentPath];
- } else {
- // Shallow ancestor queries by default.
- return [self.path isPrefixOfPath:documentPath] && self.path.length == documentPath.length - 1;
- }
- }
- /**
- * A document must have a value for every ordering clause in order to show up in the results.
- */
- - (BOOL)orderByMatchesDocument:(FSTDocument *)document {
- for (FSTSortOrder *orderBy in self.explicitSortOrders) {
- FSTFieldPath *fieldPath = orderBy.field;
- // order by key always matches
- if (![fieldPath isEqual:[FSTFieldPath keyFieldPath]] &&
- [document fieldForPath:fieldPath] == nil) {
- return NO;
- }
- }
- return YES;
- }
- /** Returns YES if the document matches all of the filters in the receiver. */
- - (BOOL)filtersMatchDocument:(FSTDocument *)document {
- for (id<FSTFilter> filter in self.filters) {
- if (![filter matchesDocument:document]) {
- return NO;
- }
- }
- return YES;
- }
- - (BOOL)boundsMatchDocument:(FSTDocument *)document {
- if (self.startAt && ![self.startAt sortsBeforeDocument:document usingSortOrder:self.sortOrders]) {
- return NO;
- }
- if (self.endAt && [self.endAt sortsBeforeDocument:document usingSortOrder:self.sortOrders]) {
- return NO;
- }
- return YES;
- }
- @end
- NS_ASSUME_NONNULL_END
|