#include "LCHits.h" #include "LCSearcher.h" #include "LCFilter.h" #include "LCSort.h" #include "LCQuery.h" #include "LCTopDocs.h" #include "LCScoreDoc.h" #include "LCHitIterator.h" #include "LCDocument.h" #include "GNUstep.h" @interface LCHits (LCPrivate) - (void) moreDocuments: (int) min; - (LCHitDocument *) hitDocument: (int) n; - (void) addToFront: (LCHitDocument *) hitDoc; - (void) remove: (LCHitDocument *) hitDoc; @end @implementation LCHits - (id) init { self = [super init]; filter = nil; sort = nil; hitDocs = [[NSMutableArray alloc] init]; numDocs = 0; maxDocs = 200; return self; } - (void) dealloc { DESTROY(hitDocs); DESTROY(weight); DESTROY(searcher); DESTROY(filter); DESTROY(sort); DESTROY(first); DESTROY(last); [super dealloc]; } - (id) initWithSearcher: (LCSearcher *) s query: (LCQuery *) q filter: (LCFilter *) f { self = [self init]; ASSIGN(weight, [q weight: s]); ASSIGN(searcher, s); ASSIGN(filter, f); [self moreDocuments: 50]; // retrieve 100 initially return self; } - (id) initWithSearcher: (LCSearcher *) s query: (LCQuery *) q filter: (LCFilter *) f sort: (LCSort *) o { self = [self init]; ASSIGN(weight, [q weight: s]); ASSIGN(searcher, s); ASSIGN(filter, f); ASSIGN(sort, o); [self moreDocuments: 50]; // retrieve 100 initially return self; } - (void) moreDocuments: (int) min { CREATE_AUTORELEASE_POOL(pool); if ([hitDocs count] > min) { min = [hitDocs count]; } int n = min * 2; // double # retrieved LCTopDocs *topDocs; if (sort) { topDocs = (LCTopDocs *)[searcher search: weight filter: filter maximum: n sort: sort]; } else { topDocs = [searcher search: weight filter: filter maximum: n]; } length = [topDocs totalHits]; NSArray * scoreDocs = [topDocs scoreDocuments]; float scoreNorm = 1.0f; if (length > 0 && [topDocs maxScore] > 1.0f) { scoreNorm = 1.0f / [topDocs maxScore]; } int end = ([scoreDocs count] < length) ? [scoreDocs count] : length; int i; for (i = [hitDocs count]; i < end; i++) { LCHitDocument *newDoc = [[LCHitDocument alloc] initWithScore: ([[scoreDocs objectAtIndex: i] score] * scoreNorm) identifier: [(LCScoreDoc *)[scoreDocs objectAtIndex: i] document]]; [hitDocs addObject: newDoc]; RELEASE(newDoc); } DESTROY(pool); } - (unsigned int) count { return length; } - (LCDocument *) document: (int) n { LCHitDocument *hitDoc = [self hitDocument: n]; RETAIN(hitDoc); // Update LRU cache of documents [self remove: hitDoc]; // remove from list, if there [self addToFront: hitDoc]; // add to front of list RELEASE(hitDoc); if (numDocs > maxDocs) { // if cache is full LCHitDocument *oldLast = last; [self remove: last]; // flush last [oldLast setDocument: nil]; // let doc get gc'd } if ([hitDoc document] == nil) { [hitDoc setDocument: [searcher document: [hitDoc identifier]]]; // cache miss: read document } return [hitDoc document]; } - (float) score: (int) n { return [[self hitDocument: n] score]; } - (int) identifier: (int) n { return [[self hitDocument: n] identifier]; } - (LCHitIterator *) iterator { return AUTORELEASE([[LCHitIterator alloc] initWithHits: self]); } - (LCHitDocument *) hitDocument: (int) n { if (n >= length) { NSLog(@"Not a valid hit number: %d", n); return nil; } if (n >= [hitDocs count]) { [self moreDocuments: n]; } return [hitDocs objectAtIndex: n]; } - (void) addToFront: (LCHitDocument *) hitDoc { if (first == nil) { ASSIGN(last, hitDoc); } else { [first setPrev: hitDoc]; } [hitDoc setNext: first]; ASSIGN(first, hitDoc); [hitDoc setPrev: nil]; numDocs++; } - (void) remove: (LCHitDocument *) hitDoc { if ([hitDoc document] == nil) { // it's not in the list return; // abort } if ([hitDoc next] == nil) { ASSIGN(last, [hitDoc prev]); } else { [[hitDoc next] setPrev: [hitDoc prev]]; } if ([hitDoc prev] == nil) { ASSIGN(first, [hitDoc next]); } else { [[hitDoc prev] setNext: [hitDoc next]]; } numDocs--; } @end @implementation LCHitDocument - (id) initWithScore: (float) s identifier: (int) i { self = [self init]; score = s; identifier = i; return self; } - (void) dealloc { DESTROY(prev); DESTROY(next); DESTROY(doc); [super dealloc]; } - (LCHitDocument *) prev { return prev; }; - (void) setPrev: (LCHitDocument *) d { if (d == nil) DESTROY(prev); else ASSIGN(prev, d); } - (LCHitDocument *) next { return next; }; - (void) setNext: (LCHitDocument *) d { if (d == nil) DESTROY(next); else ASSIGN(next, d); } - (float) score { return score; }; - (int) identifier { return identifier; }; - (LCDocument *) document { return doc; }; - (void) setDocument: (LCDocument *) d { if (d == nil) DESTROY(doc); else ASSIGN(doc, d); } @end