From 3968f6b1b42c6ba6f90b560e113f3a7901809c12 Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Sat, 1 Mar 2025 01:23:56 +0100 Subject: [PATCH] Filtering now works --- dbmodels/fts5.go | 7 + dbmodels/sorting.go | 7 + pages/suche.go | 5 +- pages/suche_beitraege.go | 112 ++++++++++- pages/suche_parameters.go | 17 ++ scratchpad.md | 3 - views/routes/suche/beitraege/body.gohtml | 180 +++++++----------- views/routes/suche/beitraege/tablehead.gohtml | 119 ++++++++++++ .../suche/components/_filterlist.gohtml | 75 ++++++++ views/transform/main.js | 10 +- 10 files changed, 408 insertions(+), 127 deletions(-) create mode 100644 views/routes/suche/beitraege/tablehead.gohtml create mode 100644 views/routes/suche/components/_filterlist.gohtml diff --git a/dbmodels/fts5.go b/dbmodels/fts5.go index e9bc560..14df672 100644 --- a/dbmodels/fts5.go +++ b/dbmodels/fts5.go @@ -94,6 +94,7 @@ var CONTENTS_FTS5_FIELDS = []string{ ENTRIES_TABLE, AGENTS_TABLE, MUSENALMID_FIELD, + MUSENALM_INHALTE_TYPE_FIELD, ANNOTATION_FIELD, COMMENT_FIELD, } @@ -375,6 +376,11 @@ func FTS5ValuesContent(content *Content, entry *Entry, agents []*Agent) []string entrystring += "; [o.J.]" } + typestring := "" + for _, typ := range content.MusenalmType() { + typestring += typ + " " + } + return []string{ content.PreferredTitle(), content.VariantTitle(), @@ -392,6 +398,7 @@ func FTS5ValuesContent(content *Content, entry *Entry, agents []*Agent) []string entrystring, agentstring, strconv.Itoa(content.MusenalmID()), + typestring, datatypes.DeleteTags(content.Annotation()), datatypes.DeleteTags(content.Comment()), } diff --git a/dbmodels/sorting.go b/dbmodels/sorting.go index ec19712..738f2ee 100644 --- a/dbmodels/sorting.go +++ b/dbmodels/sorting.go @@ -15,6 +15,13 @@ func Sort_Series_Title(series []*Series) { }) } +func Sort_Agents_Name(agents []*Agent) { + collator := collate.New(language.German) + slices.SortFunc(agents, func(i, j *Agent) int { + return collator.CompareString(i.Name(), j.Name()) + }) +} + func Sort_Entries_Title_Year(entries []*Entry) { collator := collate.New(language.German) slices.SortFunc(entries, func(i, j *Entry) int { diff --git a/pages/suche.go b/pages/suche.go index 4c28387..5355c0f 100644 --- a/pages/suche.go +++ b/pages/suche.go @@ -76,12 +76,13 @@ func (p *SuchePage) SimpleSearchReihenRequest(app core.App, engine *templating.E func (p *SuchePage) SearchBeitraegeRequest(app core.App, engine *templating.Engine, e *core.RequestEvent, params SearchParameters) error { data := make(map[string]interface{}) - - result, err := NewSearchBeitraege(app, params) + filterparams := NewBeitraegeFilterParameters(e) + result, err := NewSearchBeitraege(app, params, filterparams) if err != nil { return engine.Response404(e, err, nil) } + data["filters"] = filterparams data["parameters"] = params data["result"] = result return engine.Response200(e, p.Template+params.Collection+"/", data, p.Layout) diff --git a/pages/suche_beitraege.go b/pages/suche_beitraege.go index 0538c84..bc31ae9 100644 --- a/pages/suche_beitraege.go +++ b/pages/suche_beitraege.go @@ -2,6 +2,9 @@ package pages import ( "database/sql" + "maps" + "slices" + "sort" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes" @@ -10,8 +13,60 @@ import ( 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 +} + type SearchResultBeitraege struct { Queries []dbmodels.FTS5QueryRequest @@ -23,6 +78,10 @@ type SearchResultBeitraege struct { ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key: Content ID Pages []int + + AgentsList []*dbmodels.Agent + TypesList []string + YearList []int } func EmptyResultBeitraege() *SearchResultBeitraege { @@ -35,9 +94,11 @@ func EmptyResultBeitraege() *SearchResultBeitraege { } } -func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBeitraege, error) { +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) @@ -70,14 +131,33 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei 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{} - resultentryids := []string{} + uniqueresultentryids := map[string]bool{} + types := make(map[string]bool) for _, content := range contents { resultids = append(resultids, content.Id) - resultentryids = append(resultentryids, content.Entry()) + 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)) @@ -96,9 +176,14 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei return nil, err } - aids := []any{} + uniqueaids := map[string]bool{} for _, a := range arels { - aids = append(aids, a.Agent()) + uniqueaids[a.Agent()] = true + } + + aids := []any{} + for aid, _ := range uniqueaids { + aids = append(aids, aid) } agents, err := dbmodels.Agents_IDs(app, aids) @@ -111,6 +196,10 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei 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) @@ -122,8 +211,10 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei } 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{} @@ -142,6 +233,14 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei 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, @@ -150,6 +249,9 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei Contents: contentsmap, ContentsAgents: contentsagents, Pages: pages, + AgentsList: agents, + TypesList: tL, + YearList: yL, }, nil } diff --git a/pages/suche_parameters.go b/pages/suche_parameters.go index 8e99ee8..0d8fbe3 100644 --- a/pages/suche_parameters.go +++ b/pages/suche_parameters.go @@ -22,6 +22,7 @@ const ( BAENDE_PARAM_REFS = "references" BEITRAEGE_PARAM_ENTRY = "entry" BEITRAEGE_PARAM_INCIPT = "incipit" + BEITRAEGE_PARAM_TYPE = "type" ) type SearchParameters struct { @@ -48,6 +49,7 @@ type SearchParameters struct { YearString string EntryString string IncipitString string + TypeString string Page int } @@ -61,7 +63,9 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters, annotations := e.Request.URL.Query().Get(SEARCH_PARAM_ANNOTATIONS) == "on" annotationsstring := e.Request.URL.Query().Get(SEARCH_PARAM_ANNOTATIONS + "string") year := e.Request.URL.Query().Get(SEARCH_PARAM_YEAR) == "on" + yearstring := e.Request.URL.Query().Get(SEARCH_PARAM_YEAR + "string") + typestring := e.Request.URL.Query().Get(BEITRAEGE_PARAM_TYPE) series := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES) == "on" seriesstring := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES + "string") @@ -125,6 +129,7 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters, YearString: yearstring, EntryString: entrystring, IncipitString: incipitstring, + TypeString: typestring, }, nil } @@ -151,6 +156,7 @@ func (p SearchParameters) AllSearchTermsBeitraege() string { res = append(res, p.includedParams(p.YearString)...) res = append(res, p.includedParams(p.EntryString)...) res = append(res, p.includedParams(p.IncipitString)...) + res = append(res, p.includedParams(p.TypeString)...) return strings.Join(res, " ") } @@ -199,6 +205,7 @@ func (p SearchParameters) ToQueryParamsBeitraege() string { if p.Incipit { q += "&incipit=on" } + } if p.YearString != "" { @@ -225,6 +232,10 @@ func (p SearchParameters) ToQueryParamsBeitraege() string { q += fmt.Sprintf("&incipitstring=%s", p.IncipitString) } + if p.TypeString != "" { + q += fmt.Sprintf("&typestring=%s", p.TypeString) + } + return q } @@ -359,6 +370,12 @@ func (p SearchParameters) FieldSetBeitraege() []dbmodels.FTS5QueryRequest { ret = append(ret, req...) } + if p.TypeString != "" { + que := dbmodels.NormalizeQuery(p.TypeString) + req := dbmodels.IntoQueryRequests([]string{dbmodels.MUSENALM_INHALTE_TYPE_FIELD}, que) + ret = append(ret, req...) + } + return ret } diff --git a/scratchpad.md b/scratchpad.md index d340084..dcf1714 100644 --- a/scratchpad.md +++ b/scratchpad.md @@ -61,11 +61,8 @@ TODO danach: - Cache? -- HTMX + Smooth scroll - Personen: related - Inhaltsliste: Personen sehen komisch aus -- V\Boosted links gen by webcomponents -- Sammlungen u. Querverweise müssen die URL als Parameter bekommen - Sammlungen neuer versuch - Inhaltsliste Personen - Sortierung nach Band A-Z? diff --git a/views/routes/suche/beitraege/body.gohtml b/views/routes/suche/beitraege/body.gohtml index 12104fb..ebc4e80 100644 --- a/views/routes/suche/beitraege/body.gohtml +++ b/views/routes/suche/beitraege/body.gohtml @@ -37,6 +37,14 @@ EntryString string IncipitString string } + +.filters +type BeitraegeFilterParameters struct { + Agent string + Type string + Year string + OnlyScans bool +} */}} {{ $isAlm := false }} @@ -76,33 +84,51 @@ {{ template "_searchboxsimple" Arr $model.parameters true $q }}
- +
- +
- +
- +
- +
- +
@@ -116,114 +142,37 @@ {{- if $model.parameters.IsBeitraegeSearch -}}
-
-
- {{ if $model.parameters.Query -}} - Suche nach »{{ $model.parameters.Query }}« · - {{- end -}} - {{- if $isAlm -}} - Inhaltsnummer »{{ $model.parameters.AlmString }}« · - {{- end -}} - - {{ if eq $model.result.Count 1 -}} - Ein Beitrag - {{ else -}} - {{ $model.result.Count }} Beiträge - {{- end }} - in - {{ if eq ($model.result.Entries | len) 1 -}} - einem Band - {{ else -}} - {{ $model.result.Entries | len }} Bänden - {{- end -}} -
- {{- if gt (len $model.result.Pages) 1 }} -
- {{ if gt $model.parameters.Page 1 -}} - - - + {{ template "_filterlist" $model }} + {{ template "tablehead" $model }} + {{- if $model.result.Hits -}} +
+ {{- range $_, $hit := $model.result.Hits -}} + {{- $e := index $model.result.Entries $hit -}} + {{- $contents := index $model.result.Contents $e.Id -}} +
+
+ {{ $e.PreferredTitle }} +
+
+ {{- len $contents -}} +
+
+
+ {{- range $i, $c := $contents -}} + {{- $rels := index $model.result.ContentsAgents $c.Id -}} + {{- template "_content" Arr $c $e $rels $model.result.Agents false true + $model.parameters + -}} + {{- end -}} +
{{- end -}} - S. {{ $model.parameters.Page }} / - {{ $model.result.PagesCount }} - {{ if lt $model.parameters.Page ($model.result.PagesCount) -}} - - - - {{- end -}} -
- {{- end -}} - - {{- if not $isAlm -}} -
- - - {{/* INFO: We always redrect to letter = A bc some letters dont exist for other professions */}} -
{{- end -}}
- {{- if $model.result.Hits -}} - -
- {{- range $_, $hit := $model.result.Hits -}} - {{- $e := index $model.result.Entries $hit -}} - {{- $contents := index $model.result.Contents $e.Id -}} -
-
- {{ $e.PreferredTitle }} -
-
- {{- len $contents -}} -
-
-
- {{- range $i, $c := $contents -}} - {{- $rels := index $model.result.ContentsAgents $c.Id -}} - {{- template "_content" Arr $c $e $rels $model.result.Agents false true - $model.parameters - -}} - {{- end -}} -
- {{- end -}} -
- {{- end -}} -
- -
- {{- end -}} diff --git a/views/routes/suche/beitraege/tablehead.gohtml b/views/routes/suche/beitraege/tablehead.gohtml new file mode 100644 index 0000000..3c70951 --- /dev/null +++ b/views/routes/suche/beitraege/tablehead.gohtml @@ -0,0 +1,119 @@ +{{ $model := . }} + +{{ $isAlm := false }} +{{ $isTitle := false }} +{{ $isYear := false }} +{{ $isPerson := false }} +{{ $isAnnotation := false }} +{{ $isIncipit := false }} +{{ $isEntry := false }} + +{{- $isAlm = $model.parameters.AlmString -}} +{{- $isTitle = or $model.parameters.Title $model.parameters.TitleString -}} +{{- $isYear = or $model.parameters.Year $model.parameters.YearString -}} +{{- $isPerson = or $model.parameters.Persons $model.parameters.PersonsString -}} +{{- $isAnnotation = or $model.parameters.Annotations $model.parameters.AnnotationsString -}} +{{- $isIncipit = or $model.parameters.Incipit $model.parameters.IncipitString -}} +{{- $isEntry = or $model.parameters.Entry $model.parameters.EntryString -}} + +{{- $isBase := not (or $isTitle $isYear $isPerson $isAnnotation $isIncipit $isEntry) -}} + + +
+
+ {{ if $model.parameters.Query -}} +
Suche nach »{{ $model.parameters.Query }}«
+
·
+ {{- end -}} + {{- if $isAlm -}} +
Inhaltsnummer »{{ $model.parameters.AlmString }}«
+
·
+ {{- end -}} +
+ + {{ if eq $model.result.Count 1 -}} + Ein Beitrag + {{ else -}} + {{ $model.result.Count }} Beiträge + {{- end }} + in + {{ if eq ($model.result.Entries | len) 1 -}} + einem Band + {{ else -}} + {{ $model.result.Entries | len }} Bänden + {{- end -}} +
+ {{- if gt (len $model.result.Pages) 1 }} +
·
+
+ {{ if gt $model.parameters.Page 1 -}} + + + + {{- end -}} + Seite {{ $model.parameters.Page }} / + {{ $model.result.PagesCount }} + {{ if lt $model.parameters.Page ($model.result.PagesCount) -}} + + + + {{- end -}} +
+ {{- end -}} +
+ + {{- if not $isAlm -}} +
+ + + + + + +
+ {{- end -}} +
diff --git a/views/routes/suche/components/_filterlist.gohtml b/views/routes/suche/components/_filterlist.gohtml new file mode 100644 index 0000000..5fdd1fb --- /dev/null +++ b/views/routes/suche/components/_filterlist.gohtml @@ -0,0 +1,75 @@ +{{ $model := . }} + + +
+ + + + + +
+ + diff --git a/views/transform/main.js b/views/transform/main.js index c6508eb..03d6102 100644 --- a/views/transform/main.js +++ b/views/transform/main.js @@ -146,6 +146,10 @@ class FilterList extends HTMLElement { this.addEventListener("keydown", this.onEnter.bind(this)); this.addEventListener("focusin", this.onGainFocus.bind(this)); this.addEventListener("focusout", this.onLoseFocus.bind(this)); + + if (htmx) { + htmx.process(this); + } } attributeChangedCallback(name, oldValue, newValue) { @@ -253,6 +257,10 @@ class FilterList extends HTMLElement { return item.id; } + getHREFEncoded(item) { + return encodeURIComponent(this.getHREF(item)); + } + getSearchText(item) { if (!item) { return ""; @@ -351,7 +359,7 @@ class FilterList extends HTMLElement { .map( (item, index) => `