use generated columns and gin array indexes to query tags better.

This commit is contained in:
fiatjaf
2022-01-02 17:19:24 -03:00
parent ae3f5df0b9
commit 8091dfedbe
2 changed files with 23 additions and 18 deletions

View File

@@ -13,6 +13,12 @@ func initDB(dburl string) (*sqlx.DB, error) {
} }
_, err = db.Exec(` _, err = db.Exec(`
CREATE FUNCTION tags_to_tagvalues(jsonb) RETURNS text[]
AS 'SELECT array_agg(t->>1) FROM (SELECT jsonb_array_elements($1) AS t)s;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
CREATE TABLE IF NOT EXISTS event ( CREATE TABLE IF NOT EXISTS event (
id text NOT NULL, id text NOT NULL,
pubkey text NOT NULL, pubkey text NOT NULL,
@@ -20,14 +26,15 @@ CREATE TABLE IF NOT EXISTS event (
kind integer NOT NULL, kind integer NOT NULL,
tags jsonb NOT NULL, tags jsonb NOT NULL,
content text NOT NULL, content text NOT NULL,
sig text NOT NULL sig text NOT NULL,
tagvalues text[] GENERATED ALWAYS AS (tags_to_tagvalues(tags)) STORED
); );
CREATE UNIQUE INDEX IF NOT EXISTS ididx ON event (id); CREATE UNIQUE INDEX IF NOT EXISTS ididx ON event (id);
CREATE UNIQUE INDEX IF NOT EXISTS pubkeytimeidx ON event (pubkey, created_at); CREATE UNIQUE INDEX IF NOT EXISTS pubkeytimeidx ON event (pubkey, created_at);
CREATE INDEX IF NOT EXISTS arbitrarytagvalues ON event USING gin (tagvalues);
`) `)
log.Print(err) log.Print(err)
return db, nil return db, nil
} }
const tagConditions = `jsonb_path_match(tags, '$[*][1] == $value', jsonb_build_object('value', ?::text))`

View File

@@ -87,40 +87,38 @@ func (b *BasicRelay) QueryEvents(
conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`) conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`)
} }
tagQuery := make([]string, 0, 1)
if filter.TagE != nil { if filter.TagE != nil {
if len(filter.TagE) > 10 { if len(filter.TagE) > 10 {
// too many tags, fail everything // too many tags, fail everything
return return
} }
if len(filter.TagE) == 0 { if len(filter.TagE) == 0 {
// #e being [] mean you won't get anything // #e being [] mean you won't get anything
return return
} }
innerConditions := make([]string, len(filter.TagE)) tagQuery = append(tagQuery, filter.TagE...)
for _, e := range filter.TagE {
innerConditions = append(innerConditions, tagConditions)
params = append(params, e)
}
conditions = append(conditions, strings.Join(innerConditions, " OR "))
} }
if filter.TagP != nil { if filter.TagP != nil {
if len(filter.TagP) > 10 { if len(filter.TagP) > 10 {
// too many tags, fail everything // too many tags, fail everything
return return
} }
if len(filter.TagP) == 0 { if len(filter.TagP) == 0 {
// #p being [] mean you won't get anything // #e being [] mean you won't get anything
return return
} }
innerConditions := make([]string, len(filter.TagP)) tagQuery = append(tagQuery, filter.TagP...)
for _, p := range filter.TagP { }
innerConditions = append(innerConditions, tagConditions)
params = append(params, p) if len(tagQuery) > 0 {
arrayBuild := make([]string, len(tagQuery))
for i, tagValue := range tagQuery {
arrayBuild[i] = "?"
params = append(params, tagValue)
} }
conditions = append(conditions, strings.Join(innerConditions, " OR ")) conditions = append(conditions,
"tagvalues && ARRAY["+strings.Join(arrayBuild, ",")+"]")
} }
if filter.Since != 0 { if filter.Since != 0 {