mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-28 20:55:32 +08:00
Add support for hooking into the RKSearchIndexer via a delegate
* Supports replacing the RKSearchWord fetch strategy to enable caching for performance * Supports declining creation of search words * Supports declining of indexing for specific objects * Supports notification when objects are indexed and search words are added to the index
This commit is contained in:
@@ -20,11 +20,15 @@
|
||||
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class RKSearchWord;
|
||||
|
||||
/**
|
||||
The key for an NSArray object that indentifies the list of searchable attributes in the user info dictionary of an NSEntityDescription.
|
||||
*/
|
||||
extern NSString * const RKSearchableAttributeNamesUserInfoKey;
|
||||
|
||||
@protocol RKSearchIndexerDelegate;
|
||||
|
||||
/**
|
||||
The `RKSearchIndexer` class provides support for adding full text searching to Core Data entities and managing the indexing of managed object instances of searchable entities.
|
||||
*/
|
||||
@@ -64,6 +68,8 @@ extern NSString * const RKSearchableAttributeNamesUserInfoKey;
|
||||
*/
|
||||
@property (nonatomic, strong) NSManagedObjectContext *indexingContext;
|
||||
|
||||
@property (nonatomic, weak) id<RKSearchIndexerDelegate> delegate;
|
||||
|
||||
///---------------------------------------------------
|
||||
/// @name Indexing Changes in a Managed Object Context
|
||||
///---------------------------------------------------
|
||||
@@ -141,3 +147,74 @@ extern NSString * const RKSearchableAttributeNamesUserInfoKey;
|
||||
- (void)waitUntilAllIndexingOperationsAreFinished;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
Objects that acts as the delegate for a `RKSearchIndexer` object must adopt the `RKSearchIndexerDelegate` protocol. The delegate may customize the behavior of the search indexer to match the needs of the application in several ways. The delegate may provide an alternate implementation for fetching an existing `RKSearchWord` managed object for a given word, it is consulted when the indexer determines that a new search word object is to be inserted and may decline the insertion, and it is notified after a new search word has been inserted.
|
||||
*/
|
||||
@protocol RKSearchIndexerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
///-------------------------------------
|
||||
/// @name Fetching Existing Search Words
|
||||
///-------------------------------------
|
||||
|
||||
/**
|
||||
Asks the delegate for an existing search word object for a given word in the managed object context being indexed. If no search word is found for the given word, then `nil` is to be returned.
|
||||
|
||||
By default, the search indexer creates and executes a fetch request against the context being indexed for each word during indexing. For large data-sets, this can wind up taking a significant amount of time. By providing an implementation of `searchIndexer:searchWordForWord:inManagedObjectContext:error:`, the delegate can be used to implement a caching scheme to reduce the overhead associated with the execution of these fetch requests.
|
||||
|
||||
@param searchIndexer The search indexer object performing the indexing.
|
||||
@param word The search word for which to retrieve an existing
|
||||
@param managedObjectContext The managed object context in which indexing is taking place.
|
||||
@param error A pointer to an error object to be set in the event an error occurs.
|
||||
@return The `RKSearchWord` object corresponding to the given word, or `nil` if none could be found. In the event an error occurs, `nil` is to be returned and the value of the error property is to be set to a pointer to an `NSError` object describing the failure.
|
||||
*/
|
||||
- (RKSearchWord *)searchIndexer:(RKSearchIndexer *)searchIndexer searchWordForWord:(NSString *)word inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext error:(NSError **)error;
|
||||
|
||||
///-------------------------------------------
|
||||
/// @name Tracking Indexing of Managed Objects
|
||||
///-------------------------------------------
|
||||
|
||||
/**
|
||||
Asks the delegate is a managed object should be indexed.
|
||||
|
||||
@param searchIndexer The search indexer object performing the indexing.
|
||||
@param managedObject The managed object the indexer is preparing to index.
|
||||
@return `YES` if indexing should proceed, else `NO`.
|
||||
*/
|
||||
- (BOOL)searchIndexer:(RKSearchIndexer *)searchIndexer shouldIndexManagedObject:(NSManagedObject *)managedObject;
|
||||
|
||||
/**
|
||||
Tells the delegate that the indexer has finished indexing a managed object.
|
||||
|
||||
@param searchIndexer The search indexer object performing the indexing.
|
||||
@param managedObject The managed object the indexer has just finished indexing.
|
||||
*/
|
||||
- (void)searchIndexer:(RKSearchIndexer *)searchIndxer didIndexManagedObject:(NSManagedObject *)managedObject;
|
||||
|
||||
///-----------------------------------------
|
||||
/// @name Tracking Insertion of Search Words
|
||||
///-----------------------------------------
|
||||
|
||||
/**
|
||||
Asks the delegate if the indexer should insert a new search word for a word that does not currently exist in the index.
|
||||
|
||||
@param searchIndexer The search indexer object performing the indexing.
|
||||
@param word A search word that appears in an indexed object but does not yet exist in the index.
|
||||
@param managedObjectContext The managed object context in which indexing is taking place.
|
||||
@return `YES` if the indexer should insert an `RKSearchWord` object for the given word, else `NO`.
|
||||
*/
|
||||
- (BOOL)searchIndexer:(RKSearchIndexer *)searchIndexer shouldInsertSearchWordForWord:(NSString *)word inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
|
||||
|
||||
/**
|
||||
Tells the delegate that the indexer has inserted a new search word object for a word.
|
||||
|
||||
@param searchIndexer The search indexer object performing the indexing.
|
||||
@param searchWord The search word that was inserted.
|
||||
@param word The word for which the search word object was created.
|
||||
@param managedObjectContext The managed object context in which indexing is taking place.
|
||||
*/
|
||||
- (void)searchIndexer:(RKSearchIndexer *)searchIndexer didInsertSearchWord:(RKSearchWord *)searchWord forWord:(NSString *)word inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
|
||||
|
||||
@end
|
||||
|
||||
@@ -172,16 +172,30 @@ NSString * const RKSearchableAttributeNamesUserInfoKey = @"RestKitSearchableAttr
|
||||
NSSet *tokens = [searchTokenizer tokenize:attributeValue];
|
||||
for (NSString *word in tokens) {
|
||||
if (word && [word length] > 0) {
|
||||
fetchRequest.predicate = [predicateTemplate predicateWithSubstitutionVariables:@{ @"SEARCH_WORD" : word }];
|
||||
RKSearchWord *searchWord = nil;
|
||||
NSError *error = nil;
|
||||
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (results) {
|
||||
RKSearchWord *searchWord;
|
||||
if ([results count] == 0) {
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:searchWordForWord:inManagedObjectContext:error:)]) {
|
||||
// Let our delegate retrieve an existing search word
|
||||
searchWord = [self.delegate searchIndexer:self searchWordForWord:word inManagedObjectContext:managedObjectContext error:&error];
|
||||
} else {
|
||||
// Fall back to vanilla fetch request
|
||||
fetchRequest.predicate = [predicateTemplate predicateWithSubstitutionVariables:@{ @"SEARCH_WORD" : word }];
|
||||
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
searchWord = ([results count] > 0) ? [results objectAtIndex:0] : nil;
|
||||
}
|
||||
if (error == nil) {
|
||||
if (! searchWord) {
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:shouldInsertSearchWordForWord:inManagedObjectContext:)]) {
|
||||
if (! [self.delegate searchIndexer:self shouldInsertSearchWordForWord:word inManagedObjectContext:managedObjectContext]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
searchWord = [NSEntityDescription insertNewObjectForEntityForName:RKSearchWordEntityName inManagedObjectContext:managedObjectContext];
|
||||
searchWord.word = word;
|
||||
} else {
|
||||
searchWord = [results objectAtIndex:0];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:didInsertSearchWord:forWord:inManagedObjectContext:)]) {
|
||||
[self.delegate searchIndexer:self didInsertSearchWord:searchWord forWord:word inManagedObjectContext:managedObjectContext];
|
||||
}
|
||||
}
|
||||
|
||||
NSAssert([[searchWord managedObjectContext] isEqual:managedObjectContext], @"Serious Core Data error: Expected `NSManagedObject` for the 'RKSearchWord' entity in context %@, but got one in %@", managedObject, [searchWord managedObjectContext]);
|
||||
@@ -204,6 +218,10 @@ NSString * const RKSearchableAttributeNamesUserInfoKey = @"RestKitSearchableAttr
|
||||
[managedObject setValue:searchWords forKey:RKSearchWordsRelationshipName];
|
||||
RKLogTrace(@"Indexed search words: %@", [searchWords valueForKey:RKSearchWordAttributeName]);
|
||||
searchWordCount = [searchWords count];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:didIndexManagedObject:)]) {
|
||||
[self.delegate searchIndexer:self didIndexManagedObject:managedObject];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -232,6 +250,9 @@ NSString * const RKSearchableAttributeNamesUserInfoKey = @"RestKitSearchableAttr
|
||||
NSUInteger totalObjects = [objectsToIndex count];
|
||||
__block NSMutableSet *indexedIDs = [NSMutableSet setWithCapacity:totalObjects];
|
||||
for (NSManagedObject *managedObject in objectsToIndex) {
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:shouldIndexManagedObject:)]) {
|
||||
if (! [self.delegate searchIndexer:self shouldIndexManagedObject:managedObject]) continue;
|
||||
}
|
||||
[self indexManagedObject:managedObject withProgressBlock:^(NSManagedObject *managedObject, RKSearchWord *searchWord, BOOL *stop) {
|
||||
if (totalObjects < 250) return;
|
||||
if ([indexedIDs containsObject:[managedObject objectID]]) return;
|
||||
@@ -245,6 +266,9 @@ NSString * const RKSearchableAttributeNamesUserInfoKey = @"RestKitSearchableAttr
|
||||
} else {
|
||||
// Perform asynchronous indexing
|
||||
for (NSManagedObject *managedObject in objectsToIndex) {
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:shouldIndexManagedObject:)]) {
|
||||
if (! [self.delegate searchIndexer:self shouldIndexManagedObject:managedObject]) continue;
|
||||
}
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
[self indexManagedObject:managedObject];
|
||||
}];
|
||||
@@ -289,10 +313,16 @@ NSString * const RKSearchableAttributeNamesUserInfoKey = @"RestKitSearchableAttr
|
||||
NSManagedObject *managedObject = [self.indexingContext existingObjectWithID:objectID error:&error];
|
||||
NSAssert([[managedObject managedObjectContext] isEqual:self.indexingContext], @"Serious Core Data error: Asked for an `NSManagedObject` with ID in indexing context %@, but got one in %@", objectID, self.indexingContext, [managedObject managedObjectContext]);
|
||||
if (managedObject && error == nil) {
|
||||
[self indexManagedObject:managedObject withProgressBlock:^(NSManagedObject *managedObject, RKSearchWord *searchWord, BOOL *stop) {
|
||||
// Stop the indexing process if we have been cancelled
|
||||
if ([indexingOperation isCancelled]) *stop = YES;
|
||||
}];
|
||||
BOOL performIndexing = YES;
|
||||
if ([self.delegate respondsToSelector:@selector(searchIndexer:shouldIndexManagedObject:)]) {
|
||||
performIndexing = [self.delegate searchIndexer:self shouldIndexManagedObject:managedObject];
|
||||
}
|
||||
if (performIndexing) {
|
||||
[self indexManagedObject:managedObject withProgressBlock:^(NSManagedObject *managedObject, RKSearchWord *searchWord, BOOL *stop) {
|
||||
// Stop the indexing process if we have been cancelled
|
||||
if ([indexingOperation isCancelled]) *stop = YES;
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
RKLogError(@"Failed indexing of object %@ with error: %@", managedObject, error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user