From 8091dfedbea04f8697c8840e0c522728817c3676 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sun, 2 Jan 2022 17:19:24 -0300 Subject: [PATCH] use generated columns and gin array indexes to query tags better. --- basic/postgresql.go | 13 ++++++++++--- basic/query.go | 28 +++++++++++++--------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/basic/postgresql.go b/basic/postgresql.go index 0fd9056..d5c7d1f 100644 --- a/basic/postgresql.go +++ b/basic/postgresql.go @@ -13,6 +13,12 @@ func initDB(dburl string) (*sqlx.DB, error) { } _, 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 ( id text NOT NULL, pubkey text NOT NULL, @@ -20,14 +26,15 @@ CREATE TABLE IF NOT EXISTS event ( kind integer NOT NULL, tags jsonb 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 pubkeytimeidx ON event (pubkey, created_at); +CREATE INDEX IF NOT EXISTS arbitrarytagvalues ON event USING gin (tagvalues); `) log.Print(err) return db, nil } - -const tagConditions = `jsonb_path_match(tags, '$[*][1] == $value', jsonb_build_object('value', ?::text))` diff --git a/basic/query.go b/basic/query.go index 4ef44dc..933ddb6 100644 --- a/basic/query.go +++ b/basic/query.go @@ -87,40 +87,38 @@ func (b *BasicRelay) QueryEvents( conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`) } + tagQuery := make([]string, 0, 1) if filter.TagE != nil { if len(filter.TagE) > 10 { // too many tags, fail everything return } - if len(filter.TagE) == 0 { // #e being [] mean you won't get anything return } - innerConditions := make([]string, len(filter.TagE)) - for _, e := range filter.TagE { - innerConditions = append(innerConditions, tagConditions) - params = append(params, e) - } - conditions = append(conditions, strings.Join(innerConditions, " OR ")) + tagQuery = append(tagQuery, filter.TagE...) } - if filter.TagP != nil { if len(filter.TagP) > 10 { // too many tags, fail everything return } - if len(filter.TagP) == 0 { - // #p being [] mean you won't get anything + // #e being [] mean you won't get anything return } - innerConditions := make([]string, len(filter.TagP)) - for _, p := range filter.TagP { - innerConditions = append(innerConditions, tagConditions) - params = append(params, p) + tagQuery = append(tagQuery, filter.TagP...) + } + + 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 {