about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2024-05-11 14:43:01 +0200
committerAlan Pearce2024-05-11 14:43:01 +0200
commit76f18d677b9b0bb70f701824e715f2ed10aeb8a0 (patch)
tree3ff2dadebde49f2042535c40f5832ebfa93d1b0b
parentdbe952d1df63522e5c32b4ef3e59c43886ba9257 (diff)
downloadsearchix-76f18d677b9b0bb70f701824e715f2ed10aeb8a0.tar.lz
searchix-76f18d677b9b0bb70f701824e715f2ed10aeb8a0.tar.zst
searchix-76f18d677b9b0bb70f701824e715f2ed10aeb8a0.zip
feat: version search index mapping schema and warn if outdated
-rw-r--r--internal/search/index_meta.go73
-rw-r--r--internal/search/indexer.go34
-rw-r--r--internal/search/search.go8
3 files changed, 110 insertions, 5 deletions
diff --git a/internal/search/index_meta.go b/internal/search/index_meta.go
new file mode 100644
index 0000000..bb7e69f
--- /dev/null
+++ b/internal/search/index_meta.go
@@ -0,0 +1,73 @@
+package search
+
+import (
+	"encoding/json"
+	"log/slog"
+	"os"
+	"searchix/internal/file"
+
+	"github.com/pkg/errors"
+)
+
+const CurrentSchemaVersion = 1
+
+type IndexMeta struct {
+	path          string
+	SchemaVersion int
+}
+
+func createMeta(path string) (*IndexMeta, error) {
+	exists, err := file.Exists(path)
+	if err != nil {
+		return nil, errors.WithMessage(err, "could not check for existence of index metadata")
+	}
+	if exists {
+		return nil, errors.New("index metadata already exists")
+	}
+
+	return &IndexMeta{
+		path:          path,
+		SchemaVersion: CurrentSchemaVersion,
+	}, nil
+}
+
+func openMeta(path string) (*IndexMeta, error) {
+	j, err := os.ReadFile(path)
+	if err != nil {
+		return nil, errors.WithMessage(err, "could not open index metadata file")
+	}
+	var meta IndexMeta
+	err = json.Unmarshal(j, &meta)
+	if err != nil {
+		return nil, errors.WithMessage(err, "index metadata is corrupt, try replacing the index")
+	}
+
+	meta.checkSchemaVersion()
+
+	return &meta, nil
+}
+
+func (i *IndexMeta) checkSchemaVersion() {
+	if i.SchemaVersion < CurrentSchemaVersion {
+		slog.Warn(
+			"Index schema version out of date, suggest re-indexing",
+			"schema_version",
+			i.SchemaVersion,
+			"latest_version",
+			CurrentSchemaVersion,
+		)
+	}
+}
+
+func (i *IndexMeta) Save() error {
+	j, err := json.Marshal(i)
+	if err != nil {
+		return errors.WithMessage(err, "could not prepare index metadata for saving")
+	}
+	err = os.WriteFile(i.path, j, 0o600)
+	if err != nil {
+		return errors.WithMessage(err, "could not save index metadata")
+	}
+
+	return nil
+}
diff --git a/internal/search/indexer.go b/internal/search/indexer.go
index ea262ee..e2e23c2 100644
--- a/internal/search/indexer.go
+++ b/internal/search/indexer.go
@@ -28,6 +28,7 @@ import (
 
 type WriteIndex struct {
 	index bleve.Index
+	meta  *IndexMeta
 }
 
 func createIndexMapping() (mapping.IndexMapping, error) {
@@ -125,9 +126,13 @@ func createIndex(indexPath string) (bleve.Index, error) {
 	return idx, nil
 }
 
-const indexBaseName = "index.bleve"
+const (
+	indexBaseName = "index.bleve"
+	metaBaseName  = "meta.json"
+)
 
 var expectedDataFiles = []string{
+	metaBaseName,
 	indexBaseName,
 	"sources",
 }
@@ -137,6 +142,7 @@ func NewIndexer(dataRoot string, force bool) (*WriteIndex, error) {
 	bleve.SetLog(log.Default())
 
 	indexPath := path.Join(dataRoot, indexBaseName)
+	metaPath := path.Join(dataRoot, metaBaseName)
 
 	exists, err := file.Exists(indexPath)
 	if err != nil {
@@ -148,6 +154,7 @@ func NewIndexer(dataRoot string, force bool) (*WriteIndex, error) {
 	}
 
 	var idx bleve.Index
+	var meta *IndexMeta
 	if !exists || force {
 		if force {
 			dir, err := os.ReadDir(dataRoot)
@@ -171,18 +178,35 @@ func NewIndexer(dataRoot string, force bool) (*WriteIndex, error) {
 			}
 		}
 		idx, err = createIndex(indexPath)
+		if err != nil {
+			return nil, err
+		}
+
+		meta, err = createMeta(metaPath)
+		if err != nil {
+			return nil, err
+		}
+
+		err = meta.Save()
+		if err != nil {
+			return nil, err
+		}
 	} else {
 		idx, err = bleve.Open(indexPath)
 		if err != nil {
-			err = errors.WithMessagef(err, "could not open index at path %s", indexPath)
+			return nil, errors.WithMessagef(err, "could not open index at path %s", indexPath)
 		}
-	}
-	if err != nil {
-		return nil, err
+
+		meta, err = openMeta(metaPath)
+		if err != nil {
+			return nil, err
+		}
+
 	}
 
 	return &WriteIndex{
 		idx,
+		meta,
 	}, nil
 }
 
diff --git a/internal/search/search.go b/internal/search/search.go
index 357698c..829fefe 100644
--- a/internal/search/search.go
+++ b/internal/search/search.go
@@ -26,18 +26,26 @@ type Result struct {
 
 type ReadIndex struct {
 	index bleve.Index
+	meta  *IndexMeta
 }
 
 func Open(dataRoot string) (*ReadIndex, error) {
 	indexPath := path.Join(dataRoot, indexBaseName)
+	metaPath := path.Join(dataRoot, metaBaseName)
 
 	idx, err := bleve.Open(indexPath)
 	if err != nil {
 		return nil, errors.WithMessagef(err, "unable to open index at path %s", indexPath)
 	}
 
+	meta, err := openMeta(metaPath)
+	if err != nil {
+		return nil, errors.WithMessagef(err, "unable to open metadata at path %s", metaPath)
+	}
+
 	return &ReadIndex{
 		idx,
+		meta,
 	}, nil
 }