mirror of
				https://github.com/Theodor-Springmann-Stiftung/musenalm.git
				synced 2025-10-29 09:15:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			342 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package controllers
 | |
| 
 | |
| 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
 | |
| }
 | 
