github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/services/searchengine/bleveengine/bleve.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package bleveengine
     5  
     6  import (
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/blevesearch/bleve"
    15  	"github.com/blevesearch/bleve/analysis/analyzer/keyword"
    16  	"github.com/blevesearch/bleve/analysis/analyzer/standard"
    17  	"github.com/blevesearch/bleve/mapping"
    18  
    19  	"github.com/mattermost/mattermost-server/v5/jobs"
    20  	"github.com/mattermost/mattermost-server/v5/mlog"
    21  	"github.com/mattermost/mattermost-server/v5/model"
    22  )
    23  
    24  const (
    25  	EngineName   = "bleve"
    26  	PostIndex    = "posts"
    27  	FileIndex    = "files"
    28  	UserIndex    = "users"
    29  	ChannelIndex = "channels"
    30  )
    31  
    32  type BleveEngine struct {
    33  	PostIndex    bleve.Index
    34  	FileIndex    bleve.Index
    35  	UserIndex    bleve.Index
    36  	ChannelIndex bleve.Index
    37  	Mutex        sync.RWMutex
    38  	ready        int32
    39  	cfg          *model.Config
    40  	jobServer    *jobs.JobServer
    41  	indexSync    bool
    42  }
    43  
    44  var keywordMapping *mapping.FieldMapping
    45  var standardMapping *mapping.FieldMapping
    46  var dateMapping *mapping.FieldMapping
    47  
    48  func init() {
    49  	keywordMapping = bleve.NewTextFieldMapping()
    50  	keywordMapping.Analyzer = keyword.Name
    51  
    52  	standardMapping = bleve.NewTextFieldMapping()
    53  	standardMapping.Analyzer = standard.Name
    54  
    55  	dateMapping = bleve.NewNumericFieldMapping()
    56  }
    57  
    58  func getChannelIndexMapping() *mapping.IndexMappingImpl {
    59  	channelMapping := bleve.NewDocumentMapping()
    60  	channelMapping.AddFieldMappingsAt("Id", keywordMapping)
    61  	channelMapping.AddFieldMappingsAt("TeamId", keywordMapping)
    62  	channelMapping.AddFieldMappingsAt("NameSuggest", keywordMapping)
    63  
    64  	indexMapping := bleve.NewIndexMapping()
    65  	indexMapping.AddDocumentMapping("_default", channelMapping)
    66  
    67  	return indexMapping
    68  }
    69  
    70  func getPostIndexMapping() *mapping.IndexMappingImpl {
    71  	postMapping := bleve.NewDocumentMapping()
    72  	postMapping.AddFieldMappingsAt("Id", keywordMapping)
    73  	postMapping.AddFieldMappingsAt("TeamId", keywordMapping)
    74  	postMapping.AddFieldMappingsAt("ChannelId", keywordMapping)
    75  	postMapping.AddFieldMappingsAt("UserId", keywordMapping)
    76  	postMapping.AddFieldMappingsAt("CreateAt", dateMapping)
    77  	postMapping.AddFieldMappingsAt("Message", standardMapping)
    78  	postMapping.AddFieldMappingsAt("Type", keywordMapping)
    79  	postMapping.AddFieldMappingsAt("Hashtags", standardMapping)
    80  	postMapping.AddFieldMappingsAt("Attachments", standardMapping)
    81  
    82  	indexMapping := bleve.NewIndexMapping()
    83  	indexMapping.AddDocumentMapping("_default", postMapping)
    84  
    85  	return indexMapping
    86  }
    87  
    88  func getFileIndexMapping() *mapping.IndexMappingImpl {
    89  	fileMapping := bleve.NewDocumentMapping()
    90  	fileMapping.AddFieldMappingsAt("Id", keywordMapping)
    91  	fileMapping.AddFieldMappingsAt("CreatorId", keywordMapping)
    92  	fileMapping.AddFieldMappingsAt("ChannelId", keywordMapping)
    93  	fileMapping.AddFieldMappingsAt("CreateAt", dateMapping)
    94  	fileMapping.AddFieldMappingsAt("Name", standardMapping)
    95  	fileMapping.AddFieldMappingsAt("Content", standardMapping)
    96  	fileMapping.AddFieldMappingsAt("Extension", keywordMapping)
    97  	fileMapping.AddFieldMappingsAt("Content", standardMapping)
    98  
    99  	indexMapping := bleve.NewIndexMapping()
   100  	indexMapping.AddDocumentMapping("_default", fileMapping)
   101  
   102  	return indexMapping
   103  }
   104  
   105  func getUserIndexMapping() *mapping.IndexMappingImpl {
   106  	userMapping := bleve.NewDocumentMapping()
   107  	userMapping.AddFieldMappingsAt("Id", keywordMapping)
   108  	userMapping.AddFieldMappingsAt("SuggestionsWithFullname", keywordMapping)
   109  	userMapping.AddFieldMappingsAt("SuggestionsWithoutFullname", keywordMapping)
   110  	userMapping.AddFieldMappingsAt("TeamsIds", keywordMapping)
   111  	userMapping.AddFieldMappingsAt("ChannelsIds", keywordMapping)
   112  
   113  	indexMapping := bleve.NewIndexMapping()
   114  	indexMapping.AddDocumentMapping("_default", userMapping)
   115  
   116  	return indexMapping
   117  }
   118  
   119  func NewBleveEngine(cfg *model.Config, jobServer *jobs.JobServer) *BleveEngine {
   120  	return &BleveEngine{
   121  		cfg:       cfg,
   122  		jobServer: jobServer,
   123  	}
   124  }
   125  
   126  func (b *BleveEngine) getIndexDir(indexName string) string {
   127  	return filepath.Join(*b.cfg.BleveSettings.IndexDir, indexName+".bleve")
   128  }
   129  
   130  func (b *BleveEngine) createOrOpenIndex(indexName string, mapping *mapping.IndexMappingImpl) (bleve.Index, error) {
   131  	indexPath := b.getIndexDir(indexName)
   132  	if index, err := bleve.Open(indexPath); err == nil {
   133  		return index, nil
   134  	}
   135  
   136  	index, err := bleve.New(indexPath, mapping)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	return index, nil
   141  }
   142  
   143  func (b *BleveEngine) openIndexes() *model.AppError {
   144  	if atomic.LoadInt32(&b.ready) != 0 {
   145  		return model.NewAppError("Bleveengine.Start", "bleveengine.already_started.error", nil, "", http.StatusInternalServerError)
   146  	}
   147  
   148  	var err error
   149  	b.PostIndex, err = b.createOrOpenIndex(PostIndex, getPostIndexMapping())
   150  	if err != nil {
   151  		return model.NewAppError("Bleveengine.Start", "bleveengine.create_post_index.error", nil, err.Error(), http.StatusInternalServerError)
   152  	}
   153  
   154  	b.FileIndex, err = b.createOrOpenIndex(FileIndex, getFileIndexMapping())
   155  	if err != nil {
   156  		return model.NewAppError("Bleveengine.Start", "bleveengine.create_file_index.error", nil, err.Error(), http.StatusInternalServerError)
   157  	}
   158  
   159  	b.UserIndex, err = b.createOrOpenIndex(UserIndex, getUserIndexMapping())
   160  	if err != nil {
   161  		return model.NewAppError("Bleveengine.Start", "bleveengine.create_user_index.error", nil, err.Error(), http.StatusInternalServerError)
   162  	}
   163  
   164  	b.ChannelIndex, err = b.createOrOpenIndex(ChannelIndex, getChannelIndexMapping())
   165  	if err != nil {
   166  		return model.NewAppError("Bleveengine.Start", "bleveengine.create_channel_index.error", nil, err.Error(), http.StatusInternalServerError)
   167  	}
   168  
   169  	atomic.StoreInt32(&b.ready, 1)
   170  	return nil
   171  }
   172  
   173  func (b *BleveEngine) Start() *model.AppError {
   174  	if !*b.cfg.BleveSettings.EnableIndexing || *b.cfg.BleveSettings.IndexDir == "" {
   175  		return nil
   176  	}
   177  
   178  	b.Mutex.Lock()
   179  	defer b.Mutex.Unlock()
   180  
   181  	mlog.Info("EXPERIMENTAL: Starting Bleve")
   182  
   183  	return b.openIndexes()
   184  }
   185  
   186  func (b *BleveEngine) closeIndexes() *model.AppError {
   187  	if b.IsActive() {
   188  		if err := b.PostIndex.Close(); err != nil {
   189  			return model.NewAppError("Bleveengine.Stop", "bleveengine.stop_post_index.error", nil, err.Error(), http.StatusInternalServerError)
   190  		}
   191  
   192  		if err := b.FileIndex.Close(); err != nil {
   193  			return model.NewAppError("Bleveengine.Stop", "bleveengine.stop_file_index.error", nil, err.Error(), http.StatusInternalServerError)
   194  		}
   195  
   196  		if err := b.UserIndex.Close(); err != nil {
   197  			return model.NewAppError("Bleveengine.Stop", "bleveengine.stop_user_index.error", nil, err.Error(), http.StatusInternalServerError)
   198  		}
   199  
   200  		if err := b.ChannelIndex.Close(); err != nil {
   201  			return model.NewAppError("Bleveengine.Stop", "bleveengine.stop_channel_index.error", nil, err.Error(), http.StatusInternalServerError)
   202  		}
   203  	}
   204  
   205  	atomic.StoreInt32(&b.ready, 0)
   206  	return nil
   207  }
   208  
   209  func (b *BleveEngine) Stop() *model.AppError {
   210  	b.Mutex.Lock()
   211  	defer b.Mutex.Unlock()
   212  
   213  	mlog.Info("Stopping Bleve")
   214  
   215  	return b.closeIndexes()
   216  }
   217  
   218  func (b *BleveEngine) IsActive() bool {
   219  	return atomic.LoadInt32(&b.ready) == 1
   220  }
   221  
   222  func (b *BleveEngine) IsIndexingSync() bool {
   223  	return b.indexSync
   224  }
   225  
   226  func (b *BleveEngine) RefreshIndexes() *model.AppError {
   227  	return nil
   228  }
   229  
   230  func (b *BleveEngine) GetVersion() int {
   231  	return 0
   232  }
   233  
   234  func (b *BleveEngine) GetFullVersion() string {
   235  	return "0"
   236  }
   237  
   238  func (b *BleveEngine) GetPlugins() []string {
   239  	return []string{}
   240  }
   241  
   242  func (b *BleveEngine) GetName() string {
   243  	return EngineName
   244  }
   245  
   246  func (b *BleveEngine) TestConfig(cfg *model.Config) *model.AppError {
   247  	return nil
   248  }
   249  
   250  func (b *BleveEngine) deleteIndexes() *model.AppError {
   251  	if err := os.RemoveAll(b.getIndexDir(PostIndex)); err != nil {
   252  		return model.NewAppError("Bleveengine.PurgeIndexes", "bleveengine.purge_post_index.error", nil, err.Error(), http.StatusInternalServerError)
   253  	}
   254  	if err := os.RemoveAll(b.getIndexDir(UserIndex)); err != nil {
   255  		return model.NewAppError("Bleveengine.PurgeIndexes", "bleveengine.purge_user_index.error", nil, err.Error(), http.StatusInternalServerError)
   256  	}
   257  	if err := os.RemoveAll(b.getIndexDir(ChannelIndex)); err != nil {
   258  		return model.NewAppError("Bleveengine.PurgeIndexes", "bleveengine.purge_channel_index.error", nil, err.Error(), http.StatusInternalServerError)
   259  	}
   260  	return nil
   261  }
   262  
   263  func (b *BleveEngine) PurgeIndexes() *model.AppError {
   264  	if *b.cfg.BleveSettings.IndexDir == "" {
   265  		return nil
   266  	}
   267  
   268  	b.Mutex.Lock()
   269  	defer b.Mutex.Unlock()
   270  
   271  	mlog.Info("PurgeIndexes Bleve")
   272  	if err := b.closeIndexes(); err != nil {
   273  		return err
   274  	}
   275  
   276  	if err := b.deleteIndexes(); err != nil {
   277  		return err
   278  	}
   279  
   280  	return b.openIndexes()
   281  }
   282  
   283  func (b *BleveEngine) DataRetentionDeleteIndexes(cutoff time.Time) *model.AppError {
   284  	return nil
   285  }
   286  
   287  func (b *BleveEngine) IsAutocompletionEnabled() bool {
   288  	return *b.cfg.BleveSettings.EnableAutocomplete
   289  }
   290  
   291  func (b *BleveEngine) IsIndexingEnabled() bool {
   292  	return *b.cfg.BleveSettings.EnableIndexing
   293  }
   294  
   295  func (b *BleveEngine) IsSearchEnabled() bool {
   296  	return *b.cfg.BleveSettings.EnableSearching
   297  }
   298  
   299  func (b *BleveEngine) UpdateConfig(cfg *model.Config) {
   300  	b.Mutex.Lock()
   301  	defer b.Mutex.Unlock()
   302  
   303  	mlog.Info("UpdateConf Bleve")
   304  
   305  	if *cfg.BleveSettings.EnableIndexing != *b.cfg.BleveSettings.EnableIndexing || *cfg.BleveSettings.IndexDir != *b.cfg.BleveSettings.IndexDir {
   306  		if err := b.closeIndexes(); err != nil {
   307  			mlog.Error("Error closing Bleve indexes to update the config", mlog.Err(err))
   308  			return
   309  		}
   310  		b.cfg = cfg
   311  		if err := b.openIndexes(); err != nil {
   312  			mlog.Error("Error opening Bleve indexes after updating the config", mlog.Err(err))
   313  		}
   314  		return
   315  	}
   316  	b.cfg = cfg
   317  }