mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2025-10-29 09:15:33 +00:00
345 lines
7.7 KiB
Go
345 lines
7.7 KiB
Go
package dbmodels
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes"
|
|
"github.com/pocketbase/dbx"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/collate"
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
type SeriesEntries map[string][]*REntriesSeries
|
|
|
|
func SortSeriessesByTitle(series []*Series) {
|
|
collator := collate.New(language.German)
|
|
slices.SortFunc(series, func(i, j *Series) int {
|
|
return collator.CompareString(i.Title(), j.Title())
|
|
})
|
|
}
|
|
|
|
func MusenalmIDSearchSeries(app core.App, query string) ([]*Series, error) {
|
|
series := []*Series{}
|
|
err := app.RecordQuery(SERIES_TABLE).
|
|
Where(dbx.Like(MUSENALMID_FIELD, query).Match(true, false)).
|
|
All(&series)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return series, nil
|
|
}
|
|
|
|
func BasicSearchSeries(app core.App, query string) ([]*Series, []*Series, error) {
|
|
query = strings.TrimSpace(query)
|
|
query = datatypes.DeleteTags(query)
|
|
query = datatypes.NormalizeString(query)
|
|
query = datatypes.RemovePunctuation(query)
|
|
query = cases.Lower(language.German).String(query)
|
|
// TODO: how to normalize, which unicode normalization to use?
|
|
|
|
if query == "" {
|
|
return []*Series{}, []*Series{}, nil
|
|
}
|
|
|
|
series, err := TitleSearchSeries(app, query)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// INFO: Needing to differentiate matches
|
|
altids, err := FTS5SearchSeries(app, query)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
/// INFO: this is inefficient, but it only happens when there are matches longer than 3 characters, so we should be fine
|
|
ids := []any{}
|
|
outer_loop:
|
|
for _, id := range altids {
|
|
for _, i := range series {
|
|
sid := i.Id
|
|
if sid == id.ID {
|
|
continue outer_loop
|
|
}
|
|
}
|
|
ids = append(ids, id.ID)
|
|
}
|
|
|
|
altseries, err := SeriessesForIds(app, ids)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return series, altseries, nil
|
|
}
|
|
|
|
// INFO: expects a normalized query string
|
|
func TitleSearchSeries(app core.App, query string) ([]*Series, error) {
|
|
series := []*Series{}
|
|
queries := strings.Split(query, " ")
|
|
q := app.RecordQuery(SERIES_TABLE).
|
|
Where(dbx.Like(SERIES_TITLE_FIELD, queries[0]).Match(true, true))
|
|
|
|
if len(queries) > 1 {
|
|
for _, que := range queries[1:] {
|
|
q.AndWhere(dbx.Like(SERIES_TITLE_FIELD, que).Match(true, true))
|
|
}
|
|
}
|
|
|
|
err := q.
|
|
OrderBy(SERIES_TITLE_FIELD).
|
|
All(&series)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return series, nil
|
|
}
|
|
|
|
// INFO: expects a normalized query string
|
|
// Returns all ids that match the query
|
|
func FTS5SearchSeries(app core.App, query string) ([]*FTS5IDQueryResult, error) {
|
|
seriesids := []*FTS5IDQueryResult{}
|
|
q := NewFTS5Query().
|
|
From(SERIES_TABLE).
|
|
SelectID()
|
|
|
|
queries := strings.Split(query, " ")
|
|
for _, que := range queries {
|
|
que := datatypes.NormalizeString(que)
|
|
if len(que) >= 3 {
|
|
q.AndMatch([]string{SERIES_TITLE_FIELD, ANNOTATION_FIELD, REFERENCES_FIELD}, que)
|
|
}
|
|
}
|
|
|
|
querystring := q.Query()
|
|
if querystring == "" {
|
|
return seriesids, nil
|
|
}
|
|
|
|
err := app.DB().NewQuery(querystring).All(&seriesids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return seriesids, nil
|
|
}
|
|
|
|
func IDsForSeriesses(series []*Series) []any {
|
|
ids := []any{}
|
|
for _, s := range series {
|
|
ids = append(ids, s.Id)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func makeMapForEnrySeries(relations []*REntriesSeries, entries map[string]*Entry) SeriesEntries {
|
|
m := map[string][]*REntriesSeries{}
|
|
for _, r := range relations {
|
|
m[r.Series()] = append(m[r.Series()], r)
|
|
}
|
|
|
|
for _, rel := range m {
|
|
slices.SortFunc(rel, func(i, j *REntriesSeries) int {
|
|
ientry := entries[i.Entry()]
|
|
jentry := entries[j.Entry()]
|
|
return ientry.Year() - jentry.Year()
|
|
})
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
func EntriesForSeriesses(app core.App, series []*Series) (
|
|
SeriesEntries,
|
|
map[string]*Entry,
|
|
error) {
|
|
ids := IDsForSeriesses(series)
|
|
relations := []*core.Record{}
|
|
|
|
err := app.RecordQuery(RelationTableName(ENTRIES_TABLE, SERIES_TABLE)).
|
|
Where(dbx.HashExp{
|
|
SERIES_TABLE: ids,
|
|
}).
|
|
All(&relations)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
app.ExpandRecords(relations, []string{ENTRIES_TABLE}, nil)
|
|
bmap := map[string]*Entry{}
|
|
for _, r := range relations {
|
|
record := r.ExpandedOne(ENTRIES_TABLE)
|
|
if record == nil {
|
|
continue
|
|
}
|
|
entry := NewEntry(record)
|
|
bmap[entry.Id] = entry
|
|
}
|
|
|
|
smap := map[string][]*REntriesSeries{}
|
|
for _, r := range relations {
|
|
rel := NewREntriesSeries(r)
|
|
smap[rel.Series()] = append(smap[rel.Series()], rel)
|
|
}
|
|
|
|
for _, rel := range smap {
|
|
slices.SortFunc(rel, func(i, j *REntriesSeries) int {
|
|
ientry := bmap[i.Entry()]
|
|
jentry := bmap[j.Entry()]
|
|
return ientry.Year() - jentry.Year()
|
|
})
|
|
}
|
|
|
|
return smap, bmap, nil
|
|
}
|
|
|
|
func LettersForSeries(app core.App) ([]string, error) {
|
|
letters := []core.Record{}
|
|
ids := []string{}
|
|
|
|
err := app.RecordQuery(SERIES_TABLE).
|
|
Select("upper(substr(" + SERIES_TITLE_FIELD + ", 1, 1)) AS id").
|
|
Distinct(true).
|
|
All(&letters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, l := range letters {
|
|
ids = append(ids, l.GetString("id"))
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func AllAgentsForSeries(app core.App) ([]*Agent, error) {
|
|
rels := []*core.Record{}
|
|
// INFO: we could just fetch all relations here
|
|
err := app.RecordQuery(RelationTableName(ENTRIES_TABLE, AGENTS_TABLE)).
|
|
GroupBy(AGENTS_TABLE).
|
|
All(&rels)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
app.ExpandRecords(rels, []string{AGENTS_TABLE}, nil)
|
|
agents := []*Agent{}
|
|
for _, r := range rels {
|
|
record := r.ExpandedOne(AGENTS_TABLE)
|
|
if record == nil {
|
|
continue
|
|
}
|
|
agent := NewAgent(record)
|
|
agents = append(agents, agent)
|
|
}
|
|
|
|
SortAgentsByName(agents)
|
|
|
|
return agents, err
|
|
}
|
|
|
|
func SeriesForLetter(app core.App, letter string) ([]*Series, error) {
|
|
series := []*Series{}
|
|
err := app.RecordQuery(SERIES_TABLE).
|
|
Where(dbx.Like(SERIES_TITLE_FIELD, letter).Match(false, true)).
|
|
OrderBy(SERIES_TITLE_FIELD).
|
|
All(&series)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return series, nil
|
|
}
|
|
|
|
func SeriesForAgent(app core.App, id string) ([]*Series, SeriesEntries, map[string]*Entry, error) {
|
|
entries, _, err := EntriesForAgent(app, id)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return SeriesForEntries(app, entries)
|
|
}
|
|
|
|
func SeriesForPlace(app core.App, id string) ([]*Series, SeriesEntries, map[string]*Entry, error) {
|
|
entries, err := EntriesForPlace(app, id)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return SeriesForEntries(app, entries)
|
|
}
|
|
|
|
func SeriesForEntries(app core.App, entries []*Entry) ([]*Series, SeriesEntries, map[string]*Entry, error) {
|
|
bids := make([]any, 0, len(entries))
|
|
for _, e := range entries {
|
|
bids = append(bids, e.Id)
|
|
}
|
|
|
|
srels := []*REntriesSeries{}
|
|
err := app.RecordQuery(RelationTableName(ENTRIES_TABLE, SERIES_TABLE)).
|
|
Where(dbx.HashExp{ENTRIES_TABLE: bids}).
|
|
All(&srels)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
sids := []any{}
|
|
for _, s := range srels {
|
|
sids = append(sids, s.Series())
|
|
}
|
|
|
|
series := []*Series{}
|
|
err = app.RecordQuery(SERIES_TABLE).
|
|
Where(dbx.HashExp{ID_FIELD: sids}).
|
|
All(&series)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
bmap := make(map[string]*Entry, len(entries))
|
|
for _, e := range entries {
|
|
bmap[e.Id] = e
|
|
}
|
|
|
|
smap := makeMapForEnrySeries(srels, bmap)
|
|
|
|
return series, smap, bmap, nil
|
|
}
|
|
|
|
func SeriesForYear(app core.App, year int) ([]*Series, SeriesEntries, map[string]*Entry, error) {
|
|
series, err := EntriesForYear(app, year)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return SeriesForEntries(app, series)
|
|
}
|
|
|
|
func SeriesForId(app core.App, id string) (*Series, error) {
|
|
s := &Series{}
|
|
err := app.RecordQuery(SERIES_TABLE).
|
|
Where(dbx.HashExp{MUSENALMID_FIELD: id}).
|
|
One(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func SeriessesForIds(app core.App, ids []any) ([]*Series, error) {
|
|
series := []*Series{}
|
|
err := app.RecordQuery(SERIES_TABLE).
|
|
Where(dbx.HashExp{ID_FIELD: ids}).
|
|
All(&series)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return series, nil
|
|
}
|