mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-05-03 02:59:50 +08:00
✨ 反链面板
This commit is contained in:
parent
8ae5858302
commit
f6411dba9a
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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{}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user