Skip to content

Commit

Permalink
Restore deleted CBLQuery fullTextSnippets/Ranking API
Browse files Browse the repository at this point in the history
Was accidentally removed during ForestDB storage development.
These aren't supported yet with ForestDB but are with SQLite.
Fixes #709
  • Loading branch information
snej authored and pasin committed May 15, 2015
1 parent a95cef8 commit 7991932
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 13 deletions.
21 changes: 21 additions & 0 deletions Source/API/CBLQuery+FullTextSearch.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@
They also can't be reduced or grouped, so those properties are ignored too. */
@property (copy, nullable) NSString* fullTextQuery;

/** If set to YES, the query will collect snippets of the text surrounding each match, available
via the CBLFullTextQueryRow's -snippetWithWordStart:wordEnd: method.
(NOTE: ForestDB currently does not support snippets.) */
@property BOOL fullTextSnippets;

/** If YES (the default) the full-text query result rows will be sorted by (approximate) relevance.
If set to NO, the rows will be returned in the order the documents were added to the database,
i.e. essentially unordered; this is somewhat faster, so it can be useful if you don't care
about the ordering of the rows.
(NOTE: ForestDB currently does not support ranking.) */
@property BOOL fullTextRanking;

@end


Expand All @@ -49,6 +61,15 @@
match(es). */
@property (readonly, nullable) NSString* fullText;

/** Returns a short substring of the full text containing at least some of the matched words.
This is useful to display in search results, and is faster than fetching the .fullText.
NOTE: The "fullTextSnippets" property of the CBLQuery must be set to YES to enable this;
otherwise the result will be nil.
@param wordStart A delimiter that will be inserted before every instance of a match.
@param wordEnd A delimiter that will be inserted after every instance of a match. */
- (NSString*) snippetWithWordStart: (NSString*)wordStart
wordEnd: (NSString*)wordEnd;

/** The number of query words that were found in the fullText.
(If a query word appears more than once, only the first instance is counted.) */
@property (readonly) NSUInteger matchCount;
Expand Down
18 changes: 18 additions & 0 deletions Source/API/CBLQuery+FullTextSearch.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ - (void) setFullTextRanking:(BOOL)fullTextRanking {_fullTextRanking = fullText
@implementation CBLFullTextQueryRow
{
UInt64 _fullTextID;
NSString* _snippet;
NSMutableArray* _matchOffsets;
}

@synthesize snippet=_snippet;

- (instancetype) initWithDocID: (NSString*)docID
sequence: (SequenceNumber)sequence
Expand Down Expand Up @@ -92,11 +94,27 @@ - (NSRange) textRangeOfMatch: (NSUInteger)matchNumber {
}


- (NSString*) snippetWithWordStart: (NSString*)wordStart
wordEnd: (NSString*)wordEnd
{
if (!_snippet)
return nil;
NSMutableString* snippet = [_snippet mutableCopy];
[snippet replaceOccurrencesOfString: @"\001" withString: wordStart
options:NSLiteralSearch range:NSMakeRange(0, snippet.length)];
[snippet replaceOccurrencesOfString: @"\002" withString: wordEnd
options:NSLiteralSearch range:NSMakeRange(0, snippet.length)];
return snippet;
}


// Overridden to add FTS result info
- (NSDictionary*) asJSONDictionary {
NSMutableDictionary* dict = [[super asJSONDictionary] mutableCopy];
if (!dict[@"error"]) {
[dict removeObjectForKey: @"key"];
if (_snippet)
dict[@"snippet"] = [self snippetWithWordStart: @"[" wordEnd: @"]"];
if (_matchOffsets) {
NSMutableArray* matches = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < _matchOffsets.count; i += 4) {
Expand Down
1 change: 1 addition & 0 deletions Source/API/CouchbaseLitePrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
fullTextID: (UInt64)fullTextID
value: (id)value
storage: (id<CBL_QueryRowStorage>)storage;
@property (copy) NSString* snippet;
- (void) addTerm: (NSUInteger)term atRange: (NSRange)range;
@end

Expand Down
4 changes: 2 additions & 2 deletions Source/CBL_SQLiteViewStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -881,8 +881,8 @@ - (CBLQueryIteratorBlock) fullTextQueryWithOptions: (const CBLQueryOptions*)opti
[row addTerm: term atRange: NSMakeRange(location, length)];
}

// if (options->fullTextSnippets)
// row.snippet = [r stringForColumnIndex: 5];
if (options->fullTextSnippets)
row.snippet = [r stringForColumnIndex: 5];
if (!options.filter || options.filter(row))
[rows addObject: row];
}
Expand Down
22 changes: 11 additions & 11 deletions Unit-Tests/ViewInternal_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1199,10 +1199,12 @@ - (void) test23_FullTextQuery {
AssertEq(rows.count, 0u);
}

#if 0 // Boolean operators and snippets are not available (yet) with ForestDB

- (void) test24_FullTextQuery_Advanced {
if (!self.isSQLiteDB)
return; // Boolean operators and snippets are not available (yet) with ForestDB

RequireTestCase(CBL_View_FullTextQuery);
CBLDatabase *db = createDB();
CBLStatus status;

NSMutableArray* docs = $marray();
Expand All @@ -1213,10 +1215,9 @@ - (void) test24_FullTextQuery_Advanced {
[docs addObject: [self putDoc: $dict({@"_id", @"55555"}, {@"text", @"was barking."})]];

CBLView* view = [db viewNamed: @"fts"];
view.indexType = kCBLFullTextIndex;
[view setMapBlock: MAPBLOCK({
if (doc[@"text"])
emit(doc[@"text"], doc[@"_id"]);
emit(CBLTextKey(doc[@"text"]), doc[@"_id"]);
}) reduceBlock: NULL version: @"1"];

AssertEq([view updateIndex], kCBLStatusOK);
Expand All @@ -1229,21 +1230,21 @@ - (void) test24_FullTextQuery_Advanced {
Assert(rowIter, @"_queryFullText failed: %d", status);
Log(@"rows = %@", rowIter);
NSArray* expectedRows = $array($dict({@"id", @"44444"},
{@"matches", @[@{@"range": @[@4, @7], @"term": @0}]},
{@"matches", @[@{@"range": @[@4, @6], @"term": @0}]},
{@"snippet", @"and [STöRMy] night."},
{@"value", @"44444"}),
$dict({@"id", @"33333"},
{@"matches", @[@{@"range": @[@2, @3], @"term": @1},
@{@"range": @[@26, @3], @"term": @1}]},
@{@"range": @[@22, @3], @"term": @1}]},
{@"snippet", @"a [dog] whøse ñame was “[Dog]”"},
{@"value", @"33333"}));
AssertEqual(rowsToDicts(rowIter), expectedRows);
AssertEqualish(rowsToDicts(rowIter), expectedRows);

// Try a query with snippets:
CBLQuery* query = [view createQuery];
query.fullTextQuery = @"(was NOT barking) OR dog";
query.fullTextSnippets = YES;
rows = [[query run: NULL] allObjects];
NSArray* rows = [[query run: NULL] allObjects];
AssertEq(rows.count, 2u);

CBLFullTextQueryRow* row = rows[0];
Expand Down Expand Up @@ -1287,12 +1288,11 @@ - (void) test24_FullTextQuery_Advanced {
Log(@"after deletion, rows = %@", rowIter);

expectedRows = $array($dict({@"id", @"44444"},
{@"matches", @[@{@"range": @[@4, @7], @"term": @0}]},
{@"matches", @[@{@"range": @[@4, @6], @"term": @0}]},
{@"snippet", @"and [STöRMy] night."},
{@"value", @"44444"}));
AssertEqual(rowsToDicts(rowIter), expectedRows);
AssertEqualish(rowsToDicts(rowIter), expectedRows);
}
#endif


- (void) test25_TotalDocs {
Expand Down

0 comments on commit 7991932

Please sign in to comment.