反链面板

This commit is contained in:
Liang Ding 2022-09-29 12:11:52 +08:00
parent 8ae5858302
commit f6411dba9a
No known key found for this signature in database
GPG Key ID: 136F30F901A2231D
4 changed files with 156 additions and 0 deletions

View File

@ -38,6 +38,23 @@ func refreshBacklink(c *gin.Context) {
model.RefreshBacklink(id)
}
func getBacklinkDoc(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
defID := arg["defID"].(string)
refTreeID := arg["refTreeID"].(string)
blocks := model.GetBacklinkDoc(defID, refTreeID)
ret.Data = map[string]interface{}{
"blocks": blocks,
}
}
func getBacklink(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)

View File

@ -156,6 +156,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/ref/refreshBacklink", model.CheckAuth, refreshBacklink)
ginServer.Handle("POST", "/api/ref/getBacklink", model.CheckAuth, getBacklink)
ginServer.Handle("POST", "/api/ref/getBacklinkDoc", model.CheckAuth, getBacklinkDoc)
ginServer.Handle("POST", "/api/ref/createBacklink", model.CheckAuth, model.CheckReadonly, createBacklink)
ginServer.Handle("POST", "/api/attr/getBookmarkLabels", model.CheckAuth, getBookmarkLabels)

View File

@ -26,6 +26,7 @@ import (
"strings"
"github.com/88250/gulu"
"github.com/88250/lute"
"github.com/88250/lute/ast"
"github.com/88250/lute/parse"
"github.com/emirpasic/gods/sets/hashset"
@ -157,6 +158,128 @@ OK:
return
}
func GetBacklinkDoc(defID, refTreeID string) (ret []*Block) {
keyword := ""
beforeLen := 12
sqlBlock := sql.GetBlock(defID)
if nil == sqlBlock {
return
}
rootID := sqlBlock.RootID
var links []*Block
refs := sql.QueryRefsByDefIDRefRootID(defID, refTreeID)
refs = removeDuplicatedRefs(refs) // 同一个块中引用多个相同块时反链去重 https://github.com/siyuan-note/siyuan/issues/3317
// 为了减少查询,组装好 IDs 后一次查出
defSQLBlockIDs, refSQLBlockIDs := map[string]bool{}, map[string]bool{}
var queryBlockIDs []string
for _, ref := range refs {
defSQLBlockIDs[ref.DefBlockID] = true
refSQLBlockIDs[ref.BlockID] = true
queryBlockIDs = append(queryBlockIDs, ref.DefBlockID)
queryBlockIDs = append(queryBlockIDs, ref.BlockID)
}
querySQLBlocks := sql.GetBlocks(queryBlockIDs)
defSQLBlocksCache := map[string]*sql.Block{}
for _, defSQLBlock := range querySQLBlocks {
if nil != defSQLBlock && defSQLBlockIDs[defSQLBlock.ID] {
defSQLBlocksCache[defSQLBlock.ID] = defSQLBlock
}
}
refSQLBlocksCache := map[string]*sql.Block{}
for _, refSQLBlock := range querySQLBlocks {
if nil != refSQLBlock && refSQLBlockIDs[refSQLBlock.ID] {
refSQLBlocksCache[refSQLBlock.ID] = refSQLBlock
}
}
excludeBacklinkIDs := hashset.New()
for _, ref := range refs {
defSQLBlock := defSQLBlocksCache[(ref.DefBlockID)]
if nil == defSQLBlock {
continue
}
refSQLBlock := refSQLBlocksCache[ref.BlockID]
if nil == refSQLBlock {
continue
}
refBlock := fromSQLBlock(refSQLBlock, "", beforeLen)
if rootID == refBlock.RootID { // 排除当前文档内引用提及
excludeBacklinkIDs.Add(refBlock.RootID, refBlock.ID)
}
defBlock := fromSQLBlock(defSQLBlock, "", beforeLen)
if defBlock.RootID == rootID { // 当前文档的定义块
links = append(links, defBlock)
if ref.DefBlockID == defBlock.ID {
defBlock.Refs = append(defBlock.Refs, refBlock)
}
}
}
for _, link := range links {
for _, ref := range link.Refs {
excludeBacklinkIDs.Add(ref.RootID, ref.ID)
}
}
var linkRefs []*Block
processedParagraphs := hashset.New()
var paragraphParentIDs []string
for _, link := range links {
for _, ref := range link.Refs {
if "NodeParagraph" == ref.Type {
paragraphParentIDs = append(paragraphParentIDs, ref.ParentID)
}
}
}
paragraphParents := sql.GetBlocks(paragraphParentIDs)
for _, p := range paragraphParents {
if "i" == p.Type || "h" == p.Type {
linkRefs = append(linkRefs, fromSQLBlock(p, keyword, beforeLen))
processedParagraphs.Add(p.ID)
}
}
for _, link := range links {
for _, ref := range link.Refs {
if "NodeParagraph" == ref.Type {
if processedParagraphs.Contains(ref.ParentID) {
continue
}
}
ref.DefID = link.ID
ref.DefPath = link.Path
content := ref.Content
if "" != keyword {
_, content = search.MarkText(content, keyword, beforeLen, Conf.Search.CaseSensitive)
ref.Content = content
}
linkRefs = append(linkRefs, ref)
}
}
luteEngine := NewLute()
refTree, err := loadTreeByBlockID(refTreeID)
if nil != err {
logging.LogErrorf("load ref tree [%s] failed: %s", refTreeID, err)
return
}
linkPaths := toSubTree(linkRefs, keyword)
for _, link := range linkPaths {
for _, c := range link.Children {
n := treenode.GetNodeInTree(refTree, c.ID)
b := &Block{
Content: lute.RenderNodeBlockDOM(n, luteEngine.ParseOptions, luteEngine.RenderOptions),
}
ret = append(ret, b)
}
}
return
}
func BuildTreeBacklink(id, keyword, mentionKeyword string, beforeLen int) (boxID string, linkPaths, mentionPaths []*Path, linkRefsCount, mentionsCount int) {
linkPaths = []*Path{}
mentionPaths = []*Path{}

View File

@ -343,6 +343,21 @@ func QueryRefsByDefID(defBlockID string, containChildren bool) (ret []*Ref) {
return
}
func QueryRefsByDefIDRefRootID(defBlockID, refRootBlockID string) (ret []*Ref) {
stmt := "SELECT * FROM refs WHERE def_block_id = ? AND root_id = ?"
rows, err := query(stmt, defBlockID, refRootBlockID)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
ref := scanRefRows(rows)
ret = append(ret, ref)
}
return
}
func QueryRefsByDefIDRefID(defBlockID, refBlockID string) (ret []*Ref) {
stmt := "SELECT * FROM refs WHERE def_block_id = ? AND block_id = ?"
rows, err := query(stmt, defBlockID, refBlockID)