• Stars
    star
    190
  • Rank 203,739 (Top 5 %)
  • Language
    Objective-C
  • Created over 12 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

SearchKit backed search for Core Data

SNRSearchIndex: Core Data search backed by SearchKit

SNRSearchIndex is a simple wrapper for the SearchKit framework that is specifically focused around providing lightning fast search for Core Data databases. SearchKit is the framework that Apple's Spotlight is built on, and it's primary use is full text search of documents. That said, it's so fast that it's applicable to a wide variety of cases. I tried using SearchKit with Core Data on a hunch, and the results were far better than I expected. It's really not the code itself that's the valuable part of this project, but the concept of using a FTS framework for searching a Core Data DB.

SNRSearchIndex must be compiled under ARC. If your project has not been converted to use ARC yet, you can add the -f-objc-arc flag to SNRSearchIndex.m in the Compile Sources build phase for your project.

Usage

Creating/Opening a Search Index

This is fairly self explanatory. The -initByOpeningSearchIndexAtURL:persistentStoreCoordinator and -initByCreatingSearchIndexAtURL:persistentStoreCoordinator methods of SNRSearchIndex will open or create a search index file at the specified URL. If no URL is specified, it will automatically set the URL as [directory that the first NSPersistentStore is located in]/searchindex.

SNRSearchIndex *index = [[SNRSearchIndex alloc] initByCreatingSearchIndexAtURL:nil persistentStoreCoordinator:self.persistentStoreCoordinator];

Using a Shared Search Index

Often times, your application will only use a single search index, and it's inconvenient having to maintain references to a search index from all of the various objects that use it. Therefore, SNRSearchIndex has the +sharedIndex and +setSharedIndex: class methods to get and set a shared index that will be easily accessible from anywhere in your code.

// Set the shared index when you first create it
[SNRSearchIndex setSharedIndex:index]

// From another class...
SNRSearchIndex *theIndex = [SNRSearchIndex sharedIndex];

Setting Keywords for Managed Objects

Setting keywords to save in the search index for your managed objects is simple. Create an NSArray of NSStrings of the keywords for that object and call -setKeywords:forObjectID:. The reason that NSManagedObjectID's are used vs. the NSManagedObjects themselves is because SearchKit, and therefore SNRSearchIndex, is threadsafe and passing around the managed objects themselves is not thread safe because they are confined to a single managed object context.

Car *car = …; // NSManagedObject subclass
NSArray *keywords = [NSArray arrayWithObjects:@"car", @"red", @"leather", @"v6", nil];
[[SNRSearchIndex sharedIndex] setKeywords:keywords forObjectID:[car objectID]];

One of the main annoyances with using a separate search index is that you need to keep it in sync with the managed object context when an attribute of a model object changes that affects the keywords in the search index. Therefore, it would be convenient to use custom accessors or KVO in your NSManagedObject subclass to automatically update the keywords in the search index. Example:

@interface Game : NSManagedObject
@property (nonatomic, retain) NSString *name;
@end

@interface Game (CoreDataGeneratedPrimitiveAccessors)
- (void)setPrimitiveName:(NSString*)primitiveName;
@end

@implementation Game
- (void)setName:(NSString*)value
{
	[self willChangeValueForKey:@"name"];
	[self setPrimitiveName:value];
	[self didChangeValueForKey:@"name"];
	NSArray *keywords = [value componentsSeparatedByString:@" "]; // Create keywords by separating the name by its spaces
	[[SNRSearchIndex sharedIndex] setKeywords:keywords forObjectID:[self objectID]];
}
@end

Synchronizing Saves with NSManagedObjectContext

Calling -flush on an SNRSearchIndex will commit any changes to the backing store, much like calling -save: on an NSManagedObjectContext. Instead of having to call save on both every time changes are made, it is far simpler to implement a method in a category for NSManagedObjectContext that saves both the search index and the MOC:

@interface NSManagedObjectContext (SearchIndex)
- (BOOL)saveContextAndSearchIndex:(NSError**)error;
@end

@implementation NSManagedObjectContext (SearchIndex)
- (BOOL)saveContextAndSearchIndex:(NSError**)error
{
	BOOL success = [[SNRSearchIndex sharedIndex] flush];
	if (![self save:error]) {
		return NO;
	}
	return success;
}
@end

Creating a Search Query

The SNRSearchIndexSearch object encapsulates a single search query. Use the -searchForQuery:options: method of SNRSearchIndex to create a search query object. The values for the options parameter are documented in the header. SearchKit supports a specific set of query operators that are described here.

// Substring search for 'berry'

SNRSearchIndexSearch *search = [[SNRSearchIndex sharedIndex] searchForQuery:@"*berry" options:0];  

// this would return results for 'strawberry', 'blackberry', etc.

Performing a Search Query

Search queries are performed using the -findMatchesWithFetchLimit:maximumTime:handler method of SNRSearchIndexSearch. These parameters are documented in the header. This method can be called from a background thread, and once the search results are retrieved, they are passed to the handler block as an array of SNRSearchIndexSearchResult objects. Mapping these results to actual NSManagedObject's is trivial:

[search findMatchesWithFetchLimit:10 maximumTime:1.0 handler:^(NSArray *results) {
	NSMutableArray *objects = [NSMutableArray arrayWithCapacity:[results count]];
	for (SNRSearchIndexSearchResult *result in results) {
		NSManagedObject *object = [self.managedObjectContext existingObjectWithID:result.objectID error:nil];
		if (object) { [objects addObject:object]; }
	}
	// Do something with the objects array
}];

Note: If you're implementing a "type to search" feature, it would be a good idea to cancel the previous search query before executing a new one. Implement a property setter that retains a reference to the current search query and cancels the old one when a new one is set:

- (void)setCurrentSearch:(SNRSearchIndexSearch*)search
{
	if (_currentSearch != search) {
		[_currentSearch cancel];
		_currentSearch = search;
	}
}

Relevance Scores

SearchKit has support for relevance scores, which rank search results based on some algorithms that determine their similarity to the search query. SNRSearchIndex exposes these relevance scores, but they probably aren't that accurate nor useful for this purpose (they're more useful for full text search).

About Me

I'm Indragie Karunaratne, a 17 year old Mac and iOS developer. Visit my website to check out my portfolio and get in touch with me.

Licensing

SNRSearchIndex is licensed under the BSD license.

More Repositories

1

InAppViewDebugger

A UIView debugger (like Reveal or Xcode) that can be embedded in an app for on-device view debugging
Swift
1,871
star
2

CocoaMarkdown

Markdown parsing and rendering for iOS and OS X
Objective-C
1,200
star
3

INAppStoreWindow

NSWindow subclass with a highly customizable title bar and traffic lights
Objective-C
1,064
star
4

DominantColor

Finding dominant colors of an image using k-means clustering
Swift
959
star
5

MarkdownTextView

Rich Markdown editing control for iOS
Swift
686
star
6

SwiftAutoLayout

Tiny Swift DSL for Autolayout
Swift
655
star
7

SNRHUDKit

Code drawn AppKit HUD interface elements
Objective-C
326
star
8

swiftrsrc

Resource code generation tool for Swift
Swift
290
star
9

INDANCSClient

Objective-C Apple Notification Center Service Client
Objective-C
252
star
10

INPopoverController

A customizable popover controller for OS X
Objective-C
196
star
11

Unzip

iOS 8 Action Extension for browsing ZIP files
Objective-C
165
star
12

NSUserNotificationPrivate

Private API showcase for NSUserNotification on OS X
Objective-C
152
star
13

SNRMusicKit

All-in-one framework for browsing and playing music from various sources on iOS and OS X
Objective-C
150
star
14

WWDC-2014

Scholarship submission for WWDC 2014
Objective-C
146
star
15

Ares

Zero-setup P2P file transfer between Macs and iOS devices
Swift
133
star
16

INDockableWindow

A window to which other views can be "docked" to and separated into their own windows
Objective-C
114
star
17

INDLinkLabel

A simple, no frills UILabel subclass with support for links
Swift
82
star
18

SNRFetchedResultsController

Automatic Core Data change tracking for OS X (NSFetchedResultsController port)
Objective-C
81
star
19

INDSequentialTextSelectionManager

Sequential text selection for NSTextViews
Objective-C
74
star
20

OEGridView

High performance Core Animation-based grid view, originally from OpenEmu.
Objective-C
72
star
21

INDGIFPreviewDownloader

[iOS] Retrieves preview images for GIFs by downloading only the first frame
Objective-C
60
star
22

Chip8

CHIP-8 emulator and disassembler written in Swift
Swift
53
star
23

pdfcat

OS X utility for concatenating PDF files
Swift
49
star
24

ReactiveXPC

Signals across process boundaries
Swift
36
star
25

ObjectiveKVDB

Objective-C wrapper for kvdb (https://github.com/dinhviethoa/kvdb)
Objective-C
33
star
26

SwiftTableViews

Type-safe Table Views with Swift
Swift
33
star
27

AlamofireRACExtensions

ReactiveCocoa Swift extensions for Alamofire
Swift
32
star
28

Dial

The beginnings of a replacement Contacts app for iOS.
Objective-C
30
star
29

ReactiveBLE

ReactiveCocoa wrapper for communicating with BLE devices using CoreBluetooth
Objective-C
29
star
30

INSOCKSServer

SOCKS5 proxy server implementation in Objective-C
Objective-C
27
star
31

LiveWebPreview

Web development tool for automatically refreshing a page when the content changes.
Objective-C
20
star
32

SNRLastFMEngine

[DEPRECATED] A modern block-based Objective-C interface to the Last.fm API
Objective-C
19
star
33

AttributedString.swift

Swift library that adds type safety and string interpolation support to NSAttributedString
Swift
18
star
34

INKeychainAccess

[DEPRECATED] Objective-C Keychain Services Wrapper for OS X and iOS
Objective-C
17
star
35

tecs

Projects for The Elements of Computing Systems by Nisan and Schocken
Assembly
15
star
36

INTrafficLightsDisabler

SIMBL plugin to hide the traffic lights on Mac OS X
Objective-C
14
star
37

pebble-lifx

Pebble controller for LIFX bulbs. UAlberta CompE Club Hackathon 2014 project.
Objective-C
11
star
38

xcode-themes

Color themes for Xcode 4 and 5
6
star
39

radars

Apple Radars filed for OS X and iOS.
Objective-C
5
star
40

indragiek.github.com

http://indragie.com
HTML
3
star
41

arduino-copter

Copter game for Arduino + Adafruit TFT
C++
2
star
42

writing

Things that I write
2
star
43

advent-of-code-2020

My Rust solutions for https://adventofcode.com
Rust
1
star
44

bootstrap-no-responsive

Compiled Bootstrap with responsive features disabled
1
star