forked from coracle/zooid
Add prefix to event store
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *SqliteBackend) DeleteEvent(id nostr.ID) error {
|
||||
_, err := squirrel.Delete("events").Where(squirrel.Eq{"id": id.Hex()}).RunWith(s.db).Exec()
|
||||
_, err := squirrel.Delete(s.tmpl("{{.Prefix}}events")).Where(squirrel.Eq{"id": id.Hex()}).RunWith(s.db).Exec()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
+38
-22
@@ -1,8 +1,10 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"fiatjaf.com/nostr/eventstore"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
@@ -13,6 +15,7 @@ var _ eventstore.Store = (*SqliteBackend)(nil)
|
||||
type SqliteBackend struct {
|
||||
db *sql.DB
|
||||
Path string
|
||||
Prefix string
|
||||
FTSAvailable bool
|
||||
}
|
||||
|
||||
@@ -41,10 +44,20 @@ func (s *SqliteBackend) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqliteBackend) tmpl(t string) string {
|
||||
var buf bytes.Buffer
|
||||
err := template.Must(template.New("schema").Parse(t)).Execute(&buf, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (s *SqliteBackend) createSchema() error {
|
||||
// Create basic schema first
|
||||
basicSchema := `
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
basicSchema := s.tmpl(`
|
||||
CREATE TABLE IF NOT EXISTS {{.Prefix}}events (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at INTEGER NOT NULL,
|
||||
kind INTEGER NOT NULL,
|
||||
@@ -54,23 +67,23 @@ func (s *SqliteBackend) createSchema() error {
|
||||
sig TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_created_at ON events(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);
|
||||
CREATE INDEX IF NOT EXISTS idx_events_pubkey ON events(pubkey);
|
||||
CREATE INDEX IF NOT EXISTS idx_events_kind_pubkey ON events(kind, pubkey);
|
||||
CREATE INDEX IF NOT EXISTS idx_events_kind_pubkey_created_at ON events(kind, pubkey, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_events_created_at ON {{.Prefix}}events(created_at);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_events_kind ON {{.Prefix}}events(kind);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_events_pubkey ON {{.Prefix}}events(pubkey);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_events_kind_pubkey ON {{.Prefix}}events(kind, pubkey);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_events_kind_pubkey_created_at ON {{.Prefix}}events(kind, pubkey, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS event_tags (
|
||||
CREATE TABLE IF NOT EXISTS {{.Prefix}}event_tags (
|
||||
event_id TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE
|
||||
FOREIGN KEY (event_id) REFERENCES {{.Prefix}}events(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_event_tags_event_id ON event_tags(event_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_tags_key ON event_tags(key);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_tags_key_value ON event_tags(key, value);
|
||||
`
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_event_tags_event_id ON {{.Prefix}}event_tags(event_id);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_event_tags_key ON {{.Prefix}}event_tags(key);
|
||||
CREATE INDEX IF NOT EXISTS {{.Prefix}}idx_event_tags_key_value ON {{.Prefix}}event_tags(key, value);
|
||||
`)
|
||||
|
||||
if _, err := s.db.Exec(basicSchema); err != nil {
|
||||
return fmt.Errorf("failed to create schema: %w", err)
|
||||
@@ -78,23 +91,26 @@ func (s *SqliteBackend) createSchema() error {
|
||||
|
||||
// Try to create FTS5 schema - if it fails, continue without it
|
||||
ftsSchema := `
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS {{.Prefix}}events_fts USING fts5(
|
||||
content,
|
||||
content='events',
|
||||
content='{{.Prefix}}events',
|
||||
content_rowid='rowid'
|
||||
);
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS events_ai AFTER INSERT ON events BEGIN
|
||||
INSERT INTO events_fts(rowid, content) VALUES (new.rowid, new.content);
|
||||
CREATE TRIGGER IF NOT EXISTS {{.Prefix}}events_ai AFTER INSERT ON {{.Prefix}}events BEGIN
|
||||
INSERT INTO {{.Prefix}}events_fts(rowid, content) VALUES (new.rowid, new.content);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS events_ad AFTER DELETE ON events BEGIN
|
||||
INSERT INTO events_fts(events_fts, rowid, content) VALUES('delete', old.rowid, old.content);
|
||||
CREATE TRIGGER IF NOT EXISTS {{.Prefix}}events_ad AFTER DELETE ON {{.Prefix}}events BEGIN
|
||||
INSERT INTO {{.Prefix}}events_fts({{.Prefix}}events_fts, rowid, content)
|
||||
VALUES('delete', old.rowid, old.content);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS events_au AFTER UPDATE ON events BEGIN
|
||||
INSERT INTO events_fts(events_fts, rowid, content) VALUES('delete', old.rowid, old.content);
|
||||
INSERT INTO events_fts(rowid, content) VALUES (new.rowid, new.content);
|
||||
CREATE TRIGGER IF NOT EXISTS {{.Prefix}}events_au AFTER UPDATE ON {{.Prefix}}events BEGIN
|
||||
INSERT INTO {{.Prefix}}events_fts({{.Prefix}}events_fts, rowid, content)
|
||||
VALUES('delete', old.rowid, old.content);
|
||||
INSERT INTO {{.Prefix}}events_fts(rowid, content)
|
||||
VALUES (new.rowid, new.content);
|
||||
END;
|
||||
`
|
||||
|
||||
|
||||
+3
-3
@@ -76,12 +76,12 @@ func (s *SqliteBackend) QueryEvents(filter nostr.Filter, maxLimit int) iter.Seq[
|
||||
|
||||
func (s *SqliteBackend) buildSelectQuery(filter nostr.Filter, limit int) squirrel.SelectBuilder {
|
||||
qb := squirrel.Select("id", "created_at", "kind", "pubkey", "content", "tags", "sig").
|
||||
From("events").
|
||||
From(s.tmpl("{{.Prefix}}events")).
|
||||
OrderBy("created_at DESC")
|
||||
|
||||
// Handle search with FTS (if available)
|
||||
if filter.Search != "" && s.FTSAvailable {
|
||||
qb = qb.Join("events_fts ON events.rowid = events_fts.rowid").
|
||||
qb = qb.Join(s.tmpl("{{.Prefix}}events_fts ON {{.Prefix}}events.rowid = {{.Prefix}}events_fts.rowid")).
|
||||
Where(squirrel.Eq{"events_fts": filter.Search})
|
||||
} else if filter.Search != "" {
|
||||
// Fallback to LIKE search if FTS not available
|
||||
@@ -128,7 +128,7 @@ func (s *SqliteBackend) buildSelectQuery(filter nostr.Filter, limit int) squirre
|
||||
}
|
||||
|
||||
subQuery := squirrel.Select("event_id").
|
||||
From("event_tags").
|
||||
From(s.tmpl("{{.Prefix}}event_tags")).
|
||||
Where(squirrel.Eq{"key": tagKey}).
|
||||
Where(squirrel.Eq{"value": tagValueInterfaces})
|
||||
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import (
|
||||
func (s *SqliteBackend) SaveEvent(evt nostr.Event) error {
|
||||
// Check if event already exists
|
||||
var existingID string
|
||||
qb := squirrel.Select("id").From("events").Where(squirrel.Eq{"id": evt.ID.Hex()})
|
||||
qb := squirrel.Select("id").From(s.tmpl("{{.Prefix}}events")).Where(squirrel.Eq{"id": evt.ID.Hex()})
|
||||
err := qb.RunWith(s.db).QueryRow().Scan(&existingID)
|
||||
if err == nil {
|
||||
// Event already exists
|
||||
@@ -27,7 +27,7 @@ func (s *SqliteBackend) SaveEvent(evt nostr.Event) error {
|
||||
}
|
||||
|
||||
// Insert the event
|
||||
insertQb := squirrel.Insert("events").
|
||||
insertQb := squirrel.Insert(s.tmpl("{{.Prefix}}events")).
|
||||
Columns("id", "created_at", "kind", "pubkey", "content", "tags", "sig").
|
||||
Values(
|
||||
evt.ID.Hex(),
|
||||
@@ -48,7 +48,7 @@ func (s *SqliteBackend) SaveEvent(evt nostr.Event) error {
|
||||
// Insert single-letter tags into event_tags table
|
||||
for _, tag := range evt.Tags {
|
||||
if len(tag) >= 2 && len(tag[0]) == 1 {
|
||||
tagQb := squirrel.Insert("event_tags").
|
||||
tagQb := squirrel.Insert(s.tmpl("{{.Prefix}}event_tags")).
|
||||
Columns("event_id", "key", "value").
|
||||
Values(evt.ID.Hex(), tag[0], tag[1])
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ func TestSqliteFlow(t *testing.T) {
|
||||
os.RemoveAll("/tmp/sqlitetest.db")
|
||||
|
||||
sb := &SqliteBackend{
|
||||
Path: "/tmp/sqlitetest.db",
|
||||
Path: "/tmp/sqlitetest.db",
|
||||
Prefix: "prefix",
|
||||
}
|
||||
err := sb.Init()
|
||||
assert.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user