nano-store 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.gitignore +1 -0
  2. data/.gitmodules +0 -3
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +24 -0
  5. data/README.md +5 -0
  6. data/Rakefile +21 -1
  7. data/lib/nano_store.rb +2 -0
  8. data/lib/nano_store/finder.rb +76 -0
  9. data/lib/nano_store/model.rb +1 -32
  10. data/lib/nano_store/version.rb +1 -1
  11. data/nano-store.gemspec +1 -0
  12. data/spec/finder_spec.rb +67 -0
  13. data/spec/model_spec.rb +0 -30
  14. data/vendor/Podfile.lock +5 -0
  15. data/vendor/Pods/Headers/NanoStore/NSFNanoBag.h +306 -0
  16. data/vendor/Pods/Headers/NanoStore/NSFNanoBag_Private.h +37 -0
  17. data/vendor/Pods/Headers/NanoStore/NSFNanoEngine.h +542 -0
  18. data/vendor/Pods/Headers/NanoStore/NSFNanoEngine_Private.h +69 -0
  19. data/vendor/Pods/Headers/NanoStore/NSFNanoExpression.h +125 -0
  20. data/vendor/Pods/Headers/NanoStore/NSFNanoExpression_Private.h +35 -0
  21. data/vendor/Pods/Headers/NanoStore/NSFNanoGlobals.h +323 -0
  22. data/vendor/Pods/Headers/NanoStore/NSFNanoGlobals_Private.h +99 -0
  23. data/vendor/Pods/Headers/NanoStore/NSFNanoObject.h +298 -0
  24. data/vendor/Pods/Headers/NanoStore/NSFNanoObjectProtocol.h +119 -0
  25. data/vendor/Pods/Headers/NanoStore/NSFNanoObject_Private.h +35 -0
  26. data/vendor/Pods/Headers/NanoStore/NSFNanoPredicate.h +123 -0
  27. data/vendor/Pods/Headers/NanoStore/NSFNanoPredicate_Private.h +35 -0
  28. data/vendor/Pods/Headers/NanoStore/NSFNanoResult.h +137 -0
  29. data/vendor/Pods/Headers/NanoStore/NSFNanoResult_Private.h +43 -0
  30. data/vendor/Pods/Headers/NanoStore/NSFNanoSearch.h +381 -0
  31. data/vendor/Pods/Headers/NanoStore/NSFNanoSearch_Private.h +48 -0
  32. data/vendor/Pods/Headers/NanoStore/NSFNanoSortDescriptor.h +124 -0
  33. data/vendor/Pods/Headers/NanoStore/NSFNanoStore.h +475 -0
  34. data/vendor/Pods/Headers/NanoStore/NSFNanoStore_Private.h +57 -0
  35. data/vendor/Pods/Headers/NanoStore/NanoStore.h +463 -0
  36. data/vendor/Pods/Headers/NanoStore/NanoStore_Private.h +37 -0
  37. data/vendor/Pods/NanoStore/Classes/Advanced/NSFNanoEngine.h +542 -0
  38. data/vendor/Pods/NanoStore/Classes/Advanced/NSFNanoEngine.m +1781 -0
  39. data/vendor/Pods/NanoStore/Classes/Advanced/NSFNanoResult.h +137 -0
  40. data/vendor/Pods/NanoStore/Classes/Advanced/NSFNanoResult.m +265 -0
  41. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoBag_Private.h +37 -0
  42. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoEngine_Private.h +69 -0
  43. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoExpression_Private.h +35 -0
  44. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoGlobals_Private.h +99 -0
  45. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoObject_Private.h +35 -0
  46. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoPredicate_Private.h +35 -0
  47. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoResult_Private.h +43 -0
  48. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoSearch_Private.h +48 -0
  49. data/vendor/Pods/NanoStore/Classes/Private/NSFNanoStore_Private.h +57 -0
  50. data/vendor/Pods/NanoStore/Classes/Private/NanoStore_Private.h +37 -0
  51. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoBag.h +306 -0
  52. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoBag.m +485 -0
  53. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoExpression.h +125 -0
  54. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoExpression.m +103 -0
  55. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoGlobals.h +323 -0
  56. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoGlobals.m +145 -0
  57. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoObject.h +298 -0
  58. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoObject.m +187 -0
  59. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoObjectProtocol.h +119 -0
  60. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoPredicate.h +123 -0
  61. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoPredicate.m +130 -0
  62. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoSearch.h +381 -0
  63. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoSearch.m +859 -0
  64. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoSortDescriptor.h +124 -0
  65. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoSortDescriptor.m +79 -0
  66. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoStore.h +475 -0
  67. data/vendor/Pods/NanoStore/Classes/Public/NSFNanoStore.m +1375 -0
  68. data/vendor/Pods/NanoStore/Classes/Public/NanoStore.h +463 -0
  69. data/vendor/Pods/NanoStore/LICENSE +25 -0
  70. data/vendor/Pods/NanoStore/README.md +411 -0
  71. data/vendor/Pods/Pods-prefix.pch +3 -0
  72. data/vendor/Pods/Pods-resources.sh +15 -0
  73. data/vendor/Pods/Pods.bridgesupport +1215 -0
  74. data/vendor/Pods/Pods.xcconfig +4 -0
  75. data/vendor/Pods/Pods.xcodeproj/project.pbxproj +974 -0
  76. metadata +89 -6
@@ -0,0 +1,859 @@
1
+ /*
2
+ NSFNanoSearch.m
3
+ NanoStore
4
+
5
+ Copyright (c) 2010 Webbo, L.L.C. All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without modification, are permitted
8
+ provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
11
+ and the following disclaimer.
12
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
13
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
14
+ * Neither the name of Webbo nor the names of its contributors may be used to endorse or promote
15
+ products derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
20
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
+ SUCH DAMAGE.
25
+ */
26
+
27
+ #import "NanoStore.h"
28
+ #import "NanoStore_Private.h"
29
+ #import "NSFNanoSearch_Private.h"
30
+
31
+ @implementation NSFNanoSearch
32
+ {
33
+ /** \cond */
34
+ @protected
35
+ NSFReturnType returnedObjectType;
36
+ /** \endcond */
37
+ }
38
+
39
+
40
+ @synthesize nanoStore, attributesToBeReturned, key, attribute, value, match, expressions, groupValues, sql, sort;
41
+
42
+ // ----------------------------------------------
43
+ // Initialization / Cleanup
44
+ // ----------------------------------------------
45
+
46
+ + (NSFNanoSearch*)searchWithStore:(NSFNanoStore *)store
47
+ {
48
+ return [[self alloc]initWithStore:store];
49
+ }
50
+
51
+ - (id)initWithStore:(NSFNanoStore *)store
52
+ {
53
+ if (nil == store) {
54
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
55
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: store is nil.", [self class], _cmd]
56
+ userInfo:nil]raise];
57
+ }
58
+
59
+ if ((self = [self init])) {
60
+ nanoStore = store;
61
+ [self reset];
62
+ }
63
+
64
+ return self;
65
+ }
66
+
67
+ /** \cond */
68
+
69
+ - (void)dealloc
70
+ {
71
+ [self reset];
72
+ }
73
+
74
+ /** \endcond */
75
+
76
+ #pragma mark -
77
+
78
+ - (NSString *)sql
79
+ {
80
+ if (nil == sql)
81
+ return [self _preparedSQL];
82
+
83
+ return sql;
84
+ }
85
+
86
+ - (NSString*)description
87
+ {
88
+ NSMutableString *description = [NSMutableString string];
89
+
90
+ [description appendString:@"\n"];
91
+ [description appendString:[NSString stringWithFormat:@"NanoSearch address : 0x%x\n", self]];
92
+ [description appendString:[NSString stringWithFormat:@"Document store : 0x%x\n", nanoStore]];
93
+ [description appendString:[NSString stringWithFormat:@"Attributes to be returned : %@\n", (attributesToBeReturned ? [attributesToBeReturned componentsJoinedByString:@","] : @"All")]];
94
+ [description appendString:[NSString stringWithFormat:@"Key : %@\n", key]];
95
+ [description appendString:[NSString stringWithFormat:@"Attribute : %@\n", attribute]];
96
+ [description appendString:[NSString stringWithFormat:@"Value : %@\n", value]];
97
+ [description appendString:[NSString stringWithFormat:@"Match : %@\n", NSFStringFromMatchType(match)]];
98
+ [description appendString:[NSString stringWithFormat:@"Expressions : %@\n", expressions]];
99
+ [description appendString:[NSString stringWithFormat:@"Group Values? : %@\n", (groupValues ? @"YES" : @"NO")]];
100
+ [description appendString:[NSString stringWithFormat:@"Sort : %@\n", sort]];
101
+
102
+ return description;
103
+ }
104
+
105
+ #pragma mark -
106
+
107
+ - (id)executeSQL:(NSString *)theSQLStatement returnType:(NSFReturnType)theReturnType error:(out NSError **)outError
108
+ {
109
+ if (nil == theSQLStatement) {
110
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
111
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is nil.", [self class], _cmd]
112
+ userInfo:nil]raise];
113
+ }
114
+
115
+ if (0 == [theSQLStatement length]) {
116
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
117
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is empty.", [self class], _cmd]
118
+ userInfo:nil]raise];
119
+ }
120
+
121
+ // Make sure we don't have any lingering parameters that could mess with the results, but keep the sort descriptor(s)
122
+ NSArray *savedSort = [self sort];
123
+ [self reset];
124
+ self.sort = savedSort;
125
+
126
+ returnedObjectType = theReturnType;
127
+ sql = [theSQLStatement copy];
128
+
129
+ NSDictionary *results = [self _retrieveDataWithError:outError];
130
+
131
+ return [self _sortResultsIfApplicable:results returnType:theReturnType];
132
+ }
133
+
134
+ - (NSFNanoResult *)executeSQL:(NSString *)theSQLStatement
135
+ {
136
+ if (nil == theSQLStatement) {
137
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
138
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is nil.", [self class], _cmd]
139
+ userInfo:nil]raise];
140
+ }
141
+
142
+ if (0 == [theSQLStatement length]) {
143
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
144
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is empty.", [self class], _cmd]
145
+ userInfo:nil]raise];
146
+ }
147
+
148
+ // Make sure we don't have any lingering parameters that could mess with the results...
149
+ [self reset];
150
+
151
+ sql = theSQLStatement;
152
+
153
+ return [nanoStore _executeSQL:theSQLStatement];
154
+ }
155
+
156
+ - (NSFNanoResult *)explainSQL:(NSString *)theSQLStatement
157
+ {
158
+ if (nil == theSQLStatement) {
159
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
160
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is nil.", [self class], _cmd]
161
+ userInfo:nil]raise];
162
+ }
163
+
164
+ if (0 == [theSQLStatement length]) {
165
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
166
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is empty.", [self class], _cmd]
167
+ userInfo:nil]raise];
168
+ }
169
+
170
+ return [nanoStore _executeSQL:[NSString stringWithFormat:@"EXPLAIN %@", theSQLStatement]];
171
+ }
172
+
173
+ - (void)reset
174
+ {
175
+ attributesToBeReturned= nil;
176
+ key = nil;
177
+ attribute = nil;
178
+ value = nil;
179
+ match = NSFContains;
180
+ groupValues = NO;
181
+ sql = nil;
182
+ sort = nil;
183
+
184
+ returnedObjectType = NSFReturnObjects;
185
+ }
186
+
187
+ #pragma mark -
188
+
189
+ - (id)searchObjectsWithReturnType:(NSFReturnType)theReturnType error:(out NSError **)outError
190
+ {
191
+ returnedObjectType = theReturnType;
192
+
193
+ // Make sure we don't have a SQL statement around...
194
+ sql = nil;
195
+
196
+ id results = [self _retrieveDataWithError:outError];
197
+
198
+ return [self _sortResultsIfApplicable:results returnType:theReturnType];
199
+ }
200
+
201
+ - (id)searchObjectsAdded:(NSFDateMatchType)theDateMatch date:(NSDate *)theDate returnType:(NSFReturnType)theReturnType error:(out NSError **)outError
202
+ {
203
+ returnedObjectType = theReturnType;
204
+
205
+ // Make sure we don't have a SQL statement around...
206
+ sql = nil;
207
+
208
+ id results = [self _retrieveDataAdded:theDateMatch calendarDate:theDate error:outError];
209
+
210
+ if (NSFReturnKeys == theReturnType) {
211
+ results = [results allKeys];
212
+ }
213
+
214
+ return results;
215
+ }
216
+
217
+ - (NSNumber *)aggregateOperation:(NSFAggregateFunctionType)theFunctionType onAttribute:(NSString *)theAttribute
218
+ {
219
+ NSFReturnType savedObjectTypeReturned = returnedObjectType;
220
+ returnedObjectType = NSFReturnKeys;
221
+
222
+ NSString *savedSQL = sql;
223
+ sql = nil;
224
+
225
+ NSString *theSearchSQLStatement = [self sql];
226
+ NSMutableString *theAggregatedSQLStatement = [NSMutableString new];
227
+
228
+ switch (theFunctionType) {
229
+ case NSFAverage:
230
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT avg(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
231
+ break;
232
+ case NSFCount:
233
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT count(*) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
234
+ break;
235
+ case NSFMax:
236
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT max(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
237
+ break;
238
+ case NSFMin:
239
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT min(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
240
+ break;
241
+ case NSFTotal:
242
+ /* Note:
243
+ Sum() will throw an "integer overflow" exception if all inputs are integers or NULL and an integer overflow occurs at any point
244
+ during the computation. Total() never throws an integer overflow.
245
+ */
246
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT total(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
247
+ break;
248
+ default:
249
+ break;
250
+ }
251
+
252
+ NSFNanoResult *result = [nanoStore _executeSQL:theAggregatedSQLStatement];
253
+
254
+ returnedObjectType = savedObjectTypeReturned;
255
+ sql = savedSQL;
256
+
257
+ return [NSNumber numberWithFloat:[[result firstValue]floatValue]];
258
+ }
259
+
260
+ #pragma mark -
261
+ #pragma mark Private Methods
262
+ #pragma mark -
263
+
264
+ /** \cond */
265
+
266
+ - (NSDictionary *)_retrieveDataWithError:(out NSError **)outError
267
+ {
268
+ if (YES == [nanoStore isClosed]) {
269
+ return nil;
270
+ }
271
+
272
+ NSMutableDictionary *searchResults = [NSMutableDictionary dictionary];
273
+
274
+ NSString *aSQLQuery = sql;
275
+
276
+ if (nil != aSQLQuery) {
277
+ // We are going to check whether the user has specified the proper columns based on the search type selected.
278
+ // This is to avoid crashing, since the user shouldn't have to know which columns are involved on each type
279
+ // of search.
280
+ // We basically honor the specified query but replace the columns with the expected ones per returned type.
281
+
282
+ NSString *subStatement = [aSQLQuery substringFromIndex:[aSQLQuery rangeOfString:@"FROM" options:NSCaseInsensitiveSearch].location];
283
+ NSFReturnType returnType = returnedObjectType;
284
+ switch (returnType) {
285
+ case NSFReturnKeys:
286
+ aSQLQuery = [NSString stringWithFormat:@"SELECT NSFKey %@", subStatement];
287
+ break;
288
+ case NSFReturnObjects:
289
+ aSQLQuery = [NSString stringWithFormat:@"SELECT NSFKey, NSFPlist, NSFObjectClass %@", subStatement];
290
+ break;
291
+ }
292
+ } else {
293
+ aSQLQuery = [self _preparedSQL];
294
+ }
295
+
296
+ _NSFLog(@"_dataWithKey SQL query: %@", aSQLQuery);
297
+
298
+ sqlite3 *sqliteStore = [[nanoStore nanoStoreEngine]sqlite];
299
+ sqlite3_stmt *theSQLiteStatement = NULL;
300
+
301
+ int status = sqlite3_prepare_v2 (sqliteStore, [aSQLQuery UTF8String], -1, &theSQLiteStatement, NULL );
302
+
303
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
304
+
305
+ if (SQLITE_OK == status) {
306
+ switch (returnedObjectType) {
307
+ case NSFReturnKeys:
308
+ while (SQLITE_ROW == sqlite3_step (theSQLiteStatement)) {
309
+ // Sanity check: some queries return NULL, which would cause a crash below.
310
+ char *valueUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 0);
311
+ NSString *theValue = nil;
312
+ if (NULL != valueUTF8) {
313
+ theValue = [[NSString alloc]initWithUTF8String:valueUTF8];
314
+ } else {
315
+ theValue = [[NSNull null]description];
316
+ }
317
+
318
+ [searchResults setObject:[NSNull null] forKey:theValue];
319
+ }
320
+ break;
321
+ default:
322
+ while (SQLITE_ROW == sqlite3_step (theSQLiteStatement)) {
323
+ char *keyUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 0);
324
+ char *dictXMLUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 1);
325
+ char *objectClassUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 2);
326
+
327
+ // Sanity check: some queries return NULL, which would a crash below.
328
+ // Since these are values that are NanoStore's resposibility, they should *never* be NULL. Log it for posterity.
329
+ if ((NULL == keyUTF8) || (NULL == dictXMLUTF8) || (NULL == objectClassUTF8)) {
330
+ NSLog(@"*** Warning! These values are NanoStore's resposibility and should *never* be NULL: keyUTF8 (%s) - dictXMLUTF8 (%s) - objectClassUTF8 (%s)", keyUTF8, dictXMLUTF8, objectClassUTF8);
331
+ continue;
332
+ }
333
+
334
+ NSString *keyValue = [[NSString alloc]initWithUTF8String:keyUTF8];
335
+ NSString *dictXML = [[NSString alloc]initWithUTF8String:dictXMLUTF8];
336
+ NSString *objectClass = [[NSString alloc]initWithUTF8String:objectClassUTF8];
337
+
338
+ NSDictionary *info = [NSFNanoEngine _plistToDictionary:dictXML];
339
+ if (nil == info) {
340
+ continue;
341
+ }
342
+
343
+ if ([attributesToBeReturned count] == 0) {
344
+ // Will be released below...
345
+ } else {
346
+ // Since we want a subset of the attributes, we need to traverse
347
+ // the attribute list and find out whether the dictionary contains
348
+ // the specified attributes. If so, add them to a subset which will
349
+ // be returned as requested.
350
+
351
+ NSMutableDictionary *subset = [NSMutableDictionary new];
352
+
353
+ for (NSString *attributeValue in attributesToBeReturned) {
354
+ id theValue = [info valueForKeyPath:attributeValue];
355
+ if (nil != theValue) {
356
+ if (NSNotFound == [attributeValue rangeOfString:@"."].location) {
357
+ [subset setValue:theValue forKeyPath:attributeValue];
358
+ } else {
359
+ NSDictionary *subInfo = [self _dictionaryForKeyPath:attributeValue value:theValue];
360
+ if ([subInfo count] > 0) {
361
+ NSString *subInfoKey = [[subInfo allKeys]objectAtIndex:0];
362
+ NSString *subInfoValue = [subInfo objectForKey:subInfoKey];
363
+ [subset setValue:subInfoValue forKey:subInfoKey];
364
+ }
365
+ }
366
+ }
367
+ }
368
+
369
+ // Will be released below...
370
+ info = subset;
371
+ }
372
+
373
+ Class storedObjectClass = NSClassFromString(objectClass);
374
+ BOOL saveOriginalClassReference = NO;
375
+ if (nil == storedObjectClass) {
376
+ storedObjectClass = [NSFNanoObject class];
377
+ saveOriginalClassReference = YES;
378
+ }
379
+
380
+ id nanoObject = [[storedObjectClass alloc]initNanoObjectFromDictionaryRepresentation:info forKey:keyValue store:nanoStore];
381
+
382
+ // If this process does not have knowledge of the original class as was saved in the store, keep a reference
383
+ // so that we can later on restore the object properly (otherwise it would be stored as a NanoObject.)
384
+ if (YES == saveOriginalClassReference) {
385
+ [nanoObject _setOriginalClassString:objectClass];
386
+ }
387
+
388
+ [searchResults setObject:nanoObject forKey:keyValue];
389
+
390
+ }
391
+ break;
392
+ }
393
+
394
+ sqlite3_finalize (theSQLiteStatement);
395
+
396
+ } else {
397
+ if (nil != outError) {
398
+ NSString *msg = [NSString stringWithFormat:@"SQLite error ID: %ld", status];
399
+ *outError = [NSError errorWithDomain:NSFDomainKey
400
+ code:NSFNanoStoreErrorKey
401
+ userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"*** -[%@ %s]: %@", [self class], _cmd, msg]
402
+ forKey:NSLocalizedFailureReasonErrorKey]];
403
+ }
404
+ searchResults = nil;
405
+ }
406
+
407
+ return searchResults;
408
+ }
409
+
410
+ - (NSDictionary *)_retrieveDataAdded:(NSFDateMatchType)aDateMatch calendarDate:(NSDate *)aDate error:(out NSError **)outError
411
+ {
412
+ if ([nanoStore isClosed] == YES) {
413
+ return nil;
414
+ }
415
+
416
+ NSString *theSQLStatement = nil;
417
+ NSString *normalizedDateString = [NSFNanoStore _calendarDateToString:aDate];
418
+
419
+ switch (aDateMatch) {
420
+ case NSFBeforeDate:
421
+ theSQLStatement = [[NSString alloc]initWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ < '%@'", NSFKey, NSFPlist, NSFObjectClass, NSFKeys, NSFCalendarDate, normalizedDateString];
422
+ break;
423
+ case NSFOnDate:
424
+ theSQLStatement = [[NSString alloc]initWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ = '%@'", NSFKey, NSFPlist, NSFObjectClass, NSFKeys, NSFCalendarDate, normalizedDateString];
425
+ break;
426
+ case NSFAfterDate:
427
+ theSQLStatement = [[NSString alloc]initWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ > '%@'", NSFKey, NSFPlist, NSFObjectClass, NSFKeys, NSFCalendarDate, normalizedDateString];
428
+ break;
429
+ }
430
+
431
+ NSFNanoResult *result = [nanoStore _executeSQL:theSQLStatement];
432
+
433
+ NSMutableDictionary *searchResults = [NSMutableDictionary dictionaryWithCapacity:result.numberOfRows];
434
+
435
+ if (result.numberOfRows > 0) {
436
+ if (NSFReturnKeys == returnedObjectType) {
437
+ NSArray *resultsKeys = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFKey]];
438
+ for (NSString *resultKey in resultsKeys)
439
+ [searchResults setObject:[NSNull null] forKey:resultKey];
440
+ return searchResults;
441
+ } else {
442
+ NSArray *resultsObjectClass = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFObjectClass]];
443
+ NSArray *resultsObjects = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFPlist]];
444
+ NSArray *resultsKeys = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFKey]];
445
+ NSUInteger i, count = [resultsKeys count];
446
+
447
+ for (i = 0; i < count; i++) {
448
+ @autoreleasepool {
449
+ NSDictionary *info = [NSFNanoEngine _plistToDictionary:[resultsObjects objectAtIndex:i]];
450
+ if (nil != info) {
451
+ NSString *keyValue = [resultsKeys objectAtIndex:i];
452
+
453
+ NSString *className = [resultsObjectClass objectAtIndex:i];
454
+ Class storedObjectClass = NSClassFromString(className);
455
+ BOOL saveOriginalClassReference = NO;
456
+ if (nil == storedObjectClass) {
457
+ storedObjectClass = [NSFNanoObject class];
458
+ saveOriginalClassReference = YES;
459
+ }
460
+
461
+ id nanoObject = [[storedObjectClass alloc]initNanoObjectFromDictionaryRepresentation:info forKey:keyValue store:nanoStore];
462
+
463
+ // If this process does not have knowledge of the original class as was saved in the store, keep a reference
464
+ // so that we can later on restore the object properly (otherwise it would be stored as a NanoObject.)
465
+ if (YES == saveOriginalClassReference) {
466
+ [nanoObject _setOriginalClassString:className];
467
+ }
468
+
469
+ [searchResults setObject:nanoObject forKey:keyValue];
470
+ }
471
+ }
472
+ }
473
+ }
474
+ }
475
+
476
+ return searchResults;
477
+ }
478
+
479
+ - (NSString *)_preparedSQL
480
+ {
481
+ NSString *aSQLQuery = nil;
482
+
483
+ if (nil == expressions) {
484
+ aSQLQuery = [self _prepareSQLQueryStringWithKey:key attribute:attribute value:value matching:match];
485
+ } else {
486
+ aSQLQuery = [self _prepareSQLQueryStringWithExpressions:expressions];
487
+ }
488
+
489
+ return aSQLQuery;
490
+ }
491
+
492
+ - (NSString *)_prepareSQLQueryStringWithKey:(NSString *)aKey attribute:(NSString *)anAttribute value:(id)aValue matching:(NSFMatchType)aMatch
493
+ {
494
+ NSMutableString *theSQLStatement = nil;
495
+ NSString *attributes = nil;
496
+
497
+ if (nil != attributesToBeReturned) {
498
+ // Prepare the list of attributes we need to gather. Include NSFKEY as well.
499
+ NSMutableSet *set = [[NSMutableSet alloc]initWithArray:attributesToBeReturned];
500
+ NSArray *objects = [set allObjects];
501
+ NSMutableArray *quotedObjects = [NSMutableArray new];
502
+ for (NSString *object in objects) {
503
+ NSString *theValue = [[NSString alloc]initWithFormat:@"'%@'", object];
504
+ [quotedObjects addObject:theValue];
505
+ }
506
+ attributes = [quotedObjects componentsJoinedByString:@","];
507
+ }
508
+
509
+ NSFReturnType returnType = returnedObjectType;
510
+
511
+ if ((nil == aKey) && (nil == anAttribute) && (nil == aValue)) {
512
+ switch (returnType) {
513
+ case NSFReturnKeys:
514
+ return @"SELECT NSFKEY FROM NSFKeys";
515
+ break;
516
+ default:
517
+ return @"SELECT NSFKey, NSFPlist, NSFObjectClass FROM NSFKeys";
518
+ break;
519
+ }
520
+ } else {
521
+ switch (returnType) {
522
+ case NSFReturnKeys:
523
+ if (NO == groupValues) {
524
+ theSQLStatement = [NSMutableString stringWithString:@"SELECT DISTINCT (NSFKEY) FROM NSFValues WHERE "];
525
+ } else {
526
+ theSQLStatement = [NSMutableString stringWithString:@"SELECT NSFKEY FROM NSFValues WHERE "];
527
+ }
528
+ break;
529
+ default:
530
+ theSQLStatement = [NSMutableString stringWithString:@"SELECT NSFKEY FROM NSFValues WHERE "];
531
+ break;
532
+ }
533
+ }
534
+
535
+ NSString *segment = nil;
536
+ BOOL querySegmentWasAdded = NO;
537
+
538
+ if (nil != aKey) {
539
+ if ((nil == anAttribute) && (nil == aValue))
540
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFKey value:aKey matching:aMatch];
541
+ else
542
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFKey value:aKey matching:NSFEqualTo];
543
+ [theSQLStatement appendString:segment];
544
+ querySegmentWasAdded = YES;
545
+ }
546
+
547
+ if (nil != anAttribute) {
548
+ if (YES == querySegmentWasAdded) {
549
+ [theSQLStatement appendString:@" AND "];
550
+ }
551
+
552
+ // We need to introspect whether the attribute contains a dot "." or not. Based on the case, we'll need to GLOB the attribute
553
+ // or leave it as is.
554
+
555
+ if (NSNotFound == [anAttribute rangeOfString:@"."].location) {
556
+ segment = [NSFNanoSearch _querySegmentForAttributeColumnWithValue:anAttribute matching:aMatch valueColumnWithValue:aValue];
557
+ } else {
558
+ if (nil == aValue)
559
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFAttribute value:anAttribute matching:aMatch];
560
+ else
561
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFAttribute value:anAttribute matching:NSFEqualTo];
562
+ }
563
+
564
+ [theSQLStatement appendString:segment];
565
+ } else {
566
+ if (nil != aValue) {
567
+ if (YES == querySegmentWasAdded)
568
+ [theSQLStatement appendString:@" AND "];
569
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFValue value:aValue matching:aMatch];
570
+ [theSQLStatement appendString:segment];
571
+ }
572
+ }
573
+
574
+ if (YES == groupValues) {
575
+ [theSQLStatement appendString:@" GROUP BY NSFValue"];
576
+ }
577
+
578
+ if (NSFReturnObjects == returnType) {
579
+ if (nil != attributes)
580
+ theSQLStatement = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theSQLStatement];
581
+ else
582
+ theSQLStatement = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theSQLStatement];
583
+ }
584
+
585
+ return theSQLStatement;
586
+ }
587
+
588
+ - (NSString *)_prepareSQLQueryStringWithExpressions:(NSArray *)someExpressions
589
+ {
590
+ NSUInteger i, count = [someExpressions count];
591
+ NSMutableArray *sqlComponents = [NSMutableArray new];
592
+ NSMutableString *parentheses = [NSMutableString new];
593
+ NSFReturnType returnType = returnedObjectType;
594
+
595
+ for (i = 0; i < count; i++) {
596
+ NSFNanoExpression *expression = [someExpressions objectAtIndex:i];
597
+ NSMutableString *theSQL = nil;;
598
+
599
+ if (NSFReturnObjects == returnType)
600
+ theSQL = [[NSMutableString alloc]initWithFormat:@"SELECT NSFKEY FROM NSFValues WHERE %@", [expression description]];
601
+ else
602
+ theSQL = [[NSMutableString alloc]initWithFormat:@"SELECT DISTINCT (NSFKEY) FROM NSFValues WHERE %@", [expression description]];
603
+
604
+ if ((count > 1) && (i < count-1)) {
605
+ [theSQL appendString:@" AND NSFKEY IN ("];
606
+ [parentheses appendString:@")"];
607
+ }
608
+
609
+ [sqlComponents addObject:theSQL];
610
+ }
611
+
612
+ if ([parentheses length] > 0)
613
+ [sqlComponents addObject:parentheses];
614
+
615
+ NSString *theValue = [sqlComponents componentsJoinedByString:@""];
616
+
617
+ if (NSFReturnObjects == returnType)
618
+ theValue = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theValue];
619
+
620
+ return theValue;
621
+ }
622
+
623
+ + (NSString *)_prepareSQLQueryStringWithKeys:(NSArray *)someKeys
624
+ {
625
+ //Prepare the keys by single quoting them...
626
+ NSMutableArray *preparedKeys = [NSMutableArray new];
627
+ for (NSString *theKey in someKeys) {
628
+ NSString *quotedKey = [[NSString alloc]initWithFormat:@"'%@'", theKey];
629
+ [preparedKeys addObject:quotedKey];
630
+ }
631
+
632
+ NSMutableString *theSQLStatement = [NSMutableString stringWithString:@"SELECT DISTINCT (NSFKEY) FROM NSFValues WHERE NSFKey IN ("];
633
+ [theSQLStatement appendString:[preparedKeys componentsJoinedByString:@","]];
634
+ [theSQLStatement appendString:@")"];
635
+ theSQLStatement = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theSQLStatement];
636
+
637
+ return theSQLStatement;
638
+ }
639
+
640
+ + (NSString *)_querySegmentForColumn:(NSString *)aColumn value:(id)aValue matching:(NSFMatchType)match
641
+ {
642
+ NSMutableString *segment = [NSMutableString string];
643
+ NSMutableString *value = nil;
644
+ NSMutableString *mutatedString = nil;
645
+ NSInteger mutatedStringLength = 0;
646
+ unichar sentinelChar;
647
+
648
+ if (YES == [aValue isKindOfClass:[NSString class]]) {
649
+ switch (match) {
650
+ case NSFEqualTo:
651
+ value = [[NSMutableString alloc]initWithFormat:@"%@ = '%@'", aColumn, aValue];
652
+ [segment appendString:value];
653
+ break;
654
+ case NSFBeginsWith:
655
+ sentinelChar = [aValue characterAtIndex:[aValue length] - 1] + 1;
656
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ >= '%@' AND %@ < '%@%c')", aColumn, aValue, aColumn, aValue, sentinelChar];
657
+ [segment appendString:value];
658
+ break;
659
+ case NSFContains:
660
+ value = [[NSMutableString alloc]initWithFormat:@"%@ GLOB '*%@*'", aColumn, aValue];
661
+ [segment appendString:value];
662
+ break;
663
+ case NSFEndsWith:
664
+ value = [[NSMutableString alloc]initWithFormat:@"%@ GLOB '*%@'", aColumn, aValue];
665
+ [segment appendString:value];
666
+ break;
667
+ case NSFInsensitiveEqualTo:
668
+ value = [[NSMutableString alloc]initWithFormat:@"upper(%@) = '%@'", aColumn, [aValue uppercaseString]];
669
+ [segment appendString:value];
670
+ break;
671
+ case NSFInsensitiveBeginsWith:
672
+ mutatedString = [[NSMutableString alloc]initWithString:aValue];
673
+ mutatedStringLength = [aValue length];
674
+ value = [[NSMutableString alloc]initWithFormat:@"%c", [mutatedString characterAtIndex:mutatedStringLength - 1]+1];
675
+ [mutatedString replaceCharactersInRange:NSMakeRange(mutatedStringLength - 1, 1) withString:value];
676
+ value = [[NSMutableString alloc]initWithFormat:@"(upper(%@) >= '%@' AND upper(%@) < '%@')", aColumn, [aValue uppercaseString], aColumn, [mutatedString uppercaseString]];
677
+ [segment appendString:value];
678
+ break;
679
+ case NSFInsensitiveContains:
680
+ value = [[NSMutableString alloc]initWithFormat:@"%@ LIKE '%@%@%@'", aColumn, @"%", aValue, @"%"];
681
+ [segment appendString:value];
682
+ break;
683
+ case NSFInsensitiveEndsWith:
684
+ value = [[NSMutableString alloc]initWithFormat:@"%@ LIKE '%@%@'", aColumn, @"%", aValue];
685
+ [segment appendString:value];
686
+ break;
687
+ case NSFGreaterThan:
688
+ value = [[NSMutableString alloc]initWithFormat:@"%@ > '%@'", aColumn, aValue];
689
+ [segment appendString:value];
690
+ break;
691
+ case NSFLessThan:
692
+ value = [[NSMutableString alloc]initWithFormat:@"%@ < '%@'", aColumn, aValue];
693
+ [segment appendString:value];
694
+ break;
695
+ }
696
+ } else if (YES == [aValue isKindOfClass:[NSArray class]]) {
697
+ // Quote the parameters
698
+ NSMutableArray *quotedParameters = [[NSMutableArray alloc]initWithCapacity:[aValue count]];
699
+ value = [[NSMutableString alloc]initWithFormat:@"%@ IN (", aColumn];
700
+ for (NSString *parameter in aValue) {
701
+ NSString *quotedParameter = [[NSString alloc]initWithFormat:@"'%@'", parameter];
702
+ [quotedParameters addObject:quotedParameter];
703
+ }
704
+ //Add them to the string delimited by string
705
+ [value appendString:[quotedParameters componentsJoinedByString:@","]];
706
+ [value appendString:@")"];
707
+
708
+ // Complete the query segment
709
+ [segment appendString:value];
710
+
711
+ // Free allocated resources
712
+ }
713
+
714
+ return segment;
715
+ }
716
+
717
+ + (NSString *)_querySegmentForAttributeColumnWithValue:(id)anAttributeValue matching:(NSFMatchType)match valueColumnWithValue:(id)aValue
718
+ {
719
+ NSMutableString *segment = [NSMutableString string];
720
+ NSMutableString *value = nil;
721
+
722
+ if ((YES == [aValue isKindOfClass:[NSString class]]) || (nil == aValue)) {
723
+ if (nil == aValue) {
724
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@') OR (%@ GLOB '%@.*') OR (%@ GLOB '*.%@.*') OR (%@ GLOB '*.%@')", NSFAttribute, anAttributeValue, NSFAttribute, anAttributeValue, NSFAttribute, anAttributeValue, NSFAttribute, anAttributeValue];
725
+ [segment appendString:value];
726
+ } else {
727
+ switch (match) {
728
+ case NSFEqualTo:
729
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ = '%@') OR (%@ GLOB '%@.*' AND %@ = '%@') OR (%@ GLOB '*.%@.*' AND %@ = '%@') OR (%@ GLOB '*.%@' AND %@ = '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
730
+ [segment appendString:value];
731
+ break;
732
+ case NSFBeginsWith:
733
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ GLOB '%@*') OR (%@ GLOB '%@.*' AND %@ GLOB '%@*') OR (%@ GLOB '*.%@.*' AND %@ GLOB '%@*') OR (%@ GLOB '*.%@' AND %@ GLOB '%@*')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
734
+ [segment appendString:value];
735
+ break;
736
+ case NSFContains:
737
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ GLOB '%@') OR (%@ GLOB '%@.*' AND %@ GLOB '%@') OR (%@ GLOB '*.%@.*' AND %@ GLOB '%@') OR (%@ GLOB '*.%@' AND %@ GLOB '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
738
+ [segment appendString:value];
739
+ break;
740
+ case NSFEndsWith:
741
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ GLOB '*%@') OR (%@ GLOB '%@.*' AND %@ GLOB '*%@') OR (%@ GLOB '*.%@.*' AND %@ GLOB '*%@') OR (%@ GLOB '*.%@' AND %@ GLOB '*%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
742
+ [segment appendString:value];
743
+ break;
744
+ case NSFInsensitiveEqualTo:
745
+ aValue = [aValue uppercaseString];
746
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND upper(%@) = '%@') OR (%@ GLOB '%@.*' AND upper(%@) = '%@') OR (%@ GLOB '*.%@.*' AND upper(%@) = '%@') OR (%@ GLOB '*.%@' AND upper(%@) = '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
747
+ [segment appendString:value];
748
+ break;
749
+ case NSFInsensitiveBeginsWith:
750
+ aValue = [aValue uppercaseString];
751
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND upper(%@) GLOB '%@*') OR (%@ GLOB '%@.*' AND upper(%@) GLOB '%@*') OR (%@ GLOB '*.%@.*' AND upper(%@) GLOB '%@*') OR (%@ GLOB '*.%@' AND upper(%@) GLOB '%@*')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
752
+ [segment appendString:value];
753
+ break;
754
+ case NSFInsensitiveContains:
755
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ LIKE '%@') OR (%@ GLOB '%@.*' AND %@ LIKE '%@') OR (%@ GLOB '*.%@.*' AND %@ LIKE '%@') OR (%@ GLOB '*.%@' AND %@ LIKE '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
756
+ [segment appendString:value];
757
+ break;
758
+ case NSFInsensitiveEndsWith:
759
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ LIKE '%%%@') OR (%@ GLOB '%@.*' AND %@ LIKE '%%%@') OR (%@ GLOB '*.%@.*' AND %@ LIKE '%%%@') OR (%@ GLOB '*.%@' AND %@ LIKE '%%%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
760
+ [segment appendString:value];
761
+ break;
762
+ case NSFGreaterThan:
763
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ > '%@') OR (%@ GLOB '%@.*' AND %@ > '%@') OR (%@ GLOB '*.%@.*' AND %@ > '%@') OR (%@ GLOB '*.%@' AND %@ > '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
764
+ [segment appendString:value];
765
+ break;
766
+ case NSFLessThan:
767
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ < '%@') OR (%@ GLOB '%@.*' AND %@ < '%@') OR (%@ GLOB '*.%@.*' AND %@ < '%@') OR (%@ GLOB '*.%@' AND %@ < '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
768
+ [segment appendString:value];
769
+ break;
770
+ }
771
+ }
772
+ } else if (YES == [aValue isKindOfClass:[NSArray class]]) {
773
+ // Quote the parameters
774
+ NSMutableArray *quotedParameters = [[NSMutableArray alloc]initWithCapacity:[aValue count]];
775
+ value = [[NSMutableString alloc]initWithFormat:@"%@ IN (", NSFAttribute];
776
+ for (NSString *parameter in aValue) {
777
+ NSString *quotedParameter = [[NSString alloc]initWithFormat:@"'%@'", parameter];
778
+ [quotedParameters addObject:quotedParameter];
779
+ }
780
+ //Add them to the string delimited by string
781
+ [value appendString:[quotedParameters componentsJoinedByString:@","]];
782
+ [value appendString:@")"];
783
+
784
+ // Complete the query segment
785
+ [segment appendString:value];
786
+
787
+ // Free allocated resources
788
+ }
789
+
790
+ return segment;
791
+ }
792
+
793
+ - (NSDictionary *)_dictionaryForKeyPath:(NSString *)keyPath value:(id)theValue
794
+ {
795
+ NSMutableDictionary *info = [NSMutableDictionary dictionary];
796
+ NSMutableArray *keys = [[keyPath componentsSeparatedByString:@"."]mutableCopy];
797
+
798
+ if ([keys count] == 1) {
799
+ [info setObject:theValue forKey:keyPath];
800
+ return info;
801
+ }
802
+
803
+ NSInteger i;
804
+
805
+ for (i = 0; i < [keys count]; i++) {
806
+ NSString *keyValue = [keys objectAtIndex:i];
807
+ [keys removeObjectAtIndex:0];
808
+ NSDictionary *subInfo = [self _dictionaryForKeyPath:[keys componentsJoinedByString:@"."] value:theValue];
809
+ if (nil != subInfo)
810
+ [info setObject:subInfo forKey:keyValue];
811
+ }
812
+
813
+ return info;
814
+ }
815
+
816
+ + (NSString *)_quoteStrings:(NSArray *)strings joiningWithDelimiter:(NSString *)delimiter
817
+ {
818
+ NSMutableArray *quotedParameters = [[NSMutableArray alloc]initWithCapacity:[strings count]];
819
+ for (NSString *string in strings) {
820
+ NSString *quotedParameter = [[NSString alloc]initWithFormat:@"\"%@\"", string];
821
+ [quotedParameters addObject:quotedParameter];
822
+ }
823
+
824
+ NSString *quotedString = [quotedParameters componentsJoinedByString:@","];
825
+
826
+
827
+ return quotedString;
828
+ }
829
+
830
+ - (id)_sortResultsIfApplicable:(NSDictionary *)results returnType:(NSFReturnType)theReturnType
831
+ {
832
+ id theResults = results;
833
+
834
+ if (nil != sort) {
835
+ NSMutableArray *cocoaSortDescriptors = [NSMutableArray new];
836
+
837
+ for (NSFNanoSortDescriptor *descriptor in sort) {
838
+ NSString *targetKeyPath = [[NSString alloc]initWithFormat:@"rootObject.%@", descriptor.attribute];
839
+ NSSortDescriptor *cocoaSort = [[NSSortDescriptor alloc]initWithKey:targetKeyPath ascending:descriptor.isAscending];
840
+ [cocoaSortDescriptors addObject:cocoaSort];
841
+ }
842
+
843
+ if (NSFReturnObjects == theReturnType) {
844
+ theResults = [[results allValues]sortedArrayUsingDescriptors:cocoaSortDescriptors];
845
+ } else {
846
+ theResults = [results allKeys];
847
+ }
848
+ }
849
+ else if (NSFReturnKeys == theReturnType)
850
+ {
851
+ theResults = [results allKeys];
852
+ }
853
+
854
+ return theResults;
855
+ }
856
+
857
+ /** \endcond */
858
+
859
+ @end