Bandsuche

This commit is contained in:
Simon Martens
2025-02-26 22:18:34 +01:00
parent cf6d4a59ed
commit 7f43044f34
8 changed files with 769 additions and 87 deletions

View File

@@ -31,6 +31,15 @@ func REntriesAgents_Entry(app core.App, id string) ([]*REntriesAgents, error) {
) )
} }
func REntriesAgents_Entries(app core.App, ids []any) ([]*REntriesAgents, error) {
return TableByFields[[]*REntriesAgents](
app,
RelationTableName(ENTRIES_TABLE, AGENTS_TABLE),
ENTRIES_TABLE,
ids,
)
}
func RContentsAgents_Agent(app core.App, id string) ([]*RContentsAgents, error) { func RContentsAgents_Agent(app core.App, id string) ([]*RContentsAgents, error) {
return TableByFields[[]*RContentsAgents]( return TableByFields[[]*RContentsAgents](
app, app,

View File

@@ -19,6 +19,10 @@ func ReplaceSlashParen(s string) string {
return strings.ReplaceAll(s, "/)", "<p>") return strings.ReplaceAll(s, "/)", "<p>")
} }
func ReplaceSlashParenSlash(s string) string {
return strings.ReplaceAll(s, "/)", "/")
}
func Lower(s string) string { func Lower(s string) string {
return cases.Lower(language.German).String(s) return cases.Lower(language.German).String(s)
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/app"
"github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels"
"github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes"
"github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels"
"github.com/Theodor-Springmann-Stiftung/musenalm/templating" "github.com/Theodor-Springmann-Stiftung/musenalm/templating"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"
@@ -53,6 +52,15 @@ func (p *SuchePage) Setup(router *router.Router[*core.RequestEvent], app core.Ap
return engine.Response404(e, err, nil) return engine.Response404(e, err, nil)
} }
if paras.Query != "" {
switch paras.Collection {
case "baende":
return p.SimpleSearchBaendeRequest(app, engine, e, *paras)
case "beitraege":
return p.SimpleSearchReihenRequest(app, engine, e)
}
}
data := make(map[string]interface{}) data := make(map[string]interface{})
data["parameters"] = paras data["parameters"] = paras
return engine.Response200(e, p.Template+paras.Collection+"/", data, p.Layout) return engine.Response200(e, p.Template+paras.Collection+"/", data, p.Layout)
@@ -67,45 +75,19 @@ func (p *SuchePage) SimpleSearchReihenRequest(app core.App, engine *templating.E
func (p *SuchePage) SimpleSearchBaendeRequest(app core.App, engine *templating.Engine, e *core.RequestEvent, pp Parameters) error { func (p *SuchePage) SimpleSearchBaendeRequest(app core.App, engine *templating.Engine, e *core.RequestEvent, pp Parameters) error {
data := make(map[string]interface{}) data := make(map[string]interface{})
params, err := NewSimpleParameters(e, pp) params, err := NewSearchParameters(e, pp)
if err != nil { if err != nil {
return engine.Response404(e, err, nil) return engine.Response404(e, err, nil)
} }
query := params.NormalizeQuery() result, err := SimpleSearchBaende(app, *params)
if len(query) == 0 {
engine.Response404(e, nil, nil)
}
fields := params.FieldSetBaende()
if len(fields) == 0 {
return engine.Response404(e, nil, nil)
}
ids, err := dbmodels.FTS5Search(app, dbmodels.ENTRIES_TABLE, dbmodels.FTS5QueryRequest{
Fields: fields,
Query: query,
})
if err != nil { if err != nil {
return engine.Response500(e, err, nil) return engine.Response404(e, err, nil)
} }
idsany := datatypes.ToAny(ids) data["parameters"] = params
entries, err := dbmodels.Entries_IDs(app, idsany) data["result"] = result
if err != nil { return engine.Response200(e, p.Template+pp.Collection+"/", data, p.Layout)
return engine.Response500(e, err, nil)
}
dbmodels.Sort_Entries_Title_Year(entries)
data["entries"] = entries
data["count"] = len(entries)
eids := []any{}
for _, entry := range entries {
eids = append(eids, entry.Id)
}
return engine.Response404(e, nil, nil)
} }
const ( const (
@@ -128,7 +110,7 @@ const (
BAENDE_PARAM_TITLE = "title" BAENDE_PARAM_TITLE = "title"
BAENDE_PARAM_SERIES = "series" BAENDE_PARAM_SERIES = "series"
BAENDE_PARAM_PERSONS = "persons" BAENDE_PARAM_PERSONS = "persons"
BAENDE_PARAM_PLACES = "pubdata" BAENDE_PARAM_PLACES = "places"
BAENDE_PARAM_REFS = "references" BAENDE_PARAM_REFS = "references"
BAENDE_PARAM_ANNOTATIONS = "annotations" BAENDE_PARAM_ANNOTATIONS = "annotations"
BAENDE_PARAM_YEAR = "year" BAENDE_PARAM_YEAR = "year"
@@ -169,8 +151,10 @@ func (p *Parameters) NormalizeQuery() []string {
return dbmodels.NormalizeQuery(p.Query) return dbmodels.NormalizeQuery(p.Query)
} }
type SimpleParameters struct { type SearchParameters struct {
Parameters Parameters
Sort string
Annotations bool Annotations bool
Persons bool Persons bool
Title bool Title bool
@@ -179,9 +163,20 @@ type SimpleParameters struct {
Places bool Places bool
Refs bool Refs bool
Year bool Year bool
AnnotationsString string
PersonsString string
TitleString string
AlmString string
SeriesString string
PlacesString string
RefsString string
YearString string
TypeFilter string
} }
func NewSimpleParameters(e *core.RequestEvent, p Parameters) (*SimpleParameters, error) { func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters, error) {
q := e.Request.URL.Query().Get(PARAM_QUERY) q := e.Request.URL.Query().Get(PARAM_QUERY)
if q == "" { if q == "" {
return nil, ErrNoQuery return nil, ErrNoQuery
@@ -196,10 +191,14 @@ func NewSimpleParameters(e *core.RequestEvent, p Parameters) (*SimpleParameters,
annotations := e.Request.URL.Query().Get(BAENDE_PARAM_ANNOTATIONS) == "on" annotations := e.Request.URL.Query().Get(BAENDE_PARAM_ANNOTATIONS) == "on"
year := e.Request.URL.Query().Get(BAENDE_PARAM_YEAR) == "on" year := e.Request.URL.Query().Get(BAENDE_PARAM_YEAR) == "on"
// TODO: sanity check here if any single field is selected sort := e.Request.URL.Query().Get("sort")
if sort == "" {
sort = "year"
}
return &SimpleParameters{ return &SearchParameters{
Parameters: p, Parameters: p,
Sort: sort,
// INFO: Common parameters // INFO: Common parameters
Alm: alm, Alm: alm,
Title: title, Title: title,
@@ -214,37 +213,319 @@ func NewSimpleParameters(e *core.RequestEvent, p Parameters) (*SimpleParameters,
}, nil }, nil
} }
func (p SimpleParameters) FieldSetBaende() []string { func (p SearchParameters) ToQueryParams() string {
fields := []string{} if !p.Extended {
if p.Alm { q := fmt.Sprintf("?q=%s", p.Query)
fields = append(fields, dbmodels.MUSENALMID_FIELD) if p.Alm {
q += "&alm=on"
}
if p.Title {
q += "&title=on"
}
if p.Persons {
q += "&persons=on"
}
if p.Annotations {
q += "&annotations=on"
}
if p.Series {
q += "&series=on"
}
if p.Places {
q += "&places=on"
}
if p.Refs {
q += "&references=on"
}
if p.Year {
q += "&year=on"
}
return q
} }
if p.Title { return ""
fields = append(fields, }
dbmodels.TITLE_STMT_FIELD,
dbmodels.SUBTITLE_STMT_FIELD, func (p SearchParameters) IsBeandeSearch() bool {
dbmodels.INCIPIT_STMT_FIELD, return p.Collection == "baende" && (p.Query != "" || (p.AnnotationsString != "" || p.PersonsString != "" || p.TitleString != "" || p.AlmString != "" || p.SeriesString != "" || p.PlacesString != "" || p.RefsString != "" || p.YearString != ""))
dbmodels.VARIANT_TITLE_FIELD, }
dbmodels.PARALLEL_TITLE_FIELD,
) func (p SearchParameters) FieldSetBaende() []dbmodels.FTS5QueryRequest {
} ret := []dbmodels.FTS5QueryRequest{}
if p.Series { if p.Query != "" {
fields = append(fields, dbmodels.SERIES_TABLE) fields := []string{dbmodels.ID_FIELD}
} if p.Alm {
if p.Persons { fields = append(fields, dbmodels.MUSENALMID_FIELD)
fields = append(fields, dbmodels.RESPONSIBILITY_STMT_FIELD, dbmodels.AGENTS_TABLE) }
} if p.Title {
if p.Places { // INFO: Preferred Title is not here to avoid hitting the Reihentitel
fields = append(fields, dbmodels.PLACE_STMT_FIELD, dbmodels.PLACES_TABLE, dbmodels.PUBLICATION_STMT_FIELD) fields = append(fields,
} dbmodels.TITLE_STMT_FIELD,
if p.Refs { dbmodels.SUBTITLE_STMT_FIELD,
fields = append(fields, dbmodels.REFERENCES_FIELD) dbmodels.INCIPIT_STMT_FIELD,
} dbmodels.VARIANT_TITLE_FIELD,
if p.Annotations { dbmodels.PARALLEL_TITLE_FIELD,
fields = append(fields, dbmodels.ANNOTATION_FIELD) )
} }
if p.Year { if p.Series {
fields = append(fields, dbmodels.YEAR_FIELD) fields = append(fields, dbmodels.SERIES_TABLE)
} }
return fields if p.Persons {
fields = append(fields, dbmodels.RESPONSIBILITY_STMT_FIELD, dbmodels.AGENTS_TABLE)
}
if p.Places {
fields = append(fields, dbmodels.PLACES_TABLE, dbmodels.PLACE_STMT_FIELD)
}
if p.Refs {
fields = append(fields, dbmodels.REFERENCES_FIELD)
}
if p.Annotations {
fields = append(fields, dbmodels.ANNOTATION_FIELD)
}
if p.Year {
fields = append(fields, dbmodels.YEAR_FIELD)
}
que := p.NormalizeQuery()
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: fields,
Query: p.NormalizeQuery(),
})
}
}
if p.IsExtendedSearch() {
if p.AnnotationsString != "" {
que := dbmodels.NormalizeQuery(p.AnnotationsString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.ANNOTATION_FIELD},
Query: que,
})
}
}
if p.PersonsString != "" {
que := dbmodels.NormalizeQuery(p.PersonsString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.AGENTS_TABLE, dbmodels.RESPONSIBILITY_STMT_FIELD},
Query: que,
})
}
}
if p.TitleString != "" {
que := dbmodels.NormalizeQuery(p.TitleString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.TITLE_STMT_FIELD, dbmodels.SUBTITLE_STMT_FIELD, dbmodels.INCIPIT_STMT_FIELD, dbmodels.VARIANT_TITLE_FIELD, dbmodels.PARALLEL_TITLE_FIELD},
Query: que,
})
}
}
if p.AlmString != "" {
que := dbmodels.NormalizeQuery(p.AlmString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.MUSENALMID_FIELD},
Query: que,
})
}
}
if p.SeriesString != "" {
que := dbmodels.NormalizeQuery(p.SeriesString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.SERIES_TABLE},
Query: que,
})
}
}
if p.PlacesString != "" {
que := dbmodels.NormalizeQuery(p.PlacesString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.PLACES_TABLE, dbmodels.PLACE_STMT_FIELD},
Query: que,
})
}
}
if p.RefsString != "" {
que := dbmodels.NormalizeQuery(p.RefsString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.REFERENCES_FIELD},
Query: que,
})
}
}
if p.YearString != "" {
que := dbmodels.NormalizeQuery(p.YearString)
if len(que) > 0 {
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: []string{dbmodels.YEAR_FIELD},
Query: dbmodels.NormalizeQuery(p.YearString),
})
}
}
}
return ret
}
func (p SearchParameters) IsExtendedSearch() bool {
return p.AnnotationsString != "" || p.PersonsString != "" || p.TitleString != "" || p.AlmString != "" || p.SeriesString != "" || p.PlacesString != "" || p.RefsString != "" || p.YearString != ""
}
func (p SearchParameters) NormalizeQuery() []string {
return dbmodels.NormalizeQuery(p.Query)
}
type SearchResultBaende struct {
// these are the sorted IDs for hits
Hits []string
Series map[string]*dbmodels.Series // <- Key: Series ID
Entries map[string]*dbmodels.Entry // <- Key: Entry ID
Places map[string]*dbmodels.Place // <- All places, Key: Place IDs
Agents map[string]*dbmodels.Agent // <- Key: Agent IDs
// INFO: this is as they say doppelt gemoppelt bc of a logic error i made while tired
EntriesSeries map[string][]*dbmodels.REntriesSeries // <- Key: Entry ID
SeriesEntries map[string][]*dbmodels.REntriesSeries // <- Key: Series ID
EntriesAgents map[string][]*dbmodels.REntriesAgents // <- Key: Entry ID
}
func SimpleSearchBaende(app core.App, params SearchParameters) (*SearchResultBaende, error) {
fields := params.FieldSetBaende()
if len(fields) == 0 {
return nil, ErrNoQuery
}
ids, err := dbmodels.FTS5Search(app, dbmodels.ENTRIES_TABLE, fields...)
if err != nil {
return nil, err
}
resultids := []any{}
for _, id := range ids {
resultids = append(resultids, id.ID)
}
entries, err := dbmodels.Entries_IDs(app, resultids)
if err != nil {
return nil, err
}
entriesmap := make(map[string]*dbmodels.Entry)
for _, entry := range entries {
entriesmap[entry.Id] = entry
}
series, relations, err := Series_Entries(app, entries)
if err != nil {
return nil, err
}
seriesmap := make(map[string]*dbmodels.Series)
for _, s := range series {
seriesmap[s.Id] = s
}
relationsmap := make(map[string][]*dbmodels.REntriesSeries)
invrelationsmap := make(map[string][]*dbmodels.REntriesSeries)
for _, r := range relations {
invrelationsmap[r.Series()] = append(invrelationsmap[r.Series()], r)
relationsmap[r.Entry()] = append(relationsmap[r.Entry()], r)
}
agents, arelations, err := Agents_Entries_IDs(app, resultids)
if err != nil {
return nil, err
}
agentsmap := make(map[string]*dbmodels.Agent)
for _, a := range agents {
agentsmap[a.Id] = a
}
relationsagentsmap := make(map[string][]*dbmodels.REntriesAgents)
for _, r := range arelations {
relationsagentsmap[r.Entry()] = append(relationsagentsmap[r.Entry()], r)
}
placesids := []any{}
for _, entry := range entries {
for _, place := range entry.Places() {
placesids = append(placesids, place)
}
}
places, err := dbmodels.Places_IDs(app, placesids)
if err != nil {
return nil, err
}
placesmap := make(map[string]*dbmodels.Place)
for _, place := range places {
placesmap[place.Id] = place
}
hits := []string{}
if params.Sort == "series" {
dbmodels.Sort_Series_Title(series)
for _, s := range series {
hits = append(hits, s.Id)
}
} else {
dbmodels.Sort_Entries_Year_Title(entries)
for _, e := range entries {
hits = append(hits, e.Id)
}
}
return &SearchResultBaende{
Hits: hits,
Series: seriesmap,
Entries: entriesmap,
Places: placesmap,
Agents: agentsmap,
EntriesSeries: relationsmap,
SeriesEntries: invrelationsmap,
EntriesAgents: relationsagentsmap,
}, nil
}
func (r SearchResultBaende) Count() int {
return len(r.Entries)
}
func (r SearchResultBaende) SeriesCount() int {
return len(r.Series)
}
func Agents_Entries_IDs(app core.App, ids []any) ([]*dbmodels.Agent, []*dbmodels.REntriesAgents, error) {
relations, err := dbmodels.REntriesAgents_Entries(app, ids)
if err != nil {
return nil, nil, err
}
agentids := []any{}
for _, r := range relations {
agentids = append(agentids, r.Agent())
}
agents, err := dbmodels.Agents_IDs(app, agentids)
if err != nil {
return nil, nil, err
}
return agents, relations, nil
} }

View File

@@ -129,6 +129,7 @@ func (e *Engine) funcs() error {
e.AddFunc("Upper", functions.Upper) e.AddFunc("Upper", functions.Upper)
e.AddFunc("First", functions.First) e.AddFunc("First", functions.First)
e.AddFunc("ReplaceSlashParen", functions.ReplaceSlashParen) e.AddFunc("ReplaceSlashParen", functions.ReplaceSlashParen)
e.AddFunc("ReplaceSlashParenSlash", functions.ReplaceSlashParenSlash)
// Time & Date Functions // Time & Date Functions
e.AddFunc("Today", functions.Today) e.AddFunc("Today", functions.Today)
@@ -263,7 +264,7 @@ func (e *Engine) Response404(request *core.RequestEvent, err error, data map[str
data["page"] = requestData(request) data["page"] = requestData(request)
err2 := e.Render(&sb, "/errors/404/", data) err2 := e.Render(&sb, "/errors/404/", data)
if err != nil { if err2 != nil {
return e.Response500(request, errors.Join(err, err2), data) return e.Response500(request, errors.Join(err, err2), data)
} }

View File

@@ -29,6 +29,17 @@
<link href="/assets/css/remixicon.css" rel="stylesheet" /> <link href="/assets/css/remixicon.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="/assets/css/fonts.css" /> <link rel="stylesheet" type="text/css" href="/assets/css/fonts.css" />
<link rel="stylesheet" type="text/css" href="/assets/style.css" /> <link rel="stylesheet" type="text/css" href="/assets/style.css" />
<script type="module">
document.body.addEventListener("htmx:responseError", function (event) {
const config = event.detail.requestConfig;
if (config.boosted) {
document.body.innerHTML = event.detail.xhr.responseText;
const newUrl = event.detail.xhr.responseURL || config.url;
window.history.pushState(null, "", newUrl);
}
});
</script>
</head> </head>
<body class="w-full text-lg" hx-ext="response-targets" hx-boost="true"> <body class="w-full text-lg" hx-ext="response-targets" hx-boost="true">

View File

@@ -1,12 +1,9 @@
{{ $model := . }} {{ $model := . }}
{{/* .parameters {{/* .parameters
type Parameters struct { type SearchParameters struct {
Extended bool
Collection string
Query string
}
type SimpleParameters struct {
Parameters Parameters
Sort string
Annotations bool Annotations bool
Persons bool Persons bool
Title bool Title bool
@@ -15,9 +12,61 @@
Places bool Places bool
Refs bool Refs bool
Year bool Year bool
AnnotationsString string
PersonsString string
TitleString string
AlmString string
SeriesString string
PlacesString string
RefsString string
YearString string
TypeFilter string
}
type Parameters struct {
Query string
Collection string
Extended bool
}
type SearchResultBaende struct {
// these are the sorted IDs for hits
Hits []string
Series map[string]*dbmodels.Series // <- Key: Series ID
Entries map[string]*dbmodels.Entry // <- Key: Entry ID
Places map[string]*dbmodels.Place // <- All places, Key: Place IDs
Agents map[string]*dbmodels.Agent // <- Key: Agent IDs
EntriesSeries map[string][]*dbmodels.REntriesSeries // <- Key: Whatever the Hit IDs are
SeriesEntries map[string][]*dbmodels.REntriesSeries // <- Key: Whatever the Hit IDs are
EntriesAgents map[string][]*dbmodels.REntriesAgents // <- Key: Entry ID
} }
*/}} */}}
{{ $isAlm := false }}
{{ $isTitle := false }}
{{ $isRefs := false }}
{{ $isPlaces := false }}
{{ $isYear := false }}
{{ $isSeries := false }}
{{ $isPersons := false }}
{{ $isAnnotations := false }}
{{ if $model.parameters.Query }}
{{- $isAlm = or $model.parameters.Alm $model.parameters.AlmString -}}
{{- $isTitle = or $model.parameters.Title $model.parameters.TitleString -}}
{{- $isRefs = or $model.parameters.Refs $model.parameters.RefsString -}}
{{- $isPlaces = or $model.parameters.Places $model.parameters.PlacesString -}}
{{- $isYear = or $model.parameters.Year $model.parameters.YearString -}}
{{- $isSeries = or $model.parameters.Series $model.parameters.SeriesString -}}
{{- $isPersons = or $model.parameters.Persons $model.parameters.PersonsString -}}
{{- $isAnnotations = or $model.parameters.Annotations $model.parameters.AnnotationsString -}}
{{ end }}
{{- $isBase := not (or $isAlm $isTitle $isRefs $isPlaces $isYear $isSeries $isPersons
$isAnnotations)
-}}
<div id="searchcontrol" class="container-normal"> <div id="searchcontrol" class="container-normal">
{{- template "_heading" $model.parameters -}} {{- template "_heading" $model.parameters -}}
@@ -37,35 +86,79 @@
{{ template "_searchboxsimple" Arr $model.parameters true $q }} {{ template "_searchboxsimple" Arr $model.parameters true $q }}
<fieldset class="selectgroup"> <fieldset class="selectgroup">
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="alm" id="alm" checked /> <input
type="checkbox"
name="alm"
id="alm"
{{ if or $isBase $isAlm -}}checked{{- end -}} />
<label for="alm">Almanach-Nr.</label> <label for="alm">Almanach-Nr.</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="title" id="title" checked /> <input
type="checkbox"
name="title"
id="title"
{{ if or $isBase $isTitle -}}checked{{- end -}} />
<label for="title">Titel</label> <label for="title">Titel</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="series" id="series" checked /> <input
type="checkbox"
name="series"
id="series"
{{ if or $isBase $isSeries -}}checked{{- end -}} />
<label for="series">Reihentitel</label> <label for="series">Reihentitel</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="persons" id="persons" checked /> <input
type="checkbox"
name="persons"
id="persons"
{{ if or $isBase
$isPersons
-}}
checked
{{- end -}} />
<label for="persons">Personen &amp; Verlage</label> <label for="persons">Personen &amp; Verlage</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="pubdata" id="pubdata" checked /> <input
<label for="pubdata">Orte</label> type="checkbox"
name="places"
id="places"
{{ if or $isBase $isPlaces -}}checked{{- end -}} />
<label for="places">Orte</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="year" id="year" checked /> <input
type="checkbox"
name="year"
id="year"
{{ if or $isBase $isYear -}}checked{{- end -}} />
<label for="year">Jahr</label> <label for="year">Jahr</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="references" id="references" checked /> <input
type="checkbox"
name="references"
id="references"
{{ if or $isBase
$isRefs
-}}
checked
{{- end -}} />
<label for="references">Nachweise</label> <label for="references">Nachweise</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="annotations" id="annotations" checked /> <input
type="checkbox"
name="annotations"
id="annotations"
{{ if or $isBase
$isAnnotations
-}}
checked
{{- end -}} />
<label for="annotations">Anmerkungen</label> <label for="annotations">Anmerkungen</label>
</div> </div>
</fieldset> </fieldset>
@@ -77,3 +170,94 @@
</div> </div>
{{- template "_fieldscript" -}} {{- template "_fieldscript" -}}
{{- if $model.parameters.Query -}}
<div class="container-normal" id="searchresults">
<div class="border-b border-zinc-300 flex flex-row justify-between">
<div>
Suche nach <b>»{{ $model.parameters.Query }}«</b> &middot;
<i class="ri-book-line"></i>
{{ if eq $model.result.Count 1 -}}
Ein Band
{{ else -}}
{{ $model.result.Count }} Bände
{{- end }}
in
{{ if eq ($model.result.Series | len) 1 -}}
einer Reihe
{{ else -}}
{{ $model.result.Series | len }} Reihen
{{- end -}}
</div>
<div>
<label
for="sort"
class="align-baseline h-min self-end pb-1 mr-2 text-sm font-sans
text-stone-700"
>Sortierung</label
>
{{/* INFO: We always redrect to letter = A bc some letters dont exist for other professions */}}
<select
class="h-min pb-1 border-b-4 border-zinc-300 px-1.5"
name="sort"
id="sort"
hx-get="{{- $model.parameters.ToQueryParams -}}"
trigger="change"
hx-push-url="true"
hx-select="main"
auto-complete="off"
hx-target="main">
<option
value="year"
{{ if eq $model.parameters.Sort "year" -}}
selected
{{- end -}}>
Erscheinungsjahr
</option>
<option value="series" {{ if eq $model.parameters.Sort "series" -}}selected{{- end -}}>
Reihentitel A-Z
</option>
</select>
</div>
</div>
{{- if $model.result.Hits -}}
{{- if eq .parameters.Sort "series" -}}
<div class="mt-4">
{{- range $_, $hit := $model.result.Hits -}}
{{- $series := index $model.result.Series $hit -}}
<div class="font-serif font-bold py-1 border border-zinc-300 px-3 mt-6">
<span class="text-base font-sans pr-2 border-zinc-300">Reihe</span>
<span class="pl-2">{{ $series.Title }}</span>
</div>
{{- range $_, $rel := index $model.result.SeriesEntries $hit -}}
{{- $entry := index $model.result.Entries $rel.Entry -}}
{{- template "band" (Arr $model $entry $series.Id) -}}
{{- end -}}
{{- end -}}
</div>
{{- else -}}
{{- range $_, $hit := $model.result.Hits -}}
{{- $entry := index $model.result.Entries $hit -}}
{{- template "band" (Arr $model $entry false) -}}
{{- end -}}
{{- end -}}
{{- end -}}
<script type="module">
let elements = document.querySelectorAll('.search-text');
let mark_instance = new Mark(elements);
// INFO: we wait a little bit before marking, to settle everything
setTimeout(() => {
mark_instance.mark('{{ $model.parameters.Query }}', {
"seperateWordSearch": true,
});
}, 200);
</script>
</div>
{{- if not $model.result.Hits -}}
Keine Bände gefunden.
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,192 @@
{{- $model := index . 0 -}}
{{- $entry := index . 1 -}}
{{- $series := index . 2 -}}
{{/* .parameters
type SearchParameters struct {
Parameters
Sort string
Annotations bool
Persons bool
Title bool
Alm bool
Series bool
Places bool
Refs bool
Year bool
AnnotationsString string
PersonsString string
TitleString string
AlmString string
SeriesString string
PlacesString string
RefsString string
YearString string
TypeFilter string
}
type Parameters struct {
Query string
Collection string
Extended bool
}
type SearchResultBaende struct {
// these are the sorted IDs for hits
Hits []string
Series map[string]*dbmodels.Series // <- Key: Series ID
Entries map[string]*dbmodels.Entry // <- Key: Entry ID
Places map[string]*dbmodels.Place // <- All places, Key: Place IDs
Agents map[string]*dbmodels.Agent // <- Key: Agent IDs
EntriesSeries map[string][]*dbmodels.REntriesSeries // <- Key: Whatever the Hit IDs are
EntriesAgents map[string][]*dbmodels.REntriesAgents // <- Key: Entry ID
}
*/}}
{{- $isAlm := or $model.parameters.Alm $model.parameters.AlmString -}}
{{- $isTitle := or $model.parameters.Title $model.parameters.TitleString -}}
{{- $isRefs := or $model.parameters.Refs $model.parameters.RefsString -}}
{{- $isPlaces := or $model.parameters.Places $model.parameters.PlacesString -}}
{{- $isYear := or $model.parameters.Year $model.parameters.YearString -}}
{{- $isSeries := or $model.parameters.Series $model.parameters.SeriesString -}}
{{- $isPersons := or $model.parameters.Persons $model.parameters.PersonsString -}}
{{- $isAnnotations := or $model.parameters.Annotations $model.parameters.AnnotationsString -}}
<div class="flex flex-row max-w-[60rem]">
<div class="w-32 grow-0 shrink-0 border-r border-zinc-300 pr-3 pt-4">
<div class="flex flex-col items-end pt-1">
<div class="flex flex-row gap-x-1">
<div class="inline-block ml-1 whitespace-nowrap">
{{- if not $entry.TitleStmt -}}
<tool-tip position="right">
<i class="ri-forbid-line"></i>
<div class="data-tip">Keine nähere Erfassung</div>
</tool-tip>
{{- else if eq $entry.EditState "Edited" -}}
<tool-tip position="right">
<i class="ri-checkbox-circle-line"></i>
<div class="data-tip">Mit erfassten Beiträgen</div>
</tool-tip>
{{- else -}}
<tool-tip position="right">
<i class="ri-information-line"></i>
<div class="data-tip">Mit genaueren Titelangaben</div>
</tool-tip>
{{- end -}}
</div>
<div
class="px-2 font-sans font-bold text-sm bg-stone-100 py-0.5 w-max rounded
{{ if $isAlm }}search-text{{- end -}}">
<a
href="/almanach/{{ $entry.MusenalmID }}"
class="no-underline rounded bg-stone-100 px-1.5">
Alm
{{ $entry.MusenalmID -}}
</a>
</div>
</div>
{{ if $entry.References }}
<div
class="text-sm font-sans px-2 py-1 bg-stone-100 mt-1.5
{{ if $isRefs -}}
search-text
{{- end -}}">
{{ $entry.References }}
</div>
{{ end }}
</div>
</div>
<div class="font-serif ml-3 pt-4">
<div class="font-bold">
<a href="/almanach/{{ $entry.MusenalmID }}" class="no-underline rounded ">
{{- $entry.PreferredTitle -}}
</a>
</div>
{{- if $entry.TitleStmt -}}
<div class="italic {{ if $isTitle -}}search-text{{- end -}}">{{- $entry.TitleStmt -}}</div>
{{- end -}}
{{- if and $entry.ResponsibilityStmt (not (eq $entry.ResponsibilityStmt "unbezeichnet")) -}}
<div class="italic {{ if $isPersons -}}search-text{{- end -}}">
{{ $entry.ResponsibilityStmt -}}
</div>
{{- end -}}
<div class="">
{{- if $entry.Places -}}
{{- range $_, $placeid := $entry.Places -}}
{{- $place := index $model.result.Places $placeid -}}
{{- if $place -}}
<div class="inline pr-1 {{ if $isPlaces -}}search-text{{- end -}}">
<a href="/reihen?place={{ $place.Id }}&hidden=true">{{ $place.Name }}</a>,
</div>
{{- end -}}
{{- end -}}
{{- end -}}
<a href="/reihen/?year={{ $entry.Year }}&hidden=true">
{{- if $entry.Year -}}
<div class="inline {{ if $isYear -}}search-text{{- end -}}">{{ $entry.Year }}</div>
{{- else -}}
<div class="inline">o.J.</div>
{{- end -}}
</a>
</div>
{{- $srels := index $model.result.EntriesSeries $entry.Id -}}
{{- if $srels -}}
<div class="flex flex-row flex-wrap py-1 gap-y-1">
{{- range $_, $srel := $srels -}}
{{- $series := index $model.result.Series $srel.Series -}}
{{- if $series -}}
<div class="inline-flex flex-row text-base font-sans px-2 bg-zinc-100 rounded mr-2">
<span class="border-r pr-2 border-gray-300">Reihentitel</span
><a
href="/reihe/{{ $series.MusenalmID }}"
class="no-underline pl-2 block {{ if
$isSeries
-}}
search-text
{{- end -}}">
<b>{{- $series.Title -}}</b>
</a>
</div>
{{- end -}}
{{- end -}}
</div>
{{- end -}}
{{- $arels := index $model.result.EntriesAgents $entry.Id -}}
{{- if $arels -}}
<div class="flex flex-row flex-wrap py-1 gap-y-1">
{{- range $_, $arel := $arels -}}
{{- $agent := index $model.result.Agents $arel.Agent -}}
{{- if $agent -}}
<div class="inline-block text-base font-sans bg-slate-100 rounded mr-2">
<span class="border-r px-2 border-gray-300"> {{- $arel.Type -}} </span
><a
href="/person/{{ $agent.Id }}"
class="no-underline px-1.5 {{ if
$isPersons
-}}
search-text
{{- end -}}">
<b>{{- $agent.Name -}}</b>
</a>
</div>
{{- end -}}
{{- end -}}
</div>
{{- end -}}
{{- if $entry.Annotation -}}
<div class="text-base hyphens-auto">
<b>Anm.: </b
><span class="{{- if $isAnnotations -}}search-text{{- end -}}"
>{{- Safe
(ReplaceSlashParenSlash $entry.Annotation)
-}}</span
>
</div>
{{- end -}}
<div class="pb-4"></div>
</div>
</div>

View File

@@ -5,7 +5,7 @@
<label for="q" class="hidden">Suchbegriffe</label> <label for="q" class="hidden">Suchbegriffe</label>
<input <input
{{ if $q }}value="{{ $q }}"{{- end -}} {{ if $q }}value="{{ $q }}"{{- end }}
type="search" type="search"
name="q" name="q"
minlength="3" minlength="3"