Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 45 additions & 25 deletions source/Search.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import getNestedFieldValue from './getNestedFieldValue';
import getNestedFieldValues from './getNestedFieldValues';

import { PrefixIndexStrategy } from './IndexStrategy/index';
import { LowerCaseSanitizer } from './Sanitizer/index';
Expand Down Expand Up @@ -157,6 +158,44 @@ export class Search {
return this._searchIndex.search(tokens, this._documents);
}

/**
* @param doc
* @param fieldValue Value to index
* @private
*/
indexDocumentsValue_(doc : Object, uid : string, fieldValue : string) : void {
this._initialized = true;

var indexStrategy = this._indexStrategy;
var sanitizer = this._sanitizer;
var searchIndex = this._searchIndex;
var tokenizer = this._tokenizer;
var uidFieldName = this._uidFieldName;

if (
fieldValue != null &&
typeof fieldValue !== 'string' &&
fieldValue.toString
) {
fieldValue = fieldValue.toString();
}

if (typeof fieldValue === 'string') {
var fieldTokens = tokenizer.tokenize(sanitizer.sanitize(fieldValue));

for (var fti = 0, numFieldValues = fieldTokens.length; fti < numFieldValues; fti++) {
var fieldToken = fieldTokens[fti];
var expandedTokens = indexStrategy.expandToken(fieldToken);

for (var eti = 0, nummExpandedTokens = expandedTokens.length; eti < nummExpandedTokens; eti++) {
var expandedToken = expandedTokens[eti];

searchIndex.indexDocument(expandedToken, uid, doc);
}
}
}
}

/**
* @param documents
* @param _searchableFields Array containing property names and paths (lists of property names) to nested values
Expand All @@ -182,36 +221,17 @@ export class Search {
}

for (var sfi = 0, numSearchableFields = _searchableFields.length; sfi < numSearchableFields; sfi++) {
var fieldValue;
var fieldValue, fieldValues;
var searchableField = _searchableFields[sfi];

if (searchableField instanceof Array) {
fieldValue = getNestedFieldValue(doc, searchableField);
fieldValues = getNestedFieldValues(doc, searchableField);
for (var i = 0; i < fieldValues.length; i++) {
this.indexDocumentsValue_(doc, uid, fieldValues[i]);
}
} else {
fieldValue = doc[searchableField];
}

if (
fieldValue != null &&
typeof fieldValue !== 'string' &&
fieldValue.toString
) {
fieldValue = fieldValue.toString();
}

if (typeof fieldValue === 'string') {
var fieldTokens = tokenizer.tokenize(sanitizer.sanitize(fieldValue));

for (var fti = 0, numFieldValues = fieldTokens.length; fti < numFieldValues; fti++) {
var fieldToken = fieldTokens[fti];
var expandedTokens = indexStrategy.expandToken(fieldToken);

for (var eti = 0, nummExpandedTokens = expandedTokens.length; eti < nummExpandedTokens; eti++) {
var expandedToken = expandedTokens[eti];

searchIndex.indexDocument(expandedToken, uid, doc);
}
}
this.indexDocumentsValue_(doc, uid, fieldValue);
}
}
}
Expand Down
39 changes: 38 additions & 1 deletion source/Search.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Search } from './Search';

describe('Search', function() {
var documentBar, documentBaz, documentFoo, nestedDocumentFoo, search;
var documentBar, documentBaz, documentFoo, nestedDocumentFoo, nestedArrayDocumentFoo, search;

var validateSearchResults = function(results, expectedDocuments) {
expect(results.length).toBe(expectedDocuments.length);
Expand Down Expand Up @@ -42,6 +42,22 @@ describe('Search', function() {
title: 'nested foo'
}
}
nestedArrayDocumentFoo = {
uid: 'foo',
title: 'foo',
description: 'Is kung foo the same as kung fu?',
writers: [
{ name: 'ABC' },
{ name: 'xyz' }
],
deeplyNested: [
{
writers: [
{ name: 'deeply nested name'}
]
}
]
};
});

it('should throw an error if instantiated without the :uidFieldName parameter', function() {
Expand Down Expand Up @@ -115,6 +131,27 @@ describe('Search', function() {
validateSearchResults(search.search('nested foo'), [nestedDocumentFoo]);
});

it('should index nested arrays document properties', function() {
search.addIndex(['writers', '[]', 'name']);
search.addDocument(nestedArrayDocumentFoo);

validateSearchResults(search.search('ABC'), [nestedArrayDocumentFoo]);
});

it('should index deeply nested arrays document properties', function() {
search.addIndex(['deeplyNested', '[]', 'writers', '[]', 'name']);
search.addDocument(nestedArrayDocumentFoo);

validateSearchResults(search.search('deeply nested'), [nestedArrayDocumentFoo]);
});

it('should not give false results for deeply nested arrays document properties', function() {
search.addIndex(['deeplyNested', '[]', 'writers', '[]', 'name']);
search.addDocument(nestedArrayDocumentFoo);

validateSearchResults(search.search('something not findable'), []);
});

it('should gracefully handle broken property path', function() {
search.addIndex(['nested', 'title', 'not', 'existing']);
search.addDocument(nestedDocumentFoo);
Expand Down
30 changes: 30 additions & 0 deletions source/getNestedFieldValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Find and return nested object values.
*
* @param object to crawl
* @param path Property path
* @returns {any}
*/
export default function getNestedFieldValues(object : Object, path : Array<string>) {
path = path || [];
object = object || {};

var values = [];
var value = object;
// walk down the property path
for (var i = 0; i < path.length; i++) {
if (path[i] == "[]") {
for (var j=0; j < value.length; j++) {
values = values.concat(getNestedFieldValues(value[j], path.slice(i + 1, path.length)));
}
return values;
} else {
value = value[path[i]];
if (value == null || value == undefined) {
return [];
}
}
}

return [value];
}