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
|
"use server";
import db from "@/lib/db";
import { WordData } from "@/zoom/logic/types";
// Helper to extract tone from prosody - assuming prosody is an array of objects like [{tone: number}, ...]
const getTonesFromProsody = (prosody: any): number[] | null => {
if (Array.isArray(prosody) && prosody.length > 0) {
return prosody.map((p) => p.tone).filter((t) => typeof t === "number");
}
return null;
};
export async function fetchWordsByToneAndSyllables(
syllableCount: number,
tones: (number | null)[], // Array of tones, one for each syllable. null means any tone.
): Promise<WordData | null> {
if (syllableCount !== tones.length) {
console.error("Syllable count and tones array length mismatch");
return null;
}
const queryParams: (string | number)[] = ["th", syllableCount, syllableCount]; // lang, syllables (for WHERE), syllables (for json_array_length)
let toneConditions = "";
const toneClauses: string[] = [];
tones.forEach((tone, index) => {
if (tone !== null && typeof tone === "number") {
// Assumes SQLite's json_extract function is available and prosody is like: [{"tone": 1}, {"tone": 3}, ...]
// Path for first syllable's tone: '$[0].tone'
toneClauses.push(`json_extract(prosody, '$[${index}].tone') = ?`);
queryParams.push(tone);
}
});
if (toneClauses.length > 0) {
toneConditions = `AND ${toneClauses.join(" AND ")}`;
}
const queryString = `
SELECT id, spelling, prosody, syllables, lang, type, frequency, confidence, ipa,
(SELECT
json_group_array(json_object(
'pos', pos,
'senses', s.senses,
'forms', forms,
'etymology', etymology,
'related', related)
) FROM senses s WHERE s.parent_id = expressions.id
) as senses_array
FROM expressions
WHERE lang = ?
AND syllables = ?
AND type = 'word'
AND json_valid(prosody)
AND json_array_length(prosody) = ? -- Ensures prosody array has correct number of elements
${toneConditions}
ORDER BY RANDOM() -- Get a random word matching criteria
LIMIT 1
`;
try {
const query = db.db.query(queryString);
const row = query.get(...queryParams) as any;
if (!row) return null;
// Map to WordData (simplified, similar to initial fetch in tones.tsx or db.fetchWordBySpelling)
// This mapping might need to be more robust depending on actual WordData requirements.
const word: WordData = {
id: row.id,
spelling: row.spelling,
prosody: JSON.parse(row.prosody),
syllables: row.syllables,
lang: row.lang,
type: row.type,
frequency: row.frequency,
confidence: row.confidence,
ipa: row.ipa ? JSON.parse(row.ipa) : [],
// Senses parsing is simplified here. Adjust if full sense data is needed.
senses: row.senses_array
? JSON.parse(row.senses_array).map((s: any) => ({
pos: s.pos,
senses:
typeof s.senses === "string" ? JSON.parse(s.senses) : s.senses,
forms: typeof s.forms === "string" ? JSON.parse(s.forms) : s.forms,
etymology: s.etymology,
related:
typeof s.related === "string" ? JSON.parse(s.related) : s.related,
}))
: [],
};
return word;
} catch (error) {
console.error("Error fetching word by tone and syllables:", error);
console.error("Query:", queryString);
console.error("Params:", queryParams);
return null;
}
}
|