Messagefilter (+FTS) [WIP]
This commit is contained in:
@@ -17,42 +17,47 @@ const (
|
||||
)
|
||||
|
||||
type CursorToken struct {
|
||||
Mode Mode
|
||||
Timestamp int64
|
||||
Id int64
|
||||
Direction string
|
||||
Mode Mode
|
||||
Timestamp int64
|
||||
Id int64
|
||||
Direction string
|
||||
FilterHash string
|
||||
}
|
||||
|
||||
type cursorTokenSerialize struct {
|
||||
Timestamp *int64 `json:"ts,omitempty"`
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
Direction *string `json:"dir,omitempty"`
|
||||
Timestamp *int64 `json:"ts,omitempty"`
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
Direction *string `json:"dir,omitempty"`
|
||||
FilterHash *string `json:"f,omitempty"`
|
||||
}
|
||||
|
||||
func Start() CursorToken {
|
||||
return CursorToken{
|
||||
Mode: CTMStart,
|
||||
Timestamp: 0,
|
||||
Id: 0,
|
||||
Direction: "",
|
||||
Mode: CTMStart,
|
||||
Timestamp: 0,
|
||||
Id: 0,
|
||||
Direction: "",
|
||||
FilterHash: "",
|
||||
}
|
||||
}
|
||||
|
||||
func End() CursorToken {
|
||||
return CursorToken{
|
||||
Mode: CTMEnd,
|
||||
Timestamp: 0,
|
||||
Id: 0,
|
||||
Direction: "",
|
||||
Mode: CTMEnd,
|
||||
Timestamp: 0,
|
||||
Id: 0,
|
||||
Direction: "",
|
||||
FilterHash: "",
|
||||
}
|
||||
}
|
||||
|
||||
func Normal(ts time.Time, id int64, dir string) CursorToken {
|
||||
func Normal(ts time.Time, id int64, dir string, filter string) CursorToken {
|
||||
return CursorToken{
|
||||
Mode: CTMNormal,
|
||||
Timestamp: ts.UnixMilli(),
|
||||
Id: id,
|
||||
Direction: dir,
|
||||
Mode: CTMNormal,
|
||||
Timestamp: ts.UnixMilli(),
|
||||
Id: id,
|
||||
Direction: dir,
|
||||
FilterHash: filter,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +88,10 @@ func (c *CursorToken) Token() string {
|
||||
sertok.Direction = &c.Direction
|
||||
}
|
||||
|
||||
if c.FilterHash != "" {
|
||||
sertok.FilterHash = &c.FilterHash
|
||||
}
|
||||
|
||||
body, err := json.Marshal(sertok)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -128,6 +137,9 @@ func Decode(tok string) (CursorToken, error) {
|
||||
if tokenDeserialize.Direction != nil {
|
||||
token.Direction = *tokenDeserialize.Direction
|
||||
}
|
||||
if tokenDeserialize.FilterHash != nil {
|
||||
token.FilterHash = *tokenDeserialize.FilterHash
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
||||
"blackforestbytes.com/simplecloudnotifier/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
||||
"time"
|
||||
)
|
||||
@@ -112,7 +111,7 @@ func (db *Database) DeleteMessage(ctx TxContext, scnMessageID models.SCNMessageI
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Database) ListMessages(ctx TxContext, userid models.UserID, pageSize int, inTok cursortoken.CursorToken) ([]models.Message, cursortoken.CursorToken, error) {
|
||||
func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pageSize int, inTok cursortoken.CursorToken) ([]models.Message, cursortoken.CursorToken, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return nil, cursortoken.CursorToken{}, err
|
||||
@@ -124,13 +123,20 @@ func (db *Database) ListMessages(ctx TxContext, userid models.UserID, pageSize i
|
||||
|
||||
pageCond := ""
|
||||
if inTok.Mode == cursortoken.CTMNormal {
|
||||
pageCond = fmt.Sprintf("AND ( timestamp_real < %d OR (timestamp_real = %d AND scn_message_id < %d ) )", inTok.Timestamp, inTok.Timestamp, inTok.Id)
|
||||
pageCond = "timestamp_real < :tokts OR (timestamp_real = :tokts AND scn_message_id < :tokid )"
|
||||
}
|
||||
|
||||
rows, err := tx.Query(ctx, "SELECT messages.* FROM messages LEFT JOIN subscriptions subs on messages.channel_id = subs.channel_id WHERE subs.subscriber_user_id = :uid AND subs.confirmed = 1 "+pageCond+" ORDER BY timestamp_real DESC LIMIT :lim", sq.PP{
|
||||
"uid": userid,
|
||||
"lim": pageSize + 1,
|
||||
})
|
||||
filterCond, filterJoin, prepParams, err := filter.SQL()
|
||||
|
||||
orderClause := "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC LIMIT :lim"
|
||||
|
||||
sqlQuery := "SELECT " + "messages.*" + " FROM messages " + filterJoin + " WHERE ( " + filterCond + " ) AND ( " + pageCond + " ) " + orderClause
|
||||
|
||||
prepParams["lim"] = pageSize + 1
|
||||
prepParams["tokts"] = inTok.Timestamp
|
||||
prepParams["tokid"] = inTok.Id
|
||||
|
||||
rows, err := tx.Query(ctx, sqlQuery, prepParams)
|
||||
if err != nil {
|
||||
return nil, cursortoken.CursorToken{}, err
|
||||
}
|
||||
@@ -143,45 +149,7 @@ func (db *Database) ListMessages(ctx TxContext, userid models.UserID, pageSize i
|
||||
if len(data) <= pageSize {
|
||||
return data, cursortoken.End(), nil
|
||||
} else {
|
||||
outToken := cursortoken.Normal(data[pageSize-1].TimestampReal, data[pageSize-1].SCNMessageID.IntID(), "DESC")
|
||||
return data[0:pageSize], outToken, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) ListChannelMessages(ctx TxContext, channelid models.ChannelID, pageSize int, inTok cursortoken.CursorToken) ([]models.Message, cursortoken.CursorToken, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return nil, cursortoken.CursorToken{}, err
|
||||
}
|
||||
|
||||
if inTok.Mode == cursortoken.CTMEnd {
|
||||
return make([]models.Message, 0), cursortoken.End(), nil
|
||||
}
|
||||
|
||||
pageCond := ""
|
||||
if inTok.Mode == cursortoken.CTMNormal {
|
||||
pageCond = "AND ( timestamp_real < :tokts OR (timestamp_real = :tokts AND scn_message_id < :tokid ) )"
|
||||
}
|
||||
|
||||
rows, err := tx.Query(ctx, "SELECT * FROM messages WHERE channel_id = :cid "+pageCond+" ORDER BY timestamp_real DESC LIMIT :lim", sq.PP{
|
||||
"cid": channelid,
|
||||
"lim": pageSize + 1,
|
||||
"tokts": inTok.Timestamp,
|
||||
"tokid": inTok.Timestamp,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, cursortoken.CursorToken{}, err
|
||||
}
|
||||
|
||||
data, err := models.DecodeMessages(rows)
|
||||
if err != nil {
|
||||
return nil, cursortoken.CursorToken{}, err
|
||||
}
|
||||
|
||||
if len(data) <= pageSize {
|
||||
return data, cursortoken.End(), nil
|
||||
} else {
|
||||
outToken := cursortoken.Normal(data[pageSize-1].TimestampReal, data[pageSize-1].SCNMessageID.IntID(), "DESC")
|
||||
outToken := cursortoken.Normal(data[pageSize-1].Timestamp(), data[pageSize-1].SCNMessageID.IntID(), "DESC", filter.Hash())
|
||||
return data[0:pageSize], outToken, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +92,16 @@ CREATE TABLE messages
|
||||
priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL,
|
||||
usr_message_id TEXT NULL
|
||||
) STRICT;
|
||||
CREATE INDEX "idx_messages_channel" ON messages (owner_user_id, channel_name);
|
||||
CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (owner_user_id, usr_message_id);
|
||||
CREATE INDEX "idx_messages_senderip" ON messages (sender_ip);
|
||||
CREATE INDEX "idx_messages_sendername" ON messages (sender_name);
|
||||
CREATE INDEX "idx_messages_owner_channel" ON messages (owner_user_id, channel_name COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_owner_channel_nc" ON messages (owner_user_id, channel_name COLLATE NOCASE);
|
||||
CREATE INDEX "idx_messages_channel" ON messages (channel_name COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_channel_nc" ON messages (channel_name COLLATE NOCASE);
|
||||
CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (owner_user_id, usr_message_id COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_senderip" ON messages (sender_ip COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_sendername" ON messages (sender_name COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_sendername_nc" ON messages (sender_name COLLATE NOCASE);
|
||||
CREATE INDEX "idx_messages_title" ON messages (title COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_title_nc" ON messages (title COLLATE NOCASE);
|
||||
|
||||
|
||||
CREATE VIRTUAL TABLE messages_fts USING fts5
|
||||
|
||||
Reference in New Issue
Block a user