From c116120eef8912a5cc12f97edc80ae6947ab68ce Mon Sep 17 00:00:00 2001 From: Matt Magoffin Date: Wed, 22 Apr 2015 14:03:17 +1200 Subject: [PATCH 1/3] Add stemmingDisabled property to CLuceneSearchService, which toggles new stemmingDisabled property in BRSnowballAnalyzer to enable toggling support for stemming on/off. --- BRFullTextSearch/BRSnowballAnalyzer.cpp | 47 +++++++++++++++--------- BRFullTextSearch/BRSnowballAnalyzer.h | 6 ++- BRFullTextSearch/CLuceneSearchService.h | 7 ++++ BRFullTextSearch/CLuceneSearchService.mm | 17 +++++++++ 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/BRFullTextSearch/BRSnowballAnalyzer.cpp b/BRFullTextSearch/BRSnowballAnalyzer.cpp index 9e8531b..d10f70a 100644 --- a/BRFullTextSearch/BRSnowballAnalyzer.cpp +++ b/BRFullTextSearch/BRSnowballAnalyzer.cpp @@ -47,6 +47,15 @@ BRSnowballAnalyzer::BRSnowballAnalyzer(const TCHAR* language) { stopSet = NULL; } +/** Builds the named analyzer with the given stop words. */ +BRSnowballAnalyzer::BRSnowballAnalyzer(const TCHAR* language, const TCHAR** stopWords, bool prefixModeEnabled) { + this->language = STRDUP_TtoT(language); + + stopSet = _CLNEW CLTCSetList(true); + StopFilter::fillStopTable(stopSet,stopWords); + prefixMode = prefixModeEnabled; +} + BRSnowballAnalyzer::~BRSnowballAnalyzer(){ SavedStreams* t = reinterpret_cast(this->getPreviousTokenStream()); if (t) _CLDELETE(t->filteredTokenStream); @@ -55,16 +64,6 @@ BRSnowballAnalyzer::~BRSnowballAnalyzer(){ _CLDELETE(stopSet); } -/** Builds the named analyzer with the given stop words. - */ -BRSnowballAnalyzer::BRSnowballAnalyzer(const TCHAR* language, const TCHAR** stopWords, bool prefixModeEnabled) { - this->language = STRDUP_TtoT(language); - - stopSet = _CLNEW CLTCSetList(true); - StopFilter::fillStopTable(stopSet,stopWords); - prefixMode = prefixModeEnabled; -} - TokenStream* BRSnowballAnalyzer::tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader) { return this->tokenStream(fieldName,reader,false); } @@ -85,10 +84,12 @@ TokenStream* BRSnowballAnalyzer::tokenStream(const TCHAR* fieldName, CL_NS(util) if (stopSet != NULL) { result = _CLNEW CL_NS(analysis)::StopFilter(result, true, stopSet); } - if ( prefixMode ) { - result = _CLNEW bluerocket::lucene::analysis::SnowballPrefixFilter(result, true, language); - } else { - result = _CLNEW SnowballFilter(result, language, true); + if ( !stemmingDisabled ) { + if ( prefixMode ) { + result = _CLNEW bluerocket::lucene::analysis::SnowballPrefixFilter(result, true, language); + } else { + result = _CLNEW SnowballFilter(result, language, true); + } } return result; } @@ -108,10 +109,12 @@ TokenStream* BRSnowballAnalyzer::reusableTokenStream(const TCHAR* fieldName, Rea streams->filteredTokenStream = _CLNEW StandardFilter(streams->tokenStream, true); streams->filteredTokenStream = _CLNEW LowerCaseFilter(streams->filteredTokenStream, true); streams->filteredTokenStream = _CLNEW StopFilter(streams->filteredTokenStream, true, stopSet); - if ( prefixMode ) { - streams->filteredTokenStream = _CLNEW bluerocket::lucene::analysis::SnowballPrefixFilter(streams->filteredTokenStream, true, language); - } else { - streams->filteredTokenStream = _CLNEW SnowballFilter(streams->filteredTokenStream, language, true); + if ( !stemmingDisabled ) { + if ( prefixMode ) { + streams->filteredTokenStream = _CLNEW bluerocket::lucene::analysis::SnowballPrefixFilter(streams->filteredTokenStream, true, language); + } else { + streams->filteredTokenStream = _CLNEW SnowballFilter(streams->filteredTokenStream, language, true); + } } } else { streams->tokenStream->reset(reader); @@ -128,4 +131,12 @@ void BRSnowballAnalyzer::setPrefixMode(bool mode) { prefixMode = mode; } +bool BRSnowballAnalyzer::getStemmingDisabled() { + return stemmingDisabled; +} + +void BRSnowballAnalyzer::setStemmingDisabled(bool disabled) { + stemmingDisabled = disabled; +} + CL_NS_END2 diff --git a/BRFullTextSearch/BRSnowballAnalyzer.h b/BRFullTextSearch/BRSnowballAnalyzer.h index d6cec0b..3938085 100644 --- a/BRFullTextSearch/BRSnowballAnalyzer.h +++ b/BRFullTextSearch/BRSnowballAnalyzer.h @@ -14,7 +14,8 @@ CL_CLASS_DEF(util,BufferedReader) CL_NS_DEF2(analysis,snowball) -/** Filters {@link StandardTokenizer} with {@link StandardFilter}, {@link +/** + * Filters {@link StandardTokenizer} with {@link StandardFilter}, {@link * LowerCaseFilter}, {@link StopFilter} and {@link SnowballFilter}. * * Available stemmers are listed in {@link net.sf.snowball.ext}. The name of a @@ -25,6 +26,7 @@ class CLUCENE_CONTRIBS_EXPORT BRSnowballAnalyzer : public Analyzer { TCHAR* language; CLTCSetList* stopSet; bool prefixMode; + bool stemmingDisabled = false; class SavedStreams; @@ -41,6 +43,8 @@ class CLUCENE_CONTRIBS_EXPORT BRSnowballAnalyzer : public Analyzer { bool getPrefixMode(); void setPrefixMode(bool mode); + bool getStemmingDisabled(); + void setStemmingDisabled(bool disabled); /** Constructs a {@link StandardTokenizer} filtered by a {@link StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. */ diff --git a/BRFullTextSearch/CLuceneSearchService.h b/BRFullTextSearch/CLuceneSearchService.h index e107ecb..272ab77 100644 --- a/BRFullTextSearch/CLuceneSearchService.h +++ b/BRFullTextSearch/CLuceneSearchService.h @@ -60,6 +60,13 @@ */ @property (nonatomic, strong) NSString *defaultAnalyzerLanguage; +/** + * Turn stemming for tokenized fields on/off. Defaults to @c NO. + * + * @since 1.0.6 + */ +@property (nonatomic, getter=isStemmingDisabled) BOOL stemmingDisabled; + /** * Turn support for prefix-based searches on tokenized and stemmed fields. Defaults to @c NO. * diff --git a/BRFullTextSearch/CLuceneSearchService.mm b/BRFullTextSearch/CLuceneSearchService.mm index e01bdb7..203aa8b 100644 --- a/BRFullTextSearch/CLuceneSearchService.mm +++ b/BRFullTextSearch/CLuceneSearchService.mm @@ -213,6 +213,23 @@ - (void)setSupportStemmedPrefixSearches:(BOOL)supportStemmedPrefixSearches { } } +- (BOOL)isStemmingDisabled { + Analyzer *analyzer = [self defaultAnalyzer]; + lucene::analysis::snowball::BRSnowballAnalyzer *snowball = dynamic_cast(analyzer); + if ( snowball != NULL ) { + return (snowball->getStemmingDisabled() ? YES : NO); + } + return NO; +} + +- (void)setStemmingDisabled:(BOOL)stemmingDisabled { + Analyzer *analyzer = [self defaultAnalyzer]; + lucene::analysis::snowball::BRSnowballAnalyzer *snowball = dynamic_cast(analyzer); + if ( snowball != NULL ) { + snowball->setStemmingDisabled(stemmingDisabled ? true : false); + } +} + - (void)resetSearcher { if ( searcher.get() != NULL ) { searcher->close(); From af01b315fb13da25f2cf15b17389fbd77bca8072 Mon Sep 17 00:00:00 2001 From: Matt Magoffin Date: Wed, 22 Apr 2015 14:04:05 +1200 Subject: [PATCH 2/3] Update sample Core Data project to include settings screen for toggling both the stemmingDisabled and supportStemmedPrefixSearches properties of CLuceneSearchService dynamically at runtime. --- .../project.pbxproj | 10 +++ .../SampleCoreDataProject/AppDelegate.m | 21 +++++++ .../SampleCoreDataProject/CoreDataManager.h | 2 + .../SampleCoreDataProject/CoreDataManager.m | 14 +++++ .../SettingsViewController.h | 19 ++++++ .../SettingsViewController.m | 49 +++++++++++++++ .../SettingsViewController.xib | 62 +++++++++++++++++++ .../StickyNoteListViewController.m | 10 ++- 8 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.h create mode 100644 SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.m create mode 100644 SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.xib diff --git a/SampleCoreDataProject/SampleCoreDataProject.xcodeproj/project.pbxproj b/SampleCoreDataProject/SampleCoreDataProject.xcodeproj/project.pbxproj index 42ef630..747c7a8 100644 --- a/SampleCoreDataProject/SampleCoreDataProject.xcodeproj/project.pbxproj +++ b/SampleCoreDataProject/SampleCoreDataProject.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ C9049BBB17CAD3860099F480 /* StickyNoteViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C9049BB917CAD3860099F480 /* StickyNoteViewController.xib */; }; C9049BC217CAE7700099F480 /* stop-words.txt in Resources */ = {isa = PBXBuildFile; fileRef = C9049BC017CAE7700099F480 /* stop-words.txt */; }; C9049BC517CB10800099F480 /* Notifications.m in Sources */ = {isa = PBXBuildFile; fileRef = C9049BC417CB10800099F480 /* Notifications.m */; }; + C9B506F71AE72F1B00108562 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C9B506F51AE72F1B00108562 /* SettingsViewController.m */; }; + C9B506F81AE72F1B00108562 /* SettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C9B506F61AE72F1B00108562 /* SettingsViewController.xib */; }; EED3B88A827168F2772DB75F /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A06DBFE5733C082A1D5847E4 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -91,6 +93,9 @@ C9049BC117CAE7700099F480 /* en */ = {isa = PBXFileReference; lastKnownFileType = text; name = en; path = "stop-words.txt"; sourceTree = ""; }; C9049BC317CB10800099F480 /* Notifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Notifications.h; sourceTree = ""; }; C9049BC417CB10800099F480 /* Notifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Notifications.m; sourceTree = ""; }; + C9B506F41AE72F1B00108562 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; + C9B506F51AE72F1B00108562 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; + C9B506F61AE72F1B00108562 /* SettingsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsViewController.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -164,6 +169,9 @@ C9049BA917CAC6BA0099F480 /* CoreDataManager.m */, C9049BC317CB10800099F480 /* Notifications.h */, C9049BC417CB10800099F480 /* Notifications.m */, + C9B506F41AE72F1B00108562 /* SettingsViewController.h */, + C9B506F51AE72F1B00108562 /* SettingsViewController.m */, + C9B506F61AE72F1B00108562 /* SettingsViewController.xib */, C9049BB217CACD870099F480 /* StickyNoteListViewController.h */, C9049BB317CACD870099F480 /* StickyNoteListViewController.m */, C9049BB417CACD870099F480 /* StickyNoteListViewController.xib */, @@ -298,6 +306,7 @@ C9049B2117CAC48E0099F480 /* InfoPlist.strings in Resources */, C9049B2917CAC48F0099F480 /* Default.png in Resources */, C9049B2B17CAC48F0099F480 /* Default@2x.png in Resources */, + C9B506F81AE72F1B00108562 /* SettingsViewController.xib in Resources */, C9049B2D17CAC48F0099F480 /* Default-568h@2x.png in Resources */, C9049BB617CACD870099F480 /* StickyNoteListViewController.xib in Resources */, C9049BBB17CAD3860099F480 /* StickyNoteViewController.xib in Resources */, @@ -348,6 +357,7 @@ C9049B2317CAC48E0099F480 /* main.m in Sources */, C9049B2717CAC48E0099F480 /* AppDelegate.m in Sources */, C9049BAA17CAC6BA0099F480 /* CoreDataManager.m in Sources */, + C9B506F71AE72F1B00108562 /* SettingsViewController.m in Sources */, C9049BAD17CAC9CD0099F480 /* Model.xcdatamodeld in Sources */, C9049BB017CACA5C0099F480 /* StickyNote.m in Sources */, C9049BB517CACD870099F480 /* StickyNoteListViewController.m in Sources */, diff --git a/SampleCoreDataProject/SampleCoreDataProject/AppDelegate.m b/SampleCoreDataProject/SampleCoreDataProject/AppDelegate.m index 742f706..6dc8b69 100644 --- a/SampleCoreDataProject/SampleCoreDataProject/AppDelegate.m +++ b/SampleCoreDataProject/SampleCoreDataProject/AppDelegate.m @@ -11,6 +11,7 @@ #import #import #import "CoreDataManager.h" +#import "SettingsViewController.h" #import "StickyNoteListViewController.h" @implementation AppDelegate { @@ -31,7 +32,27 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.viewController.searchService = searchService; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:self.viewController]; [self.window makeKeyAndVisible]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(settingsChanged:) name:NSUserDefaultsDidChangeNotification object:nil]; return YES; } +- (void)settingsChanged:(NSNotification *)notification { + NSUserDefaults *defaults = notification.object; + const BOOL stemmingDisabled = [defaults boolForKey:kStemmingDisabledKey]; + const BOOL stemmingPrefixEnabled = [defaults boolForKey:kStemmingPrefixSupportEnabledKey]; + BOOL reindex = NO; + if ( stemmingDisabled != searchService.stemmingDisabled ) { + searchService.stemmingDisabled = stemmingDisabled; + reindex = YES; + } + if ( stemmingPrefixEnabled != searchService.supportStemmedPrefixSearches ) { + searchService.supportStemmedPrefixSearches = stemmingPrefixEnabled; + reindex = YES; + } + if ( reindex ) { + [coreDataManager reindex]; + } +} + @end diff --git a/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.h b/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.h index ad17001..ea25a2c 100644 --- a/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.h +++ b/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.h @@ -14,4 +14,6 @@ @property (nonatomic, strong) id searchService; +- (void)reindex; + @end diff --git a/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.m b/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.m index de734bb..3bf936c 100644 --- a/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.m +++ b/SampleCoreDataProject/SampleCoreDataProject/CoreDataManager.m @@ -11,6 +11,7 @@ #import #import #import "Notifications.h" +#import "StickyNote.h" @implementation CoreDataManager @@ -77,4 +78,17 @@ - (void)maintainSearchIndexFromManagedObjectDidSave:(NSNotification *)notificati }]; } +- (void)reindex { + [self.searchService bulkUpdateIndex:^(id updateContext) { + NSLog(@"Reindexing..."); + [NSManagedObjectContext MR_resetContextForCurrentThread]; // make sure we pull in latest data + for ( StickyNote *note in [StickyNote MR_findAll] ) { + [self.searchService addObjectToIndex:note context:updateContext]; + } + } queue:dispatch_get_main_queue() finished:^(int updateCount, NSError *error) { + NSLog(@"Reindexing complete."); + [[NSNotificationCenter defaultCenter] postNotificationName:SearchIndexDidChange object:nil]; + }]; +} + @end diff --git a/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.h b/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.h new file mode 100644 index 0000000..304310a --- /dev/null +++ b/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.h @@ -0,0 +1,19 @@ +// +// SettingsViewController.h +// SampleCoreDataProject +// +// Created by Matt on 4/22/15. +// Copyright (c) 2015 Blue Rocket, Inc. All rights reserved. +// + +#import + +extern NSString * const kStemmingDisabledKey; +extern NSString * const kStemmingPrefixSupportEnabledKey; + +/** + Provide a UI for changing system settings. Add settings are stored in @c NSUserDefaults. + */ +@interface SettingsViewController : UIViewController + +@end diff --git a/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.m b/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.m new file mode 100644 index 0000000..bf09e6a --- /dev/null +++ b/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.m @@ -0,0 +1,49 @@ +// +// SettingsViewController.m +// SampleCoreDataProject +// +// Created by Matt on 4/22/15. +// Copyright (c) 2015 Blue Rocket, Inc. All rights reserved. +// + +#import "SettingsViewController.h" + +NSString * const kStemmingDisabledKey = @"StemmingDisabled"; +NSString * const kStemmingPrefixSupportEnabledKey = @"StemmingPrefixSupportEnabled"; + +@interface SettingsViewController () +@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topMarginConstraint; +@property (strong, nonatomic) IBOutlet UISwitch *stemmingSwitch; +@property (strong, nonatomic) IBOutlet UISwitch *stemmingPrefixSwitch; +@end + +@implementation SettingsViewController + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self updateUIFromModel]; +} + +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + self.topMarginConstraint.constant = (self.topLayoutGuide.length + 20); +} + +- (void)updateUIFromModel { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + self.stemmingSwitch.on = ([defaults boolForKey:kStemmingDisabledKey] != YES); + self.stemmingPrefixSwitch.enabled = self.stemmingSwitch.on; + self.stemmingPrefixSwitch.on = [defaults boolForKey:kStemmingPrefixSupportEnabledKey]; +} + +- (IBAction)switchDidChange:(UISwitch *)sender { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if ( sender == self.stemmingSwitch ) { + [defaults setBool:!sender.on forKey:kStemmingDisabledKey]; + [self updateUIFromModel]; + } else if ( sender == self.stemmingPrefixSwitch ) { + [defaults setBool:sender.on forKey:kStemmingPrefixSupportEnabledKey]; + } +} + +@end diff --git a/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.xib b/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.xib new file mode 100644 index 0000000..b5ec62a --- /dev/null +++ b/SampleCoreDataProject/SampleCoreDataProject/SettingsViewController.xib @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampleCoreDataProject/SampleCoreDataProject/StickyNoteListViewController.m b/SampleCoreDataProject/SampleCoreDataProject/StickyNoteListViewController.m index 8d6c27a..d935fcc 100644 --- a/SampleCoreDataProject/SampleCoreDataProject/StickyNoteListViewController.m +++ b/SampleCoreDataProject/SampleCoreDataProject/StickyNoteListViewController.m @@ -11,6 +11,7 @@ #import #import #import "Notifications.h" +#import "SettingsViewController.h" #import "StickyNote.h" #import "StickyNoteViewController.h" @@ -25,7 +26,9 @@ @implementation StickyNoteListViewController { - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if ( (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) ) { self.navigationItem.title = @"Sticky Notes"; - self.navigationItem.leftBarButtonItem = self.editButtonItem; + + UIBarButtonItem *settings = [[UIBarButtonItem alloc] initWithTitle:@"Setup" style:UIBarButtonItemStylePlain target:self action:@selector(viewSettings:)]; + self.navigationItem.leftBarButtonItems = @[self.editButtonItem, settings]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addStickyNote:)]; @@ -45,6 +48,11 @@ - (void)viewWillAppear:(BOOL)animated { } } +- (IBAction)viewSettings:(id)sender { + SettingsViewController *dest = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil]; + [self.navigationController pushViewController:dest animated:YES]; +} + - (IBAction)addStickyNote:(id)sender { StickyNoteViewController *editor = [[StickyNoteViewController alloc] initWithNibName:@"StickyNoteViewController" bundle:nil]; [self.navigationController pushViewController:editor animated:YES]; From fd881c7a533bff5728396fea5e73e6d9cdfcf535 Mon Sep 17 00:00:00 2001 From: Matt Magoffin Date: Wed, 22 Apr 2015 14:04:18 +1200 Subject: [PATCH 3/3] Bump Pod version. --- BRFullTextSearch.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BRFullTextSearch.podspec b/BRFullTextSearch.podspec index e52ad0e..59c4a90 100644 --- a/BRFullTextSearch.podspec +++ b/BRFullTextSearch.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "BRFullTextSearch" - s.version = "1.0.5" + s.version = "1.0.6" s.summary = "iOS Objective-C full text search engine." s.description = <<-DESC This project provides a way to integrate full-text search