Initial commit: DreamDict HTTP dictionary service
Go-based dictionary backend providing word validation, definitions, synonyms, and cross-language translation for English, French, and Portuguese. Designed as a self-hosted replacement for Wordnik, backing GogoBee. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
193
internal/dictionary/query.go
Normal file
193
internal/dictionary/query.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package dictionary
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (d *Dictionary) IsValidWord(word, lang string) (bool, error) {
|
||||
var exists bool
|
||||
err := d.db.QueryRow(
|
||||
"SELECT EXISTS(SELECT 1 FROM words WHERE word = ? AND lang = ?)",
|
||||
strings.ToLower(word), lang,
|
||||
).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dictionary: is valid word: %w", err)
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (d *Dictionary) RandomWord(lang string, opts Options) (string, error) {
|
||||
query := "SELECT word FROM words WHERE lang = ?"
|
||||
args := []any{lang}
|
||||
|
||||
if opts.POS != "" {
|
||||
query += " AND pos = ?"
|
||||
args = append(args, opts.POS)
|
||||
}
|
||||
if opts.MinLength > 0 {
|
||||
query += " AND LENGTH(word) >= ?"
|
||||
args = append(args, opts.MinLength)
|
||||
}
|
||||
if opts.MaxLength > 0 {
|
||||
query += " AND LENGTH(word) <= ?"
|
||||
args = append(args, opts.MaxLength)
|
||||
}
|
||||
if opts.MinFrequency > 0 {
|
||||
query += " AND frequency >= ?"
|
||||
args = append(args, opts.MinFrequency)
|
||||
}
|
||||
|
||||
query += " ORDER BY RANDOM() LIMIT 1"
|
||||
|
||||
var word string
|
||||
err := d.db.QueryRow(query, args...).Scan(&word)
|
||||
if err == sql.ErrNoRows {
|
||||
return "", ErrNoMatch
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("dictionary: random word: %w", err)
|
||||
}
|
||||
return word, nil
|
||||
}
|
||||
|
||||
func (d *Dictionary) Define(word, lang string) ([]Definition, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT d.pos, d.gloss, d.source, d.priority
|
||||
FROM definitions d
|
||||
JOIN words w ON w.id = d.word_id
|
||||
WHERE w.word = ? AND w.lang = ?
|
||||
ORDER BY d.priority ASC, d.id ASC`,
|
||||
strings.ToLower(word), lang,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dictionary: define: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var defs []Definition
|
||||
for rows.Next() {
|
||||
var def Definition
|
||||
var pos sql.NullString
|
||||
if err := rows.Scan(&pos, &def.Gloss, &def.Source, &def.Priority); err != nil {
|
||||
return nil, fmt.Errorf("dictionary: define scan: %w", err)
|
||||
}
|
||||
def.POS = pos.String
|
||||
defs = append(defs, def)
|
||||
}
|
||||
if defs == nil {
|
||||
defs = []Definition{}
|
||||
}
|
||||
return defs, rows.Err()
|
||||
}
|
||||
|
||||
func (d *Dictionary) Synonyms(word, lang string) ([]string, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT DISTINCT s.synonym
|
||||
FROM synonyms s
|
||||
JOIN words w ON w.id = s.word_id
|
||||
WHERE w.word = ? AND w.lang = ?
|
||||
ORDER BY s.synonym`,
|
||||
strings.ToLower(word), lang,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dictionary: synonyms: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var syns []string
|
||||
for rows.Next() {
|
||||
var syn string
|
||||
if err := rows.Scan(&syn); err != nil {
|
||||
return nil, fmt.Errorf("dictionary: synonyms scan: %w", err)
|
||||
}
|
||||
syns = append(syns, syn)
|
||||
}
|
||||
if syns == nil {
|
||||
syns = []string{}
|
||||
}
|
||||
return syns, rows.Err()
|
||||
}
|
||||
|
||||
func (d *Dictionary) Translate(word, fromLang, toLang string) ([]string, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT DISTINCT t.translation
|
||||
FROM translations t
|
||||
JOIN words w ON w.id = t.word_id
|
||||
WHERE w.word = ? AND w.lang = ? AND t.target_lang = ?
|
||||
ORDER BY t.translation`,
|
||||
strings.ToLower(word), fromLang, toLang,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dictionary: translate: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var trans []string
|
||||
for rows.Next() {
|
||||
var t string
|
||||
if err := rows.Scan(&t); err != nil {
|
||||
return nil, fmt.Errorf("dictionary: translate scan: %w", err)
|
||||
}
|
||||
trans = append(trans, t)
|
||||
}
|
||||
if trans == nil {
|
||||
trans = []string{}
|
||||
}
|
||||
return trans, rows.Err()
|
||||
}
|
||||
|
||||
func (d *Dictionary) WordCount() (map[string]int, error) {
|
||||
rows, err := d.db.Query("SELECT lang, COUNT(*) FROM words GROUP BY lang")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dictionary: word count: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
counts := make(map[string]int)
|
||||
for rows.Next() {
|
||||
var lang string
|
||||
var count int
|
||||
if err := rows.Scan(&lang, &count); err != nil {
|
||||
return nil, fmt.Errorf("dictionary: word count scan: %w", err)
|
||||
}
|
||||
counts[lang] = count
|
||||
}
|
||||
return counts, rows.Err()
|
||||
}
|
||||
|
||||
func (d *Dictionary) Meta(key string) (string, error) {
|
||||
var val string
|
||||
err := d.db.QueryRow("SELECT value FROM meta WHERE key = ?", key).Scan(&val)
|
||||
if err == sql.ErrNoRows {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("dictionary: meta %s: %w", key, err)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (d *Dictionary) DefCount() (map[string]int, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT w.lang, COUNT(*)
|
||||
FROM definitions d
|
||||
JOIN words w ON w.id = d.word_id
|
||||
GROUP BY w.lang`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dictionary: def count: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
counts := make(map[string]int)
|
||||
for rows.Next() {
|
||||
var lang string
|
||||
var count int
|
||||
if err := rows.Scan(&lang, &count); err != nil {
|
||||
return nil, fmt.Errorf("dictionary: def count scan: %w", err)
|
||||
}
|
||||
counts[lang] = count
|
||||
}
|
||||
return counts, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user