siyuan/kernel/sql/block_ref_query.go

420 lines
11 KiB
Go

// SiYuan - Build Your Eternal Digital Garden
// 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 sql
import (
"database/sql"
"sort"
"strings"
"github.com/88250/gulu"
"github.com/88250/lute/parse"
"github.com/emirpasic/gods/sets/hashset"
"github.com/siyuan-note/logging"
)
func QueryVirtualRefKeywords(name, alias, anchor, doc bool) (ret []string) {
ret, ok := getVirtualRefKeywordsCache()
if ok {
return ret
}
if name {
ret = append(ret, queryNames()...)
}
if alias {
ret = append(ret, queryAliases()...)
}
if anchor {
ret = append(ret, queryRefTexts()...)
}
if doc {
ret = append(ret, queryDocTitles()...)
}
ret = gulu.Str.RemoveDuplicatedElem(ret)
sort.SliceStable(ret, func(i, j int) bool {
return len(ret[i]) >= len(ret[j])
})
setVirtualRefKeywords(ret)
return
}
func queryRefTexts() (ret []string) {
ret = []string{}
sqlStmt := "SELECT DISTINCT content FROM refs LIMIT 1024"
rows, err := query(sqlStmt)
if nil != err {
logging.LogErrorf("sql query failed: %s", sqlStmt, err)
return
}
defer rows.Close()
set := hashset.New()
for rows.Next() {
var refText string
rows.Scan(&refText)
if "" == strings.TrimSpace(refText) {
continue
}
set.Add(refText)
}
for _, refText := range set.Values() {
ret = append(ret, refText.(string))
}
return
}
func QueryRootChildrenRefCount(defRootID string) (ret map[string]int) {
ret = map[string]int{}
rows, err := query("SELECT def_block_id, COUNT(*) AS ref_cnt FROM refs WHERE def_block_root_id = ? GROUP BY def_block_id", defRootID)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
var id string
var cnt int
if err = rows.Scan(&id, &cnt); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
ret[id] = cnt
}
return
}
func QueryRootBlockRefCount() (ret map[string]int) {
ret = map[string]int{}
rows, err := query("SELECT def_block_root_id, COUNT(*) AS ref_cnt FROM refs GROUP BY def_block_root_id")
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
var id string
var cnt int
if err = rows.Scan(&id, &cnt); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
ret[id] = cnt
}
return
}
func QueryDefRootBlocksByRefRootID(refRootID string) (ret []*Block) {
rows, err := query("SELECT * FROM blocks WHERE id IN (SELECT DISTINCT def_block_root_id FROM refs WHERE root_id = ?)", refRootID)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
if block := scanBlockRows(rows); nil != block {
ret = append(ret, block)
}
}
return
}
func QueryRefRootBlocksByDefRootID(defRootID string) (ret []*Block) {
rows, err := query("SELECT * FROM blocks WHERE id IN (SELECT DISTINCT root_id FROM refs WHERE def_block_root_id = ?)", defRootID)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
if block := scanBlockRows(rows); nil != block {
ret = append(ret, block)
}
}
return
}
func GetRefText(defBlockID string) string {
block := GetBlock(defBlockID)
if nil == block {
if strings.HasPrefix(defBlockID, "assets") {
return defBlockID
}
return "block not found"
}
if "" != block.Name {
return block.Name
}
switch block.Type {
case "d":
return block.Content
case "query_embed":
return "Query Embed Block " + block.Markdown
case "iframe":
return "IFrame " + block.Markdown
case "tb":
return "Thematic Break"
case "video":
return "Video " + block.Markdown
case "audio":
return "Audio " + block.Markdown
}
if block.IsContainerBlock() {
subTree := parse.Parse("", []byte(block.Markdown), luteEngine.ParseOptions)
return GetContainerText(subTree.Root)
}
return block.Content
}
func QueryBlockDefIDsByRefText(refText string, excludeIDs []string) (ret []string) {
ret = queryDefIDsByDefText(refText, excludeIDs)
ret = append(ret, queryDefIDsByNameAlias(refText, excludeIDs)...)
ret = append(ret, queryDocIDsByTitle(refText, excludeIDs)...)
ret = gulu.Str.RemoveDuplicatedElem(ret)
return
}
func queryDefIDsByDefText(keyword string, excludeIDs []string) (ret []string) {
ret = []string{}
notIn := "('" + strings.Join(excludeIDs, "','") + "')"
rows, err := query("SELECT DISTINCT(def_block_id) FROM refs WHERE content = ? AND def_block_id NOT IN "+notIn, keyword)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
var id string
if err = rows.Scan(&id); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
ret = append(ret, id)
}
return
}
func queryDefIDsByNameAlias(keyword string, excludeIDs []string) (ret []string) {
ret = []string{}
notIn := "('" + strings.Join(excludeIDs, "','") + "')"
rows, err := query("SELECT DISTINCT(id), name, alias FROM blocks WHERE (name = ? OR alias LIKE ?) AND id NOT IN "+notIn, keyword, "%"+keyword+"%")
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
var id, name, alias string
if err = rows.Scan(&id, &name, &alias); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
if name == keyword {
ret = append(ret, id)
continue
}
var hitAlias bool
aliases := strings.Split(alias, ",")
for _, a := range aliases {
if "" == a {
continue
}
if keyword == a {
hitAlias = true
}
}
if strings.Contains(alias, keyword) && !hitAlias {
continue
}
ret = append(ret, id)
}
return
}
func QueryChildDefIDsByRootDefID(rootDefID string) (ret []string) {
ret = []string{}
rows, err := query("SELECT DISTINCT(def_block_id) FROM refs WHERE def_block_root_id = ?", rootDefID)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
var id string
if err = rows.Scan(&id); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
ret = append(ret, id)
}
return
}
func QueryRefIDsByDefID(defID string, containChildren bool) (refIDs, refTexts []string) {
refIDs = []string{}
var rows *sql.Rows
var err error
if containChildren {
rows, err = query("SELECT block_id, content FROM refs WHERE def_block_root_id = ?", defID)
} else {
rows, err = query("SELECT block_id, content FROM refs WHERE def_block_id = ?", defID)
}
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
for rows.Next() {
var id, content string
if err = rows.Scan(&id, &content); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
refIDs = append(refIDs, id)
refTexts = append(refTexts, content)
}
return
}
func QueryRefsRecent() (ret []*Ref) {
rows, err := query("SELECT * FROM refs GROUP BY def_block_id ORDER BY id desc LIMIT 32")
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 QueryRefsByDefID(defBlockID string, containChildren bool) (ret []*Ref) {
sqlBlock := GetBlock(defBlockID)
if nil == sqlBlock {
return
}
var rows *sql.Rows
var err error
if "d" == sqlBlock.Type {
rows, err = query("SELECT * FROM refs WHERE def_block_root_id = ?", defBlockID)
} else {
if containChildren {
blockIDs := queryBlockChildrenIDs(defBlockID)
var params []string
for _, id := range blockIDs {
params = append(params, "\""+id+"\"")
}
rows, err = query("SELECT * FROM refs WHERE def_block_id IN (" + strings.Join(params, ",") + ")")
} else {
rows, err = query("SELECT * FROM refs WHERE def_block_id = ?", defBlockID)
}
}
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)
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 DefRefs(condition string) (ret []map[*Block]*Block) {
ret = []map[*Block]*Block{}
stmt := "SELECT ref.*, r.block_id || '@' || r.def_block_id AS rel FROM blocks AS ref, refs AS r WHERE ref.id = r.block_id"
if "" != condition {
stmt += " AND " + condition
}
rows, err := query(stmt)
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
refs := map[string]*Block{}
for rows.Next() {
var ref Block
var rel string
if err = rows.Scan(&ref.ID, &ref.ParentID, &ref.RootID, &ref.Hash, &ref.Box, &ref.Path, &ref.HPath, &ref.Name, &ref.Alias, &ref.Memo, &ref.Tag, &ref.Content, &ref.FContent, &ref.Markdown, &ref.Length, &ref.Type, &ref.SubType, &ref.IAL, &ref.Sort, &ref.Created, &ref.Updated,
&rel); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
refs[rel] = &ref
}
rows, err = query("SELECT def.* FROM blocks AS def, refs AS r WHERE def.id = r.def_block_id")
if nil != err {
logging.LogErrorf("sql query failed: %s", err)
return
}
defer rows.Close()
defs := map[string]*Block{}
for rows.Next() {
if def := scanBlockRows(rows); nil != def {
defs[def.ID] = def
}
}
for rel, ref := range refs {
defID := strings.Split(rel, "@")[1]
def := defs[defID]
if nil == def {
continue
}
defRef := map[*Block]*Block{}
defRef[def] = ref
ret = append(ret, defRef)
}
return
}
func scanRefRows(rows *sql.Rows) (ret *Ref) {
var ref Ref
if err := rows.Scan(&ref.ID, &ref.DefBlockID, &ref.DefBlockParentID, &ref.DefBlockRootID, &ref.DefBlockPath, &ref.BlockID, &ref.RootID, &ref.Box, &ref.Path, &ref.Content, &ref.Markdown, &ref.Type); nil != err {
logging.LogErrorf("query scan field failed: %s", err)
return
}
ret = &ref
return
}