2
0

Limit fuzzy searching to a depth of 10.

Avoids choking when query string becomes long and there are excessively
long site name entries.
This commit is contained in:
Maarten Billemont 2020-05-23 12:14:22 -04:00
parent 7091e2ee1b
commit 8582c934c2
6 changed files with 51 additions and 53 deletions

View File

@ -45,7 +45,7 @@
@property(nonatomic, readonly) BOOL stored; @property(nonatomic, readonly) BOOL stored;
@property(nonatomic, readonly) BOOL transient; @property(nonatomic, readonly) BOOL transient;
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups; - (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups;
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user; - (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc; - (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;

View File

@ -31,12 +31,12 @@
@implementation MPSiteModel @implementation MPSiteModel
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups { - (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
[self setEntity:entity fuzzyGroups:fuzzyGroups]; [self setEntity:entity queryGroups:queryGroups];
self.initialized = YES; self.initialized = YES;
return self; return self;
@ -53,23 +53,25 @@
return self; return self;
} }
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups { - (void)setEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
if ([self.entityOID isEqual:entity.permanentObjectID]) if ([self.entityOID isEqual:entity.permanentObjectID])
return; return;
self.entityOID = entity.permanentObjectID; self.entityOID = entity.permanentObjectID;
NSString *siteName = entity.name; NSString *siteName = entity.name;
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName]; NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [fuzzyGroups count]; ++f) { if ([attributedSiteName length])
s = [siteName rangeOfString:fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch for (NSUInteger f = 0, s = 0; f < [queryGroups count]; ++f, ++s) {
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location; s = [siteName rangeOfString:queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
if (s == NSNotFound) range:NSMakeRange( s, [siteName length] - s )].location;
break; if (s == NSNotFound)
break;
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
range:NSMakeRange( s, [queryGroups[f] length] )];
}
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
range:NSMakeRange( s, [fuzzyGroups[f] length] )];
}
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = NSCenterTextAlignment; paragraphStyle.alignment = NSCenterTextAlignment;
[attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )]; [attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];

View File

@ -611,22 +611,20 @@
return; return;
} }
static NSRegularExpression *fuzzyRE;
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
} );
prof_new( @"updateSites" ); prof_new( @"updateSites" );
NSString *queryString = self.siteField.stringValue; NSString *queryString = self.siteField.stringValue;
NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"] stringByAppendingString:@"*"]; NSMutableArray *queryGroups = [NSMutableArray new];
NSMutableString *queryPattern = [NSMutableString new];
[queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (substringRange.location < 10) {
[queryGroups addObject:substring];
[queryPattern appendString:@"*"];
}
[queryPattern appendString:substring];
}];
[queryPattern appendString:@"*"];
prof_rewind( @"queryPattern" ); prof_rewind( @"queryPattern" );
NSMutableArray *fuzzyGroups = [NSMutableArray new];
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
}];
prof_rewind( @"fuzzyRE" );
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
prof_rewind( @"moc" ); prof_rewind( @"moc" );
@ -648,7 +646,7 @@
BOOL exact = NO; BOOL exact = NO;
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]]; NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
for (MPSiteEntity *site in siteResults) { for (MPSiteEntity *site in siteResults) {
[newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]]; [newSites addObject:[[MPSiteModel alloc] initWithEntity:site queryGroups:queryGroups]];
exact |= [site.name isEqualToString:queryString]; exact |= [site.name isEqualToString:queryString];
} }
prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact ); prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact );

View File

@ -27,7 +27,7 @@ typedef NS_ENUM ( NSUInteger, MPSiteCellMode ) {
@interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate> @interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
@property(nonatomic) NSArray *fuzzyGroups; @property(nonatomic) NSArray *queryGroups;
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated; - (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated; - (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;

View File

@ -135,7 +135,7 @@
[super prepareForReuse]; [super prepareForReuse];
self.siteOID = nil; self.siteOID = nil;
self.fuzzyGroups = nil; self.queryGroups = nil;
self.transientSite = nil; self.transientSite = nil;
self.mode = MPPasswordCellModePassword; self.mode = MPPasswordCellModePassword;
[self updateAnimated:NO]; [self updateAnimated:NO];
@ -150,11 +150,11 @@
#pragma mark - State #pragma mark - State
- (void)setFuzzyGroups:(NSArray *)fuzzyGroups { - (void)setQueryGroups:(NSArray *)queryGroups {
if (self.fuzzyGroups == fuzzyGroups) if (self.queryGroups == queryGroups)
return; return;
_fuzzyGroups = fuzzyGroups; _queryGroups = queryGroups;
[self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]]; [self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
} }
@ -656,14 +656,14 @@
NSString *siteName = self.transientSite?: site.name; NSString *siteName = self.transientSite?: site.name;
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""]; NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
if ([attributedSiteName length]) if ([attributedSiteName length])
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [self.fuzzyGroups count]; ++f) { for (NSUInteger f = 0, s = 0; f < [self.queryGroups count]; ++f, ++s) {
s = [siteName rangeOfString:self.fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch s = [siteName rangeOfString:self.queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location; range:NSMakeRange( s, [siteName length] - s )].location;
if (s == NSNotFound) if (s == NSNotFound)
break; break;
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor] [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor]
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )]; range:NSMakeRange( s, [self.queryGroups[f] length] )];
} }
if (self.transientSite) if (self.transientSite)

View File

@ -36,7 +36,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate> @interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController; @property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property(nonatomic, strong) NSArray *fuzzyGroups; @property(nonatomic, strong) NSArray *queryGroups;
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet; @property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
@property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource; @property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource;
@property(nonatomic, weak) UIViewController *popdownVC; @property(nonatomic, weak) UIViewController *popdownVC;
@ -162,7 +162,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MPSiteCell *cell = [MPSiteCell dequeueFromCollectionView:collectionView indexPath:indexPath]; MPSiteCell *cell = [MPSiteCell dequeueFromCollectionView:collectionView indexPath:indexPath];
[cell setFuzzyGroups:self.fuzzyGroups]; [cell setQueryGroups:self.queryGroups];
id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item]; id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
if ([item isKindOfClass:[MPSiteEntity class]]) if ([item isKindOfClass:[MPSiteEntity class]])
[cell setSite:item animated:NO]; [cell setSite:item animated:NO];
@ -355,21 +355,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (void)reloadSites { - (void)reloadSites {
[self.fetchedResultsController.managedObjectContext performBlock:^{ [self.fetchedResultsController.managedObjectContext performBlock:^{
static NSRegularExpression *fuzzyRE;
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
} );
NSString *queryString = self.query; NSString *queryString = self.query;
NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"] NSMutableArray *queryGroups = [NSMutableArray new];
stringByAppendingString:@"*"]; NSMutableString *queryPattern = [NSMutableString new];
NSMutableArray *fuzzyGroups = [NSMutableArray new]; [queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length ) usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { if (substringRange.location < 10) {
[fuzzyGroups addObject:[queryString substringWithRange:result.range]]; [queryGroups addObject:substring];
}]; [queryPattern appendString:@"*"];
self.fuzzyGroups = fuzzyGroups; }
[queryPattern appendString:substring];
}];
[queryPattern appendString:@"*"];
self.queryGroups = queryGroups;
NSError *error = nil; NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate = self.fetchedResultsController.fetchRequest.predicate =
@ -382,7 +380,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
toSections:[self createDataSource] toSections:[self createDataSource]
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) { reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
for (MPSiteCell *cell in self.collectionView.visibleCells) for (MPSiteCell *cell in self.collectionView.visibleCells)
[cell setFuzzyGroups:self.fuzzyGroups]; [cell setQueryGroups:self.queryGroups];
}]; }];
} ); } );
}]; }];