Files
musenalm/pages/suche_beitraege.go
2025-03-07 16:39:19 +01:00

342 lines
7.6 KiB
Go

package pages
import (
"database/sql"
"maps"
"slices"
"sort"
"github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels"
"github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes"
"github.com/pocketbase/pocketbase/core"
)
const (
DEFAULT_PAGESIZE = 80
FILTER_PARAM_BEIAEGE_AGENT = "agentfilter"
FILTER_PARAM_BEIAEGE_TYPE = "typefilter"
FILTER_PARAM_BEIAEGE_ONLYSCANS = "onlyscans"
FILTER_PARAM_BEIAEGE_YEAR = "yearfilter"
)
type BeitraegeFilterParameters struct {
Agent string
Type string
Year string
OnlyScans bool
}
func NewBeitraegeFilterParameters(ev *core.RequestEvent) BeitraegeFilterParameters {
agent := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_AGENT)
typ := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_TYPE)
year := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_YEAR)
onlyscans := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_ONLYSCANS) == "on"
return BeitraegeFilterParameters{
Agent: agent,
Type: typ,
Year: year,
OnlyScans: onlyscans,
}
}
func (p *BeitraegeFilterParameters) FieldSetBeitraege() []dbmodels.FTS5QueryRequest {
ret := []dbmodels.FTS5QueryRequest{}
if p.Agent != "" {
q := "\"" + p.Agent + "\""
que := dbmodels.NormalizeQuery(q)
req := dbmodels.IntoQueryRequests([]string{dbmodels.AGENTS_TABLE}, que)
ret = append(ret, req...)
}
if p.Type != "" {
q := "\"" + p.Type + "\""
que := dbmodels.NormalizeQuery(q)
req := dbmodels.IntoQueryRequests([]string{dbmodels.MUSENALM_INHALTE_TYPE_FIELD}, que)
ret = append(ret, req...)
}
if p.Year != "" {
q := "\"" + p.Year + "\""
que := dbmodels.NormalizeQuery(q)
req := dbmodels.IntoQueryRequests([]string{dbmodels.ENTRIES_TABLE}, que)
ret = append(ret, req...)
}
return ret
}
func (p BeitraegeFilterParameters) ToQueryParams() string {
r := ""
if p.Agent != "" {
r += "&" + FILTER_PARAM_BEIAEGE_AGENT + "=" + p.Agent
}
if p.Type != "" {
r += "&" + FILTER_PARAM_BEIAEGE_TYPE + "=" + p.Type
}
if p.Year != "" {
r += "&" + FILTER_PARAM_BEIAEGE_YEAR + "=" + p.Year
}
if p.OnlyScans {
r += "&" + FILTER_PARAM_BEIAEGE_ONLYSCANS + "=on"
}
return r
}
func (p BeitraegeFilterParameters) ToQueryParamsWOScans() string {
r := ""
if p.Agent != "" {
r += "&" + FILTER_PARAM_BEIAEGE_AGENT + "=" + p.Agent
}
if p.Type != "" {
r += "&" + FILTER_PARAM_BEIAEGE_TYPE + "=" + p.Type
}
if p.Year != "" {
r += "&" + FILTER_PARAM_BEIAEGE_YEAR + "=" + p.Year
}
return r
}
type SearchResultBeitraege struct {
Queries []dbmodels.FTS5QueryRequest
// these are the sorted IDs for hits
Hits []string
Entries map[string]*dbmodels.Entry // <- Key: Entry ID
Agents map[string]*dbmodels.Agent // <- Key: Agent IDs
Contents map[string][]*dbmodels.Content // <- Key: Entry ID, or year
ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key: Content ID
Pages []int
AgentsList []*dbmodels.Agent
TypesList []string
YearList []int
}
func EmptyResultBeitraege() *SearchResultBeitraege {
return &SearchResultBeitraege{
Hits: []string{},
Entries: make(map[string]*dbmodels.Entry),
Agents: make(map[string]*dbmodels.Agent),
Contents: make(map[string][]*dbmodels.Content),
ContentsAgents: make(map[string][]*dbmodels.RContentsAgents),
}
}
func NewSearchBeitraege(app core.App, params SearchParameters, filters BeitraegeFilterParameters) (*SearchResultBeitraege, error) {
contents := []*dbmodels.Content{}
queries := params.FieldSetBeitraege()
fqueries := filters.FieldSetBeitraege()
queries = append(queries, fqueries...)
if params.AlmString != "" {
e, err := dbmodels.Contents_MusenalmID(app, params.AlmString)
if err != nil && err == sql.ErrNoRows {
return EmptyResultBeitraege(), nil
} else if err != nil {
return nil, err
}
contents = append(contents, e)
} else {
if len(queries) == 0 {
return nil, ErrNoQuery
}
hits, err := dbmodels.FTS5Search(app, dbmodels.CONTENTS_TABLE, queries...)
if err != nil {
return nil, err
} else if len(hits) == 0 {
return EmptyResultBeitraege(), nil
}
ids := []any{}
for _, hit := range hits {
ids = append(ids, hit.ID)
}
cs, err := dbmodels.Contents_IDs(app, ids)
if err != nil {
return nil, err
}
if filters.OnlyScans {
scans := []*dbmodels.Content{}
for _, c := range cs {
if len(c.Scans()) > 0 {
scans = append(scans, c)
}
}
cs = scans
}
contents = append(contents, cs...)
}
resultids := []any{}
uniqueresultentryids := map[string]bool{}
types := make(map[string]bool)
for _, content := range contents {
resultids = append(resultids, content.Id)
uniqueresultentryids[content.Entry()] = true
for _, typ := range content.MusenalmType() {
types[typ] = true
}
}
resultentryids := []any{}
for entryid, _ := range uniqueresultentryids {
resultentryids = append(resultentryids, entryid)
}
entries, err := dbmodels.Entries_IDs(app, datatypes.ToAny(resultentryids))
if err != nil {
return nil, err
}
if params.Sort == "year" {
dbmodels.Sort_Entries_Year_Title(entries)
} else {
dbmodels.Sort_Entries_Title_Year(entries)
}
arels, err := dbmodels.RContentsAgents_Contents(app, resultids)
if err != nil {
return nil, err
}
uniqueaids := map[string]bool{}
for _, a := range arels {
uniqueaids[a.Agent()] = true
}
aids := []any{}
for aid, _ := range uniqueaids {
aids = append(aids, aid)
}
agents, err := dbmodels.Agents_IDs(app, aids)
if err != nil {
return nil, err
}
contentsmap := make(map[string][]*dbmodels.Content)
for _, c := range contents {
contentsmap[c.Entry()] = append(contentsmap[c.Entry()], c)
}
for _, c := range contentsmap {
dbmodels.Sort_Contents_Numbering(c)
}
contentsagents := make(map[string][]*dbmodels.RContentsAgents)
for _, a := range arels {
contentsagents[a.Content()] = append(contentsagents[a.Content()], a)
}
agentsmap := make(map[string]*dbmodels.Agent)
for _, a := range agents {
agentsmap[a.Id] = a
}
entriesmap := make(map[string]*dbmodels.Entry)
years := make(map[int]bool)
for _, e := range entries {
entriesmap[e.Id] = e
years[e.Year()] = true
}
hits := []string{}
for _, e := range entries {
hits = append(hits, e.Id)
}
pages := PagesMap(hits, contentsmap, DEFAULT_PAGESIZE)
if params.Page < 1 || params.Page > len(pages) {
params.Page = 1
}
if params.Page == len(pages) {
hits = hits[pages[params.Page-1]:]
} else {
hits = hits[pages[params.Page-1]:pages[params.Page]]
}
tL := slices.Collect(maps.Keys(types))
sort.Strings(tL)
yL := slices.Collect(maps.Keys(years))
sort.Ints(yL)
dbmodels.Sort_Agents_Name(agents)
return &SearchResultBeitraege{
Queries: queries,
Hits: hits,
Entries: entriesmap,
Agents: agentsmap,
Contents: contentsmap,
ContentsAgents: contentsagents,
Pages: pages,
AgentsList: agents,
TypesList: tL,
YearList: yL,
}, nil
}
func (p *SearchResultBeitraege) CountEntries() int {
return len(p.Entries)
}
func (p *SearchResultBeitraege) Count() int {
cnt := 0
for _, c := range p.Contents {
cnt += len(c)
}
return cnt
}
func (p *SearchResultBeitraege) PagesCount() int {
return len(p.Pages) - 1
}
func PagesMap[T any](hits []string, hitmap map[string][]*T, pagesize int) []int {
ret := []int{0}
m := 0
for i, hit := range hits {
m += len(hitmap[hit])
if m >= pagesize {
ret = append(ret, i)
m = 0
}
}
if m > 0 {
ret = append(ret, len(hits))
}
return ret
}
func PagesArray[T any](hits []T, pagesize int) []int {
ret := []int{0}
m := 0
for i := range hits {
m++
if m >= pagesize {
ret = append(ret, i)
m = 0
}
}
if m > 0 {
ret = append(ret, len(hits))
}
return ret
}