about summary refs log tree commit diff stats
path: root/internal
diff options
context:
space:
mode:
authorAlan Pearce2024-05-07 21:32:36 +0200
committerAlan Pearce2024-05-07 21:32:36 +0200
commit212e5cf6621c99e46dbb37c860dab8938968bb19 (patch)
treeabd06307e406290c17f453360e92dc6462f5533e /internal
parent32c4f1ddd704984dad79ad059619b127dcc7de2f (diff)
downloadsearchix-212e5cf6621c99e46dbb37c860dab8938968bb19.tar.lz
searchix-212e5cf6621c99e46dbb37c860dab8938968bb19.tar.zst
searchix-212e5cf6621c99e46dbb37c860dab8938968bb19.zip
feat: search multiple sources
Diffstat (limited to 'internal')
-rw-r--r--internal/search/search.go46
-rw-r--r--internal/server/server.go48
2 files changed, 56 insertions, 38 deletions
diff --git a/internal/search/search.go b/internal/search/search.go
index fc53ad0..b449512 100644
--- a/internal/search/search.go
+++ b/internal/search/search.go
@@ -17,15 +17,17 @@ import (
 
 const maxResults = 10
 
-var index bleve.Index
-var docs sync.Map
-
 type Result[T options.NixOption] struct {
 	*bleve.SearchResult
 	Results []T
 }
 
-func Index() error {
+type Index[T options.NixOption] struct {
+	index bleve.Index
+	docs  *sync.Map
+}
+
+func New[T options.NixOption](kind string) (*Index[T], error) {
 	bleve.SetLog(log.Default())
 	textFieldMapping := bleve.NewTextFieldMapping()
 	nameMapping := bleve.NewTextFieldMapping()
@@ -41,18 +43,19 @@ func Index() error {
 	indexMapping.AddDocumentMapping("option", optionMapping)
 
 	var err error
-	index, err = bleve.NewMemOnly(indexMapping)
+	index, err := bleve.NewMemOnly(indexMapping)
 	// index, err = bleve.New(path.Join(cfg.DataPath, const indexFilename = "index.bleve"), indexMapping)
 
 	if err != nil {
-		return errors.WithMessage(err, "error opening index")
+		return nil, errors.WithMessage(err, "error opening index")
 	}
 	batch := index.NewBatch()
-	_ = batch
 
-	jsonFile, err := os.Open(path.Join("data", "processed", "darwin-options.json"))
+	var docs sync.Map
+
+	jsonFile, err := os.Open(path.Join("data", "processed", kind+".json"))
 	if err != nil {
-		return errors.WithMessage(err, "error opening json file")
+		return nil, errors.WithMessage(err, "error opening json file")
 	}
 
 	dec := jstream.NewDecoder(jsonFile, 1)
@@ -63,35 +66,38 @@ func Index() error {
 		Result:      &opt,
 	})
 	if err != nil {
-		return errors.WithMessage(err, "could not create struct decoder")
+		return nil, errors.WithMessage(err, "could not create struct decoder")
 	}
 	for mv := range dec.Stream() {
 		err := ms.Decode(mv.Value) // stores in opt
 
 		if err != nil {
-			return errors.WithMessagef(err, "could not decode object into option, object: %#v", mv.Value)
+			return nil, errors.WithMessagef(err, "could not decode object into option, object: %#v", mv.Value)
 		}
 
 		docs.Store(opt.Option, opt)
 
 		err = batch.Index(opt.Option, opt)
 		if err != nil {
-			return errors.WithMessagef(err, "could not index option %s", opt.Option)
+			return nil, errors.WithMessagef(err, "could not index option %s", opt.Option)
 		}
 	}
 	err = index.Batch(batch)
 	if err != nil {
-		return errors.WithMessage(err, "failed to run batch index operation")
+		return nil, errors.WithMessage(err, "failed to run batch index operation")
 	}
 
-	return nil
+	return &Index[T]{
+		index,
+		&docs,
+	}, nil
 }
 
-func Search[T options.NixOption](ctx context.Context, keyword string) (*Result[T], error) {
+func (index *Index[T]) Search(ctx context.Context, keyword string) (*Result[T], error) {
 	query := bleve.NewMatchQuery(keyword)
 	search := bleve.NewSearchRequest(query)
 
-	bleveResult, err := index.SearchInContext(ctx, search)
+	bleveResult, err := index.index.SearchInContext(ctx, search)
 	select {
 	case <-ctx.Done():
 		return nil, ctx.Err()
@@ -102,7 +108,7 @@ func Search[T options.NixOption](ctx context.Context, keyword string) (*Result[T
 
 		results := make([]T, min(maxResults, bleveResult.Total))
 		for i, result := range bleveResult.Hits {
-			doc, _ := docs.Load(result.ID)
+			doc, _ := index.docs.Load(result.ID)
 			results[i] = doc.(T)
 			if i > maxResults {
 				break
@@ -115,9 +121,3 @@ func Search[T options.NixOption](ctx context.Context, keyword string) (*Result[T
 		}, nil
 	}
 }
-
-func Load(name string) any {
-	doc, _ := docs.Load(name)
-
-	return doc
-}
diff --git a/internal/server/server.go b/internal/server/server.go
index e3cc82d..3b85061 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -72,10 +72,23 @@ func applyDevModeOverrides(config *cfg.Config) {
 	config.CSP.ConnectSrc = slices.Insert(config.CSP.ConnectSrc, 0, "'self'")
 }
 
+var index = map[string]*search.Index[options.NixOption]{}
+
+var sourceFileName = map[string]string{
+	"darwin":       "darwin-options",
+	"home-manager": "home-manager-options",
+	"nixos":        "nixos-options-nixos-unstable",
+}
+
 func init() {
-	err := search.Index()
-	if err != nil {
-		log.Fatalf("could not build search index, error: %#v", err)
+	var err error
+
+	for source, filename := range sourceFileName {
+		index[source], err = search.New(filename)
+		if err != nil {
+			log.Fatalf("could not build search index, error: %#v", err)
+		}
+
 	}
 }
 
@@ -122,19 +135,19 @@ func New(runtimeConfig *Config) (*Server, error) {
 		}
 	})
 
-	mux.HandleFunc("/search/{source}", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/options/{source}/search", func(w http.ResponseWriter, r *http.Request) {
 		source := r.PathValue("source")
-		switch source {
-		case "nixos", "darwin", "home-manager":
-			err := templates["search"].Execute(w, TemplateData{
-				LiveReload: jsSnippet,
-				Source:     source,
-			})
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-			}
-		default:
+		if index[source] == nil {
 			http.Error(w, "Unknown source", http.StatusNotFound)
+
+			return
+		}
+		err := templates["search"].Execute(w, TemplateData{
+			LiveReload: jsSnippet,
+			Source:     source,
+		})
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 	})
 
@@ -143,7 +156,12 @@ func New(runtimeConfig *Config) (*Server, error) {
 		source := r.PathValue("source")
 		ctx, cancel := context.WithTimeoutCause(r.Context(), timeout, errors.New("timeout"))
 		defer cancel()
-		results, err := search.Search(ctx, r.URL.Query().Get("query"))
+		if index[source] == nil {
+			http.Error(w, "Unknown source", http.StatusNotFound)
+
+			return
+		}
+		results, err := index[source].Search(ctx, r.URL.Query().Get("query"))
 		if err != nil {
 			if err == context.DeadlineExceeded {
 				http.Error(w, "Search timed out", http.StatusInternalServerError)