Skip to content

Commit

Permalink
Cleanup and removal of brain damage.
Browse files Browse the repository at this point in the history
  • Loading branch information
objectiveous committed Mar 10, 2009
1 parent c8d2eb5 commit 25ab001
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 125 deletions.
53 changes: 32 additions & 21 deletions Classes/SBCouchEnumerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,43 @@

@interface SBCouchEnumerator : NSEnumerator {
//@public
NSInteger totalRows;

/*!
totalRows is the number of items in a view. It does not account for start/end key "filters"
that get applied. This means, that you can have instance of SBCouchEnumerator with 10,000 totalRows
but only 2 json objects in the rows array.
Using a reduce function may also complicate matters.
*/
NSInteger totalRows;
NSInteger offset;

//@protected
NSInteger batchSize;
NSString *startKey;
SBCouchDatabase *couchDatabase;
NSString *viewPath;
SBCouchView *couchView;
NSMutableArray *rows;
NSInteger currentIndex;
NSString *viewName;

// This array will need to be managed somehow in order to prevent it from
// chewing up loads of memory when paginating through a huge database
NSInteger currentIndex;
NSInteger sizeOfLastFetch;
// These are used to create query params. Might be a good idea to
// to move them into a SBCouchQueryOptions. The rational is that the view name is the
// resource, not its p

SBCouchQueryOptions *queryOptions;
}

@property NSInteger totalRows;
@property NSInteger batchSize;
@property (retain) NSString *startKey;
@property (retain) SBCouchDatabase *couchDatabase;
@property (retain) NSString *viewPath;
@property (retain) NSMutableArray *rows;
@property NSInteger currentIndex;
@property (copy) NSString *viewName;

-(id)initWithBatchesOf:(NSInteger)count database:(SBCouchDatabase*)database view:(NSString*)view;
-(id)initWithBatchesOf:(NSInteger)count database:(SBCouchDatabase*)database couchView:(SBCouchView*)couchView;
@property NSInteger totalRows;
@property NSInteger offset;
@property (retain) SBCouchView *couchView;
@property (retain) NSMutableArray *rows;

/*! We take a copy of the views query options because we will be mutating
it as we paginate a view. In other words, we're going to be changing
the startkey and endkey.
*/
@property (copy) SBCouchQueryOptions *queryOptions;
@property NSInteger currentIndex;
@property NSInteger sizeOfLastFetch;

-(id)initWithView:(SBCouchView*)couchView;

#pragma mark Abstract Methods from NSEnumerator
-(id)nextObject;
Expand Down
169 changes: 65 additions & 104 deletions Classes/SBCouchEnumerator.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,92 +12,31 @@

@implementation SBCouchEnumerator

@synthesize couchDatabase;
@synthesize couchView;
@synthesize totalRows;
@synthesize batchSize;
@synthesize viewPath;
@synthesize offset;
@synthesize rows;
@synthesize currentIndex;
@synthesize startKey;
@synthesize viewName;
@synthesize queryOptions;
@synthesize sizeOfLastFetch;


// XXX This is so horrible I can barley stand to look at it. Calls to the database ought to live in the database class.
// use a callback or something if necissary.
-(id)initWithBatchesOf:(NSInteger)count database:(SBCouchDatabase*)database couchView:(SBCouchView*)couchView{
NSString *tempView = [couchView JSONRepresentation];
NSData *body = [tempView dataUsingEncoding:NSUTF8StringEncoding];
SBCouchServer *server = [database couchServer];

NSString *urlString = [NSString stringWithFormat:@"http://%@:%u/%@/%@", server.host, server.port, couchView.couchDatabase, @"_temp_view?limit=10&group=true"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

[request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:body];
[request setHTTPMethod:@"POST"];

NSError *error;
NSHTTPURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSLog(@"status code %i", [response statusCode]);
NSLog(@"headers %@", [[response allHeaderFields] JSONRepresentation]);

NSDictionary *etf;
if (200 == [response statusCode]) {
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
etf = [json JSONValue];
NSLog(@"--> %@", [etf JSONRepresentation]);
}
-(id)initWithView:(SBCouchView*)aCouchView{

self = [super init];
if(self != nil){
[self setViewName:couchView.name];
[self setCurrentIndex:0];

[self setBatchSize:-1];
[self setCouchDatabase:database];
[self setViewPath:couchView.name];
if(etf != nil){
NSArray *arrayOfRows = [etf objectForKey:@"rows"];
[self setTotalRows:[arrayOfRows count]];
[self setRows:[etf objectForKey:@"rows"]];
}
// Setting the currentIndex to -1 is used to indicate that we don't have an index yet.
self.currentIndex = -1;
self.couchView = aCouchView;
// take a copy of the queryOptions for purposes of pagination.
self.queryOptions = aCouchView.queryOptions;
self.rows = [NSMutableArray arrayWithCapacity:10];
}
return self;
return self;
}
// a count 0f 0 or less means fetch everything. That is, use not limit expression
-(id)initWithBatchesOf:(NSInteger)count database:(SBCouchDatabase*)database view:(NSString*)view{
NSString *url;
if(count > 0)
url = [NSString stringWithFormat:@"%@?limit=%i&group=true", view, count];
else
url = view;


NSDictionary *etf = [database get:url];

self = [super init];
if(self != nil){
[self setViewName:view];
[self setCurrentIndex:0];
[self setTotalRows:[[etf objectForKey:@"total_rows"] integerValue]]; //{"total_rows":7,"offset":3 TODO need to subtract the offset
[self setBatchSize:count];
[self setCouchDatabase:database];
[self setViewPath:view];
[self setRows:[etf objectForKey:@"rows"]];
}
return self;
}



-(void) dealloc{
[startKey release], startKey = nil;
[viewPath release], viewName = nil;
[couchDatabase release];
[self.rows release];
[super dealloc];

}
Expand All @@ -111,63 +50,85 @@ -(id)itemAtIndex:(NSInteger)idx{
[self fetchNextPage];
if( [self itemAtIndex:idx]){
// TODO might want to autorelase this
SBCouchDocument *doc = [[SBCouchDocument alloc] initWithNSDictionary:[rows objectAtIndex:idx] couchDatabase:self.couchDatabase];
doc.couchDatabase = self.couchDatabase;
SBCouchDocument *doc = [[SBCouchDocument alloc] initWithNSDictionary:[rows objectAtIndex:idx] couchDatabase:self.couchView.couchDatabase];
return doc;
}else{
return nil;
}
}
// TODO Might want to autorelease this.
SBCouchDocument *doc = [[SBCouchDocument alloc] initWithNSDictionary:[rows objectAtIndex:idx] couchDatabase:self.couchDatabase];
doc.couchDatabase = self.couchDatabase;

SBCouchDocument *doc = [[SBCouchDocument alloc] initWithNSDictionary:[rows objectAtIndex:idx] couchDatabase:self.couchView.couchDatabase];
return doc;
}
- (id)nextObject{
if( (currentIndex >= [rows count]) && [rows count] < totalRows){

-(BOOL)shouldFetchNextBatch{
if(self.currentIndex == -1)
return YES;

// if the index is >= to the number of rows we can fetch more,
// but if the size of the last fetch was larger than the batch size (i.e limit)
//
// The default for limit is zero and sizeOfLastFetch is set to -1 when
if(currentIndex >= [rows count] && self.sizeOfLastFetch >= self.queryOptions.limit)
return YES;

[self setStartKey:[[rows lastObject] objectForKey:@"id"]];
return NO;
}
- (id)nextObject{
// At some point lastObjectsID will
if([self shouldFetchNextBatch]){
//[self setStartKey:[[rows lastObject] objectForKey:@"id"]];
NSString *lastObjectsID = [[self.rows lastObject] objectForKey:@"id"];
// The first time through, we won't have any rows
if(lastObjectsID)
self.queryOptions.startkey = lastObjectsID;

[self fetchNextPage];
}

// If the call to fetchNextPage did not expand the number of rows to a number
// greater than currentIndex
if(currentIndex >= [rows count]){
// free up the rows array
[rows release], rows = nil;
//[rows release], rows = nil;
return nil;
}


id object = [rows objectAtIndex:currentIndex];


[self setCurrentIndex:[self currentIndex] +1 ];
// TODO might want to autorelease this.
SBCouchDocument *doc = [[SBCouchDocument alloc] initWithNSDictionary:object couchDatabase:self.couchDatabase];
// We do this because calls to _all_docs do not have a _id property and because
// the dictionary we are working with at this point does not have an _id but it
// does represent an actual document (for example a design doc) that we may want
// to interact with.
SBCouchDocument *doc = [[SBCouchDocument alloc] initWithNSDictionary:object couchDatabase:self.couchView.couchDatabase];
// XXX Is this a proper identity?
doc.identity = [doc objectForKey:@"id"];
return doc;
}
- (NSArray *)allObjects{
return nil;
}

-(void)fetchNextPage{
NSMutableString *viewUrl = [NSMutableString stringWithFormat:@"%@?", [self viewName]];

[viewUrl appendFormat:@"startkey=\"%@\"&", [self startKey]];
[viewUrl appendFormat:@"startkey_docid=\"%@\"&", [self startKey]];
[viewUrl appendString:@"skip=1&"];
[viewUrl appendFormat:@"limit=%i&",[self batchSize]];
[viewUrl appendString:@"group=true"];

STIGDebug(@"Using URL [%@]", [viewUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] );
NSDictionary *etf = [[self couchDatabase] get:[viewUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
-(void)fetchNextPage{
// contruct a new URL using our own copy of the query options
NSString *contructedUrl = [NSString stringWithFormat:@"%@?%@", self.couchView.name, [self.queryOptions queryString]];
//NSString *viewUrl = [self.couchView urlString];
NSDictionary *etf = [self.couchView.couchDatabase get:contructedUrl];

// If this is our first attempt at a fetch, we need to initialize the currentIndex
if(self.currentIndex == -1 ){
self.totalRows = [[etf objectForKey:@"total_rows"] integerValue];
self.offset = [[etf objectForKey:@"offset"] integerValue];
self.currentIndex = 0;
// Since this is not our first fetch, set the skip value to 1
// XXX This should be moved someplace where its only ever called
// once. No need to set this on every fetch.
self.queryOptions.skip=1;
}

[rows addObjectsFromArray:[etf objectForKey:@"rows"]];
NSArray *newRows = [etf objectForKey:@"rows"];
[rows addObjectsFromArray:newRows];
if([newRows count] <= 0){
self.sizeOfLastFetch = -1;
}else{
self.sizeOfLastFetch = [newRows count];
}
}

@end

0 comments on commit 25ab001

Please sign in to comment.