Skip to content

Commit ef8b01a

Browse files
committed
Improve search performance
1 parent f83f806 commit ef8b01a

File tree

6 files changed

+61
-46
lines changed

6 files changed

+61
-46
lines changed

dest/simple-jekyll-search.js

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -218,33 +218,14 @@
218218
return { ...target, ...source };
219219
}
220220
function isJSON(json) {
221-
try {
222-
return !!(json instanceof Object && JSON.parse(JSON.stringify(json)));
223-
} catch (_err) {
224-
return false;
225-
}
221+
return Array.isArray(json) || json !== null && typeof json === "object";
226222
}
227223
function NoSort() {
228224
return 0;
229225
}
230226
function isObject(obj) {
231227
return Boolean(obj) && Object.prototype.toString.call(obj) === "[object Object]";
232228
}
233-
function clone(input) {
234-
if (input === null || typeof input !== "object") {
235-
return input;
236-
}
237-
if (Array.isArray(input)) {
238-
return input.map((item) => clone(item));
239-
}
240-
const output = {};
241-
for (const key in input) {
242-
if (Object.prototype.hasOwnProperty.call(input, key)) {
243-
output[key] = clone(input[key]);
244-
}
245-
}
246-
return output;
247-
}
248229
const DEFAULT_OPTIONS = {
249230
searchInput: null,
250231
resultsContainer: null,
@@ -278,6 +259,7 @@
278259
class Repository {
279260
constructor(initialOptions = {}) {
280261
this.data = [];
262+
this.excludePatterns = [];
281263
this.setOptions(initialOptions);
282264
}
283265
put(input) {
@@ -297,19 +279,22 @@
297279
if (!criteria) {
298280
return [];
299281
}
300-
return clone(this.findMatches(this.data, criteria).sort(this.options.sortMiddleware));
282+
const matches = this.findMatches(this.data, criteria).sort(this.options.sortMiddleware);
283+
return matches.map((item) => ({ ...item }));
301284
}
302285
setOptions(newOptions) {
303286
let strategy = (newOptions == null ? void 0 : newOptions.strategy) || DEFAULT_OPTIONS.strategy;
304287
if ((newOptions == null ? void 0 : newOptions.fuzzy) && !(newOptions == null ? void 0 : newOptions.strategy)) {
305288
console.warn('[Simple Jekyll Search] Warning: fuzzy option is deprecated. Use strategy: "fuzzy" instead.');
306289
strategy = "fuzzy";
307290
}
291+
const exclude = (newOptions == null ? void 0 : newOptions.exclude) || DEFAULT_OPTIONS.exclude;
292+
this.excludePatterns = exclude.map((pattern) => new RegExp(pattern));
308293
this.options = {
309294
limit: (newOptions == null ? void 0 : newOptions.limit) || DEFAULT_OPTIONS.limit,
310295
searchStrategy: this.searchStrategy(strategy),
311296
sortMiddleware: (newOptions == null ? void 0 : newOptions.sortMiddleware) || DEFAULT_OPTIONS.sortMiddleware,
312-
exclude: (newOptions == null ? void 0 : newOptions.exclude) || DEFAULT_OPTIONS.exclude,
297+
exclude,
313298
strategy
314299
};
315300
}
@@ -355,12 +340,8 @@
355340
return hasMatch ? result : void 0;
356341
}
357342
isExcluded(term) {
358-
for (const excludedTerm of this.options.exclude) {
359-
if (new RegExp(excludedTerm).test(String(term))) {
360-
return true;
361-
}
362-
}
363-
return false;
343+
const termStr = String(term);
344+
return this.excludePatterns.some((regex) => regex.test(termStr));
364345
}
365346
searchStrategy(strategy) {
366347
if (StrategyFactory.isValidStrategy(strategy)) {

dest/simple-jekyll-search.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/assets/js/simple-jekyll-search.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Repository.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { FuzzySearchStrategy, LiteralSearchStrategy, WildcardSearchStrategy } from './SearchStrategies/SearchStrategy';
22
import { Matcher } from './SearchStrategies/types';
33
import { StrategyFactory, StrategyType } from './SearchStrategies/StrategyFactory';
4-
import { clone, isObject } from './utils';
4+
import { isObject } from './utils';
55
import { DEFAULT_OPTIONS } from './utils/default';
66
import { RepositoryData, RepositoryOptions } from './utils/types';
77

88
export class Repository {
99
private data: RepositoryData[] = [];
1010
private options!: Required<Omit<RepositoryOptions, 'fuzzy'>> & Pick<RepositoryOptions, 'fuzzy'>;
11+
private excludePatterns: RegExp[] = [];
1112

1213
constructor(initialOptions: RepositoryOptions = {}) {
1314
this.setOptions(initialOptions);
@@ -32,7 +33,8 @@ export class Repository {
3233
if (!criteria) {
3334
return [];
3435
}
35-
return clone(this.findMatches(this.data, criteria).sort(this.options.sortMiddleware));
36+
const matches = this.findMatches(this.data, criteria).sort(this.options.sortMiddleware);
37+
return matches.map(item => ({ ...item }));
3638
}
3739

3840
public setOptions(newOptions: RepositoryOptions): void {
@@ -43,11 +45,14 @@ export class Repository {
4345
strategy = 'fuzzy';
4446
}
4547

48+
const exclude = newOptions?.exclude || DEFAULT_OPTIONS.exclude;
49+
this.excludePatterns = exclude.map(pattern => new RegExp(pattern));
50+
4651
this.options = {
4752
limit: newOptions?.limit || DEFAULT_OPTIONS.limit,
4853
searchStrategy: this.searchStrategy(strategy),
4954
sortMiddleware: newOptions?.sortMiddleware || DEFAULT_OPTIONS.sortMiddleware,
50-
exclude: newOptions?.exclude || DEFAULT_OPTIONS.exclude,
55+
exclude: exclude,
5156
strategy: strategy,
5257
};
5358
}
@@ -101,12 +106,8 @@ export class Repository {
101106
}
102107

103108
private isExcluded(term: any): boolean {
104-
for (const excludedTerm of this.options.exclude) {
105-
if (new RegExp(excludedTerm).test(String(term))) {
106-
return true;
107-
}
108-
}
109-
return false;
109+
const termStr = String(term);
110+
return this.excludePatterns.some(regex => regex.test(termStr));
110111
}
111112

112113
private searchStrategy(

src/utils.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@ export function merge<T>(target: T, source: Partial<T>): T {
55
}
66

77
export function isJSON(json: any): boolean {
8-
try {
9-
return !!(json instanceof Object && JSON.parse(JSON.stringify(json)));
10-
11-
} catch (_err) {
12-
return false;
13-
}
8+
return Array.isArray(json) || (json !== null && typeof json === 'object');
149
}
1510

1611
export function NoSort(): number {

tests/utils.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,46 @@ describe('utils', () => {
5151
});
5252

5353
describe('isJSON', () => {
54-
it('returns true if is JSON object', () => {
54+
it('returns true for plain objects', () => {
5555
expect(isJSON({ foo: 'bar' })).toBe(true);
56+
expect(isJSON({})).toBe(true);
57+
expect(isJSON({ nested: { key: 'value' } })).toBe(true);
58+
});
59+
60+
it('returns true for arrays', () => {
61+
expect(isJSON([])).toBe(true);
62+
expect(isJSON([1, 2, 3])).toBe(true);
63+
expect(isJSON([{ foo: 'bar' }])).toBe(true);
64+
});
65+
66+
it('returns false for null', () => {
67+
expect(isJSON(null)).toBe(false);
68+
});
69+
70+
it('returns false for undefined', () => {
71+
expect(isJSON(undefined)).toBe(false);
72+
});
73+
74+
it('returns false for primitives', () => {
75+
expect(isJSON(42)).toBe(false);
76+
expect(isJSON(0)).toBe(false);
77+
expect(isJSON('string')).toBe(false);
78+
expect(isJSON('')).toBe(false);
79+
expect(isJSON(true)).toBe(false);
80+
expect(isJSON(false)).toBe(false);
81+
});
82+
83+
it('returns true for Date objects', () => {
84+
expect(isJSON(new Date())).toBe(true);
85+
});
86+
87+
it('returns true for RegExp objects', () => {
88+
expect(isJSON(/regex/)).toBe(true);
89+
});
90+
91+
it('returns false for functions', () => {
92+
expect(isJSON(() => {})).toBe(false);
93+
expect(isJSON(function() {})).toBe(false);
5694
});
5795
});
5896

0 commit comments

Comments
 (0)