Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions changelog/unreleased/enhancement-search-optimize-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Enhancement: Add `ocis search optimize` CLI command

Added a new `ocis search optimize` command that compacts the search index
by merging Bleve segments, without re-indexing content. The command opens
the index directly (without requiring the search service to be running),
making it safe to run during maintenance windows without blocking search
queries.

This is useful after bulk reindexing operations that create many small
index segments, which can degrade search performance over time.

https://github.com/owncloud/ocis/pull/12136
40 changes: 40 additions & 0 deletions services/search/pkg/command/optimize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package command

import (
"context"
"fmt"

"github.com/urfave/cli/v2"

"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
"github.com/owncloud/ocis/v2/services/search/pkg/config"
"github.com/owncloud/ocis/v2/services/search/pkg/config/parser"
"github.com/owncloud/ocis/v2/services/search/pkg/engine"
)

// Optimize is the entrypoint for the optimize command.
func Optimize(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "optimize",
Usage: "compact the search index by merging segments, without re-indexing content",
Category: "index management",
Before: func(_ *cli.Context) error {
return configlog.ReturnFatal(parser.ParseConfig(cfg))
},
Action: func(_ *cli.Context) error {
eng, closer, err := engine.NewEngineFromConfig(cfg)
if err != nil {
return err
}
defer closer.Close()

fmt.Println("optimizing search index...")
if err := eng.Optimize(context.Background()); err != nil {
fmt.Println("failed to optimize index: " + err.Error())
return err
}
fmt.Println("index optimization complete")
return nil
},
}
}
1 change: 1 addition & 0 deletions services/search/pkg/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func GetCommands(cfg *config.Config) cli.Commands {

// interaction with this service
Index(cfg),
Optimize(cfg),

// infos about this service
Health(cfg),
Expand Down
34 changes: 30 additions & 4 deletions services/search/pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package engine
import (
"context"
"errors"
"fmt"
"io"
"regexp"

"github.com/blevesearch/bleve/v2/search"
storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"

searchMessage "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
searchService "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
bleveEngine "github.com/owncloud/ocis/v2/services/search/pkg/engine/bleve"
"github.com/owncloud/ocis/v2/services/search/pkg/config"
"github.com/owncloud/ocis/v2/services/search/pkg/content"
"github.com/owncloud/ocis/v2/services/search/pkg/query/bleve"
)

// ErrResourceNotFound is returned when a resource is not present in the index.
Expand All @@ -29,12 +34,33 @@ type Engine interface {
Restore(id string) error
Purge(id string) error
DocCount() (uint64, error)
Optimize(ctx context.Context) error
}

// Optimizer is an optional interface that Engine implementations may support
// to trigger index compaction. Callers should type-assert before use.
type Optimizer interface {
Optimize(ctx context.Context) error
// NewEngineFromConfig creates an Engine from the search service configuration.
// The returned io.Closer must be called to release the underlying index
// resources. This factory is used by CLI commands that need direct engine
// access without starting the full gRPC service.
func NewEngineFromConfig(cfg *config.Config) (Engine, io.Closer, error) {
switch cfg.Engine.Type {
case "bleve":
bleveMapping, err := BuildBleveMapping()
if err != nil {
return nil, nil, err
}

var indexGetter bleveEngine.IndexGetter
indexGetter = bleveEngine.NewIndexGetterPersistent(cfg.Engine.Bleve.Datapath, bleveMapping)
if cfg.Engine.Bleve.Scale {
indexGetter = bleveEngine.NewIndexGetterPersistentScale(cfg.Engine.Bleve.Datapath, bleveMapping)
}

eng := NewBleveEngine(indexGetter, bleve.DefaultCreator)
return eng, eng, nil

default:
return nil, nil, fmt.Errorf("unknown search engine: %s", cfg.Engine.Type)
}
}

// Resource is the entity that is stored in the index.
Expand Down
46 changes: 46 additions & 0 deletions services/search/pkg/engine/mocks/engine.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions services/search/pkg/search/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,13 +491,6 @@ func (s *Service) IndexSpace(spaceID *provider.StorageSpaceId) error {

logDocCount(s.engine, s.logger)

if opt, ok := s.engine.(engine.Optimizer); ok {
s.logger.Info().Msg("optimizing search index after space walk")
if err := opt.Optimize(ownerCtx); err != nil {
s.logger.Warn().Err(err).Msg("index optimization failed")
}
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions services/search/pkg/search/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ var _ = Describe("Searchprovider", func() {
Status: status.NewOK(ctx),
}, nil)
indexClient.On("DocCount").Return(uint64(1), nil)
indexClient.On("Optimize", mock.Anything).Return(nil)
})

Describe("New", func() {
Expand Down
31 changes: 6 additions & 25 deletions services/search/pkg/service/grpc/v0/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ import (
"github.com/owncloud/ocis/v2/services/search/pkg/config"
"github.com/owncloud/ocis/v2/services/search/pkg/content"
"github.com/owncloud/ocis/v2/services/search/pkg/engine"
bleveEngine "github.com/owncloud/ocis/v2/services/search/pkg/engine/bleve"
"github.com/owncloud/ocis/v2/services/search/pkg/query/bleve"
"github.com/owncloud/ocis/v2/services/search/pkg/search"
)

Expand All @@ -43,29 +41,12 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, func(), error)
cfg := options.Config

// initialize search engine
var eng engine.Engine
switch cfg.Engine.Type {
case "bleve":
bleveMapping, err := engine.BuildBleveMapping()
if err != nil {
return nil, teardown, err
}

var indexGetter bleveEngine.IndexGetter
indexGetter = bleveEngine.NewIndexGetterPersistent(cfg.Engine.Bleve.Datapath, bleveMapping)
if cfg.Engine.Bleve.Scale {
indexGetter = bleveEngine.NewIndexGetterPersistentScale(cfg.Engine.Bleve.Datapath, bleveMapping)
}

bleveEngine := engine.NewBleveEngine(indexGetter, bleve.DefaultCreator)

teardown = func() {
_ = bleveEngine.Close()
}
eng = bleveEngine

default:
return nil, teardown, fmt.Errorf("unknown search engine: %s", cfg.Engine.Type)
eng, engCloser, err := engine.NewEngineFromConfig(cfg)
if err != nil {
return nil, teardown, err
}
teardown = func() {
_ = engCloser.Close()
}

// initialize gateway
Expand Down