Procházet zdrojové kódy

:art: Attribute View map to database table https://github.com/siyuan-note/siyuan/issues/7677

Liang Ding před 2 roky
rodič
revize
2de23a977b
3 změnil soubory, kde provedl 84 přidání a 45 odebrání
  1. 30 22
      kernel/av/av.go
  2. 25 11
      kernel/model/attribute_view.go
  3. 29 12
      kernel/sql/queue.go

+ 30 - 22
kernel/av/av.go

@@ -18,6 +18,7 @@
 package av
 
 import (
+	"bytes"
 	"database/sql"
 	"os"
 	"path/filepath"
@@ -162,38 +163,45 @@ func getAttributeViewDataPath(avID string) (ret string) {
 	return
 }
 
-func dropAttributeViewTableColumn(db *sql.DB, avID string, column string) (err error) {
-	_, err = db.Exec("ALTER TABLE `av_" + avID + "` DROP COLUMN `" + column + "`")
-	if nil != err {
-		logging.LogErrorf("drop column [%s] failed: %s", column, err)
-		return
+func RebuildAttributeViewTable(tx *sql.Tx, av *AttributeView) (err error) {
+	avID := av.ID
+	var columns []string
+	for _, c := range av.Columns {
+		columns = append(columns, "`"+c.ID+"`")
 	}
-	return
-}
 
-func addAttributeViewTableColumn(db *sql.DB, avID string, column string) (err error) {
-	_, err = db.Exec("ALTER TABLE `av_" + avID + "` ADD COLUMN `" + column + "`")
-	if nil != err {
-		logging.LogErrorf("add column [%s] failed: %s", column, err)
-		return
-	}
-	return
-}
-
-func dropAttributeViewTable(db *sql.DB, avID string) (err error) {
-	_, err = db.Exec("DROP TABLE IF EXISTS `av_" + avID + "`")
+	_, err = tx.Exec("DROP TABLE IF EXISTS `av_" + avID + "`")
 	if nil != err {
 		logging.LogErrorf("drop table [%s] failed: %s", avID, err)
 		return
 	}
-	return
-}
 
-func createAttributeViewTable(db *sql.DB, avID string, column []string) (err error) {
-	_, err = db.Exec("CREATE TABLE IF NOT EXISTS `av_" + avID + "` (id, " + strings.Join(column, ", ") + ")")
+	_, err = tx.Exec("CREATE TABLE IF NOT EXISTS `av_" + avID + "` (" + strings.Join(columns, ", ") + ")")
 	if nil != err {
 		logging.LogErrorf("create table [%s] failed: %s", avID, err)
 		return
 	}
+
+	for _, r := range av.Rows {
+		buf := bytes.Buffer{}
+		for i, _ := range r.Cells {
+			buf.WriteString("?")
+			if i < len(r.Cells)-1 {
+				buf.WriteString(", ")
+			}
+		}
+
+		var values []interface{}
+		for _, c := range r.Cells {
+			values = append(values, c.Value)
+		}
+
+		_, err = tx.Exec("INSERT INTO `av_"+avID+"` VALUES ("+buf.String()+")", values...)
+		if nil != err {
+			logging.LogErrorf("insert row [%s] failed: %s", r.ID, err)
+			return
+		}
+	}
+
 	return
 }

+ 25 - 11
kernel/model/attribute_view.go

@@ -48,11 +48,25 @@ func (tx *Transaction) doInsertAttrViewBlock(operation *Operation) (ret *TxErr)
 	}
 
 	avID := operation.ParentID
+	var avs []*av.AttributeView
 	for _, id := range operation.SrcIDs {
-		if err = addAttributeViewBlock(id, avID, tree, tx); nil != err {
-			return &TxErr{code: TxErrWriteAttributeView, id: avID, msg: err.Error()}
+		var av *av.AttributeView
+		var avErr error
+		if av, avErr = addAttributeViewBlock(id, avID, tree, tx); nil != avErr {
+			return &TxErr{code: TxErrWriteAttributeView, id: avID, msg: avErr.Error()}
 		}
+
+		if nil == av {
+			continue
+		}
+
+		avs = append(avs, av)
+	}
+
+	for _, av := range avs {
+		sql.RebuildAttributeViewQueue(av)
 	}
+
 	return
 }
 
@@ -150,7 +164,7 @@ func removeAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transa
 	return
 }
 
-func addAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transaction) (err error) {
+func addAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transaction) (ret *av.AttributeView, err error) {
 	node := treenode.GetNodeInTree(tree, blockID)
 	if nil == node {
 		err = ErrBlockNotFound
@@ -168,13 +182,13 @@ func addAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transacti
 		return
 	}
 
-	attrView, err := av.ParseAttributeView(avID)
+	ret, err = av.ParseAttributeView(avID)
 	if nil != err {
 		return
 	}
 
 	// 不允许重复添加相同的块到属性视图中
-	for _, row := range attrView.Rows {
+	for _, row := range ret.Rows {
 		if row.Cells[0].Value == blockID {
 			return
 		}
@@ -182,11 +196,11 @@ func addAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transacti
 
 	row := av.NewRow()
 	row.Cells = append(row.Cells, &av.Cell{ID: ast.NewNodeID(), Value: blockID})
-	if 1 < len(attrView.Columns) {
-		// 将列作为属性添加到块中
+	if 1 < len(ret.Columns) {
 		attrs := parse.IAL2Map(node.KramdownIAL)
-		for _, col := range attrView.Columns[1:] {
-			attrs["av"+col.ID] = ""
+		for _, col := range ret.Columns[1:] {
+			attrs["av"+col.ID] = "" // 将列作为属性添加到块中
+			row.Cells = append(row.Cells, &av.Cell{ID: ast.NewNodeID(), Value: ""})
 		}
 
 		if err = setNodeAttrsWithTx(tx, node, tree, attrs); nil != err {
@@ -194,7 +208,7 @@ func addAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transacti
 		}
 	}
 
-	attrView.Rows = append(attrView.Rows, row)
-	err = av.SaveAttributeView(attrView)
+	ret.Rows = append(ret.Rows, row)
+	err = av.SaveAttributeView(ret)
 	return
 }

+ 29 - 12
kernel/sql/queue.go

@@ -28,6 +28,7 @@ import (
 	"github.com/88250/lute/parse"
 	"github.com/siyuan-note/eventbus"
 	"github.com/siyuan-note/logging"
+	"github.com/siyuan-note/siyuan/kernel/av"
 	"github.com/siyuan-note/siyuan/kernel/task"
 	"github.com/siyuan-note/siyuan/kernel/util"
 )
@@ -40,18 +41,18 @@ var (
 )
 
 type dbQueueOperation struct {
-	inQueueTime time.Time
-	action      string // upsert/delete/delete_id/rename/rename_sub_tree/delete_box/delete_box_refs/insert_refs/index/delete_ids/update_block_content/delete_assets
-
-	indexPath                     string      // index
-	upsertTree                    *parse.Tree // upsert/insert_refs/update_refs/delete_refs
-	removeTreeBox, removeTreePath string      // delete
-	removeTreeIDBox, removeTreeID string      // delete_id
-	removeTreeIDs                 []string    // delete_ids
-	box                           string      // delete_box/delete_box_refs/index
-	renameTree                    *parse.Tree // rename/rename_sub_tree
-	block                         *Block      // update_block_content
-	removeAssetHashes             []string    // delete_assets
+	inQueueTime                   time.Time
+	action                        string            // upsert/delete/delete_id/rename/rename_sub_tree/delete_box/delete_box_refs/insert_refs/index/delete_ids/update_block_content/delete_assets/av_rebuild
+	indexPath                     string            // index
+	upsertTree                    *parse.Tree       // upsert/insert_refs/update_refs/delete_refs
+	removeTreeBox, removeTreePath string            // delete
+	removeTreeIDBox, removeTreeID string            // delete_id
+	removeTreeIDs                 []string          // delete_ids
+	box                           string            // delete_box/delete_box_refs/index
+	renameTree                    *parse.Tree       // rename/rename_sub_tree
+	block                         *Block            // update_block_content
+	removeAssetHashes             []string          // delete_assets
+	av                            *av.AttributeView // av_rebuild
 }
 
 func FlushTxJob() {
@@ -188,6 +189,8 @@ func execOp(op *dbQueueOperation, tx *sql.Tx, context map[string]interface{}) (e
 		err = updateBlockContent(tx, op.block)
 	case "delete_assets":
 		err = deleteAssetsByHashes(tx, op.removeAssetHashes)
+	case "av_rebuild":
+		err = av.RebuildAttributeViewTable(tx, op.av)
 	default:
 		msg := fmt.Sprintf("unknown operation [%s]", op.action)
 		logging.LogErrorf(msg)
@@ -196,6 +199,20 @@ func execOp(op *dbQueueOperation, tx *sql.Tx, context map[string]interface{}) (e
 	return
 }
 
+func RebuildAttributeViewQueue(av *av.AttributeView) {
+	dbQueueLock.Lock()
+	defer dbQueueLock.Unlock()
+
+	newOp := &dbQueueOperation{av: av, inQueueTime: time.Now(), action: "av_rebuild"}
+	for i, op := range operationQueue {
+		if "av_rebuild" == op.action && op.av.ID == av.ID {
+			operationQueue[i] = newOp
+			return
+		}
+	}
+	operationQueue = append(operationQueue, newOp)
+}
+
 func BatchRemoveAssetsQueue(hashes []string) {
 	if 1 > len(hashes) {
 		return