Browse files

Add some support for filters

* -changesSinceSequence: now takes an optional filter block. This is not yet hooked up to the _changes feed implementation.
* TDPusher also takes a filter block to filter what's pushed.
* TDPuller takes a filter function _name_ that's sent to the remote database's _changes feed. (Parameters aren't supported yet.)
  • Loading branch information...
1 parent 95c7e30 commit e6b78ebd875c52307cdeefd58af99ed32aa4396c @snej snej committed Jan 1, 2012
View
3 Source/ChangeTracker/TDChangeTracker.h
@@ -40,6 +40,7 @@ typedef enum TDChangeTrackerMode {
id<TDChangeTrackerClient> _client;
TDChangeTrackerMode _mode;
NSUInteger _lastSequenceNumber;
+ NSString* _filterName;
}
- (id)initWithDatabaseURL: (NSURL*)databaseURL
@@ -53,6 +54,8 @@ typedef enum TDChangeTrackerMode {
@property (readonly, nonatomic) NSUInteger lastSequenceNumber;
@property (nonatomic, assign) id<TDChangeTrackerClient> client;
+@property (copy) NSString* filterName;
+
- (BOOL) start;
- (void) stop;
View
12 Source/ChangeTracker/TDChangeTracker.m
@@ -23,7 +23,7 @@
@implementation TDChangeTracker
@synthesize lastSequenceNumber=_lastSequenceNumber, databaseURL=_databaseURL, mode=_mode;
-@synthesize client=_client;
+@synthesize client=_client, filterName=_filterName;
- (id)initWithDatabaseURL: (NSURL*)databaseURL
mode: (TDChangeTrackerMode)mode
@@ -63,9 +63,13 @@ - (NSString*) databaseName {
- (NSString*) changesFeedPath {
static NSString* const kModeNames[3] = {@"normal", @"longpoll", @"continuous"};
- return [NSString stringWithFormat: @"_changes?feed=%@&heartbeat=300000&since=%u",
- kModeNames[_mode],
- _lastSequenceNumber];
+ NSMutableString* path;
+ path = [NSMutableString stringWithFormat: @"_changes?feed=%@&heartbeat=300000&since=%u",
+ kModeNames[_mode],
+ _lastSequenceNumber];
+ if (_filterName)
+ [path appendFormat: @"&filter=%@", _filterName];
+ return path;
}
- (NSURL*) changesFeedURL {
View
6 Source/TDDatabase.h
@@ -26,6 +26,9 @@ extern NSString* const TDDatabaseChangeNotification;
typedef BOOL (^TDValidationBlock) (TDRevision* newRevision,
id<TDValidationContext> context);
+/** Filter block, used in changes feeds and replication. */
+typedef BOOL (^TDFilterBlock) (TDRevision* revision);
+
/** A TouchDB database. */
@interface TDDatabase : NSObject
@@ -90,7 +93,8 @@ typedef BOOL (^TDValidationBlock) (TDRevision* newRevision,
@property (readonly) NSArray* allViews;
- (TDRevisionList*) changesSinceSequence: (SequenceNumber)lastSequence
- options: (const struct TDQueryOptions*)options;
+ options: (const struct TDQueryOptions*)options
+ filter: (TDFilterBlock)filter;
@end
View
23 Source/TDDatabase.m
@@ -491,14 +491,17 @@ - (NSArray*) getRevisionHistory: (TDRevision*)rev {
- (TDRevisionList*) changesSinceSequence: (SequenceNumber)lastSequence
options: (const TDQueryOptions*)options
+ filter: (TDFilterBlock)filter
{
if (!options) options = &kDefaultTDQueryOptions;
-
- FMResultSet* r = [_fmdb executeQuery: @"SELECT sequence, docid, revid, deleted FROM revs, docs "
- "WHERE sequence > ? AND current=1 "
- "AND revs.doc_id = docs.doc_id "
- "ORDER BY sequence LIMIT ?",
- $object(lastSequence), $object(options->limit)];
+ BOOL includeDocs = options->includeDocs || (filter != NULL);
+
+ NSString* sql = $sprintf(@"SELECT sequence, docid, revid, deleted %@ FROM revs, docs "
+ "WHERE sequence > ? AND current=1 "
+ "AND revs.doc_id = docs.doc_id "
+ "ORDER BY sequence LIMIT ?",
+ (includeDocs ? @", json" : @""));
+ FMResultSet* r = [_fmdb executeQuery: sql, $object(lastSequence), $object(options->limit)];
if (!r)
return nil;
TDRevisionList* changes = [[[TDRevisionList alloc] init] autorelease];
@@ -507,7 +510,13 @@ - (TDRevisionList*) changesSinceSequence: (SequenceNumber)lastSequence
revID: [r stringForColumnIndex: 2]
deleted: [r boolForColumnIndex: 3]];
rev.sequence = [r longLongIntForColumnIndex: 0];
- [changes addRev: rev];
+ if (includeDocs) {
+ [self expandStoredJSON: [r dataForColumnIndex: 4]
+ intoRevision: rev
+ withAttachments: NO];
+ }
+ if (!filter || filter(rev))
+ [changes addRev: rev];
[rev release];
}
[r close];
View
18 Source/TDDatabase_Tests.m
@@ -87,6 +87,21 @@
CAssertNil([db putRevision: rev2Input prevRevisionID: rev1.revID status: &status]);
CAssertEq(status, 409);
+ // Check the changes feed, with and without filters:
+ TDRevisionList* changes = [db changesSinceSequence: 0 options: NULL filter: NULL];
+ Log(@"Changes = %@", changes);
+ CAssertEq(changes.count, 1u);
+
+ changes = [db changesSinceSequence: 0 options: NULL filter:^BOOL(TDRevision *revision) {
+ return [[revision.properties objectForKey: @"status"] isEqual: @"updated!"];
+ }];
+ CAssertEq(changes.count, 1u);
+
+ changes = [db changesSinceSequence: 0 options: NULL filter:^BOOL(TDRevision *revision) {
+ return [[revision.properties objectForKey: @"status"] isEqual: @"not updated!"];
+ }];
+ CAssertEq(changes.count, 0u);
+
// Delete it:
TDRevision* revD = [[[TDRevision alloc] initWithDocID: rev2.docID revID: nil deleted: YES] autorelease];
revD = [db putRevision: revD prevRevisionID: rev2.revID status: &status];
@@ -98,7 +113,8 @@
readRev = [db getDocumentWithID: revD.docID revisionID: nil withAttachments: NO];
CAssertNil(readRev);
- TDRevisionList* changes = [db changesSinceSequence: 0 options: NULL];
+ // Check the changes feed again after the deletion:
+ changes = [db changesSinceSequence: 0 options: NULL filter: NULL];
Log(@"Changes = %@", changes);
CAssertEq(changes.count, 1u);
View
3 Source/TDPuller.h
@@ -19,6 +19,9 @@
NSMutableArray* _revsToPull;
NSUInteger _httpConnectionCount;
TDBatcher* _revsToInsert;
+ NSString* _filterName;
}
+@property (copy) NSString* filterName;
+
@end
View
5 Source/TDPuller.m
@@ -38,11 +38,15 @@ - (NSArray*) knownCurrentRevIDsOf: (TDRevision*)rev;
@implementation TDPuller
+@synthesize filterName=_filterName;
+
+
- (void)dealloc {
[_changeTracker stop];
[_changeTracker release];
[_revsToPull release];
[_revsToInsert release];
+ [_filterName release];
[super dealloc];
}
@@ -67,6 +71,7 @@ - (void) start {
mode: (_continuous ? kLongPoll :kOneShot)
lastSequence: [_lastSequence intValue]
client: self];
+ _changeTracker.filterName = _filterName;
[_changeTracker start];
[self asyncTaskStarted];
}
View
6 Source/TDPusher.h
@@ -7,9 +7,15 @@
//
#import "TDPuller.h"
+#import "TDDatabase.h"
/** Replicator that pushes to a remote CouchDB. */
@interface TDPusher : TDReplicator
+{
+ TDFilterBlock _filter;
+}
+
+@property (copy) TDFilterBlock filter;
@end
View
17 Source/TDPusher.m
@@ -25,6 +25,15 @@
@implementation TDPusher
+@synthesize filter=_filter;
+
+
+- (void)dealloc {
+ [_filter release];
+ [super dealloc];
+}
+
+
- (BOOL) isPush {
return YES;
}
@@ -37,7 +46,7 @@ - (void) start {
// Process existing changes since the last push:
TDRevisionList* changes = [_db changesSinceSequence: [_lastSequence longLongValue]
- options: nil];
+ options: nil filter: _filter];
if (changes.count > 0)
[self processInbox: changes];
@@ -55,10 +64,12 @@ - (void) stop {
- (void) dbChanged: (NSNotification*)n {
NSDictionary* userInfo = n.userInfo;
- // Skip revisions that came from the database I'm syncing to:
+ // Skip revisions that originally came from the database I'm syncing to:
if ([[userInfo objectForKey: @"source"] isEqual: _remote])
return;
- [self addToInbox: [userInfo objectForKey: @"rev"]];
+ TDRevision* rev = [userInfo objectForKey: @"rev"];
+ if (!_filter || _filter(rev))
+ [self addToInbox: rev];
}
View
2 Source/TDRouter.m
@@ -474,7 +474,7 @@ - (TDStatus) do_GET_changes: (TDDatabase*)db {
return 400;
int since = [[self query: @"since"] intValue];
- TDRevisionList* changes = [db changesSinceSequence: since options: &options];
+ TDRevisionList* changes = [db changesSinceSequence: since options: &options filter: NULL];
if (!changes)
return 500;
View
4 Source/TDView.m
@@ -25,8 +25,8 @@
const TDQueryOptions kDefaultTDQueryOptions = {
- nil, nil, 0,
- UINT_MAX, 0,
+ nil, nil,
+ 0, UINT_MAX, 0,
NO, NO, NO, YES, NO, NO
};

0 comments on commit e6b78eb

Please sign in to comment.