Files
firebase-ios-sdk/FirebaseDatabase/Sources/Core/FQueryParams.m
T

393 lines
12 KiB
Objective-C
Raw Normal View History

2017-05-15 12:27:07 -07:00
/*
* 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.
*/
2020-06-22 07:58:52 -07:00
#import "FirebaseDatabase/Sources/Core/FQueryParams.h"
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
#import "FirebaseDatabase/Sources/Core/View/Filter/FIndexedFilter.h"
#import "FirebaseDatabase/Sources/Core/View/Filter/FLimitedFilter.h"
#import "FirebaseDatabase/Sources/Core/View/Filter/FNodeFilter.h"
#import "FirebaseDatabase/Sources/FIndex.h"
#import "FirebaseDatabase/Sources/FPriorityIndex.h"
#import "FirebaseDatabase/Sources/FRangedFilter.h"
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
#import "FirebaseDatabase/Sources/Utilities/FValidation.h"
2017-05-15 12:27:07 -07:00
@interface FQueryParams ()
2019-08-05 13:27:47 -07:00
@property(nonatomic, readwrite) BOOL limitSet;
@property(nonatomic, readwrite) NSInteger limit;
2017-05-15 12:27:07 -07:00
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong, readwrite) NSString *viewFrom;
2017-05-15 12:27:07 -07:00
/**
2019-08-05 13:27:47 -07:00
* indexStartValue is anything you can store as a priority / value.
*/
@property(nonatomic, strong, readwrite) id<FNode> indexStartValue;
@property(nonatomic, strong, readwrite) NSString *indexStartKey;
2017-05-15 12:27:07 -07:00
/**
2019-08-05 13:27:47 -07:00
* indexStartValue is anything you can store as a priority / value.
*/
@property(nonatomic, strong, readwrite) id<FNode> indexEndValue;
@property(nonatomic, strong, readwrite) NSString *indexEndKey;
2017-05-15 12:27:07 -07:00
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong, readwrite) id<FIndex> index;
2017-05-15 12:27:07 -07:00
@end
@implementation FQueryParams
2019-08-05 13:27:47 -07:00
+ (FQueryParams *)defaultInstance {
2017-05-15 12:27:07 -07:00
static FQueryParams *defaultParams = nil;
static dispatch_once_t defaultParamsToken;
dispatch_once(&defaultParamsToken, ^{
2019-08-05 13:27:47 -07:00
defaultParams = [[FQueryParams alloc] init];
2017-05-15 12:27:07 -07:00
});
return defaultParams;
}
- (id)init {
self = [super init];
if (self) {
self->_limitSet = NO;
self->_limit = 0;
self->_viewFrom = nil;
self->_indexStartValue = nil;
self->_indexStartKey = nil;
self->_indexEndValue = nil;
self->_indexEndKey = nil;
self->_index = [FPriorityIndex priorityIndex];
}
return self;
}
/**
2019-08-05 13:27:47 -07:00
* Only valid if hasStart is true
*/
- (id)indexStartValue {
2017-05-15 12:27:07 -07:00
NSAssert([self hasStart], @"Only valid if start has been set");
return _indexStartValue;
}
/**
2019-08-05 13:27:47 -07:00
* Only valid if hasStart is true.
* @return The starting key name for the range defined by these query parameters
*/
- (NSString *)indexStartKey {
2017-05-15 12:27:07 -07:00
NSAssert([self hasStart], @"Only valid if start has been set");
if (_indexStartKey == nil) {
return [FUtilities minName];
} else {
return _indexStartKey;
}
}
/**
2019-08-05 13:27:47 -07:00
* Only valid if hasEnd is true.
*/
- (id)indexEndValue {
2017-05-15 12:27:07 -07:00
NSAssert([self hasEnd], @"Only valid if end has been set");
return _indexEndValue;
}
/**
2019-08-05 13:27:47 -07:00
* Only valid if hasEnd is true.
* @return The end key name for the range defined by these query parameters
*/
- (NSString *)indexEndKey {
2017-05-15 12:27:07 -07:00
NSAssert([self hasEnd], @"Only valid if end has been set");
if (_indexEndKey == nil) {
return [FUtilities maxName];
} else {
return _indexEndKey;
}
}
/**
2019-08-05 13:27:47 -07:00
* @return true if a limit has been set and has been explicitly anchored
*/
- (BOOL)hasAnchoredLimit {
2017-05-15 12:27:07 -07:00
return self.limitSet && self.viewFrom != nil;
}
/**
2019-08-05 13:27:47 -07:00
* Only valid to call if limitSet returns true
*/
- (NSInteger)limit {
2017-05-15 12:27:07 -07:00
NSAssert(self.limitSet, @"Only valid if limit has been set");
return _limit;
}
- (BOOL)hasStart {
return self->_indexStartValue != nil;
}
- (BOOL)hasEnd {
return self->_indexEndValue != nil;
}
2019-08-05 13:27:47 -07:00
- (id)copyWithZone:(NSZone *)zone {
2017-05-15 12:27:07 -07:00
// Immutable
return self;
}
2019-08-05 13:27:47 -07:00
- (id)mutableCopy {
FQueryParams *other = [[[self class] alloc] init];
2017-05-15 12:27:07 -07:00
// Maybe need to do extra copying here
other->_limitSet = _limitSet;
other->_limit = _limit;
other->_indexStartValue = _indexStartValue;
other->_indexStartKey = _indexStartKey;
other->_indexEndValue = _indexEndValue;
other->_indexEndKey = _indexEndKey;
other->_viewFrom = _viewFrom;
other->_index = _index;
return other;
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)limitTo:(NSInteger)newLimit {
2017-05-15 12:27:07 -07:00
FQueryParams *newParams = [self mutableCopy];
newParams->_limitSet = YES;
newParams->_limit = newLimit;
newParams->_viewFrom = nil;
return newParams;
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)limitToFirst:(NSInteger)newLimit {
2017-05-15 12:27:07 -07:00
FQueryParams *newParams = [self mutableCopy];
newParams->_limitSet = YES;
newParams->_limit = newLimit;
newParams->_viewFrom = kFQPViewFromLeft;
return newParams;
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)limitToLast:(NSInteger)newLimit {
2017-05-15 12:27:07 -07:00
FQueryParams *newParams = [self mutableCopy];
newParams->_limitSet = YES;
newParams->_limit = newLimit;
newParams->_viewFrom = kFQPViewFromRight;
return newParams;
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)startAt:(id<FNode>)indexValue childKey:(NSString *)key {
2017-05-15 12:27:07 -07:00
NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
FQueryParams *newParams = [self mutableCopy];
newParams->_indexStartValue = indexValue;
newParams->_indexStartKey = key;
return newParams;
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)startAt:(id<FNode>)indexValue {
2017-05-15 12:27:07 -07:00
return [self startAt:indexValue childKey:nil];
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)endAt:(id<FNode>)indexValue childKey:(NSString *)key {
2017-05-15 12:27:07 -07:00
NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
FQueryParams *newParams = [self mutableCopy];
newParams->_indexEndValue = indexValue;
newParams->_indexEndKey = key;
return newParams;
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)endAt:(id<FNode>)indexValue {
2017-05-15 12:27:07 -07:00
return [self endAt:indexValue childKey:nil];
}
2019-08-05 13:27:47 -07:00
- (FQueryParams *)orderBy:(id)newIndex {
2017-05-15 12:27:07 -07:00
FQueryParams *newParams = [self mutableCopy];
newParams->_index = newIndex;
return newParams;
}
2019-08-05 13:27:47 -07:00
- (NSDictionary *)wireProtocolParams {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
2017-05-15 12:27:07 -07:00
if ([self hasStart]) {
2019-08-05 13:27:47 -07:00
[dict setObject:[self.indexStartValue valForExport:YES]
forKey:kFQPIndexStartValue];
2017-05-15 12:27:07 -07:00
// Don't use property as it will be [MIN-NAME]
if (self->_indexStartKey != nil) {
[dict setObject:self->_indexStartKey forKey:kFQPIndexStartName];
}
}
if ([self hasEnd]) {
2019-08-05 13:27:47 -07:00
[dict setObject:[self.indexEndValue valForExport:YES]
forKey:kFQPIndexEndValue];
2017-05-15 12:27:07 -07:00
// Don't use property as it will be [MAX-NAME]
if (self->_indexEndKey != nil) {
[dict setObject:self->_indexEndKey forKey:kFQPIndexEndName];
}
}
if (self.limitSet) {
2019-08-05 13:27:47 -07:00
[dict setObject:[NSNumber numberWithInteger:self.limit]
forKey:kFQPLimit];
2017-05-15 12:27:07 -07:00
NSString *vf = self.viewFrom;
if (vf == nil) {
// limit() rather than limitToFirst or limitToLast was called.
// This means that only one of startSet or endSet is true. Use them
2019-08-05 13:27:47 -07:00
// to calculate which side of the view to anchor to. If neither is
// set, Anchor to end
2017-05-15 12:27:07 -07:00
if ([self hasStart]) {
vf = kFQPViewFromLeft;
} else {
vf = kFQPViewFromRight;
}
}
[dict setObject:vf forKey:kFQPViewFrom];
}
2019-08-05 13:27:47 -07:00
// For now, priority index is the default, so we only specify if it's some
// other index.
2017-05-15 12:27:07 -07:00
if (![self.index isEqual:[FPriorityIndex priorityIndex]]) {
[dict setObject:[self.index queryDefinition] forKey:kFQPIndex];
}
return dict;
}
+ (FQueryParams *)fromQueryObject:(NSDictionary *)dict {
if (dict.count == 0) {
return [FQueryParams defaultInstance];
}
FQueryParams *params = [[FQueryParams alloc] init];
if (dict[kFQPLimit] != nil) {
params->_limitSet = YES;
params->_limit = [dict[kFQPLimit] integerValue];
}
if (dict[kFQPIndexStartValue] != nil) {
2019-08-05 13:27:47 -07:00
params->_indexStartValue =
[FSnapshotUtilities nodeFrom:dict[kFQPIndexStartValue]];
2017-05-15 12:27:07 -07:00
if (dict[kFQPIndexStartName] != nil) {
params->_indexStartKey = dict[kFQPIndexStartName];
}
}
if (dict[kFQPIndexEndValue] != nil) {
2019-08-05 13:27:47 -07:00
params->_indexEndValue =
[FSnapshotUtilities nodeFrom:dict[kFQPIndexEndValue]];
2017-05-15 12:27:07 -07:00
if (dict[kFQPIndexEndName] != nil) {
params->_indexEndKey = dict[kFQPIndexEndName];
}
}
if (dict[kFQPViewFrom] != nil) {
NSString *viewFrom = dict[kFQPViewFrom];
2019-08-05 13:27:47 -07:00
if (![viewFrom isEqualToString:kFQPViewFromLeft] &&
![viewFrom isEqualToString:kFQPViewFromRight]) {
[NSException raise:NSInvalidArgumentException
format:@"Unknown view from parameter: %@", viewFrom];
2017-05-15 12:27:07 -07:00
}
params->_viewFrom = viewFrom;
}
NSString *index = dict[kFQPIndex];
if (index != nil) {
params->_index = [FIndex indexFromQueryDefinition:index];
}
return params;
}
2019-08-05 13:27:47 -07:00
- (BOOL)isViewFromLeft {
2017-05-15 12:27:07 -07:00
if (self.viewFrom != nil) {
// Not null, we can just check
return [self.viewFrom isEqualToString:kFQPViewFromLeft];
} else {
// If start is set, it's view from left. Otherwise not.
return self.hasStart;
}
}
2019-08-05 13:27:47 -07:00
- (id<FNodeFilter>)nodeFilter {
2017-05-15 12:27:07 -07:00
if (self.loadsAllData) {
return [[FIndexedFilter alloc] initWithIndex:self.index];
} else if (self.limitSet) {
return [[FLimitedFilter alloc] initWithQueryParams:self];
} else {
return [[FRangedFilter alloc] initWithQueryParams:self];
}
}
2019-08-05 13:27:47 -07:00
- (BOOL)isValid {
return !(self.hasStart && self.hasEnd && self.limitSet &&
!self.hasAnchoredLimit);
2017-05-15 12:27:07 -07:00
}
2019-08-05 13:27:47 -07:00
- (BOOL)loadsAllData {
2017-05-15 12:27:07 -07:00
return !(self.hasStart || self.hasEnd || self.limitSet);
}
2019-08-05 13:27:47 -07:00
- (BOOL)isDefault {
return [self loadsAllData] &&
[self.index isEqual:[FPriorityIndex priorityIndex]];
2017-05-15 12:27:07 -07:00
}
2019-08-05 13:27:47 -07:00
- (NSString *)description {
2017-05-15 12:27:07 -07:00
return [[self wireProtocolParams] description];
}
2019-08-05 13:27:47 -07:00
- (BOOL)isEqual:(id)obj {
2017-05-15 12:27:07 -07:00
if (self == obj) {
return YES;
}
if (![obj isKindOfClass:[self class]]) {
return NO;
}
FQueryParams *other = (FQueryParams *)obj;
2019-08-05 13:27:47 -07:00
if (self->_limitSet != other->_limitSet)
return NO;
if (self->_limit != other->_limit)
return NO;
2020-06-26 14:34:27 -07:00
if ((self->_index != other->_index) &&
![self->_index isEqual:other->_index])
2019-08-05 13:27:47 -07:00
return NO;
if ((self->_indexStartKey != other->_indexStartKey) &&
![self->_indexStartKey isEqualToString:other->_indexStartKey])
return NO;
if ((self->_indexStartValue != other->_indexStartValue) &&
![self->_indexStartValue isEqual:other->_indexStartValue])
return NO;
if ((self->_indexEndKey != other->_indexEndKey) &&
![self->_indexEndKey isEqualToString:other->_indexEndKey])
return NO;
if ((self->_indexEndValue != other->_indexEndValue) &&
![self->_indexEndValue isEqual:other->_indexEndValue])
return NO;
if ([self isViewFromLeft] != [other isViewFromLeft])
return NO;
2017-05-15 12:27:07 -07:00
return YES;
}
2019-08-05 13:27:47 -07:00
- (NSUInteger)hash {
2017-05-15 12:27:07 -07:00
NSUInteger result = _limitSet ? _limit : 0;
result = 31 * result + ([self isViewFromLeft] ? 1231 : 1237);
result = 31 * result + [_indexStartKey hash];
result = 31 * result + [_indexStartValue hash];
result = 31 * result + [_indexEndKey hash];
result = 31 * result + [_indexEndValue hash];
result = 31 * result + [_index hash];
return result;
}
@end