Skip to content

Commit a65600a

Browse files
committed
Added support for multi-word searching. Greatly improved efficiency
1 parent 2038d8b commit a65600a

File tree

3 files changed

+44
-81
lines changed

3 files changed

+44
-81
lines changed

src/lib/components/LexiconXMLView.svelte

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,31 @@
11
<script>
2-
import { base } from '$app/paths';
32
import { get } from 'svelte/store';
43
import config from '$lib/data/config';
54
import { convertStyle } from '$lib/data/stores';
6-
import initSqlJs from 'sql.js';
75
import { afterUpdate, onMount } from 'svelte';
6+
import { initializeDatabase } from '$lib/data/stores/lexicon';
87
import { selectedLanguageStore, vernacularLanguageStore, vernacularWordsStore } from '$lib/data/stores/lexicon.ts';
98
109
export let wordIds;
1110
export let onSelectWord;
1211
1312
let xmlData = '';
1413
15-
let singleEntryStyles = config.singleEntryStyles;
16-
17-
async function queryXmlByWordId(wordId) {
14+
async function queryXmlByWordId(wordIds) {
1815
try {
19-
const SQL = await initSqlJs({
20-
locateFile: (file) => `${base}/wasm/sql-wasm.wasm`
21-
});
16+
let db = await initializeDatabase({ fetch });
2217
23-
const response = await fetch(`${base}/data.sqlite`);
24-
if (!response.ok) {
25-
throw new Error(
26-
`Failed to fetch database: ${response.status} ${response.statusText}`
27-
);
28-
}
29-
const buffer = await response.arrayBuffer();
30-
const db = new SQL.Database(new Uint8Array(buffer));
31-
if (!db) {
32-
console.error('Database not initialized');
33-
return null;
34-
}
35-
36-
const stmt = db.prepare('SELECT xml FROM entries WHERE id = ?');
37-
stmt.bind([wordId]);
38-
39-
let result = null;
40-
if (stmt.step()) {
41-
result = stmt.getAsObject().xml;
42-
}
43-
stmt.free();
44-
db.close();
18+
let results;
19+
const dynamicQuery = wordIds
20+
.map(() => `id = ?`)
21+
.join(' OR ');
22+
const dynamicParams = wordIds.map((id) => id);
23+
results = db.exec(`SELECT xml FROM entries WHERE ${dynamicQuery}`, dynamicParams);
24+
console.log('results:' , results[0].values);
4525
46-
return result;
26+
return results[0].values;
4727
} catch (error) {
48-
console.error(`Error querying XML for word ID ${wordId}:`, error);
28+
console.error(`Error querying XML for word IDs ${wordIds}:`, error);
4929
return null;
5030
}
5131
}
@@ -128,7 +108,7 @@
128108
return;
129109
}
130110
131-
let xmlResults = await Promise.all(wordIds.map(queryXmlByWordId));
111+
let xmlResults = await queryXmlByWordId(wordIds);
132112
133113
// Insert an `<hr>` tag or a visible separator between entries
134114
xmlData =
@@ -164,7 +144,7 @@
164144
}
165145
166146
function applyStyles() {
167-
for (let stl of singleEntryStyles) {
147+
for (let stl of config.singleEntryStyles) {
168148
for (let elm of document.querySelectorAll(stl.name)) {
169149
elm.style = convertStyle(stl.properties);
170150
}
Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,44 @@
1-
import { thaw } from '$lib/scripts/thaw';
2-
import type {
3-
QueryGenerator,
4-
SearchOptions,
5-
SearchQuery
6-
} from '$lib/search/domain/interfaces/data-interfaces';
7-
import type { Message } from '$lib/utils/worker-messenger/message';
8-
import { Messenger, type MessageIO } from '$lib/utils/worker-messenger/messenger';
9-
import { ProskommaSearchRepositoryImpl } from './data/repositories/pk-search-repository-impl';
10-
import { ProksommaWorkerSearchRepository } from './data/repositories/pk-worker-search-repository';
11-
import { ScriptureRepositoryImpl } from './data/repositories/scripture-repository-impl';
12-
import type { ScriptureRepository } from './domain/interfaces/data-interfaces';
13-
import { SearchQueryInternal } from './domain/search-query-internal';
14-
import { SearchSessionInternal } from './domain/search-session-internal';
15-
import initSqlJs from 'sql.js';
1+
import type { SearchOptions } from '$lib/search/domain/interfaces/data-interfaces';
162
import { initializeDatabase } from '$lib/data/stores/lexicon';
173

18-
// const messageIO: MessageIO = {
19-
// postMessage: function (message: Message): void {
20-
// self.postMessage(message);
21-
// },
22-
23-
// setOnMessage: function (handler: (event: MessageEvent<Message>) => void): void {
24-
// self.onmessage = handler;
25-
// }
26-
// };
27-
28-
// const messenger = new Messenger(messageIO);
29-
304
// Search the dictionary based on the phrase and options
315
export async function searchDictionary(phrase: string, options: SearchOptions) {
6+
7+
const searchWords = phrase.split(' ');
8+
329
const column = options.accentsAndTones ? 'word' : 'word_no_accents';
3310

34-
const searchPattern = options.wholeWords ? `${phrase}` : `%${phrase}%`;
35-
3611
let db = await initializeDatabase({ fetch })
37-
let results = db.exec(`SELECT locations FROM search_words WHERE ${column} LIKE ?`, [searchPattern]);
38-
39-
if (!results.length || !results[0].values.length) {
12+
let results;
13+
const dynamicQuery = searchWords
14+
.map(() => `${column} LIKE ?`)
15+
.join(' OR ');
16+
const dynamicParams = searchWords.map(word => options.wholeWords ? word : `%${word}%`);
17+
results = db.exec(`SELECT locations FROM search_words WHERE ${dynamicQuery}`, dynamicParams);
18+
console.log('results:' , results);
19+
20+
if (!results?.length || !results[0]?.values?.length) {
4021
return [];
4122
}
4223

4324
// Extract and process locations from the query result
44-
let locations = results[0].values[0][0].split(' ').map(loc => {
45-
const [id, weight] = loc.split('(').map(v => v.replace(')', ''));
46-
return { id: parseInt(id, 10), weight: parseInt(weight, 10) };
25+
let locations = results[0].values.flatMap(value =>
26+
value[0].split(' ').map(loc => {
27+
const [id, weight] = loc.split('(').map(v => v.replace(')', ''));
28+
return { id: parseInt(id, 10), weight: parseInt(weight, 10) };
29+
})
30+
);
31+
32+
// Remove duplicates by creating a Map with unique IDs
33+
const uniqueLocationsMap = new Map();
34+
locations.forEach(location => {
35+
if (!uniqueLocationsMap.has(location.id) || uniqueLocationsMap.get(location.id).weight < location.weight) {
36+
uniqueLocationsMap.set(location.id, location);
37+
}
4738
});
39+
locations = Array.from(uniqueLocationsMap.values());
4840

4941
locations = (locations.sort((a, b) => b.weight - a.weight));
5042
const ids = locations.map(location => location.id);
5143
return ids;
52-
}
53-
54-
// async function makeQuery(phrase: string, options: SearchOptions): Promise<SearchQuery> {
55-
// const dictionaryResults = await searchDictionary(phrase, options);
56-
// const scriptureRepo = makeScriptureRepository();
57-
// const scriptureResults = await scriptureRepo.queryVerses(phrase, options);
58-
// return new SearchQueryInternal(phrase, options, scriptureRepo);
59-
// }
60-
61-
// const session = new SearchSessionInternal(makeQuery);
62-
// messenger.setInboundHandler(session.getRequestHandler());
44+
}

src/routes/lexicon/search/+page.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import SearchForm from '$lib/components/SearchForm.svelte';
66
import WordNavigationStrip from '$lib/components/WordNavigationStrip.svelte';
77
import config from '$lib/data/config';
8-
import { t, themeColors } from '$lib/data/stores';
8+
import { themeColors } from '$lib/data/stores';
99
import { vernacularLanguageStore, vernacularWordsStore } from '$lib/data/stores/lexicon';
1010
import { SearchIcon } from '$lib/icons';
1111
import { getRoute } from '$lib/navigate';
@@ -77,6 +77,7 @@
7777
on:click={() => {
7878
wordIds = null;
7979
searchWord = '';
80+
selectedWord = null;
8081
goto(getRoute(`/lexicon/search`));
8182
}}
8283
>

0 commit comments

Comments
 (0)