Fix Locking
This commit is contained in:
parent
f8ab5c8358
commit
3e07e2f06e
@ -43,7 +43,6 @@ type fuzzyResult struct {
|
|||||||
type resultBuffer struct {
|
type resultBuffer struct {
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
hits int
|
hits int
|
||||||
mu sync.Mutex
|
|
||||||
results []fuzzyResult
|
results []fuzzyResult
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +55,7 @@ var (
|
|||||||
|
|
||||||
var searchTargets map[table.TechnicalName][]string = make(map[table.TechnicalName][]string)
|
var searchTargets map[table.TechnicalName][]string = make(map[table.TechnicalName][]string)
|
||||||
var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer)
|
var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer)
|
||||||
|
var resultsBufferMu sync.Mutex
|
||||||
|
|
||||||
func Load() error {
|
func Load() error {
|
||||||
keywords, err := model.GetKeywordsModel()
|
keywords, err := model.GetKeywordsModel()
|
||||||
@ -95,6 +95,9 @@ func GetPage(r *http.Request) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetBuffer(searchTerm string) (*resultBuffer, error) {
|
func GetBuffer(searchTerm string) (*resultBuffer, error) {
|
||||||
|
resultsBufferMu.Lock()
|
||||||
|
defer resultsBufferMu.Unlock()
|
||||||
|
|
||||||
buffer, exists := resultsBuffer[searchTerm]
|
buffer, exists := resultsBuffer[searchTerm]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -186,11 +189,13 @@ func BuildBuffer(searchTerm string) error {
|
|||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
resultsBufferMu.Lock()
|
||||||
resultsBuffer[searchTerm] = &resultBuffer{
|
resultsBuffer[searchTerm] = &resultBuffer{
|
||||||
createdAt: time.Now(),
|
createdAt: time.Now(),
|
||||||
hits: hits,
|
hits: hits,
|
||||||
results: fuzzyResults,
|
results: fuzzyResults,
|
||||||
}
|
}
|
||||||
|
resultsBufferMu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -212,9 +217,6 @@ func GetPages(buffer *resultBuffer, p int) (int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel {
|
func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel {
|
||||||
buffer.mu.Lock()
|
|
||||||
defer buffer.mu.Unlock()
|
|
||||||
|
|
||||||
resultsModel := model.NewResultsModel()
|
resultsModel := model.NewResultsModel()
|
||||||
for i := page * pageStep; i < page*pageStep+pageStep; i++ {
|
for i := page * pageStep; i < page*pageStep+pageStep; i++ {
|
||||||
if i > len(buffer.results)-1 {
|
if i > len(buffer.results)-1 {
|
||||||
@ -295,11 +297,12 @@ func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
func ClearOldBuffers() {
|
func ClearOldBuffers() {
|
||||||
|
resultsBufferMu.Lock()
|
||||||
|
defer resultsBufferMu.Unlock()
|
||||||
|
|
||||||
for key, buffer := range resultsBuffer {
|
for key, buffer := range resultsBuffer {
|
||||||
if time.Since(buffer.createdAt) >= time.Minute*10 {
|
if time.Since(buffer.createdAt) >= time.Minute*10 {
|
||||||
buffer.mu.Lock()
|
|
||||||
delete(resultsBuffer, key)
|
delete(resultsBuffer, key)
|
||||||
buffer.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
db_table "api-cds-search/cmd/database/table"
|
||||||
|
|
||||||
"code.achtarmig.org/pas/ui/element"
|
"code.achtarmig.org/pas/ui/element"
|
||||||
"code.achtarmig.org/pas/ui/element/container"
|
"code.achtarmig.org/pas/ui/element/container"
|
||||||
|
"code.achtarmig.org/pas/ui/element/form"
|
||||||
"code.achtarmig.org/pas/ui/element/input"
|
"code.achtarmig.org/pas/ui/element/input"
|
||||||
"code.achtarmig.org/pas/ui/element/option"
|
"code.achtarmig.org/pas/ui/element/option"
|
||||||
"code.achtarmig.org/pas/ui/element/table"
|
"code.achtarmig.org/pas/ui/element/table"
|
||||||
@ -21,14 +24,22 @@ func cdsDetails() {
|
|||||||
p.OnRequest = onRequestDetails
|
p.OnRequest = onRequestDetails
|
||||||
container.New(p, "Details", func(p *container.Element) {
|
container.New(p, "Details", func(p *container.Element) {
|
||||||
p.Justify = option.JustifyCenter
|
p.Justify = option.JustifyCenter
|
||||||
input.New(p, "q", input.TypeText, func(p *input.Element) {
|
form.New(p, "qForm", func(p *form.Element) {
|
||||||
p.Placeholder = "CDSViewTechnicalName"
|
p.Method = http.MethodGet
|
||||||
p.Shape = option.ShapeRound
|
p.Target = "#DetailsTable"
|
||||||
p.SetHTMX(element.HTMX{
|
p.URL = "/cds"
|
||||||
Method: http.MethodGet,
|
p.PushURL = true
|
||||||
Target: "#DetailsTable",
|
input.New(p, "q", input.TypeText, func(p *input.Element) {
|
||||||
URL: "/cds",
|
p.ValidateInput = true
|
||||||
PushURL: true,
|
p.RequestDelay = 200
|
||||||
|
p.Parameter = true
|
||||||
|
p.Placeholder = "CDSViewTechnicalName"
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
|
p.OnValidate = func(e *input.Element, v any) error {
|
||||||
|
_, err := db_table.GetCDSView(db_table.TechnicalName(v.(string)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -43,7 +54,7 @@ func cdsDetails() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func onRequestDetails(e *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
|
func onRequestDetails(e *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
|
||||||
cds, _ := e.GetElement("q").(*input.Element).GetDataText()
|
cds, _ := e.GetElement("q").(*input.Element).GetDataString()
|
||||||
|
|
||||||
e.Title = fmt.Sprintf("CDSDetails - %s", cds)
|
e.Title = fmt.Sprintf("CDSDetails - %s", cds)
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ func noResults(v *view.Element) element.ElementInterface {
|
|||||||
cont := v.GetElement("SearchResultsContainer")
|
cont := v.GetElement("SearchResultsContainer")
|
||||||
cont.DeleteAllChildren()
|
cont.DeleteAllChildren()
|
||||||
|
|
||||||
pl, _ := placeholder.New(cont, "", func(p *placeholder.Element) {
|
pl := placeholder.New(cont, "", func(p *placeholder.Element) {
|
||||||
p.Icon = option.IconLoupe
|
p.Icon = option.IconLoupe
|
||||||
p.Title = "No Results"
|
p.Title = "No Results"
|
||||||
p.Subtitle = "Try refining your search term"
|
p.Subtitle = "Try refining your search term"
|
||||||
|
@ -19,7 +19,7 @@ func noSearch(v *view.Element) element.ElementInterface {
|
|||||||
cont := v.GetElement("SearchResultsContainer")
|
cont := v.GetElement("SearchResultsContainer")
|
||||||
cont.DeleteAllChildren()
|
cont.DeleteAllChildren()
|
||||||
|
|
||||||
pl, _ := placeholder.New(cont, "", func(p *placeholder.Element) {
|
pl := placeholder.New(cont, "", func(p *placeholder.Element) {
|
||||||
p.Icon = option.IconLoupe
|
p.Icon = option.IconLoupe
|
||||||
p.Title = "Search CDS-Views"
|
p.Title = "Search CDS-Views"
|
||||||
p.Subtitle = "Find CDS-Views from the Business Accelerator Hub"
|
p.Subtitle = "Find CDS-Views from the Business Accelerator Hub"
|
||||||
@ -33,7 +33,7 @@ func searchView() {
|
|||||||
p.Title = "CDS-Search"
|
p.Title = "CDS-Search"
|
||||||
p.OnRequest = func(v *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
|
p.OnRequest = func(v *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
|
||||||
inputQuery := v.GetElement("q").(*input.Element)
|
inputQuery := v.GetElement("q").(*input.Element)
|
||||||
inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataText()
|
inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataString()
|
||||||
|
|
||||||
searchTerm := search.GetSearchTerm(inputQueryValue)
|
searchTerm := search.GetSearchTerm(inputQueryValue)
|
||||||
if inputQueryValue == "" {
|
if inputQueryValue == "" {
|
||||||
@ -43,7 +43,7 @@ func searchView() {
|
|||||||
inputPage := v.GetElement("p").(*input.Element)
|
inputPage := v.GetElement("p").(*input.Element)
|
||||||
inputMaxPage := v.GetElement("MaxPage").(*input.Element)
|
inputMaxPage := v.GetElement("MaxPage").(*input.Element)
|
||||||
|
|
||||||
currentPage, _ := inputPage.GetDataNumber()
|
currentPage, _ := inputPage.GetDataInt()
|
||||||
|
|
||||||
if inputQuery.GetDataChanged() && inputQueryValue != "" {
|
if inputQuery.GetDataChanged() && inputQueryValue != "" {
|
||||||
buf, err := search.GetBuffer(searchTerm)
|
buf, err := search.GetBuffer(searchTerm)
|
||||||
@ -70,25 +70,24 @@ func searchView() {
|
|||||||
|
|
||||||
return view.ProcessElements{
|
return view.ProcessElements{
|
||||||
Partial: []element.ElementInterface{v.GetElement("SearchResultsContainer")},
|
Partial: []element.ElementInterface{v.GetElement("SearchResultsContainer")},
|
||||||
OOBUpdate: []element.ElementInterface{inputPage, inputMaxPage},
|
OOBUpdate: []element.ElementInterface{v.GetElement("pagerForm")},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container.New(p, "MainContainer", func(p *container.Element) {
|
container.New(p, "MainContainer", func(p *container.Element) {
|
||||||
p.Justify = option.JustifyCenter
|
p.Justify = option.JustifyCenter
|
||||||
form.New(p, "SearchForm", func(p *form.Element) {
|
form.New(p, "SearchForm", func(p *form.Element) {
|
||||||
p.SetHTMX(element.HTMX{
|
p.Method = http.MethodGet
|
||||||
Method: http.MethodGet,
|
p.Target = "#SearchResultsContainer>*"
|
||||||
Target: "#SearchResultsContainer>*",
|
p.Swap = "outerHTML"
|
||||||
Swap: "outerHTML",
|
p.URL = "/search"
|
||||||
URL: "/search",
|
p.PushURL = true
|
||||||
PushURL: true,
|
p.Params = "q, p"
|
||||||
Params: "q, p",
|
|
||||||
})
|
|
||||||
|
|
||||||
container.New(p, "SearchFormLinked", func(p *container.Element) {
|
container.New(p, "SearchFormLinked", func(p *container.Element) {
|
||||||
p.Justify = option.JustifyCenter
|
p.Justify = option.JustifyCenter
|
||||||
p.LinkedHorizontal = true
|
p.LinkedHorizontal = true
|
||||||
input.New(p, "q", input.TypeText, func(p *input.Element) {
|
input.New(p, "q", input.TypeText, func(p *input.Element) {
|
||||||
|
p.Parameter = true
|
||||||
p.Placeholder = "Search here"
|
p.Placeholder = "Search here"
|
||||||
p.Shape = option.ShapeRound
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
@ -110,38 +109,43 @@ func searchView() {
|
|||||||
|
|
||||||
container.New(p, "CenterPager", func(p *container.Element) {
|
container.New(p, "CenterPager", func(p *container.Element) {
|
||||||
p.Justify = option.JustifyCenter
|
p.Justify = option.JustifyCenter
|
||||||
container.New(p, "Pager", func(p *container.Element) {
|
container.New(p, "RightCenterPager", func(p *container.Element) {
|
||||||
p.MaxWidth = 42
|
p.MaxWidth = 42
|
||||||
p.MinWidth = 10
|
p.MinWidth = 10
|
||||||
p.Justify = option.JustifyRight
|
p.Justify = option.JustifyRight
|
||||||
p.LinkedHorizontal = true
|
form.New(p, "pagerForm", func(p *form.Element) {
|
||||||
input.New(p, "p", input.TypeNumber, func(p *input.Element) {
|
p.MaxWidth = -1
|
||||||
p.Shape = option.ShapeRound
|
p.MinWidth = 8
|
||||||
|
p.Method = http.MethodGet
|
||||||
|
p.Include = "[name='q'], [name='p']"
|
||||||
|
p.Target = "#SearchResultsContainer>*"
|
||||||
|
p.URL = "/search"
|
||||||
|
p.Swap = "outerHTML"
|
||||||
|
p.Trigger = "change from:#p"
|
||||||
|
p.PushURL = true
|
||||||
|
container.New(p, "LinkPager", func(p *container.Element) {
|
||||||
|
p.LinkedHorizontal = true
|
||||||
|
input.New(p, "p", input.TypeNumber, func(p *input.Element) {
|
||||||
|
p.Parameter = true
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
p.SetMin = true
|
p.SetMin = true
|
||||||
p.Min = 0
|
p.Min = 0
|
||||||
|
|
||||||
p.SetMax = true
|
p.SetMax = true
|
||||||
p.Max = 0
|
p.Max = 0
|
||||||
|
|
||||||
p.MinWidth = 4
|
p.MinWidth = 4
|
||||||
|
})
|
||||||
p.SetHTMX(element.HTMX{
|
input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) {
|
||||||
Method: http.MethodGet,
|
p.Disabled = true
|
||||||
Include: "[name='q'], [name='p']",
|
p.Shape = option.ShapeRound
|
||||||
Target: "#SearchResultsContainer>*",
|
p.MinWidth = 4
|
||||||
URL: "/search",
|
p.SetMin = true
|
||||||
Swap: "outerHTML",
|
p.SetMax = true
|
||||||
PushURL: true,
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) {
|
|
||||||
p.Disabled = true
|
|
||||||
p.Shape = option.ShapeRound
|
|
||||||
p.MinWidth = 4
|
|
||||||
p.SetMin = true
|
|
||||||
p.SetMax = true
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module api-cds-search
|
|||||||
go 1.23.7
|
go 1.23.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa
|
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3
|
||||||
github.com/glebarez/go-sqlite v1.22.0
|
github.com/glebarez/go-sqlite v1.22.0
|
||||||
github.com/go-chi/chi v1.5.5
|
github.com/go-chi/chi v1.5.5
|
||||||
github.com/lithammer/fuzzysearch v1.1.8
|
github.com/lithammer/fuzzysearch v1.1.8
|
||||||
|
12
go.sum
12
go.sum
@ -1,11 +1,11 @@
|
|||||||
code.achtarmig.org/pas/ui v0.0.0-20250517190325-787754158160 h1:nhr5BUMdLRJrj/DnO4LZdsPwgIz3vYvaf08LdND7Eu8=
|
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250517190325-787754158160/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
|
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520192537-02c6f64d9a4f h1:gj8Lq5QgB8LlOxqrPPmj673gW9brX3TsYI9Ijp281Vw=
|
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520192537-02c6f64d9a4f/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
|
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520194044-0b9d43111bb8 h1:Zzr3PGFp5qVZxRrZOOWSe5Vua7s3dTjIAZmF00XglN8=
|
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520194044-0b9d43111bb8/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
|
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa h1:rMY1ksN8wtFO8ago/jMEgz52F/pFQIZLfN1YFSyTeEg=
|
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa h1:rMY1ksN8wtFO8ago/jMEgz52F/pFQIZLfN1YFSyTeEg=
|
||||||
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
|
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776 h1:VM1tir3yk+4iuJbDRVPmsLjrk1rJ+gTRPJh6LFTMMh0=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d h1:/vt1BgqVi8fW7cdZWGMOfMarIRkrQuBMBdKhwEziaGI=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3 h1:Lsq0qH31XZG5b9j51y9vmXx+ZBmLD5H64aPlNBVjDfc=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||||
|
Loading…
Reference in New Issue
Block a user