nano-store 0.2.3

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