vikunja/pkg/models/typesense.go

304 lines
7.6 KiB
Go

// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2023 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"fmt"
"github.com/typesense/typesense-go/typesense"
"github.com/typesense/typesense-go/typesense/api"
"github.com/typesense/typesense-go/typesense/api/pointer"
)
var typesenseClient *typesense.Client
func InitTypesense() {
if !config.TypesenseEnabled.GetBool() {
return
}
typesenseClient = typesense.NewClient(
typesense.WithServer(config.TypesenseURL.GetString()),
typesense.WithAPIKey(config.TypesenseAPIKey.GetString()))
}
func CreateTypesenseCollections() error {
taskSchema := &api.CollectionSchema{
Name: "tasks",
EnableNestedFields: pointer.True(),
Fields: []api.Field{
{
Name: "id",
Type: "string",
},
{
Name: "title",
Type: "string",
},
{
Name: "description",
Type: "string",
},
{
Name: "done",
Type: "bool",
},
{
Name: "done_at",
Type: "int64", // unix timestamp
Optional: pointer.True(),
},
{
Name: "due_date",
Type: "int64", // unix timestamp
Optional: pointer.True(),
},
{
Name: "project_id",
Type: "int64",
},
{
Name: "repeat_after",
Type: "int64",
},
{
Name: "repeat_mode",
Type: "int32",
},
{
Name: "priority",
Type: "int64",
},
{
Name: "start_date",
Type: "int64", // unix timestamp
Optional: pointer.True(),
},
{
Name: "end_date",
Type: "int64", // unix timestamp
Optional: pointer.True(),
},
{
Name: "hex_color",
Type: "string",
},
{
Name: "percent_done",
Type: "float",
},
{
Name: "identifier",
Type: "string",
},
{
Name: "index",
Type: "int64",
},
{
Name: "uid",
Type: "string",
},
{
Name: "cover_image_attachment_id",
Type: "int64",
},
{
Name: "created",
Type: "int64", // unix timestamp
},
{
Name: "updated",
Type: "int64", // unix timestamp
},
{
Name: "bucket_id",
Type: "int64",
},
{
Name: "position",
Type: "float",
},
{
Name: "kanban_position",
Type: "float",
},
{
Name: "created_by_id",
Type: "int64",
},
{
Name: "reminders",
Type: "object[]", // TODO
Optional: pointer.True(),
},
{
Name: "assignees",
Type: "object[]", // TODO
Optional: pointer.True(),
},
{
Name: "labels",
Type: "object[]", // TODO
Optional: pointer.True(),
},
{
Name: "related_tasks",
Type: "object[]", // TODO
Optional: pointer.True(),
},
{
Name: "attachments",
Type: "object[]", // TODO
Optional: pointer.True(),
},
{
Name: "comments",
Type: "object[]", // TODO
Optional: pointer.True(),
},
},
}
// delete any collection which might exist
_, _ = typesenseClient.Collection("tasks").Delete()
_, err := typesenseClient.Collections().Create(taskSchema)
return err
}
func ReindexAllTasks() (err error) {
tasks := make(map[int64]*Task)
s := db.NewSession()
defer s.Close()
err = s.Find(tasks)
if err != nil {
return err
}
err = addMoreInfoToTasks(s, tasks, &user.User{ID: 1})
if err != nil {
return err
}
for _, task := range tasks {
searchTask := convertTaskToTypesenseTask(task)
comment := &TaskComment{TaskID: task.ID}
searchTask.Comments, _, _, err = comment.ReadAll(s, task.CreatedBy, "", -1, -1)
if err != nil {
return err
}
_, err = typesenseClient.Collection("tasks").
Documents().
Create(searchTask)
if err != nil {
return err
}
}
return nil
}
type typesenseTask struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Done bool `json:"done"`
DoneAt int64 `json:"done_at"`
DueDate int64 `json:"due_date"`
ProjectID int64 `json:"project_id"`
RepeatAfter int64 `json:"repeat_after"`
RepeatMode int `json:"repeat_mode"`
Priority int64 `json:"priority"`
StartDate int64 `json:"start_date"`
EndDate int64 `json:"end_date"`
HexColor string `json:"hex_color"`
PercentDone float64 `json:"percent_done"`
Identifier string `json:"identifier"`
Index int64 `json:"index"`
UID string `json:"uid"`
CoverImageAttachmentID int64 `json:"cover_image_attachment_id"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
BucketID int64 `json:"bucket_id"`
Position float64 `json:"position"`
KanbanPosition float64 `json:"kanban_position"`
CreatedByID int64 `json:"created_by_id"`
Reminders interface{} `json:"reminders"`
Assignees interface{} `json:"assignees"`
Labels interface{} `json:"labels"`
//RelatedTasks interface{} `json:"related_tasks"`
Attachments interface{} `json:"attachments"`
Comments interface{} `json:"comments"`
}
func convertTaskToTypesenseTask(task *Task) *typesenseTask {
tt := &typesenseTask{
ID: fmt.Sprintf("%d", task.ID),
Title: task.Title,
Description: task.Description,
Done: task.Done,
DoneAt: task.DoneAt.UTC().Unix(),
DueDate: task.DueDate.UTC().Unix(),
ProjectID: task.ProjectID,
RepeatAfter: task.RepeatAfter,
RepeatMode: int(task.RepeatMode),
Priority: task.Priority,
StartDate: task.StartDate.UTC().Unix(),
EndDate: task.EndDate.UTC().Unix(),
HexColor: task.HexColor,
PercentDone: task.PercentDone,
Identifier: task.Identifier,
Index: task.Index,
UID: task.UID,
CoverImageAttachmentID: task.CoverImageAttachmentID,
Created: task.Created.UTC().Unix(),
Updated: task.Updated.UTC().Unix(),
BucketID: task.BucketID,
Position: task.Position,
KanbanPosition: task.KanbanPosition,
CreatedByID: task.CreatedByID,
Reminders: task.Reminders,
Assignees: task.Assignees,
Labels: task.Labels,
//RelatedTasks: task.RelatedTasks,
Attachments: task.Attachments,
}
if task.DoneAt.IsZero() {
tt.DoneAt = 0
}
if task.DueDate.IsZero() {
tt.DueDate = 0
}
if task.StartDate.IsZero() {
tt.StartDate = 0
}
if task.EndDate.IsZero() {
tt.EndDate = 0
}
return tt
}