diff --git a/kernel/model/history.go b/kernel/model/history.go index 29015c95a..cc831129b 100644 --- a/kernel/model/history.go +++ b/kernel/model/history.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "time" @@ -711,3 +712,40 @@ func indexHistoryDir(name string, luteEngine *lute.Lute) (err error) { } return } + +func fullTextSearchHistory(query string, page int) (ret []*History, matchedBlockCount, matchedRootCount int) { + query = gulu.Str.RemoveInvisible(query) + query = stringQuery(query) + + table := "histories_fts_case_insensitive" + projections := "type, op, title, content, path" + stmt := "SELECT " + projections + " FROM " + table + " WHERE " + table + " MATCH '{title content}:(" + query + ")'" + stmt += " ORDER BY created DESC LIMIT " + strconv.Itoa(page) + sqlHistories := sql.SelectHistoriesRawStmt(stmt) + if 1 > len(sqlHistories) { + ret = []*History{} + return + } + + var items []*HistoryItem + var tmpTime int64 + for _, sqlHistory := range sqlHistories { + unixSec, _ := strconv.ParseInt(sqlHistory.Created, 10, 64) + if 0 == tmpTime { + tmpTime = unixSec + } + if tmpTime == unixSec { + items = append(items, &HistoryItem{ + Title: sqlHistory.Title, + Path: filepath.Join(util.HistoryDir, sqlHistory.Path), + }) + } else { + ret = append(ret, &History{ + HCreated: time.Unix(unixSec, 0).Format("2006-01-02 15:04:05"), + Items: items, + }) + items = []*HistoryItem{} + } + } + return +} diff --git a/kernel/sql/history.go b/kernel/sql/history.go index a8388d2a6..7a33bba5c 100644 --- a/kernel/sql/history.go +++ b/kernel/sql/history.go @@ -20,6 +20,8 @@ import ( "database/sql" "fmt" "strings" + + "github.com/siyuan-note/logging" ) type History struct { @@ -31,6 +33,31 @@ type History struct { Path string } +func SelectHistoriesRawStmt(stmt string) (ret []*History) { + rows, err := historyDB.Query(stmt) + if nil != err { + logging.LogWarnf("sql query [%s] failed: %s", stmt, err) + return + } + defer rows.Close() + for rows.Next() { + if history := scanHistoryRows(rows); nil != history { + ret = append(ret, history) + } + } + return +} + +func scanHistoryRows(rows *sql.Rows) (ret *History) { + var history History + if err := rows.Scan(&history.Type, &history.Op, &history.Title, &history.Content, &history.Created, &history.Path); nil != err { + logging.LogErrorf("query scan field failed: %s\n%s", err, logging.ShortStack()) + return + } + ret = &history + return +} + func DeleteHistoriesByPathPrefix(tx *sql.Tx, pathPrefix string) (err error) { stmt := "DELETE FROM histories_fts_case_insensitive WHERE path LIKE ?" if err = execStmtTx(tx, stmt, pathPrefix+"%"); nil != err {