373 lines
11 KiB
Objective-C
373 lines
11 KiB
Objective-C
/*
|
|
* 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 "FQueryParams.h"
|
|
#import "FValidation.h"
|
|
#import "FConstants.h"
|
|
#import "FIndex.h"
|
|
#import "FPriorityIndex.h"
|
|
#import "FUtilities.h"
|
|
#import "FNodeFilter.h"
|
|
#import "FIndexedFilter.h"
|
|
#import "FLimitedFilter.h"
|
|
#import "FRangedFilter.h"
|
|
#import "FNode.h"
|
|
#import "FSnapshotUtilities.h"
|
|
|
|
@interface FQueryParams ()
|
|
|
|
@property (nonatomic, readwrite) BOOL limitSet;
|
|
@property (nonatomic, readwrite) NSInteger limit;
|
|
|
|
@property (nonatomic, strong, readwrite) NSString *viewFrom;
|
|
/**
|
|
* indexStartValue is anything you can store as a priority / value.
|
|
*/
|
|
@property (nonatomic, strong, readwrite) id<FNode> indexStartValue;
|
|
@property (nonatomic, strong, readwrite) NSString *indexStartKey;
|
|
/**
|
|
* indexStartValue is anything you can store as a priority / value.
|
|
*/
|
|
@property (nonatomic, strong, readwrite) id<FNode> indexEndValue;
|
|
@property (nonatomic, strong, readwrite) NSString *indexEndKey;
|
|
|
|
@property (nonatomic, strong, readwrite) id<FIndex> index;
|
|
|
|
@end
|
|
|
|
@implementation FQueryParams
|
|
|
|
+ (FQueryParams *) defaultInstance {
|
|
static FQueryParams *defaultParams = nil;
|
|
static dispatch_once_t defaultParamsToken;
|
|
dispatch_once(&defaultParamsToken, ^{
|
|
defaultParams = [[FQueryParams alloc] init];
|
|
});
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Only valid if hasStart is true
|
|
*/
|
|
- (id) indexStartValue {
|
|
NSAssert([self hasStart], @"Only valid if start has been set");
|
|
return _indexStartValue;
|
|
}
|
|
|
|
/**
|
|
* Only valid if hasStart is true.
|
|
* @return The starting key name for the range defined by these query parameters
|
|
*/
|
|
- (NSString *) indexStartKey {
|
|
NSAssert([self hasStart], @"Only valid if start has been set");
|
|
if (_indexStartKey == nil) {
|
|
return [FUtilities minName];
|
|
} else {
|
|
return _indexStartKey;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Only valid if hasEnd is true.
|
|
*/
|
|
- (id) indexEndValue {
|
|
NSAssert([self hasEnd], @"Only valid if end has been set");
|
|
return _indexEndValue;
|
|
}
|
|
|
|
/**
|
|
* Only valid if hasEnd is true.
|
|
* @return The end key name for the range defined by these query parameters
|
|
*/
|
|
- (NSString *) indexEndKey {
|
|
NSAssert([self hasEnd], @"Only valid if end has been set");
|
|
if (_indexEndKey == nil) {
|
|
return [FUtilities maxName];
|
|
} else {
|
|
return _indexEndKey;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return true if a limit has been set and has been explicitly anchored
|
|
*/
|
|
- (BOOL) hasAnchoredLimit {
|
|
return self.limitSet && self.viewFrom != nil;
|
|
}
|
|
|
|
/**
|
|
* Only valid to call if limitSet returns true
|
|
*/
|
|
- (NSInteger) limit {
|
|
NSAssert(self.limitSet, @"Only valid if limit has been set");
|
|
return _limit;
|
|
}
|
|
|
|
- (BOOL)hasStart {
|
|
return self->_indexStartValue != nil;
|
|
}
|
|
|
|
- (BOOL)hasEnd {
|
|
return self->_indexEndValue != nil;
|
|
}
|
|
|
|
- (id) copyWithZone:(NSZone *)zone {
|
|
// Immutable
|
|
return self;
|
|
}
|
|
|
|
- (id) mutableCopy {
|
|
FQueryParams* other = [[[self class] alloc] init];
|
|
// 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;
|
|
}
|
|
|
|
- (FQueryParams *) limitTo:(NSInteger)newLimit {
|
|
FQueryParams *newParams = [self mutableCopy];
|
|
newParams->_limitSet = YES;
|
|
newParams->_limit = newLimit;
|
|
newParams->_viewFrom = nil;
|
|
return newParams;
|
|
}
|
|
|
|
- (FQueryParams *) limitToFirst:(NSInteger)newLimit {
|
|
FQueryParams *newParams = [self mutableCopy];
|
|
newParams->_limitSet = YES;
|
|
newParams->_limit = newLimit;
|
|
newParams->_viewFrom = kFQPViewFromLeft;
|
|
return newParams;
|
|
}
|
|
|
|
- (FQueryParams *) limitToLast:(NSInteger)newLimit {
|
|
FQueryParams *newParams = [self mutableCopy];
|
|
newParams->_limitSet = YES;
|
|
newParams->_limit = newLimit;
|
|
newParams->_viewFrom = kFQPViewFromRight;
|
|
return newParams;
|
|
}
|
|
|
|
- (FQueryParams *) startAt:(id<FNode>)indexValue childKey:(NSString *)key {
|
|
NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
|
|
FQueryParams *newParams = [self mutableCopy];
|
|
newParams->_indexStartValue = indexValue;
|
|
newParams->_indexStartKey = key;
|
|
return newParams;
|
|
}
|
|
|
|
- (FQueryParams *) startAt:(id<FNode>)indexValue {
|
|
return [self startAt:indexValue childKey:nil];
|
|
}
|
|
|
|
- (FQueryParams *) endAt:(id<FNode>)indexValue childKey:(NSString *)key {
|
|
NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
|
|
FQueryParams *newParams = [self mutableCopy];
|
|
newParams->_indexEndValue = indexValue;
|
|
newParams->_indexEndKey = key;
|
|
return newParams;
|
|
}
|
|
|
|
- (FQueryParams *) endAt:(id<FNode>)indexValue {
|
|
return [self endAt:indexValue childKey:nil];
|
|
}
|
|
|
|
- (FQueryParams *) orderBy:(id)newIndex {
|
|
FQueryParams *newParams = [self mutableCopy];
|
|
newParams->_index = newIndex;
|
|
return newParams;
|
|
}
|
|
|
|
- (NSDictionary *) wireProtocolParams {
|
|
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
|
|
if ([self hasStart]) {
|
|
[dict setObject:[self.indexStartValue valForExport:YES] forKey:kFQPIndexStartValue];
|
|
|
|
// Don't use property as it will be [MIN-NAME]
|
|
if (self->_indexStartKey != nil) {
|
|
[dict setObject:self->_indexStartKey forKey:kFQPIndexStartName];
|
|
}
|
|
}
|
|
|
|
if ([self hasEnd]) {
|
|
[dict setObject:[self.indexEndValue valForExport:YES] forKey:kFQPIndexEndValue];
|
|
|
|
// Don't use property as it will be [MAX-NAME]
|
|
if (self->_indexEndKey != nil) {
|
|
[dict setObject:self->_indexEndKey forKey:kFQPIndexEndName];
|
|
}
|
|
}
|
|
|
|
if (self.limitSet) {
|
|
[dict setObject:[NSNumber numberWithInteger:self.limit] forKey:kFQPLimit];
|
|
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
|
|
// to calculate which side of the view to anchor to. If neither is set,
|
|
// Anchor to end
|
|
if ([self hasStart]) {
|
|
vf = kFQPViewFromLeft;
|
|
} else {
|
|
vf = kFQPViewFromRight;
|
|
}
|
|
}
|
|
[dict setObject:vf forKey:kFQPViewFrom];
|
|
}
|
|
|
|
// For now, priority index is the default, so we only specify if it's some other index.
|
|
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) {
|
|
params->_indexStartValue = [FSnapshotUtilities nodeFrom:dict[kFQPIndexStartValue]];
|
|
if (dict[kFQPIndexStartName] != nil) {
|
|
params->_indexStartKey = dict[kFQPIndexStartName];
|
|
}
|
|
}
|
|
|
|
if (dict[kFQPIndexEndValue] != nil) {
|
|
params->_indexEndValue = [FSnapshotUtilities nodeFrom:dict[kFQPIndexEndValue]];
|
|
if (dict[kFQPIndexEndName] != nil) {
|
|
params->_indexEndKey = dict[kFQPIndexEndName];
|
|
}
|
|
}
|
|
|
|
if (dict[kFQPViewFrom] != nil) {
|
|
NSString *viewFrom = dict[kFQPViewFrom];
|
|
if (![viewFrom isEqualToString:kFQPViewFromLeft] && ![viewFrom isEqualToString:kFQPViewFromRight]) {
|
|
[NSException raise:NSInvalidArgumentException format:@"Unknown view from paramter: %@", viewFrom];
|
|
}
|
|
params->_viewFrom = viewFrom;
|
|
}
|
|
|
|
NSString *index = dict[kFQPIndex];
|
|
if (index != nil) {
|
|
params->_index = [FIndex indexFromQueryDefinition:index];
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
- (BOOL) isViewFromLeft {
|
|
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;
|
|
}
|
|
}
|
|
|
|
- (id<FNodeFilter>) nodeFilter {
|
|
if (self.loadsAllData) {
|
|
return [[FIndexedFilter alloc] initWithIndex:self.index];
|
|
} else if (self.limitSet) {
|
|
return [[FLimitedFilter alloc] initWithQueryParams:self];
|
|
} else {
|
|
return [[FRangedFilter alloc] initWithQueryParams:self];
|
|
}
|
|
}
|
|
|
|
|
|
- (BOOL) isValid {
|
|
return !(self.hasStart && self.hasEnd && self.limitSet && !self.hasAnchoredLimit);
|
|
}
|
|
|
|
- (BOOL) loadsAllData {
|
|
return !(self.hasStart || self.hasEnd || self.limitSet);
|
|
}
|
|
|
|
- (BOOL) isDefault {
|
|
return [self loadsAllData] && [self.index isEqual:[FPriorityIndex priorityIndex]];
|
|
}
|
|
|
|
- (NSString *) description {
|
|
return [[self wireProtocolParams] description];
|
|
}
|
|
|
|
- (BOOL) isEqual:(id)obj {
|
|
if (self == obj) {
|
|
return YES;
|
|
}
|
|
if (![obj isKindOfClass:[self class]]) {
|
|
return NO;
|
|
}
|
|
FQueryParams *other = (FQueryParams *)obj;
|
|
if (self->_limitSet != other->_limitSet) return NO;
|
|
if (self->_limit != other->_limit) return NO;
|
|
if ((self->_index != other->_index) && ![self->_index isEqual:other->_index]) 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;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSUInteger) hash {
|
|
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
|