116 lines
3.3 KiB
Go
116 lines
3.3 KiB
Go
|
|
package db_test
|
||
|
|
|
||
|
|
import (
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"apigo.cc/go/db"
|
||
|
|
_ "modernc.org/sqlite"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestAutonomousFTS(t *testing.T) {
|
||
|
|
dbPath := "test_fts.db"
|
||
|
|
dbInst := db.GetDB("sqlite://"+dbPath, nil)
|
||
|
|
defer os.Remove(dbPath)
|
||
|
|
defer dbInst.Exec("DROP TABLE IF EXISTS fts_test")
|
||
|
|
defer dbInst.Exec("DROP TABLE IF EXISTS fts_test_fts")
|
||
|
|
|
||
|
|
schema := `== Default ==
|
||
|
|
fts_test
|
||
|
|
id AI
|
||
|
|
title TI // Fulltext title
|
||
|
|
content TI // Fulltext content
|
||
|
|
status i
|
||
|
|
`
|
||
|
|
err := dbInst.Sync(schema)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal("Sync error:", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 1. Verify schema
|
||
|
|
row := dbInst.Query("SELECT \"sql\" FROM \"sqlite_master\" WHERE \"type\"='table' AND \"name\"='fts_test'").MapOnR1()
|
||
|
|
sqlStr := ""
|
||
|
|
if row["sql"] != nil {
|
||
|
|
sqlStr = row["sql"].(string)
|
||
|
|
}
|
||
|
|
if !strings.Contains(sqlStr, "title_tokens") || !strings.Contains(sqlStr, "content_tokens") {
|
||
|
|
t.Fatalf("Shadow columns missing in main table: %s", sqlStr)
|
||
|
|
}
|
||
|
|
|
||
|
|
row = dbInst.Query("SELECT \"name\" FROM \"sqlite_master\" WHERE \"type\"='table' AND \"name\"='fts_test_fts'").MapOnR1()
|
||
|
|
if row["name"] == nil {
|
||
|
|
t.Fatal("FTS virtual table missing")
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Test Insert
|
||
|
|
dbInst.Insert("fts_test", map[string]any{
|
||
|
|
"title": "你好世界",
|
||
|
|
"content": "这是一段测试文本",
|
||
|
|
"status": 1,
|
||
|
|
})
|
||
|
|
|
||
|
|
// Check if tokens are populated in main table
|
||
|
|
row = dbInst.Query("SELECT title_tokens, content_tokens FROM fts_test WHERE id=1").MapOnR1()
|
||
|
|
if row["title_tokens"] == nil || row["title_tokens"] == "" {
|
||
|
|
t.Fatal("Tokens not populated in main table")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if tokens are in FTS table
|
||
|
|
row = dbInst.Query("SELECT * FROM fts_test_fts").MapOnR1()
|
||
|
|
if row["title_tokens"] == nil || row["title_tokens"] == "" {
|
||
|
|
t.Fatal("Tokens not populated in FTS table")
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. Test Query Interception (LIKE -> FTS)
|
||
|
|
// Searching for "世界" should match "你好世界"
|
||
|
|
res := dbInst.Query("SELECT * FROM fts_test WHERE title LIKE ?", "%世界%")
|
||
|
|
list := res.MapResults()
|
||
|
|
if len(list) != 1 {
|
||
|
|
t.Fatalf("Query failed to find match via FTS redirection, found %d", len(list))
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Test Update
|
||
|
|
dbInst.Update("fts_test", map[string]any{"title": "更新后的标题"}, "id=?", 1)
|
||
|
|
row = dbInst.Query("SELECT title_tokens FROM fts_test WHERE id=1").MapOnR1()
|
||
|
|
if !strings.Contains(row["title_tokens"].(string), "更新") {
|
||
|
|
t.Fatalf("Tokens not updated: %v", row["title_tokens"])
|
||
|
|
}
|
||
|
|
|
||
|
|
// 5. Test Multiple Fields & Alias
|
||
|
|
dbInst.Insert("fts_test", map[string]any{
|
||
|
|
"title": "测试标题",
|
||
|
|
"content": "北京大学是一个好学校",
|
||
|
|
"status": 1,
|
||
|
|
})
|
||
|
|
|
||
|
|
// Search in content using alias
|
||
|
|
res = dbInst.Query("SELECT t.title FROM fts_test AS t WHERE t.content LIKE ?", "%北京大学%")
|
||
|
|
list = res.MapResults()
|
||
|
|
if len(list) != 1 {
|
||
|
|
t.Fatalf("Alias query failed, found %d", len(list))
|
||
|
|
}
|
||
|
|
|
||
|
|
// 6. Test Hardcoded Literals
|
||
|
|
res = dbInst.Query("SELECT * FROM fts_test WHERE title LIKE '%标题%'")
|
||
|
|
list = res.MapResults()
|
||
|
|
if len(list) != 2 {
|
||
|
|
t.Fatalf("Hardcoded literal query failed, found %d", len(list))
|
||
|
|
}
|
||
|
|
|
||
|
|
// 7. Test Various Identifier Styles
|
||
|
|
styles := []string{
|
||
|
|
"SELECT * FROM fts_test WHERE `title` LIKE ?",
|
||
|
|
"SELECT * FROM fts_test WHERE \"title\" LIKE ?",
|
||
|
|
"SELECT * FROM fts_test WHERE 'title' LIKE ?",
|
||
|
|
"SELECT * FROM fts_test WHERE `fts_test`.`title` LIKE ?",
|
||
|
|
}
|
||
|
|
for _, sql := range styles {
|
||
|
|
res = dbInst.Query(sql, "%测试%")
|
||
|
|
list = res.MapResults()
|
||
|
|
if len(list) != 1 {
|
||
|
|
t.Errorf("Style failed: %s, found %d", sql, len(list))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|