Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d0ec6da
Add: oneword token search based on the search patter we use
DylanAustin-TheDreamer Mar 24, 2026
4f966ff
removed: line reference comments
DylanAustin-TheDreamer Mar 24, 2026
8643466
add: sutta finder variable at the top for one word query
DylanAustin-TheDreamer Mar 24, 2026
5d5d6c8
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Mar 25, 2026
d86b787
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 3, 2026
1860aa4
create: onewordtoken function and TDD - first failed now passes
DylanAustin-TheDreamer Apr 3, 2026
76b34f9
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 6, 2026
26eff64
trying to get item titles from results array of objects
DylanAustin-TheDreamer Apr 10, 2026
fd5e9db
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 11, 2026
e799cbc
add: fallback for oneword search results back to results on no results
DylanAustin-TheDreamer Apr 11, 2026
c6f4827
add: findoneword function in completed form with TDD
DylanAustin-TheDreamer Apr 11, 2026
c0649bc
add: new normalizetitle function and better results handling
DylanAustin-TheDreamer Apr 12, 2026
703e8c9
update: findoneword...() with matching against joinedTitles made from…
DylanAustin-TheDreamer Apr 13, 2026
2c16c89
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 13, 2026
c0be5b6
Merge branches 'dylan-search-function' and 'main' of https://github.c…
DylanAustin-TheDreamer Apr 14, 2026
2905284
add: new test cases to normalizeSuttaTitles
DylanAustin-TheDreamer Apr 16, 2026
55cb72e
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 16, 2026
24d19d6
add: remove the from beginning, better nikaya index handling and upda…
DylanAustin-TheDreamer Apr 16, 2026
77e02b5
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 19, 2026
549d6c4
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 23, 2026
a50ba3c
add: several test cases to match cases suggested on PR review, to sho…
DylanAustin-TheDreamer Apr 23, 2026
8a653f6
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 25, 2026
332e7e5
Add: extra test cases and new normalizeSuttaTitles() handling
DylanAustin-TheDreamer Apr 25, 2026
d9b6d97
remove: un-used tokenResults variable
DylanAustin-TheDreamer Apr 25, 2026
8907d60
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 27, 2026
49dd5cd
update: test case names to match test cases properly
DylanAustin-TheDreamer Apr 27, 2026
cf14f49
Merge branch 'main' of https://github.com/DylanAustin-TheDreamer/budd…
DylanAustin-TheDreamer Apr 27, 2026
a2d8a89
update: test case description for testing parse lal sutra
DylanAustin-TheDreamer Apr 27, 2026
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions assets/js/search_functions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
// Parameters
var BMAX = 250; // Max blurb size in characters
var RMAX = 100; // Max number of results to display
var joinedTitles = []

function normalizeSuttaTitles (obj) {
var joinedTitleDatabase = []

for (var i in obj){
const item = obj[i];
if (!item || item.type !== "content" || item.category !== "canon") continue;
const title = item.title || "";
const titleJoin = title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/^\s*(?:DN|MN|SN|AN|KN|LAL|DA|MA|SA|EA|SNP|DHP|ITI|THAG|THIG|UD|NIDD|CV|BV|AP|JA|PV|VV|KP|PTS)\s*\d+(?:\.\d+)?\s*[:.-]?\s*/i, "").replace(/\s*[:\-–]\s*.*$/, "").toLowerCase().replace(/[^a-z0-9]/g, "");
const removedTheOnJoin = titleJoin.replace(/^\s*(?:the)\s*/i, "");
if(removedTheOnJoin.includes('sutta') || removedTheOnJoin.includes('sutra') || removedTheOnJoin.includes('gatha')) {
joinedTitleDatabase.push({
ref: i,
title: removedTheOnJoin,
matchData: { metadata: {} }
});
}
}
return joinedTitleDatabase;
}

function getPositions(result, field) {
var positions = [];
Expand Down Expand Up @@ -169,10 +190,27 @@ function displaySearchResults(results) {
}
}

function findOneWordSuttaTitleMatches(query, joinedTitles) {
var tokenResults = [];
const normalizedQuery = query.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]/g, "");
for (var i in joinedTitles){
const item = joinedTitles[i]
if(item.title === normalizedQuery){
tokenResults.push({
ref: item.ref,
score: 1,
matchData: { metadata: {} }
});
}
}
return tokenResults;
}

function handleSearchMessage(data, searchFn) {
var results = [];
var warning = "";
var words = data.q.trim().split(" ");

for (var i = 0; i < words.length; i++) {
const s = words[i].trim();
if (!s.startsWith("+") && !s.startsWith("-") && s.length > 1 && lunr.stopWordFilter(s)) {
Expand Down Expand Up @@ -213,10 +251,11 @@ function handleSearchMessage(data, searchFn) {
});
});
}
finalResults = results.length ? results : findOneWordSuttaTitleMatches(data.q.trim(), joinedTitles);
return {
"warninghtml": warning,
"html": displaySearchResults(results),
"count": results ? results.length : 0,
"html": displaySearchResults(finalResults),
"count": finalResults ? finalResults.length : 0,
"q": data.q,
"filterquery": data.filterquery,
"qt": data.qt
Expand Down
2 changes: 2 additions & 0 deletions assets/js/search_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ var idx = lunr(function () {
}
});

joinedTitles = normalizeSuttaTitles(store);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah. That's what I meant! You got it 🙂


self.onmessage = function(e) {
self.postMessage(handleSearchMessage(e.data, idx.search.bind(idx)));
}
126 changes: 125 additions & 1 deletion assets/js/tests/search-worker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,16 @@ vm.runInContext(
'this.resultMatched = resultMatched;\n' +
'this.addMatchHighlights = addMatchHighlights;\n' +
'this.getBlurbForResult = getBlurbForResult;\n' +
'this.normalizeSuttaTitles = normalizeSuttaTitles;\n' +
'this.findOneWordSuttaTitleMatches = findOneWordSuttaTitleMatches;\n' +
'this.handleSearchMessage = handleSearchMessage;\n' +
'this.displaySearchResults = displaySearchResults;\n',
sandbox
);

const {
categoryName, getPositions, resultMatched,
addMatchHighlights, getBlurbForResult, handleSearchMessage
addMatchHighlights, getBlurbForResult, oneWordToken, normalizeSuttaTitles, findOneWordSuttaTitleMatches, handleSearchMessage
} = sandbox;

// ── categoryName ────────────────────────────────────────────────────
Expand Down Expand Up @@ -296,6 +298,128 @@ describe('getBlurbForResult', () => {
});
});

// ── normalizeSuttaTitles ─────────────────────────────────────────────
describe('normalizeSuttaTitles', () => {

it('returns an array of database objects with a new normalized title', () => {
const mockStore = {
id1: {
title: 'MN 35 Cūḷa Saccaka Sutta: The Shorter Discourse With Saccaka',
type: 'content',
category: 'canon'
}
};
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 1);
assert.equal(result[0].ref, 'id1');
assert.equal(result[0].title, 'culasaccakasutta');
});

it('remove words after sutta. Also handles sutra', () => {
const mockStore = {
id1: {
title: 'MA 128 Upasaka Sutra: Discourse on the White-Clad Disciple',
type: 'content',
category: 'canon'
}
};
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 1);
assert.equal(result[0].ref, 'id1');
assert.equal(result[0].title, 'upasakasutra');
});

it('can parse a lal sutra', () => {
const mockStore = {
id1: {
title: 'Lal 26 Dharmacakrapravartana Sūtra: The Discourse that Set the Dharma-Wheel Rolling',
type: 'content',
category: 'canon'
}
};
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 1);
assert.equal(result[0].ref, 'id1');
assert.equal(result[0].title, 'dharmacakrapravartanasutra');
});

it('can parse Therigathas', () => {
Comment thread
khemarato marked this conversation as resolved.
const mockStore = {
id1: {
title: "Thig 3.8 Somā Therīgāthā: Somā's Verses",
type: 'content',
category: 'canon'
}
};
const mockStore2 = {
id1: {
title: "Thag 1.7 Bhalliya Theragāthā: Bhalliya's Verse",
Comment thread
khemarato marked this conversation as resolved.
type: 'content',
category: 'canon'
}
};
const result2 = normalizeSuttaTitles(mockStore2);
assert.equal(result2.length, 1);
assert.equal(result2[0].ref, 'id1');
assert.equal(result2[0].title, 'bhalliyatheragatha');
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 1);
assert.equal(result[0].ref, 'id1');
assert.equal(result[0].title, 'somatherigatha');
});

it('handles "the" and removes it from a string if it appears at the beginning', () => {
const mockStore = {
id1: {
title: 'DN 22 The Mahāsatipaṭṭhāna Sutta: The Long Discourse about the Ways of Attending to Mindfulness',
type: 'content',
category: 'canon'
}
};
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 1);
assert.equal(result[0].ref, 'id1');
assert.equal(result[0].title, 'mahasatipatthanasutta');
});

it('filters out objects with types that are not equal to content', () => {
const mockStore = {
id1: {
title: 'MN 3 Dhammadāyāda Sutta: Heirs in the Teaching',
Comment thread
khemarato marked this conversation as resolved.
type: 'content',
category: 'av'
}
};
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 0);
});

it('filters out objects where titles do not include sutta, sutra, or gatha', () => {
Comment thread
khemarato marked this conversation as resolved.
const mockStore = {
id1: {
title: 'The Hairy Spider Climbed Up The Waterspout: A day in the life Part 3 ',
type: 'content',
category: 'canon'
}
};
const result = normalizeSuttaTitles(mockStore);
assert.equal(result.length, 0);
});

Comment thread
khemarato marked this conversation as resolved.

});

// ── findOneWordSuttaTitleMatches ─────────────────────────────────────────────
describe('findOneWordSuttaTitleMatches', () => {
it('returns matched item when query matches title exactly', () => {
const mockStore = {
'id1': { title: 'culasaccakasutta', type: 'content', category: 'canon' }
};
const result = findOneWordSuttaTitleMatches('culasaccakasutta', mockStore);
assert.equal(toLocal(result).length, 1);
});
});

// ── handleSearchMessage ─────────────────────────────────────────────

describe('handleSearchMessage', () => {
Expand Down