forked from Kapeli/Dash-iOS
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDHQueuedDB.m
More file actions
192 lines (177 loc) · 7.68 KB
/
DHQueuedDB.m
File metadata and controls
192 lines (177 loc) · 7.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//
// Copyright (C) 2016 Kapeli
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#import "DHQueuedDB.h"
#import "DHDBSearcher.h"
@implementation DHQueuedDB
+ (DHQueuedDB *)queueWithDocset:(DHDocset *)docset query:(NSString *)query typeLimit:(NSString *)typeLimit isFuzzy:(BOOL)isFuzzy
{
DHQueuedDB *queue = [[DHQueuedDB alloc] init];
queue.resultDictionary = [NSMutableDictionary dictionary];
queue.query = query;
queue.queryQueue = [NSMutableArray array];
queue.dbPath = docset.optimisedIndexPath;
queue.docset = docset;
queue.db = [FMDatabase databaseWithPath:queue.dbPath];
queue.lock = [DHDocset stepLock];
[queue.lock lock];
BOOL didOpen = [queue.db openWithFlags:SQLITE_OPEN_READONLY];
[queue.db registerFTSExtensions];
[queue.db setLogsErrors:YES];
[queue.lock unlockWithCondition:DHLockSearchOnly];
if(!didOpen)
{
return nil;
}
if(!isFuzzy)
{
NSString *ftsEscapedQuery = [query stringByReplacingSpecialFTSCharacters];
NSString *prefixedQuery = [ftsEscapedQuery stringByAppendingString:@"*"];
NSString *likeQuery = [prefixedQuery stringByReplacingOccurrencesOfString:@"*" withString:@"%"];
[queue.queryQueue addObject:[DHQueuedDB queuedQueryDictionary:@"SELECT path, name, type FROM searchIndex s, queryIndex q WHERE q.rowid = s.rowid AND q.perfect MATCH ?" andArgs:@[ftsEscapedQuery]]];
[queue.queryQueue addObject:[DHQueuedDB queuedQueryDictionary:@"SELECT path, name, type FROM searchIndex s, queryIndex q WHERE q.rowid = s.rowid AND q.prefix MATCH ? LIMIT 200" andArgs:@[prefixedQuery]]];
[queue.queryQueue addObject:[DHQueuedDB queuedQueryDictionary:@"SELECT path, name, type FROM searchIndex s, queryIndex q WHERE q.rowid = s.rowid AND q.suffixes MATCH ? AND q.prefix NOT LIKE ? LIMIT 200" andArgs:@[ftsEscapedQuery, likeQuery]]];
[queue.queryQueue addObject:[DHQueuedDB queuedQueryDictionary:@"SELECT path, name, type FROM searchIndex s, queryIndex q WHERE q.rowid = s.rowid AND q.suffixes MATCH ? AND q.prefix NOT LIKE ? LIMIT 200" andArgs:@[[NSString stringWithFormat:@"%@ NOT %@", prefixedQuery, ftsEscapedQuery], likeQuery]]];
}
else if(query.length > 2)
{
NSString *escapedQuery = [query stringByReplacingOccurrencesOfString:@"~" withString:@"~~"];
escapedQuery = [escapedQuery stringByReplacingOccurrencesOfString:@"_" withString:@"~_"];
escapedQuery = [escapedQuery stringByReplacingOccurrencesOfString:@"%" withString:@"~%"];
NSString *wildcardEverywhere = [escapedQuery stringByAddingWildcardsEverywhere:@"~"];
[queue.queryQueue addObject:[DHQueuedDB queuedQueryDictionary:@"SELECT path, name, type FROM searchIndex WHERE name LIKE ? ESCAPE '~' AND name NOT LIKE ? ESCAPE '~' LIMIT 300;" andArgs:@[wildcardEverywhere, [[@"%" stringByAppendingString:escapedQuery] stringByAppendingString:@"%"]]]];
}
if(typeLimit)
{
for(NSMutableDictionary *dictionary in queue.queryQueue)
{
[dictionary[@"args"] insertObject:typeLimit atIndex:0];
dictionary[@"sqlQuery"] = [dictionary[@"sqlQuery"] stringByReplacingOccurrencesOfString:@" WHERE " withString:@" WHERE type = ? AND "];
}
}
return queue;
}
+ (NSDictionary *)queuedQueryDictionary:(NSString *)aQuery andArgs:(NSArray *)args
{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:aQuery, @"sqlQuery", [args mutableCopy], @"args", nil];
}
- (BOOL)next
{
[DHDBSearcher checkForInterrupt];
[self.lock lock];
if([self.currentRS next])
{
[self.lock unlockWithCondition:DHLockSearchOnly];
[DHDBSearcher checkForInterrupt];
return YES;
}
[self.lock unlockWithCondition:DHLockSearchOnly];
[DHDBSearcher checkForInterrupt];
return NO;
}
- (BOOL)step
{
[DHDBSearcher checkForInterrupt];
if(self.queryQueue.count)
{
NSDictionary *aQuery = (self.queryQueue)[0];
[self.lock lock];
self.currentRS = [self resultSetFromQueuedQueryDictionary:aQuery];
[self.lock unlockWithCondition:DHLockSearchOnly];
[self.queryQueue removeObjectAtIndex:0];
[DHDBSearcher checkForInterrupt];
return YES;
}
[self close];
[DHDBSearcher checkForInterrupt];
return NO;
}
- (DHDBResult *)currentDBResult
{
DHDBResult *result = [DHDBResult resultWithDocset:self.docset resultSet:self.currentRS];
return [self prepareResult:result];
}
- (DHDBResult *)prepareResult:(DHDBResult *)result
{
[result setQuery:self.query];
NSString *name = [[result name] stringByReplacingOccurrencesOfString:@" " withString:@""];
NSString *originalName = [[result originalName] stringByReplacingOccurrencesOfString:@" " withString:@""];
if(![[result originalName] contains:self.query] && [originalName contains:self.query])
{
result.whitespaceMatch = YES;
}
self.hadPerfect = [originalName hasCaseInsensitiveSuffix:self.query] || [originalName hasCaseInsensitivePrefix:self.query];
[DHDBSearcher checkForInterrupt];
result.queryIsPrefix = [name hasCaseInsensitivePrefix:self.query];
result.queryIsSuffix = [name hasCaseInsensitiveSuffix:self.query];
result.perfectMatch = result.queryIsPrefix && result.queryIsSuffix && name.length == self.query.length;
if(self.query.length)
{
result.matchesQueryAtAll = (result.queryIsSuffix || result.queryIsPrefix || [name rangeOfString:self.query options:NSCaseInsensitiveSearch].location != NSNotFound);
result.originalMatchesQueryAtAll = result.matchesQueryAtAll || [originalName rangeOfString:self.query options:NSCaseInsensitiveSearch].location != NSNotFound;
result.queryIsPrefixOfOriginal = [originalName hasCaseInsensitivePrefix:self.query];
result.queryIsSuffixOfOriginal = [originalName hasCaseInsensitiveSuffix:self.query];
result.perfectMatchOriginal = result.queryIsPrefixOfOriginal && result.queryIsSuffixOfOriginal && originalName.length == self.query.length;
}
[result highlightWithQuery:self.query];
if(result.fuzzyShouldIgnore)
{
return nil;
}
return result;
}
- (FMResultSet *)resultSetFromQueuedQueryDictionary:(NSDictionary *)query
{
return [self.db executeQuery:query[@"sqlQuery"] withArgumentsInArray:query[@"args"]];
}
- (void)close
{
self.currentRS = nil;
if(self.db && self.db.sqliteHandle)
{
[self.lock lock];
[self.db close];
[self.lock unlockWithCondition:DHLockAllAllowed];
}
self.db = nil;
}
- (void)addResultToResultDictionary:(DHDBResult *)result
{
NSString *sortType = result.sortType;
NSMutableArray *typeResults = (self.resultDictionary)[sortType];
if(!typeResults)
{
typeResults = [NSMutableArray array];
(self.resultDictionary)[sortType] = typeResults;
}
[typeResults addObject:result];
}
- (void)sortResultDictionary
{
for(NSMutableArray *queueResults in [[self resultDictionary] allValues])
{
[queueResults sortUsingSelector:@selector(compareFuziness:)];
}
}
- (NSMutableArray *)resultsForType:(NSString *)type
{
return (self.resultDictionary)[type];
}
- (void)dealloc
{
[self close];
}
@end