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