Abschluss Bandsuche

This commit is contained in:
Simon Martens
2025-02-27 13:02:23 +01:00
parent 57f95122c8
commit d2e7f91c92
11 changed files with 330 additions and 195 deletions

View File

@@ -26,14 +26,13 @@ func AgentForId(app core.App, id string) (*Agent, error) {
func FTS5SearchAgents(app core.App, query string) ([]*Agent, error) { func FTS5SearchAgents(app core.App, query string) ([]*Agent, error) {
a := []*Agent{} a := []*Agent{}
q := NormalizeQuery(query) q := NormalizeQuery(query)
if len(q) == 0 { req := IntoQueryRequests([]string{AGENTS_NAME_FIELD, AGENTS_PSEUDONYMS_FIELD, REFERENCES_FIELD, AGENTS_BIOGRAPHICAL_DATA_FIELD, ANNOTATION_FIELD}, q)
if len(req) == 0 {
return a, nil return a, nil
} }
ids, err := FTS5Search(app, AGENTS_TABLE, FTS5QueryRequest{ ids, err := FTS5Search(app, AGENTS_TABLE, req...)
Fields: []string{AGENTS_NAME_FIELD, AGENTS_PSEUDONYMS_FIELD, REFERENCES_FIELD, AGENTS_BIOGRAPHICAL_DATA_FIELD, ANNOTATION_FIELD},
Query: q,
})
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -2,8 +2,10 @@ package dbmodels
import ( import (
"errors" "errors"
"fmt"
"strconv" "strconv"
"strings" "strings"
"unicode"
"github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes" "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
@@ -99,22 +101,125 @@ var CONTENTS_FTS5_FIELDS = []string{
var ErrInvalidQuery = errors.New("invalid input into the search function") var ErrInvalidQuery = errors.New("invalid input into the search function")
func NormalizeQuery(query string) []string { type Query struct {
query = datatypes.NormalizeString(query) Include []string // Phrases that should be matched
query = datatypes.DeleteTags(query) Exclude []string // Phrases that should not be matched
query = datatypes.RemovePunctuation(query) UnsafeI []string // Phrases < 3 characters
query = cases.Lower(language.German).String(query) UnsafeE []string // Phrases < 3 characters excluded
// TODO: how to normalize, which unicode normalization to use? }
split := strings.Split(query, " ") // Parses query strings like
res := []string{} // word another "this is a phrase" -notthis aword -alsonotthis -"also not this"
for _, s := range split { // into seperate phrases
if len(s) > 2 { func NormalizeQuery(query string) Query {
res = append(res, s) query = datatypes.NormalizeString(query)
// TODO: how to normalize, which unicode normalization to use?
// query = datatypes.RemovePunctuation(query)
query = cases.Lower(language.German).String(query)
var include []string
var exclude []string
var unsafeI []string
var unsafeE []string
isInQuotes := false
isExcluded := false
var cToken strings.Builder
at := func() {
if cToken.Len() == 0 {
return
}
t := cToken.String()
if len(t) < 3 && isExcluded {
unsafeE = append(unsafeE, t)
return
} else if len(t) < 3 {
unsafeI = append(unsafeI, t)
return
}
if len(t) >= 3 && isExcluded {
exclude = append(exclude, t)
return
} else if len(t) >= 3 {
include = append(include, t)
return
} }
} }
return res reset := func() {
isInQuotes = false
isExcluded = false
cToken.Reset()
}
addToken := func() {
at()
reset()
}
for _, r := range query {
fmt.Printf("Rune: %v\n", r)
if r == '"' {
if isInQuotes {
addToken()
} else if cToken.Len() == 0 {
isInQuotes = true
}
// INFO: - is punctuation, so the order of cases is important
} else if r == 45 && cToken.Len() == 0 {
isExcluded = true
} else if unicode.IsSpace(r) && !isInQuotes {
addToken()
} else if unicode.IsPunct(r) && !isInQuotes {
addToken()
} else {
cToken.WriteRune(r)
}
}
if cToken.Len() > 0 {
at()
}
fmt.Printf("Query: %v\n", query)
fmt.Printf("Include: %v\n", include)
fmt.Printf("Exclude: %v\n", exclude)
fmt.Printf("UnsafeI: %v\n", unsafeI)
fmt.Printf("UnsafeE: %v\n", unsafeE)
return Query{
Include: include,
Exclude: exclude,
UnsafeI: unsafeI,
UnsafeE: unsafeE,
}
}
// INFO: Takes in fields and a Query object
func IntoQueryRequests(f []string, q Query) []FTS5QueryRequest {
ret := []FTS5QueryRequest{}
if len(q.Include) > 0 {
ret = append(ret, FTS5QueryRequest{
Fields: f,
Query: q.Include,
OP: OP_AND,
})
}
if len(q.Exclude) > 0 {
ret = append(ret, FTS5QueryRequest{
Fields: f,
Query: q.Exclude,
OP: OP_NOT,
})
}
return ret
} }
func FTS5Search(app core.App, table string, mapfq ...FTS5QueryRequest) ([]*FTS5IDQueryResult, error) { func FTS5Search(app core.App, table string, mapfq ...FTS5QueryRequest) ([]*FTS5IDQueryResult, error) {
@@ -125,7 +230,16 @@ func FTS5Search(app core.App, table string, mapfq ...FTS5QueryRequest) ([]*FTS5I
q := NewFTS5Query().From(table).SelectID() q := NewFTS5Query().From(table).SelectID()
for _, v := range mapfq { for _, v := range mapfq {
for _, que := range v.Query { for _, que := range v.Query {
q.AndMatch(v.Fields, que) switch v.OP {
case OP_AND:
q.AndMatch(v.Fields, que)
case OP_OR:
q.OrMatch(v.Fields, que)
case OP_NOT:
q.NotMatch(v.Fields, que)
case NONE:
q.AndMatch(v.Fields, que)
}
} }
} }

View File

@@ -9,6 +9,7 @@ import (
type FTS5QueryRequest struct { type FTS5QueryRequest struct {
Fields []string Fields []string
Query []string Query []string
OP Operator
} }
type FTS5IDQueryResult struct { type FTS5IDQueryResult struct {

View File

@@ -112,6 +112,11 @@ func Series_IDs(app core.App, ids []any) ([]*Series, error) {
return TableByIDs[[]*Series](app, SERIES_TABLE, ids) return TableByIDs[[]*Series](app, SERIES_TABLE, ids)
} }
func Series_MusenalmID(app core.App, id string) (*Series, error) {
ret, err := TableByField[Series](app, SERIES_TABLE, MUSENALMID_FIELD, id)
return &ret, err
}
func Series_ID(app core.App, id string) (*Series, error) { func Series_ID(app core.App, id string) (*Series, error) {
ret, err := TableByID[Series](app, SERIES_TABLE, id) ret, err := TableByID[Series](app, SERIES_TABLE, id)
return &ret, err return &ret, err

View File

@@ -43,14 +43,13 @@ func BasicSearchSeries(app core.App, query string) ([]*Series, []*Series, error)
// INFO: Needing to differentiate matches // INFO: Needing to differentiate matches
querysplit := NormalizeQuery(query) querysplit := NormalizeQuery(query)
if len(querysplit) == 0 { req := IntoQueryRequests([]string{SERIES_TITLE_FIELD, ANNOTATION_FIELD, REFERENCES_FIELD}, querysplit)
if len(req) == 0 {
return series, []*Series{}, nil return series, []*Series{}, nil
} }
altids, err := FTS5Search(app, SERIES_TABLE, FTS5QueryRequest{ altids, err := FTS5Search(app, SERIES_TABLE, req...)
Fields: []string{SERIES_TITLE_FIELD, ANNOTATION_FIELD, REFERENCES_FIELD},
Query: querysplit,
})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -35,13 +35,13 @@ func (p *ReihePage) Setup(router *router.Router[*core.RequestEvent], app core.Ap
router.GET(URL_REIHE, func(e *core.RequestEvent) error { router.GET(URL_REIHE, func(e *core.RequestEvent) error {
id := e.Request.PathValue("id") id := e.Request.PathValue("id")
data := make(map[string]interface{}) data := make(map[string]interface{})
reihe, err := dbmodels.Series_ID(app, id) reihe, err := dbmodels.Series_MusenalmID(app, id)
if err != nil { if err != nil || reihe == nil || reihe.Id == "" {
return engine.Response404(e, err, data) return engine.Response404(e, err, data)
} }
data["series"] = reihe data["series"] = reihe
entries, relations, err := Entries_Series_IDs(app, []any{id}) entries, relations, err := Entries_Series_IDs(app, []any{reihe.Id})
if err != nil { if err != nil {
return engine.Response404(e, err, data) return engine.Response404(e, err, data)
} }

View File

@@ -1,9 +1,11 @@
package pages package pages
import ( import (
"database/sql"
"fmt" "fmt"
"net/http" "net/http"
"slices" "slices"
"strings"
"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"
@@ -54,7 +56,7 @@ func (p *SuchePage) Setup(router *router.Router[*core.RequestEvent], app core.Ap
allparas, _ := NewSearchParameters(e, *paras) allparas, _ := NewSearchParameters(e, *paras)
if paras.Query != "" || allparas.IsBaendeSearch() { if allparas.IsBaendeSearch() {
return p.SearchBaendeRequest(app, engine, e, *allparas) return p.SearchBaendeRequest(app, engine, e, *allparas)
} }
@@ -140,7 +142,7 @@ func NewParameters(e *core.RequestEvent) (*Parameters, error) {
}, nil }, nil
} }
func (p *Parameters) NormalizeQuery() []string { func (p *Parameters) NormalizeQuery() dbmodels.Query {
return dbmodels.NormalizeQuery(p.Query) return dbmodels.NormalizeQuery(p.Query)
} }
@@ -151,7 +153,6 @@ type SearchParameters struct {
Annotations bool Annotations bool
Persons bool Persons bool
Title bool Title bool
Alm bool
Series bool Series bool
Places bool Places bool
Refs bool Refs bool
@@ -170,7 +171,6 @@ type SearchParameters struct {
} }
func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters, error) { func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters, error) {
alm := e.Request.URL.Query().Get(BAENDE_PARAM_ALM_NR) == "on"
title := e.Request.URL.Query().Get(BAENDE_PARAM_TITLE) == "on" title := e.Request.URL.Query().Get(BAENDE_PARAM_TITLE) == "on"
series := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES) == "on" series := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES) == "on"
persons := e.Request.URL.Query().Get(BAENDE_PARAM_PERSONS) == "on" persons := e.Request.URL.Query().Get(BAENDE_PARAM_PERSONS) == "on"
@@ -180,14 +180,19 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters,
year := e.Request.URL.Query().Get(BAENDE_PARAM_YEAR) == "on" year := e.Request.URL.Query().Get(BAENDE_PARAM_YEAR) == "on"
almstring := e.Request.URL.Query().Get(BAENDE_PARAM_ALM_NR + "string") almstring := e.Request.URL.Query().Get(BAENDE_PARAM_ALM_NR + "string")
titlestring := e.Request.URL.Query().Get(BAENDE_PARAM_TITLE + "string") titlestring := e.Request.URL.Query().Get(BAENDE_PARAM_TITLE + "string")
seriesstring := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES + "string") seriesstring := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES + "string")
personsstring := e.Request.URL.Query().Get(BAENDE_PARAM_PERSONS + "string") personsstring := e.Request.URL.Query().Get(BAENDE_PARAM_PERSONS + "string")
placesstring := e.Request.URL.Query().Get(BAENDE_PARAM_PLACES + "string") placesstring := e.Request.URL.Query().Get(BAENDE_PARAM_PLACES + "string")
refsstring := e.Request.URL.Query().Get(BAENDE_PARAM_REFS + "string")
annotationsstring := e.Request.URL.Query().Get(BAENDE_PARAM_ANNOTATIONS + "string") annotationsstring := e.Request.URL.Query().Get(BAENDE_PARAM_ANNOTATIONS + "string")
yearstring := e.Request.URL.Query().Get(BAENDE_PARAM_YEAR + "string") yearstring := e.Request.URL.Query().Get(BAENDE_PARAM_YEAR + "string")
refss := e.Request.URL.Query().Get(BAENDE_PARAM_REFS + "string")
if refss != "" {
refss = "\"" + refss + "\""
}
sort := e.Request.URL.Query().Get("sort") sort := e.Request.URL.Query().Get("sort")
if sort == "" { if sort == "" {
sort = "year" sort = "year"
@@ -197,7 +202,6 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters,
Parameters: p, Parameters: p,
Sort: sort, Sort: sort,
// INFO: Common parameters // INFO: Common parameters
Alm: alm,
Title: title, Title: title,
Persons: persons, Persons: persons,
Annotations: annotations, Annotations: annotations,
@@ -214,15 +218,36 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters,
SeriesString: seriesstring, SeriesString: seriesstring,
PersonsString: personsstring, PersonsString: personsstring,
PlacesString: placesstring, PlacesString: placesstring,
RefsString: refsstring, RefsString: refss,
AnnotationsString: annotationsstring, AnnotationsString: annotationsstring,
YearString: yearstring, YearString: yearstring,
}, nil }, nil
} }
func (p SearchParameters) AllSearchTerms() string { func (p SearchParameters) AllSearchTerms() string {
q := p.Query + " " + p.AnnotationsString + " " + p.PersonsString + " " + p.TitleString + " " + p.AlmString + " " + p.SeriesString + " " + p.PlacesString + " " + p.RefsString + " " + p.YearString res := []string{}
return q res = append(res, p.includedParams(p.Query)...)
res = append(res, p.includedParams(p.AnnotationsString)...)
res = append(res, p.includedParams(p.PersonsString)...)
res = append(res, p.includedParams(p.TitleString)...)
res = append(res, p.includedParams(p.SeriesString)...)
res = append(res, p.includedParams(p.PlacesString)...)
res = append(res, p.includedParams(p.RefsString)...)
res = append(res, p.includedParams(p.YearString)...)
res = append(res, p.AlmString)
return strings.Join(res, " ")
}
func (p SearchParameters) includedParams(q string) []string {
res := []string{}
que := dbmodels.NormalizeQuery(q)
for _, qq := range que.Include {
res = append(res, qq)
}
for _, qq := range que.UnsafeI {
res = append(res, qq)
}
return res
} }
func (p SearchParameters) ToQueryParams() string { func (p SearchParameters) ToQueryParams() string {
@@ -235,31 +260,28 @@ func (p SearchParameters) ToQueryParams() string {
if p.Query != "" { if p.Query != "" {
q += fmt.Sprintf("q=%s", p.Query) q += fmt.Sprintf("q=%s", p.Query)
}
if p.Alm { if p.Title {
q += "&alm=on" q += "&title=on"
} }
if p.Title { if p.Persons {
q += "&title=on" q += "&persons=on"
} }
if p.Persons { if p.Annotations {
q += "&persons=on" q += "&annotations=on"
} }
if p.Annotations { if p.Series {
q += "&annotations=on" q += "&series=on"
} }
if p.Series { if p.Places {
q += "&series=on" q += "&places=on"
} }
if p.Places { if p.Refs {
q += "&places=on" q += "&references=on"
} }
if p.Refs { if p.Year {
q += "&references=on" q += "&year=on"
} }
if p.Year {
q += "&year=on"
} }
if p.YearString != "" { if p.YearString != "" {
@@ -274,9 +296,6 @@ func (p SearchParameters) ToQueryParams() string {
if p.TitleString != "" { if p.TitleString != "" {
q += fmt.Sprintf("&titlestring=%s", p.TitleString) q += fmt.Sprintf("&titlestring=%s", p.TitleString)
} }
if p.AlmString != "" {
q += fmt.Sprintf("&almstring=%s", p.AlmString)
}
if p.SeriesString != "" { if p.SeriesString != "" {
q += fmt.Sprintf("&seriesstring=%s", p.SeriesString) q += fmt.Sprintf("&seriesstring=%s", p.SeriesString)
} }
@@ -291,16 +310,13 @@ func (p SearchParameters) ToQueryParams() string {
} }
func (p SearchParameters) IsBaendeSearch() bool { func (p SearchParameters) IsBaendeSearch() bool {
return p.Collection == "baende" && (p.Query != "" || (p.AnnotationsString != "" || p.PersonsString != "" || p.TitleString != "" || p.AlmString != "" || p.SeriesString != "" || p.PlacesString != "" || p.RefsString != "" || p.YearString != "")) return p.Collection == "baende" && (p.Query != "" || p.AlmString != "" || p.AnnotationsString != "" || p.PersonsString != "" || p.TitleString != "" || p.SeriesString != "" || p.PlacesString != "" || p.RefsString != "" || p.YearString != "")
} }
func (p SearchParameters) FieldSetBaende() []dbmodels.FTS5QueryRequest { func (p SearchParameters) FieldSetBaende() []dbmodels.FTS5QueryRequest {
ret := []dbmodels.FTS5QueryRequest{} ret := []dbmodels.FTS5QueryRequest{}
if p.Query != "" { if p.Query != "" {
fields := []string{dbmodels.ID_FIELD} fields := []string{dbmodels.ID_FIELD}
if p.Alm {
fields = append(fields, dbmodels.MUSENALMID_FIELD)
}
if p.Title { if p.Title {
// INFO: Preferred Title is not here to avoid hitting the Reihentitel // INFO: Preferred Title is not here to avoid hitting the Reihentitel
fields = append(fields, fields = append(fields,
@@ -331,93 +347,50 @@ func (p SearchParameters) FieldSetBaende() []dbmodels.FTS5QueryRequest {
} }
que := p.NormalizeQuery() que := p.NormalizeQuery()
req := dbmodels.IntoQueryRequests(fields, que)
if len(que) > 0 { ret = append(ret, req...)
ret = append(ret, dbmodels.FTS5QueryRequest{
Fields: fields,
Query: p.NormalizeQuery(),
})
}
} }
if p.AnnotationsString != "" { if p.AnnotationsString != "" {
que := dbmodels.NormalizeQuery(p.AnnotationsString) que := dbmodels.NormalizeQuery(p.AnnotationsString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.ANNOTATION_FIELD}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
Fields: []string{dbmodels.ANNOTATION_FIELD},
Query: que,
})
}
} }
if p.PersonsString != "" { if p.PersonsString != "" {
que := dbmodels.NormalizeQuery(p.PersonsString) que := dbmodels.NormalizeQuery(p.PersonsString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.AGENTS_TABLE, dbmodels.RESPONSIBILITY_STMT_FIELD}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
Fields: []string{dbmodels.AGENTS_TABLE, dbmodels.RESPONSIBILITY_STMT_FIELD},
Query: que,
})
}
} }
if p.TitleString != "" { if p.TitleString != "" {
que := dbmodels.NormalizeQuery(p.TitleString) que := dbmodels.NormalizeQuery(p.TitleString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.TITLE_STMT_FIELD, dbmodels.SUBTITLE_STMT_FIELD, dbmodels.INCIPIT_STMT_FIELD, dbmodels.VARIANT_TITLE_FIELD, dbmodels.PARALLEL_TITLE_FIELD}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
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 != "" { if p.SeriesString != "" {
que := dbmodels.NormalizeQuery(p.SeriesString) que := dbmodels.NormalizeQuery(p.SeriesString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.SERIES_TABLE}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
Fields: []string{dbmodels.SERIES_TABLE},
Query: que,
})
}
} }
if p.PlacesString != "" { if p.PlacesString != "" {
que := dbmodels.NormalizeQuery(p.PlacesString) que := dbmodels.NormalizeQuery(p.PlacesString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.PLACES_TABLE, dbmodels.PLACE_STMT_FIELD}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
Fields: []string{dbmodels.PLACES_TABLE, dbmodels.PLACE_STMT_FIELD},
Query: que,
})
}
} }
if p.RefsString != "" { if p.RefsString != "" {
que := dbmodels.NormalizeQuery(p.RefsString) que := dbmodels.NormalizeQuery(p.RefsString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.REFERENCES_FIELD}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
Fields: []string{dbmodels.REFERENCES_FIELD},
Query: que,
})
}
} }
if p.YearString != "" { if p.YearString != "" {
que := dbmodels.NormalizeQuery(p.YearString) que := dbmodels.NormalizeQuery(p.YearString)
if len(que) > 0 { req := dbmodels.IntoQueryRequests([]string{dbmodels.YEAR_FIELD}, que)
ret = append(ret, dbmodels.FTS5QueryRequest{ ret = append(ret, req...)
Fields: []string{dbmodels.YEAR_FIELD},
Query: dbmodels.NormalizeQuery(p.YearString),
})
}
} }
return ret return ret
@@ -427,11 +400,13 @@ func (p SearchParameters) IsExtendedSearch() bool {
return p.AnnotationsString != "" || p.PersonsString != "" || p.TitleString != "" || p.AlmString != "" || p.SeriesString != "" || p.PlacesString != "" || p.RefsString != "" || p.YearString != "" return p.AnnotationsString != "" || p.PersonsString != "" || p.TitleString != "" || p.AlmString != "" || p.SeriesString != "" || p.PlacesString != "" || p.RefsString != "" || p.YearString != ""
} }
func (p SearchParameters) NormalizeQuery() []string { func (p SearchParameters) NormalizeQuery() dbmodels.Query {
return dbmodels.NormalizeQuery(p.Query) return dbmodels.NormalizeQuery(p.Query)
} }
type SearchResultBaende struct { type SearchResultBaende struct {
Queries []dbmodels.FTS5QueryRequest
// these are the sorted IDs for hits // these are the sorted IDs for hits
Hits []string Hits []string
Series map[string]*dbmodels.Series // <- Key: Series ID Series map[string]*dbmodels.Series // <- Key: Series ID
@@ -445,25 +420,57 @@ type SearchResultBaende struct {
EntriesAgents map[string][]*dbmodels.REntriesAgents // <- Key: Entry ID EntriesAgents map[string][]*dbmodels.REntriesAgents // <- Key: Entry ID
} }
func SimpleSearchBaende(app core.App, params SearchParameters) (*SearchResultBaende, error) { func EmptyResultBaende() *SearchResultBaende {
fields := params.FieldSetBaende() return &SearchResultBaende{
if len(fields) == 0 { Hits: []string{},
return nil, ErrNoQuery Series: make(map[string]*dbmodels.Series),
Entries: make(map[string]*dbmodels.Entry),
Places: make(map[string]*dbmodels.Place),
Agents: make(map[string]*dbmodels.Agent),
EntriesSeries: make(map[string][]*dbmodels.REntriesSeries),
SeriesEntries: make(map[string][]*dbmodels.REntriesSeries),
EntriesAgents: make(map[string][]*dbmodels.REntriesAgents),
} }
}
ids, err := dbmodels.FTS5Search(app, dbmodels.ENTRIES_TABLE, fields...) func SimpleSearchBaende(app core.App, params SearchParameters) (*SearchResultBaende, error) {
if err != nil { entries := []*dbmodels.Entry{}
return nil, err queries := params.FieldSetBaende()
if params.AlmString != "" {
e, err := dbmodels.Entries_MusenalmID(app, params.AlmString)
if err != nil && err == sql.ErrNoRows {
return EmptyResultBaende(), nil
} else if err != nil {
return nil, err
}
entries = append(entries, e)
} else {
if len(queries) == 0 {
return nil, ErrNoQuery
}
ids, err := dbmodels.FTS5Search(app, dbmodels.ENTRIES_TABLE, queries...)
if err != nil {
return nil, err
}
resultids := []any{}
for _, id := range ids {
resultids = append(resultids, id.ID)
}
e, err := dbmodels.Entries_IDs(app, resultids)
if err != nil {
return nil, err
}
entries = e
} }
resultids := []any{} resultids := []any{}
for _, id := range ids { for _, entry := range entries {
resultids = append(resultids, id.ID) resultids = append(resultids, entry.Id)
}
entries, err := dbmodels.Entries_IDs(app, resultids)
if err != nil {
return nil, err
} }
entriesmap := make(map[string]*dbmodels.Entry) entriesmap := make(map[string]*dbmodels.Entry)

View File

@@ -7,7 +7,6 @@
Annotations bool Annotations bool
Persons bool Persons bool
Title bool Title bool
Alm bool
Series bool Series bool
Places bool Places bool
Refs bool Refs bool
@@ -52,7 +51,7 @@
{{ $isPersons := false }} {{ $isPersons := false }}
{{ $isAnnotations := false }} {{ $isAnnotations := false }}
{{- $isAlm = or $model.parameters.Alm $model.parameters.AlmString -}} {{- $isAlm = $model.parameters.AlmString -}}
{{- $isTitle = or $model.parameters.Title $model.parameters.TitleString -}} {{- $isTitle = or $model.parameters.Title $model.parameters.TitleString -}}
{{- $isRefs = or $model.parameters.Refs $model.parameters.RefsString -}} {{- $isRefs = or $model.parameters.Refs $model.parameters.RefsString -}}
{{- $isPlaces = or $model.parameters.Places $model.parameters.PlacesString -}} {{- $isPlaces = or $model.parameters.Places $model.parameters.PlacesString -}}
@@ -61,16 +60,33 @@
{{- $isPersons = or $model.parameters.Persons $model.parameters.PersonsString -}} {{- $isPersons = or $model.parameters.Persons $model.parameters.PersonsString -}}
{{- $isAnnotations = or $model.parameters.Annotations $model.parameters.AnnotationsString -}} {{- $isAnnotations = or $model.parameters.Annotations $model.parameters.AnnotationsString -}}
{{- $isBase := not (or $isAlm $isTitle $isRefs $isPlaces $isYear $isSeries $isPersons {{- $isBase := not (or $isTitle $isRefs $isPlaces $isYear $isSeries $isPersons
$isAnnotations) $isAnnotations)
-}} -}}
<div id="searchcontrol" class="container-normal"> <div id="searchcontrol" class="container-normal">
{{- template "_heading" $model.parameters -}} {{- template "_heading" $model.parameters -}}
<div id="" class="border-l border-zinc-300 px-8 py-10 relative"> <div id="searchform" class="border-l border-zinc-300 px-8 py-10 relative">
<form <form
id="searchform" id="lookupform"
class="w-full font-serif grid grid-cols-12 gap-x-4 mb-4"
method="get"
action="/suche/baende"
autocomplete="off">
<label for="almstring" class="col-span-3 align-middle hidden">Almanach-Nummer:</label>
<input
autocomplete="off"
type="search"
name="almstring"
id="almstring"
value="{{ $model.parameters.AlmString }}"
placeholder="Alm-Nummer"
class="w-full col-span-3 placeholder:italic" />
<button id="submitbutton" type="submit" class="col-span-2">Nachschlagen</button>
</form>
<form
id="simplesearchform"
class="w-full font-serif" class="w-full font-serif"
method="get" method="get"
action="/suche/baende" action="/suche/baende"
@@ -83,14 +99,6 @@
{{- end -}} {{- end -}}
{{ template "_searchboxsimple" Arr $model.parameters true $q }} {{ template "_searchboxsimple" Arr $model.parameters true $q }}
<fieldset class="selectgroup"> <fieldset class="selectgroup">
<div class="selectgroup-option">
<input
type="checkbox"
name="alm"
id="alm"
{{ if or $isBase $isAlm -}}checked{{- end -}} />
<label for="alm">Almanach-Nr.</label>
</div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input <input
type="checkbox" type="checkbox"
@@ -172,12 +180,6 @@
name="titlestring" name="titlestring"
id="titlestring" id="titlestring"
value="{{ $model.parameters.TitleString }}" /> value="{{ $model.parameters.TitleString }}" />
<label for="almstring">Almanach-Nr.</label>
<input
type="search"
name="almstring"
id="almstring"
value="{{ $model.parameters.AlmString }}" />
<label for="seriesstring">Reihentitel</label> <label for="seriesstring">Reihentitel</label>
<input <input
type="search" type="search"
@@ -231,6 +233,9 @@
{{ if $model.parameters.Query -}} {{ if $model.parameters.Query -}}
Suche nach <b>»{{ $model.parameters.Query }}«</b> &middot; Suche nach <b>»{{ $model.parameters.Query }}«</b> &middot;
{{- end -}} {{- end -}}
{{- if $isAlm -}}
Almanach-Nummer <b>»{{ $model.parameters.AlmString }}«</b> &middot;
{{- end -}}
<i class="ri-book-line"></i> <i class="ri-book-line"></i>
{{ if eq $model.result.Count 1 -}} {{ if eq $model.result.Count 1 -}}
Ein Band Ein Band
@@ -245,37 +250,39 @@
{{- end -}} {{- end -}}
</div> </div>
<div> {{- if not $isAlm -}}
<label <div>
for="sort" <label
class="align-baseline h-min self-end pb-1 mr-2 text-sm font-sans for="sort"
class="align-baseline h-min self-end pb-1 mr-2 text-sm font-sans
text-stone-700" text-stone-700"
>Sortierung</label >Sortierung</label
> >
{{/* INFO: We always redrect to letter = A bc some letters dont exist for other professions */}} {{/* INFO: We always redrect to letter = A bc some letters dont exist for other professions */}}
<select <select
class="h-min pb-1 border-b-4 border-zinc-300 px-1.5" class="h-min pb-1 border-b-4 border-zinc-300 px-1.5"
name="sort" name="sort"
id="sort" id="sort"
hx-get="{{- $model.parameters.ToQueryParams -}}" hx-get="{{- $model.parameters.ToQueryParams -}}"
trigger="change" trigger="change"
hx-push-url="true" hx-push-url="true"
hx-select="main" hx-select="main"
auto-complete="off" auto-complete="off"
hx-target="main"> hx-target="main">
<option <option
value="year" value="year"
{{ if eq $model.parameters.Sort "year" -}} {{ if eq $model.parameters.Sort "year" -}}
selected selected
{{- end -}}> {{- end -}}>
Erscheinungsjahr Erscheinungsjahr
</option> </option>
<option value="series" {{ if eq $model.parameters.Sort "series" -}}selected{{- end -}}> <option value="series" {{ if eq $model.parameters.Sort "series" -}}selected{{- end -}}>
Reihentitel A-Z Reihentitel A-Z
</option> </option>
</select> </select>
</div> </div>
{{- end -}}
</div> </div>
{{- if $model.result.Hits -}} {{- if $model.result.Hits -}}

View File

@@ -9,7 +9,6 @@
Annotations bool Annotations bool
Persons bool Persons bool
Title bool Title bool
Alm bool
Series bool Series bool
Places bool Places bool
Refs bool Refs bool
@@ -44,7 +43,7 @@
} }
*/}} */}}
{{- $isAlm := or $model.parameters.Alm $model.parameters.AlmString -}} {{- $isAlm := $model.parameters.AlmString -}}
{{- $isTitle := or $model.parameters.Title $model.parameters.TitleString -}} {{- $isTitle := or $model.parameters.Title $model.parameters.TitleString -}}
{{- $isRefs := or $model.parameters.Refs $model.parameters.RefsString -}} {{- $isRefs := or $model.parameters.Refs $model.parameters.RefsString -}}
{{- $isPlaces := or $model.parameters.Places $model.parameters.PlacesString -}} {{- $isPlaces := or $model.parameters.Places $model.parameters.PlacesString -}}
@@ -61,7 +60,7 @@
<div class="inline-block ml-1 whitespace-nowrap"> <div class="inline-block ml-1 whitespace-nowrap">
{{- if not $entry.TitleStmt -}} {{- if not $entry.TitleStmt -}}
<tool-tip position="right"> <tool-tip position="right">
<i class="ri-forbid-line"></i> <i class="ri-forbid-2-line"></i>
<div class="data-tip">Keine nähere Erfassung</div> <div class="data-tip">Keine nähere Erfassung</div>
</tool-tip> </tool-tip>
{{- else if eq $entry.EditState "Edited" -}} {{- else if eq $entry.EditState "Edited" -}}

View File

@@ -9,9 +9,11 @@
Die Suche durchsucht ganze Datensätze nach dem Vorkommen aller eingegebenen Suchbegriffe. Felder Die Suche durchsucht ganze Datensätze nach dem Vorkommen aller eingegebenen Suchbegriffe. Felder
können oben einzeln aus der Suche ausgeschlossen werden. Auch partielle Treffer in Worten werden können oben einzeln aus der Suche ausgeschlossen werden. Auch partielle Treffer in Worten werden
angezeigt. Wörter mit weniger als drei Zeichen, Sonderzeichen &ndash; auch Satzzeichen &ndash; angezeigt. Wörter mit weniger als drei Zeichen, Sonderzeichen &ndash; auch Satzzeichen &ndash;
sowie die Groß- und Kleinschreibung werden dabei ignoriert sowie Groß- und Kleinschreibung werden dabei ignoriert. Mit einem Minus [-] können Begiffe
ausgeschlossen, mit Anfühungsstrichen [""] zusammenhängende Phrasen gesucht werden
{{- if $extendable }} {{- if $extendable }}
(für mehr Optionen s. die &rarr; <a href="?extended=true">erweiterte Suche</a>) (für eine genaue Eingenzung von Feldern s. die &rarr;
<a href="?extended=true">erweiterte Suche</a>)
{{- end -}}. {{- end -}}.
</div> </div>
</div> </div>

View File

@@ -25,8 +25,10 @@
<script type="module"> <script type="module">
const form = document.getElementById("searchform"); const form = document.getElementById("simplesearchform");
const submitBtn = document.getElementById("submitbutton"); if (form) {
const submitBtn = form.getElementById("submitbutton");
}
function checkValidity() { function checkValidity() {
if (form.checkValidity()) { if (form.checkValidity()) {