Implement PathToRoot and PathToLeaf with one query
After fetching all of a conversation's messages, we traverse the message's Parent or SelectedReply fields to build the message "path" in-memory
This commit is contained in:
parent
ea576d24a6
commit
60a474d516
|
@ -4,6 +4,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -299,48 +300,107 @@ func (s *SQLStore) CloneBranch(messageToClone model.Message) (*model.Message, ui
|
||||||
return &newMessage, replyCount, nil
|
return &newMessage, replyCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathToRoot traverses message Parent until reaching the tree root
|
func fetchMessages(db *gorm.DB) ([]model.Message, error) {
|
||||||
func (s *SQLStore) PathToRoot(message *model.Message) ([]model.Message, error) {
|
var messages []model.Message
|
||||||
if message == nil {
|
if err := db.Find(&messages).Error; err != nil {
|
||||||
return nil, fmt.Errorf("Message is nil")
|
return nil, fmt.Errorf("Could not fetch messages: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messageMap := make(map[uint]model.Message)
|
||||||
|
for i, message := range messages {
|
||||||
|
messageMap[messages[i].ID] = message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map to store replies by their parent ID
|
||||||
|
repliesMap := make(map[uint][]model.Message)
|
||||||
|
for i, message := range messages {
|
||||||
|
if messages[i].ParentID != nil {
|
||||||
|
repliesMap[*messages[i].ParentID] = append(repliesMap[*messages[i].ParentID], message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign replies, parent, and selected reply to each message
|
||||||
|
for i := range messages {
|
||||||
|
if replies, exists := repliesMap[messages[i].ID]; exists {
|
||||||
|
messages[i].Replies = make([]model.Message, len(replies))
|
||||||
|
for j, m := range replies {
|
||||||
|
messages[i].Replies[j] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if messages[i].ParentID != nil {
|
||||||
|
if parent, exists := messageMap[*messages[i].ParentID]; exists {
|
||||||
|
messages[i].Parent = &parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if messages[i].SelectedReplyID != nil {
|
||||||
|
if selectedReply, exists := messageMap[*messages[i].SelectedReplyID]; exists {
|
||||||
|
messages[i].SelectedReply = &selectedReply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return messages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLStore) buildPath(message *model.Message, getNext func(*model.Message) *uint) ([]model.Message, error) {
|
||||||
|
var messages []model.Message
|
||||||
|
messages, err := fetchMessages(s.db.Where("conversation_id = ?", message.ConversationID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map to store messages by their ID
|
||||||
|
messageMap := make(map[uint]*model.Message)
|
||||||
|
for i := range messages {
|
||||||
|
messageMap[messages[i].ID] = &messages[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the path
|
||||||
var path []model.Message
|
var path []model.Message
|
||||||
current := message
|
nextID := &message.ID
|
||||||
|
|
||||||
for {
|
for {
|
||||||
path = append([]model.Message{*current}, path...)
|
current, exists := messageMap[*nextID]
|
||||||
if current.Parent == nil {
|
if !exists {
|
||||||
break
|
return nil, fmt.Errorf("Message with ID %d not found in conversation", *nextID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
path = append(path, *current)
|
||||||
current, err = s.MessageByID(*current.ParentID)
|
|
||||||
if err != nil {
|
nextID = getNext(current)
|
||||||
return nil, fmt.Errorf("finding parent message: %w", err)
|
if nextID == nil {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathToRoot traverses message Parent until reaching the tree root
|
||||||
|
func (s *SQLStore) PathToRoot(message *model.Message) ([]model.Message, error) {
|
||||||
|
if message == nil || message.ID <= 0 {
|
||||||
|
return nil, fmt.Errorf("Message is nil or has invalid ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := s.buildPath(message, func(m *model.Message) *uint {
|
||||||
|
return m.ParentID
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slices.Reverse(path)
|
||||||
|
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathToLeaf traverses message SelectedReply until reaching a tree leaf
|
// PathToLeaf traverses message SelectedReply until reaching a tree leaf
|
||||||
func (s *SQLStore) PathToLeaf(message *model.Message) ([]model.Message, error) {
|
func (s *SQLStore) PathToLeaf(message *model.Message) ([]model.Message, error) {
|
||||||
if message == nil {
|
if message == nil || message.ID <= 0 {
|
||||||
return nil, fmt.Errorf("Message is nil")
|
return nil, fmt.Errorf("Message is nil or has invalid ID")
|
||||||
}
|
}
|
||||||
var path []model.Message
|
|
||||||
current := message
|
|
||||||
for {
|
|
||||||
path = append(path, *current)
|
|
||||||
if current.SelectedReplyID == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
return s.buildPath(message, func(m *model.Message) *uint {
|
||||||
current, err = s.MessageByID(*current.SelectedReplyID)
|
return m.SelectedReplyID
|
||||||
if err != nil {
|
})
|
||||||
return nil, fmt.Errorf("finding selected reply: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) LatestConversationMessages() ([]model.Message, error) {
|
func (s *SQLStore) LatestConversationMessages() ([]model.Message, error) {
|
||||||
|
|
Loading…
Reference in New Issue