-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathsearch.js
More file actions
154 lines (135 loc) · 5.05 KB
/
search.js
File metadata and controls
154 lines (135 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import {strict as assert} from 'node:assert';
import {search} from '../dist/search.js';
const item = name => ({name});
//> Most of the tests operate on this pre-set list of items to search
const ITEMS = [
item('Linus Lee'),
item('@thesephist'),
item('@geohot'),
item('linuslee'),
item('linus is a person'),
item('@dlwlrma'),
];
describe('basic search', () => {
it('search empty array', () => {
assert.deepEqual(search([], 'query', x => x.name), []);
});
it('search with empty query', () => {
assert.deepEqual(search(ITEMS, '', x => x.name), ITEMS);
});
it('search with 1 letter returns correct result', () => {
assert.deepEqual(search(ITEMS, 'l', x => x.name), [
item('Linus Lee'),
item('linuslee'),
item('linus is a person'),
]);
});
it('search does not match from middle of words', () => {
assert.deepEqual(search(ITEMS, 'w', x => x.name), []);
});
it('multi-word search returns correct result', () => {
assert.deepEqual(search(ITEMS, 'linus lee', x => x.name), [
item('Linus Lee'),
]);
});
it('searching words out of order returns correct result', () => {
assert.deepEqual(search(ITEMS, 'lee linus', x => x.name), [
item('Linus Lee'),
]);
});
it('search works even if the last query word is incomplete', () => {
assert.deepEqual(search(ITEMS, 'linus le', x => x.name), [
item('Linus Lee'),
]);
});
it('search query may contain newlines, tabs, and multiple consecutive spaces', () => {
assert.deepEqual(search(ITEMS, ' linus\t is\nperson\t', x => x.name), [
item('linus is a person'),
]);
});
it('correctly implements TF-IDF ranking', () => {
//> In this example, "mango" has much higher IDF (is a higher-signal
// word) in the corpus than "apple", which appears in nearly every
// document. Therefore, documents that mention "mango" more times
// (relative to the length of the document) should rank higher.
assert.deepEqual(
search([
// matches
item('mango mango mango apple'),
item('mango apple mango apple'),
item('apple mango apple mango apple mango apple mango'),
item('apple apple apple apple apple apple apple apple mango'),
// rejects
item('apple apple apple'),
item('mango mango mango'),
item('applemango'),
item('mangoapple'),
item('apple 1'),
item('apple 2'),
item('apple 3'),
item('apple 4'),
item('apple 5'),
item('apple 6'),
item('apple 7'),
item('apple 8'),
item('apple 9'),
], 'apple mango', x => x.name),
[
item('mango mango mango apple'),
item('mango apple mango apple'),
item('apple mango apple mango apple mango apple mango'),
item('apple apple apple apple apple apple apple apple mango'),
]
);
});
});
describe('custom search-by predicates', () => {
it('default predicate is provided as x => x', () => {
assert.deepEqual(
search([
'university',
'uni of california',
'university of california',
], 'uni of cali'),
[
'uni of california',
]
);
});
it('accepts and uses a custom predicate', () => {
assert.deepEqual(search(ITEMS, 'sunil ee', x => x.name.split('').reverse().join('')), [
item('Linus Lee'),
]);
});
});
describe('search modes', () => {
it('in mode: word, search does not match if any words are incomplete', () => {
assert.deepEqual(search(ITEMS, 'linu lee', x => x.name, {mode: 'word'}), []);
});
it('in mode: prefix, every query word may be incomplete', () => {
assert.deepEqual(search(ITEMS, 'linu le', x => x.name, {mode: 'prefix'}), [
item('Linus Lee'),
]);
});
it('in mode: autocomplete, only the last query word may be incomplete', () => {
assert.deepEqual(search(ITEMS, 'linus le', x => x.name, {mode: 'autocomplete'}), [
item('Linus Lee'),
]);
assert.deepEqual(search(ITEMS, 'linu le', x => x.name, {mode: 'autocomplete'}), []);
});
});
describe('case sensitivity', () => {
it('caseSensitive: true omits non-matching results', () => {
assert.deepEqual(search(ITEMS, 'l', x => x.name, {caseSensitive: true}), [
item('linuslee'),
item('linus is a person'),
]);
});
it('caseSensitive: false includes case-insensitive results', () => {
assert.deepEqual(search(ITEMS, 'l', x => x.name, {caseSensitive: false}), [
item('Linus Lee'),
item('linuslee'),
item('linus is a person'),
]);
});
});