Skip to content

Commit 12ad899

Browse files
author
Brian Vaughn
committed
Search uid field can now be an array (for nested/deep keys).
1 parent 8e9cacb commit 12ad899

File tree

7 files changed

+104
-36
lines changed

7 files changed

+104
-36
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## 1.4.0
4+
Search uid field can now be an array (for nested/deep keys).
5+
36
## 1.3.7
47
Fixed `package.json` to include correct files.
58

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "js-search",
3-
"version": "1.3.7",
3+
"version": "1.4.0",
44
"description": "### What is it?",
55
"main": "dist/commonjs/index.js",
66
"directories": {

source/Search.js

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// @flow
2+
import getNestedFieldValue from './getNestedFieldValue';
23

34
import { PrefixIndexStrategy } from './IndexStrategy/index';
45
import { LowerCaseSanitizer } from './Sanitizer/index';
@@ -29,14 +30,14 @@ export class Search {
2930

3031
_searchIndex : ISearchIndex;
3132
_tokenizer : ITokenizer;
32-
_uidFieldName : string;
33+
_uidFieldName : string | Array<string>;
3334

3435
/**
3536
* Constructor.
3637
* @param uidFieldName Field containing values that uniquely identify search documents; this field's values are used
3738
* to ensure that a search result set does not contain duplicate objects.
3839
*/
39-
constructor(uidFieldName : string) {
40+
constructor(uidFieldName : string | Array<string>) {
4041
this._uidFieldName = uidFieldName;
4142

4243
// Set default/recommended strategies
@@ -168,7 +169,13 @@ export class Search {
168169

169170
for (var di = 0, numDocuments = documents.length; di < numDocuments; di++) {
170171
var doc = documents[di];
171-
var uid = doc[uidFieldName];
172+
var uid;
173+
174+
if (uidFieldName instanceof Array) {
175+
uid = getNestedFieldValue(doc, uidFieldName);
176+
} else {
177+
uid = doc[uidFieldName];
178+
}
172179

173180
for (var sfi = 0, numSearchableFields = _searchableFields.length; sfi < numSearchableFields; sfi++) {
174181
var fieldValue;
@@ -206,28 +213,3 @@ export class Search {
206213
}
207214
}
208215
}
209-
210-
/**
211-
* Find and return a nested object value.
212-
*
213-
* @param object to crawl
214-
* @param path Property path
215-
* @returns {any}
216-
*/
217-
function getNestedFieldValue(object : Object, path : Array<string>) {
218-
path = path || [];
219-
object = object || {};
220-
221-
var value = object;
222-
223-
// walk down the property path
224-
for (var i = 0; i < path.length; i++) {
225-
value = value[path[i]];
226-
227-
if (value == null) {
228-
return null;
229-
}
230-
}
231-
232-
return value;
233-
}

source/Search.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,35 @@ describe('Search', function() {
115115

116116
validateSearchResults(search.search('nested foo'), []);
117117
});
118+
119+
it('should support nested uid paths', function() {
120+
const melissaSmith = {
121+
name: 'Melissa Smith',
122+
123+
login: {
124+
userId: 2562,
125+
username: 'heavycat937'
126+
}
127+
};
128+
const johnSmith = {
129+
name: 'John Smith',
130+
131+
login: {
132+
userId: 54213,
133+
username: 'jhon123'
134+
}
135+
};
136+
137+
var search = new Search(['login', 'userId']);
138+
search.addIndex('title');
139+
search.addIndex(['login', 'username']);
140+
search.addIndex('name');
141+
search.addIndex('email');
142+
search.addDocuments([melissaSmith, johnSmith]);
143+
144+
validateSearchResults(search.search('Smith'), [melissaSmith, johnSmith]);
145+
validateSearchResults(search.search('allen'), [melissaSmith, johnSmith]);
146+
validateSearchResults(search.search('heavycat937'), [melissaSmith]);
147+
validateSearchResults(search.search('jhon123'), [johnSmith]);
148+
});
118149
});

source/SearchIndex/TfIdfSearchIndex.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// @flow
2+
import getNestedFieldValue from '../getNestedFieldValue';
23

34
import type { ISearchIndex } from './SearchIndex';
45

@@ -25,11 +26,11 @@ type ITfIdfUidMetadata = {
2526
* Search index capable of returning results matching a set of tokens and ranked according to TF-IDF.
2627
*/
2728
export class TfIdfSearchIndex implements ISearchIndex {
28-
_uidFieldName : string;
29+
_uidFieldName : string | Array<string>;
2930
_tokenToIdfCache : {[token : string] : number};
3031
_tokenMap : ITfIdfTokenMap;
3132

32-
constructor(uidFieldName : string) {
33+
constructor(uidFieldName : string | Array<string>) {
3334
this._uidFieldName = uidFieldName;
3435
this._tokenToIdfCache = {};
3536
this._tokenMap = {};
@@ -108,10 +109,6 @@ export class TfIdfSearchIndex implements ISearchIndex {
108109
documents.push(uidToDocumentMap[uid]);
109110
}
110111

111-
var tokenMap = this._tokenMap;
112-
var tokenToIdfCache = this._tokenToIdfCache;
113-
var uidFieldName = this._uidFieldName;
114-
115112
var calculateTfIdf = this._createCalculateTfIdf();
116113

117114
// Return documents sorted by TF-IDF
@@ -155,7 +152,13 @@ export class TfIdfSearchIndex implements ISearchIndex {
155152
inverseDocumentFrequency = 0;
156153
}
157154

158-
var uid:any = document && document[uidFieldName];
155+
var uid:any;
156+
if (uidFieldName instanceof Array) {
157+
uid = document && getNestedFieldValue(document, uidFieldName);
158+
} else {
159+
uid = document && document[uidFieldName];
160+
}
161+
159162
var termFrequency:number =
160163
typeof tokenMap[token] !== 'undefined' &&
161164
typeof tokenMap[token].$uidMap[uid] !== 'undefined'

source/SearchIndex/TfIdfSearchIndex.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,29 @@ describe('Search', function() {
138138
expect(results[2]).toEqual(documentB);
139139
});
140140
});
141+
142+
it('should support nested uid paths', function() {
143+
const melissaSmith = {
144+
name: 'Melissa Smith',
145+
login: {
146+
userId: 2562
147+
}
148+
};
149+
const johnSmith = {
150+
name: 'John Smith',
151+
login: {
152+
userId: 54213
153+
}
154+
};
155+
156+
var searchIndex = new TfIdfSearchIndex(['login', 'userId']);
157+
searchIndex.indexDocument(['Melissa'], 2562, melissaSmith);
158+
searchIndex.indexDocument(['Smith'], 2562, melissaSmith);
159+
searchIndex.indexDocument(['John'], 54213, johnSmith);
160+
searchIndex.indexDocument(['Smith'], 54213, johnSmith);
161+
162+
expect(searchIndex.search(['Melissa'], [melissaSmith, johnSmith])).toEqual([melissaSmith]);
163+
expect(searchIndex.search(['John'], [melissaSmith, johnSmith])).toEqual([johnSmith]);
164+
expect(searchIndex.search(['Smith'], [melissaSmith, johnSmith])).toEqual([melissaSmith, johnSmith]);
165+
});
141166
});

source/getNestedFieldValue.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Find and return a nested object value.
3+
*
4+
* @param object to crawl
5+
* @param path Property path
6+
* @returns {any}
7+
*/
8+
export default function getNestedFieldValue(object : Object, path : Array<string>) {
9+
path = path || [];
10+
object = object || {};
11+
12+
var value = object;
13+
14+
// walk down the property path
15+
for (var i = 0; i < path.length; i++) {
16+
value = value[path[i]];
17+
18+
if (value == null) {
19+
return null;
20+
}
21+
}
22+
23+
return value;
24+
}

0 commit comments

Comments
 (0)