siyuan/kernel/api/search.go

485 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package api
import (
"net/http"
"strings"
"github.com/88250/gulu"
"github.com/gin-gonic/gin"
"github.com/siyuan-note/siyuan/kernel/model"
"github.com/siyuan-note/siyuan/kernel/util"
)
func listInvalidBlockRefs(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
page := 1
if nil != arg["page"] {
page = int(arg["page"].(float64))
}
if 0 >= page {
page = 1
}
pageSize := 32
if nil != arg["pageSize"] {
pageSize = int(arg["pageSize"].(float64))
}
if 0 >= pageSize {
pageSize = 32
}
blocks, matchedBlockCount, matchedRootCount, pageCount := model.ListInvalidBlockRefs(page, pageSize)
ret.Data = map[string]interface{}{
"blocks": blocks,
"matchedBlockCount": matchedBlockCount,
"matchedRootCount": matchedRootCount,
"pageCount": pageCount,
}
}
func getAssetContent(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
id := arg["id"].(string)
query := arg["query"].(string)
queryMethod := int(arg["queryMethod"].(float64))
ret.Data = map[string]interface{}{
"assetContent": model.GetAssetContent(id, query, queryMethod),
}
return
}
func fullTextSearchAssetContent(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
if !model.IsPaidUser() {
ret.Code = 1
return
}
page, pageSize, query, types, method, orderBy := parseSearchAssetContentArgs(arg)
assetContents, matchedAssetCount, pageCount := model.FullTextSearchAssetContent(query, types, method, orderBy, page, pageSize)
ret.Data = map[string]interface{}{
"assetContents": assetContents,
"matchedAssetCount": matchedAssetCount,
"pageCount": pageCount,
}
}
func findReplace(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
_, _, _, paths, boxes, types, method, orderBy, groupBy := parseSearchBlockArgs(arg)
k := arg["k"].(string)
r := arg["r"].(string)
idsArg := arg["ids"].([]interface{})
var ids []string
for _, id := range idsArg {
ids = append(ids, id.(string))
}
replaceTypes := map[string]bool{}
// text, imgText, imgTitle, imgSrc, aText, aTitle, aHref, code, em, strong, inlineMath, inlineMemo, kbd, mark, s, sub, sup, tag, u
// docTitle, codeBlock, mathBlock, htmlBlock
if nil != arg["replaceTypes"] {
replaceTypesArg := arg["replaceTypes"].(map[string]interface{})
for t, b := range replaceTypesArg {
replaceTypes[t] = b.(bool)
}
}
err := model.FindReplace(k, r, replaceTypes, ids, paths, boxes, types, method, orderBy, groupBy)
if nil != err {
ret.Code = 1
ret.Msg = err.Error()
ret.Data = map[string]interface{}{"closeTimeout": 5000}
return
}
return
}
func searchAsset(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
k := arg["k"].(string)
var exts []string
if extsArg := arg["exts"]; nil != extsArg {
for _, ext := range extsArg.([]interface{}) {
exts = append(exts, ext.(string))
}
}
ret.Data = model.SearchAssetsByName(k, exts)
return
}
func searchTag(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
k := arg["k"].(string)
tags := model.SearchTags(k)
if 1 > len(tags) {
tags = []string{}
}
ret.Data = map[string]interface{}{
"tags": tags,
"k": k,
}
}
func searchWidget(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
keyword := arg["k"].(string)
blocks := model.SearchWidget(keyword)
ret.Data = map[string]interface{}{
"blocks": blocks,
"k": keyword,
}
}
func removeTemplate(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
path := arg["path"].(string)
err := model.RemoveTemplate(path)
if nil != err {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func searchTemplate(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
keyword := arg["k"].(string)
blocks := model.SearchTemplate(keyword)
ret.Data = map[string]interface{}{
"blocks": blocks,
"k": keyword,
}
}
func getEmbedBlock(c *gin.Context) {
// Query embed block supports executing JavaScript https://github.com/siyuan-note/siyuan/issues/9648
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
embedBlockID := arg["embedBlockID"].(string)
includeIDsArg := arg["includeIDs"].([]interface{})
var includeIDs []string
for _, includeID := range includeIDsArg {
includeIDs = append(includeIDs, includeID.(string))
}
headingMode := 0 // 0带标题下方块
headingModeArg := arg["headingMode"]
if nil != headingModeArg {
headingMode = int(headingModeArg.(float64))
}
breadcrumb := false
breadcrumbArg := arg["breadcrumb"]
if nil != breadcrumbArg {
breadcrumb = breadcrumbArg.(bool)
}
blocks := model.GetEmbedBlock(embedBlockID, includeIDs, headingMode, breadcrumb)
ret.Data = map[string]interface{}{
"blocks": blocks,
}
}
func updateEmbedBlock(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
id := arg["id"].(string)
content := arg["content"].(string)
err := model.UpdateEmbedBlock(id, content)
if nil != err {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func searchEmbedBlock(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
embedBlockID := arg["embedBlockID"].(string)
stmt := arg["stmt"].(string)
excludeIDsArg := arg["excludeIDs"].([]interface{})
var excludeIDs []string
for _, excludeID := range excludeIDsArg {
excludeIDs = append(excludeIDs, excludeID.(string))
}
headingMode := 0 // 0带标题下方块
headingModeArg := arg["headingMode"]
if nil != headingModeArg {
headingMode = int(headingModeArg.(float64))
}
breadcrumb := false
breadcrumbArg := arg["breadcrumb"]
if nil != breadcrumbArg {
breadcrumb = breadcrumbArg.(bool)
}
blocks := model.SearchEmbedBlock(embedBlockID, stmt, excludeIDs, headingMode, breadcrumb)
ret.Data = map[string]interface{}{
"blocks": blocks,
}
}
func searchRefBlock(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
reqId := arg["reqId"]
ret.Data = map[string]interface{}{"reqId": reqId}
if nil == arg["id"] {
return
}
isSquareBrackets := false
if isSquareBracketsArg := arg["isSquareBrackets"]; nil != isSquareBracketsArg {
isSquareBrackets = isSquareBracketsArg.(bool)
}
rootID := arg["rootID"].(string)
id := arg["id"].(string)
keyword := arg["k"].(string)
beforeLen := int(arg["beforeLen"].(float64))
blocks, newDoc := model.SearchRefBlock(id, rootID, keyword, beforeLen, isSquareBrackets)
ret.Data = map[string]interface{}{
"blocks": blocks,
"newDoc": newDoc,
"k": util.EscapeHTML(keyword),
"reqId": arg["reqId"],
}
}
func fullTextSearchBlock(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
page, pageSize, query, paths, boxes, types, method, orderBy, groupBy := parseSearchBlockArgs(arg)
blocks, matchedBlockCount, matchedRootCount, pageCount := model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page, pageSize)
ret.Data = map[string]interface{}{
"blocks": blocks,
"matchedBlockCount": matchedBlockCount,
"matchedRootCount": matchedRootCount,
"pageCount": pageCount,
}
}
func parseSearchBlockArgs(arg map[string]interface{}) (page, pageSize int, query string, paths, boxes []string, types map[string]bool, method, orderBy, groupBy int) {
page = 1
if nil != arg["page"] {
page = int(arg["page"].(float64))
}
if 0 >= page {
page = 1
}
pageSize = 32
if nil != arg["pageSize"] {
pageSize = int(arg["pageSize"].(float64))
}
if 0 >= pageSize {
pageSize = 32
}
queryArg := arg["query"]
if nil != queryArg {
query = queryArg.(string)
}
pathsArg := arg["paths"]
if nil != pathsArg {
for _, p := range pathsArg.([]interface{}) {
path := p.(string)
box := strings.TrimSpace(strings.Split(path, "/")[0])
if "" != box {
boxes = append(boxes, box)
}
path = strings.TrimSpace(strings.TrimPrefix(path, box))
if "" != path {
paths = append(paths, path)
}
}
paths = gulu.Str.RemoveDuplicatedElem(paths)
boxes = gulu.Str.RemoveDuplicatedElem(boxes)
}
if nil != arg["types"] {
typesArg := arg["types"].(map[string]interface{})
types = map[string]bool{}
for t, b := range typesArg {
types[t] = b.(bool)
}
}
// method0关键字1查询语法2SQL3正则表达式
methodArg := arg["method"]
if nil != methodArg {
method = int(methodArg.(float64))
}
// orderBy0按块类型默认1按创建时间升序2按创建时间降序3按更新时间升序4按更新时间降序5按内容顺序仅在按文档分组时6按相关度升序7按相关度降序
orderByArg := arg["orderBy"]
if nil != orderByArg {
orderBy = int(orderByArg.(float64))
}
// groupBy 0不分组1按文档分组
groupByArg := arg["groupBy"]
if nil != groupByArg {
groupBy = int(groupByArg.(float64))
}
return
}
func parseSearchAssetContentArgs(arg map[string]interface{}) (page, pageSize int, query string, types map[string]bool, method, orderBy int) {
page = 1
if nil != arg["page"] {
page = int(arg["page"].(float64))
}
if 0 >= page {
page = 1
}
pageSize = 32
if nil != arg["pageSize"] {
pageSize = int(arg["pageSize"].(float64))
}
if 0 >= pageSize {
pageSize = 32
}
queryArg := arg["query"]
if nil != queryArg {
query = queryArg.(string)
}
if nil != arg["types"] {
typesArg := arg["types"].(map[string]interface{})
types = map[string]bool{}
for t, b := range typesArg {
types[t] = b.(bool)
}
}
// method0关键字1查询语法2SQL3正则表达式
methodArg := arg["method"]
if nil != methodArg {
method = int(methodArg.(float64))
}
// orderBy0按相关度降序1按相关度升序2按更新时间升序3按更新时间降序
orderByArg := arg["orderBy"]
if nil != orderByArg {
orderBy = int(orderByArg.(float64))
}
return
}