github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/store/sqlstore/file_info_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"database/sql"
     8  	"fmt"
     9  	"regexp"
    10  	"strconv"
    11  	"strings"
    12  
    13  	sq "github.com/Masterminds/squirrel"
    14  	"github.com/pkg/errors"
    15  
    16  	"github.com/masterhung0112/hk_server/v5/einterfaces"
    17  	"github.com/masterhung0112/hk_server/v5/model"
    18  	"github.com/masterhung0112/hk_server/v5/shared/mlog"
    19  	"github.com/masterhung0112/hk_server/v5/store"
    20  )
    21  
    22  type SqlFileInfoStore struct {
    23  	*SqlStore
    24  	metrics     einterfaces.MetricsInterface
    25  	queryFields []string
    26  }
    27  
    28  func (fs SqlFileInfoStore) ClearCaches() {
    29  }
    30  
    31  func newSqlFileInfoStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface) store.FileInfoStore {
    32  	s := &SqlFileInfoStore{
    33  		SqlStore: sqlStore,
    34  		metrics:  metrics,
    35  	}
    36  
    37  	s.queryFields = []string{
    38  		"FileInfo.Id",
    39  		"FileInfo.CreatorId",
    40  		"FileInfo.PostId",
    41  		"FileInfo.CreateAt",
    42  		"FileInfo.UpdateAt",
    43  		"FileInfo.DeleteAt",
    44  		"FileInfo.Path",
    45  		"FileInfo.ThumbnailPath",
    46  		"FileInfo.PreviewPath",
    47  		"FileInfo.Name",
    48  		"FileInfo.Extension",
    49  		"FileInfo.Size",
    50  		"FileInfo.MimeType",
    51  		"FileInfo.Width",
    52  		"FileInfo.Height",
    53  		"FileInfo.HasPreviewImage",
    54  		"FileInfo.MiniPreview",
    55  		"Coalesce(FileInfo.Content, '') AS Content",
    56  		"Coalesce(FileInfo.RemoteId, '') AS RemoteId",
    57  	}
    58  
    59  	for _, db := range sqlStore.GetAllConns() {
    60  		table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id")
    61  		table.ColMap("Id").SetMaxSize(26)
    62  		table.ColMap("CreatorId").SetMaxSize(26)
    63  		table.ColMap("PostId").SetMaxSize(26)
    64  		table.ColMap("Path").SetMaxSize(512)
    65  		table.ColMap("ThumbnailPath").SetMaxSize(512)
    66  		table.ColMap("PreviewPath").SetMaxSize(512)
    67  		table.ColMap("Name").SetMaxSize(256)
    68  		table.ColMap("Content").SetMaxSize(0)
    69  		table.ColMap("Extension").SetMaxSize(64)
    70  		table.ColMap("MimeType").SetMaxSize(256)
    71  		table.ColMap("RemoteId").SetMaxSize(26)
    72  	}
    73  
    74  	return s
    75  }
    76  
    77  func (fs SqlFileInfoStore) createIndexesIfNotExists() {
    78  	fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt")
    79  	fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt")
    80  	fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt")
    81  	fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId")
    82  	fs.CreateIndexIfNotExists("idx_fileinfo_extension_at", "FileInfo", "Extension")
    83  	fs.CreateFullTextIndexIfNotExists("idx_fileinfo_name_txt", "FileInfo", "Name")
    84  	if fs.DriverName() == model.DATABASE_DRIVER_POSTGRES {
    85  		fs.CreateFullTextFuncIndexIfNotExists("idx_fileinfo_name_splitted", "FileInfo", "Translate(Name, '.,-', '   ')")
    86  	}
    87  	fs.CreateFullTextIndexIfNotExists("idx_fileinfo_content_txt", "FileInfo", "Content")
    88  }
    89  
    90  func (fs SqlFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, error) {
    91  	info.PreSave()
    92  	if err := info.IsValid(); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	if err := fs.GetMaster().Insert(info); err != nil {
    97  		return nil, errors.Wrap(err, "failed to save FileInfo")
    98  	}
    99  	return info, nil
   100  }
   101  
   102  func (fs SqlFileInfoStore) GetByIds(ids []string) ([]*model.FileInfo, error) {
   103  	query := fs.getQueryBuilder().
   104  		Select(append(fs.queryFields, "COALESCE(P.ChannelId, '') as ChannelId")...).
   105  		From("FileInfo").
   106  		LeftJoin("Posts as P ON FileInfo.PostId=P.Id").
   107  		Where(sq.Eq{"FileInfo.Id": ids}).
   108  		Where(sq.Eq{"FileInfo.DeleteAt": 0}).
   109  		OrderBy("FileInfo.CreateAt DESC")
   110  
   111  	queryString, args, err := query.ToSql()
   112  	if err != nil {
   113  		return nil, errors.Wrap(err, "file_info_tosql")
   114  	}
   115  
   116  	var infos []*model.FileInfo
   117  	if _, err := fs.GetReplica().Select(&infos, queryString, args...); err != nil {
   118  		return nil, errors.Wrap(err, "failed to find FileInfos")
   119  	}
   120  	return infos, nil
   121  }
   122  
   123  func (fs SqlFileInfoStore) Upsert(info *model.FileInfo) (*model.FileInfo, error) {
   124  	info.PreSave()
   125  	if err := info.IsValid(); err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	n, err := fs.GetMaster().Update(info)
   130  	if err != nil {
   131  		return nil, errors.Wrap(err, "failed to update FileInfo")
   132  	}
   133  	if n == 0 {
   134  		if err = fs.GetMaster().Insert(info); err != nil {
   135  			return nil, errors.Wrap(err, "failed to save FileInfo")
   136  		}
   137  	}
   138  	return info, nil
   139  }
   140  
   141  func (fs SqlFileInfoStore) get(id string, fromMaster bool) (*model.FileInfo, error) {
   142  	info := &model.FileInfo{}
   143  
   144  	query := fs.getQueryBuilder().
   145  		Select(fs.queryFields...).
   146  		From("FileInfo").
   147  		Where(sq.Eq{"Id": id}).
   148  		Where(sq.Eq{"DeleteAt": 0})
   149  
   150  	queryString, args, err := query.ToSql()
   151  	if err != nil {
   152  		return nil, errors.Wrap(err, "file_info_tosql")
   153  	}
   154  
   155  	db := fs.GetReplica()
   156  	if fromMaster {
   157  		db = fs.GetMaster()
   158  	}
   159  
   160  	if err := db.SelectOne(info, queryString, args...); err != nil {
   161  		if err == sql.ErrNoRows {
   162  			return nil, store.NewErrNotFound("FileInfo", id)
   163  		}
   164  		return nil, errors.Wrapf(err, "failed to get FileInfo with id=%s", id)
   165  	}
   166  	return info, nil
   167  }
   168  
   169  func (fs SqlFileInfoStore) Get(id string) (*model.FileInfo, error) {
   170  	return fs.get(id, false)
   171  }
   172  
   173  func (fs SqlFileInfoStore) GetFromMaster(id string) (*model.FileInfo, error) {
   174  	return fs.get(id, true)
   175  }
   176  
   177  func (fs SqlFileInfoStore) GetWithOptions(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, error) {
   178  	if perPage < 0 {
   179  		return nil, store.NewErrLimitExceeded("perPage", perPage, "value used in pagination while getting FileInfos")
   180  	} else if page < 0 {
   181  		return nil, store.NewErrLimitExceeded("page", page, "value used in pagination while getting FileInfos")
   182  	}
   183  	if perPage == 0 {
   184  		return nil, nil
   185  	}
   186  
   187  	if opt == nil {
   188  		opt = &model.GetFileInfosOptions{}
   189  	}
   190  
   191  	query := fs.getQueryBuilder().
   192  		Select(fs.queryFields...).
   193  		From("FileInfo")
   194  
   195  	if len(opt.ChannelIds) > 0 {
   196  		query = query.Join("Posts ON FileInfo.PostId = Posts.Id").
   197  			Where(sq.Eq{"Posts.ChannelId": opt.ChannelIds})
   198  	}
   199  
   200  	if len(opt.UserIds) > 0 {
   201  		query = query.Where(sq.Eq{"FileInfo.CreatorId": opt.UserIds})
   202  	}
   203  
   204  	if opt.Since > 0 {
   205  		query = query.Where(sq.GtOrEq{"FileInfo.CreateAt": opt.Since})
   206  	}
   207  
   208  	if !opt.IncludeDeleted {
   209  		query = query.Where("FileInfo.DeleteAt = 0")
   210  	}
   211  
   212  	if opt.SortBy == "" {
   213  		opt.SortBy = model.FILEINFO_SORT_BY_CREATED
   214  	}
   215  	sortDirection := "ASC"
   216  	if opt.SortDescending {
   217  		sortDirection = "DESC"
   218  	}
   219  
   220  	switch opt.SortBy {
   221  	case model.FILEINFO_SORT_BY_CREATED:
   222  		query = query.OrderBy("FileInfo.CreateAt " + sortDirection)
   223  	case model.FILEINFO_SORT_BY_SIZE:
   224  		query = query.OrderBy("FileInfo.Size " + sortDirection)
   225  	default:
   226  		return nil, store.NewErrInvalidInput("FileInfo", "<sortOption>", opt.SortBy)
   227  	}
   228  
   229  	query = query.OrderBy("FileInfo.Id ASC") // secondary sort for sort stability
   230  
   231  	query = query.Limit(uint64(perPage)).Offset(uint64(perPage * page))
   232  
   233  	queryString, args, err := query.ToSql()
   234  	if err != nil {
   235  		return nil, errors.Wrap(err, "file_info_tosql")
   236  	}
   237  	var infos []*model.FileInfo
   238  	if _, err := fs.GetReplica().Select(&infos, queryString, args...); err != nil {
   239  		return nil, errors.Wrap(err, "failed to find FileInfos")
   240  	}
   241  	return infos, nil
   242  }
   243  
   244  func (fs SqlFileInfoStore) GetByPath(path string) (*model.FileInfo, error) {
   245  	info := &model.FileInfo{}
   246  
   247  	query := fs.getQueryBuilder().
   248  		Select(fs.queryFields...).
   249  		From("FileInfo").
   250  		Where(sq.Eq{"Path": path}).
   251  		Where(sq.Eq{"DeleteAt": 0}).
   252  		Limit(1)
   253  
   254  	queryString, args, err := query.ToSql()
   255  	if err != nil {
   256  		return nil, errors.Wrap(err, "file_info_tosql")
   257  	}
   258  
   259  	if err := fs.GetReplica().SelectOne(info, queryString, args...); err != nil {
   260  		if err == sql.ErrNoRows {
   261  			return nil, store.NewErrNotFound("FileInfo", fmt.Sprintf("path=%s", path))
   262  		}
   263  
   264  		return nil, errors.Wrapf(err, "failed to get FileInfo with path=%s", path)
   265  	}
   266  	return info, nil
   267  }
   268  
   269  func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string, deleted bool) {
   270  }
   271  
   272  func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster, includeDeleted, allowFromCache bool) ([]*model.FileInfo, error) {
   273  	var infos []*model.FileInfo
   274  
   275  	dbmap := fs.GetReplica()
   276  
   277  	if readFromMaster {
   278  		dbmap = fs.GetMaster()
   279  	}
   280  
   281  	query := fs.getQueryBuilder().
   282  		Select(fs.queryFields...).
   283  		From("FileInfo").
   284  		Where(sq.Eq{"PostId": postId}).
   285  		OrderBy("CreateAt")
   286  
   287  	if !includeDeleted {
   288  		query = query.Where("DeleteAt = 0")
   289  	}
   290  
   291  	queryString, args, err := query.ToSql()
   292  	if err != nil {
   293  		return nil, errors.Wrap(err, "file_info_tosql")
   294  	}
   295  
   296  	if _, err := dbmap.Select(&infos, queryString, args...); err != nil {
   297  		return nil, errors.Wrapf(err, "failed to find FileInfos with postId=%s", postId)
   298  	}
   299  	return infos, nil
   300  }
   301  
   302  func (fs SqlFileInfoStore) GetForUser(userId string) ([]*model.FileInfo, error) {
   303  	var infos []*model.FileInfo
   304  
   305  	dbmap := fs.GetReplica()
   306  
   307  	query := fs.getQueryBuilder().
   308  		Select(fs.queryFields...).
   309  		From("FileInfo").
   310  		Where(sq.Eq{"CreatorId": userId}).
   311  		Where(sq.Eq{"DeleteAt": 0}).
   312  		OrderBy("CreateAt")
   313  
   314  	queryString, args, err := query.ToSql()
   315  	if err != nil {
   316  		return nil, errors.Wrap(err, "file_info_tosql")
   317  	}
   318  
   319  	if _, err := dbmap.Select(&infos, queryString, args...); err != nil {
   320  		return nil, errors.Wrapf(err, "failed to find FileInfos with creatorId=%s", userId)
   321  	}
   322  	return infos, nil
   323  }
   324  
   325  func (fs SqlFileInfoStore) AttachToPost(fileId, postId, creatorId string) error {
   326  	sqlResult, err := fs.GetMaster().Exec(`
   327  		UPDATE
   328  			FileInfo
   329  		SET
   330  			PostId = :PostId
   331  		WHERE
   332  			Id = :Id
   333  			AND PostId = ''
   334  			AND (CreatorId = :CreatorId OR CreatorId = 'nouser')
   335  	`, map[string]interface{}{
   336  		"PostId":    postId,
   337  		"Id":        fileId,
   338  		"CreatorId": creatorId,
   339  	})
   340  	if err != nil {
   341  		return errors.Wrapf(err, "failed to update FileInfo with id=%s and postId=%s", fileId, postId)
   342  	}
   343  
   344  	count, err := sqlResult.RowsAffected()
   345  	if err != nil {
   346  		// RowsAffected should never fail with the MySQL or Postgres drivers
   347  		return errors.Wrap(err, "unable to retrieve rows affected")
   348  	} else if count == 0 {
   349  		// Could not attach the file to the post
   350  		return store.NewErrInvalidInput("FileInfo", "<id, postId, creatorId>", fmt.Sprintf("<%s, %s, %s>", fileId, postId, creatorId))
   351  	}
   352  	return nil
   353  }
   354  
   355  func (fs SqlFileInfoStore) SetContent(fileId, content string) error {
   356  	query := fs.getQueryBuilder().
   357  		Update("FileInfo").
   358  		Set("Content", content).
   359  		Where(sq.Eq{"Id": fileId})
   360  
   361  	queryString, args, err := query.ToSql()
   362  	if err != nil {
   363  		return errors.Wrap(err, "file_info_tosql")
   364  	}
   365  
   366  	_, err = fs.GetMaster().Exec(queryString, args...)
   367  	if err != nil {
   368  		return errors.Wrapf(err, "failed to update FileInfo content with id=%s", fileId)
   369  	}
   370  
   371  	return nil
   372  }
   373  
   374  func (fs SqlFileInfoStore) DeleteForPost(postId string) (string, error) {
   375  	if _, err := fs.GetMaster().Exec(
   376  		`UPDATE
   377  				FileInfo
   378  			SET
   379  				DeleteAt = :DeleteAt
   380  			WHERE
   381  				PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil {
   382  		return "", errors.Wrapf(err, "failed to update FileInfo with postId=%s", postId)
   383  	}
   384  	return postId, nil
   385  }
   386  
   387  func (fs SqlFileInfoStore) PermanentDelete(fileId string) error {
   388  	if _, err := fs.GetMaster().Exec(
   389  		`DELETE FROM
   390  				FileInfo
   391  			WHERE
   392  				Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil {
   393  		return errors.Wrapf(err, "failed to delete FileInfo with id=%s", fileId)
   394  	}
   395  	return nil
   396  }
   397  
   398  func (fs SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
   399  	var query string
   400  	if fs.DriverName() == "postgres" {
   401  		query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))"
   402  	} else {
   403  		query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit"
   404  	}
   405  
   406  	sqlResult, err := fs.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit})
   407  	if err != nil {
   408  		return 0, errors.Wrap(err, "failed to delete FileInfos in batch")
   409  	}
   410  
   411  	rowsAffected, err := sqlResult.RowsAffected()
   412  	if err != nil {
   413  		return 0, errors.Wrapf(err, "unable to retrieve rows affected")
   414  	}
   415  
   416  	return rowsAffected, nil
   417  }
   418  
   419  func (fs SqlFileInfoStore) PermanentDeleteByUser(userId string) (int64, error) {
   420  	query := "DELETE from FileInfo WHERE CreatorId = :CreatorId"
   421  
   422  	sqlResult, err := fs.GetMaster().Exec(query, map[string]interface{}{"CreatorId": userId})
   423  	if err != nil {
   424  		return 0, errors.Wrapf(err, "failed to delete FileInfo with creatorId=%s", userId)
   425  	}
   426  
   427  	rowsAffected, err := sqlResult.RowsAffected()
   428  	if err != nil {
   429  		return 0, errors.Wrapf(err, "unable to retrieve rows affected")
   430  	}
   431  
   432  	return rowsAffected, nil
   433  }
   434  
   435  func (fs SqlFileInfoStore) Search(paramsList []*model.SearchParams, userId, teamId string, page, perPage int) (*model.FileInfoList, error) {
   436  	// Since we don't support paging for DB search, we just return nothing for later pages
   437  	if page > 0 {
   438  		return model.NewFileInfoList(), nil
   439  	}
   440  	if err := model.IsSearchParamsListValid(paramsList); err != nil {
   441  		return nil, err
   442  	}
   443  	query := fs.getQueryBuilder().
   444  		Select(append(fs.queryFields, "Coalesce(P.ChannelId, '') AS ChannelId")...).
   445  		From("FileInfo").
   446  		LeftJoin("Posts as P ON FileInfo.PostId=P.Id").
   447  		LeftJoin("Channels as C ON C.Id=P.ChannelId").
   448  		LeftJoin("ChannelMembers as CM ON C.Id=CM.ChannelId").
   449  		Where(sq.Or{sq.Eq{"C.TeamId": teamId}, sq.Eq{"C.TeamId": ""}}).
   450  		Where(sq.Eq{"FileInfo.DeleteAt": 0}).
   451  		OrderBy("FileInfo.CreateAt DESC").
   452  		Limit(100)
   453  
   454  	for _, params := range paramsList {
   455  		params.Terms = removeNonAlphaNumericUnquotedTerms(params.Terms, " ")
   456  
   457  		if !params.IncludeDeletedChannels {
   458  			query = query.Where(sq.Eq{"C.DeleteAt": 0})
   459  		}
   460  
   461  		if !params.SearchWithoutUserId {
   462  			query = query.Where(sq.Eq{"CM.UserId": userId})
   463  		}
   464  
   465  		if len(params.InChannels) != 0 {
   466  			query = query.Where(sq.Eq{"C.Id": params.InChannels})
   467  		}
   468  
   469  		if len(params.Extensions) != 0 {
   470  			query = query.Where(sq.Eq{"FileInfo.Extension": params.Extensions})
   471  		}
   472  
   473  		if len(params.ExcludedExtensions) != 0 {
   474  			query = query.Where(sq.NotEq{"FileInfo.Extension": params.ExcludedExtensions})
   475  		}
   476  
   477  		if len(params.ExcludedChannels) != 0 {
   478  			query = query.Where(sq.NotEq{"C.Id": params.ExcludedChannels})
   479  		}
   480  
   481  		if len(params.FromUsers) != 0 {
   482  			query = query.Where(sq.Eq{"FileInfo.CreatorId": params.FromUsers})
   483  		}
   484  
   485  		if len(params.ExcludedUsers) != 0 {
   486  			query = query.Where(sq.NotEq{"FileInfo.CreatorId": params.ExcludedUsers})
   487  		}
   488  
   489  		// handle after: before: on: filters
   490  		if params.OnDate != "" {
   491  			onDateStart, onDateEnd := params.GetOnDateMillis()
   492  			query = query.Where(sq.Expr("FileInfo.CreateAt BETWEEN ? AND ?", strconv.FormatInt(onDateStart, 10), strconv.FormatInt(onDateEnd, 10)))
   493  		} else {
   494  			if params.ExcludedDate != "" {
   495  				excludedDateStart, excludedDateEnd := params.GetExcludedDateMillis()
   496  				query = query.Where(sq.Expr("FileInfo.CreateAt NOT BETWEEN ? AND ?", strconv.FormatInt(excludedDateStart, 10), strconv.FormatInt(excludedDateEnd, 10)))
   497  			}
   498  
   499  			if params.AfterDate != "" {
   500  				afterDate := params.GetAfterDateMillis()
   501  				query = query.Where(sq.GtOrEq{"FileInfo.CreateAt": strconv.FormatInt(afterDate, 10)})
   502  			}
   503  
   504  			if params.BeforeDate != "" {
   505  				beforeDate := params.GetBeforeDateMillis()
   506  				query = query.Where(sq.LtOrEq{"FileInfo.CreateAt": strconv.FormatInt(beforeDate, 10)})
   507  			}
   508  
   509  			if params.ExcludedAfterDate != "" {
   510  				afterDate := params.GetExcludedAfterDateMillis()
   511  				query = query.Where(sq.Lt{"FileInfo.CreateAt": strconv.FormatInt(afterDate, 10)})
   512  			}
   513  
   514  			if params.ExcludedBeforeDate != "" {
   515  				beforeDate := params.GetExcludedBeforeDateMillis()
   516  				query = query.Where(sq.Gt{"FileInfo.CreateAt": strconv.FormatInt(beforeDate, 10)})
   517  			}
   518  		}
   519  
   520  		terms := params.Terms
   521  		excludedTerms := params.ExcludedTerms
   522  
   523  		// these chars have special meaning and can be treated as spaces
   524  		for _, c := range specialSearchChar {
   525  			terms = strings.Replace(terms, c, " ", -1)
   526  			excludedTerms = strings.Replace(excludedTerms, c, " ", -1)
   527  		}
   528  
   529  		if terms == "" && excludedTerms == "" {
   530  			// we've already confirmed that we have a channel or user to search for
   531  		} else if fs.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   532  			// Parse text for wildcards
   533  			if wildcard, err := regexp.Compile(`\*($| )`); err == nil {
   534  				terms = wildcard.ReplaceAllLiteralString(terms, ":* ")
   535  				excludedTerms = wildcard.ReplaceAllLiteralString(excludedTerms, ":* ")
   536  			}
   537  
   538  			excludeClause := ""
   539  			if excludedTerms != "" {
   540  				excludeClause = " & !(" + strings.Join(strings.Fields(excludedTerms), " | ") + ")"
   541  			}
   542  
   543  			queryTerms := ""
   544  			if params.OrTerms {
   545  				queryTerms = "(" + strings.Join(strings.Fields(terms), " | ") + ")" + excludeClause
   546  			} else {
   547  				queryTerms = "(" + strings.Join(strings.Fields(terms), " & ") + ")" + excludeClause
   548  			}
   549  
   550  			query = query.Where(sq.Or{
   551  				sq.Expr("to_tsvector('english', FileInfo.Name) @@  to_tsquery('english', ?)", queryTerms),
   552  				sq.Expr("to_tsvector('english', Translate(FileInfo.Name, '.,-', '   ')) @@  to_tsquery('english', ?)", queryTerms),
   553  				sq.Expr("to_tsvector('english', FileInfo.Content) @@  to_tsquery('english', ?)", queryTerms),
   554  			})
   555  		} else if fs.DriverName() == model.DATABASE_DRIVER_MYSQL {
   556  			var err error
   557  			terms, err = removeMysqlStopWordsFromTerms(terms)
   558  			if err != nil {
   559  				return nil, errors.Wrap(err, "failed to remove Mysql stop-words from terms")
   560  			}
   561  
   562  			if terms == "" {
   563  				return model.NewFileInfoList(), nil
   564  			}
   565  
   566  			excludeClause := ""
   567  			if excludedTerms != "" {
   568  				excludeClause = " -(" + excludedTerms + ")"
   569  			}
   570  
   571  			queryTerms := ""
   572  			if params.OrTerms {
   573  				queryTerms = terms + excludeClause
   574  			} else {
   575  				splitTerms := []string{}
   576  				for _, t := range strings.Fields(terms) {
   577  					splitTerms = append(splitTerms, "+"+t)
   578  				}
   579  				queryTerms = strings.Join(splitTerms, " ") + excludeClause
   580  			}
   581  			query = query.Where(sq.Or{
   582  				sq.Expr("MATCH (FileInfo.Name) AGAINST (? IN BOOLEAN MODE)", queryTerms),
   583  				sq.Expr("MATCH (FileInfo.Content) AGAINST (? IN BOOLEAN MODE)", queryTerms),
   584  			})
   585  		}
   586  	}
   587  
   588  	queryString, args, err := query.ToSql()
   589  	if err != nil {
   590  		return nil, errors.Wrap(err, "file_info_tosql")
   591  	}
   592  
   593  	list := model.NewFileInfoList()
   594  	fileInfos := []*model.FileInfo{}
   595  	_, err = fs.GetSearchReplica().Select(&fileInfos, queryString, args...)
   596  	if err != nil {
   597  		mlog.Warn("Query error searching files.", mlog.Err(err))
   598  		// Don't return the error to the caller as it is of no use to the user. Instead return an empty set of search results.
   599  	} else {
   600  		for _, f := range fileInfos {
   601  			list.AddFileInfo(f)
   602  			list.AddOrder(f.Id)
   603  		}
   604  	}
   605  	list.MakeNonNil()
   606  	return list, nil
   607  }
   608  
   609  func (fs SqlFileInfoStore) CountAll() (int64, error) {
   610  	query := fs.getQueryBuilder().
   611  		Select("COUNT(*)").
   612  		From("FileInfo").
   613  		Where("DeleteAt = 0")
   614  
   615  	queryString, args, err := query.ToSql()
   616  	if err != nil {
   617  		return int64(0), errors.Wrap(err, "count_tosql")
   618  	}
   619  
   620  	count, err := fs.GetReplica().SelectInt(queryString, args...)
   621  	if err != nil {
   622  		return int64(0), errors.Wrap(err, "failed to count Files")
   623  	}
   624  	return count, nil
   625  }
   626  
   627  func (fs SqlFileInfoStore) GetFilesBatchForIndexing(startTime, endTime int64, limit int) ([]*model.FileForIndexing, error) {
   628  	var files []*model.FileForIndexing
   629  	sql, args, _ := fs.getQueryBuilder().
   630  		Select(append(fs.queryFields, "Coalesce(p.ChannelId, '') AS ChannelId")...).
   631  		From("FileInfo").
   632  		LeftJoin("Posts AS p ON FileInfo.PostId = p.Id").
   633  		Where(sq.GtOrEq{"FileInfo.CreateAt": startTime}).
   634  		Where(sq.Lt{"FileInfo.CreateAt": endTime}).
   635  		OrderBy("FileInfo.CreateAt").
   636  		Limit(uint64(limit)).
   637  		ToSql()
   638  	_, err := fs.GetSearchReplica().Select(&files, sql, args...)
   639  	if err != nil {
   640  		return nil, errors.Wrap(err, "failed to find Files")
   641  	}
   642  
   643  	return files, nil
   644  }