2023-06-24 20:39:55 +08:00
// SiYuan - Refactor your thinking
2022-05-26 15:18:53 +08:00
// 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 (
2024-06-27 10:51:04 +08:00
"bytes"
2022-05-26 15:18:53 +08:00
"database/sql"
"sort"
"strings"
2022-06-23 19:35:59 +08:00
"github.com/88250/gulu"
2022-05-26 15:18:53 +08:00
"github.com/88250/lute/parse"
"github.com/emirpasic/gods/sets/hashset"
2022-07-17 12:22:32 +08:00
"github.com/siyuan-note/logging"
2022-11-20 22:56:51 +08:00
"github.com/siyuan-note/siyuan/kernel/search"
2022-05-26 15:18:53 +08:00
)
2023-11-10 11:01:06 +08:00
func GetRefDuplicatedDefRootIDs ( ) ( ret [ ] string ) {
2023-11-10 11:52:46 +08:00
rows , err := query ( "SELECT DISTINCT def_block_root_id FROM `refs` GROUP BY def_block_id, def_block_root_id, block_id HAVING COUNT(*) > 1" )
2024-09-04 04:40:50 +03:00
if err != nil {
2023-11-10 11:01:06 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
rows . Scan ( & id )
ret = append ( ret , id )
}
return
}
2024-06-28 22:13:23 +08:00
func QueryVirtualRefKeywords ( name , alias , anchor , doc bool , searchIgnoreLines , refSearchIgnoreLines [ ] string ) ( ret [ ] string ) {
2022-05-26 15:18:53 +08:00
if name {
2024-06-28 22:13:23 +08:00
ret = append ( ret , queryNames ( searchIgnoreLines ) ... )
2022-05-26 15:18:53 +08:00
}
if alias {
2024-06-28 22:13:23 +08:00
ret = append ( ret , queryAliases ( searchIgnoreLines ) ... )
2022-05-26 15:18:53 +08:00
}
if anchor {
2024-06-28 22:13:23 +08:00
ret = append ( ret , queryRefTexts ( refSearchIgnoreLines ) ... )
2022-05-26 15:18:53 +08:00
}
if doc {
2024-06-28 22:13:23 +08:00
ret = append ( ret , queryDocTitles ( searchIgnoreLines ) ... )
2022-05-26 15:18:53 +08:00
}
2022-06-23 19:35:59 +08:00
ret = gulu . Str . RemoveDuplicatedElem ( ret )
2022-05-26 15:18:53 +08:00
sort . SliceStable ( ret , func ( i , j int ) bool {
return len ( ret [ i ] ) >= len ( ret [ j ] )
} )
return
}
2024-06-28 22:13:23 +08:00
func queryRefTexts ( refSearchIgnoreLines [ ] string ) ( ret [ ] string ) {
2022-05-26 15:18:53 +08:00
ret = [ ] string { }
2024-06-28 22:13:23 +08:00
sqlStmt := "SELECT DISTINCT content FROM refs WHERE 1 = 1"
buf := bytes . Buffer { }
for _ , line := range refSearchIgnoreLines {
buf . WriteString ( " AND " )
buf . WriteString ( line )
}
sqlStmt += buf . String ( )
sqlStmt += " LIMIT 10240"
2022-05-26 15:18:53 +08:00
rows , err := query ( sqlStmt )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
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
}
2023-01-31 19:40:55 +08:00
func QueryRefCount ( defIDs [ ] string ) ( ret map [ string ] int ) {
ret = map [ string ] int { }
ids := strings . Join ( defIDs , "','" )
ids = "('" + ids + "')"
rows , err := query ( "SELECT def_block_id, COUNT(*) AS ref_cnt FROM refs WHERE def_block_id IN " + ids + " GROUP BY def_block_id" )
2024-09-04 04:40:50 +03:00
if err != nil {
2023-01-31 19:40:55 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
var cnt int
2024-09-04 04:40:50 +03:00
if err = rows . Scan ( & id , & cnt ) ; err != nil {
2023-01-31 19:40:55 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
return
}
ret [ id ] = cnt
}
return
}
2022-05-26 15:18:53 +08:00
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 )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
var cnt int
2024-09-04 04:40:50 +03:00
if err = rows . Scan ( & id , & cnt ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
ret [ id ] = cnt
}
return
}
func QueryRootBlockRefCount ( ) ( ret map [ string ] int ) {
ret = map [ string ] int { }
2025-01-13 18:05:19 +08:00
rows , err := query ( "SELECT def_block_root_id, COUNT(DISTINCT block_id) AS ref_cnt FROM refs GROUP BY def_block_root_id" )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
var cnt int
2024-09-04 04:40:50 +03:00
if err = rows . Scan ( & id , & cnt ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
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 )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
2023-02-14 10:49:03 +08:00
func QueryRefRootBlocksByDefRootIDs ( defRootIDs [ ] string ) ( ret map [ string ] [ ] * Block ) {
ret = map [ string ] [ ] * Block { }
stmt := "SELECT r.def_block_root_id, b.* FROM refs AS r, blocks AS b ON r.def_block_root_id IN ('" + strings . Join ( defRootIDs , "','" ) + "')" + " AND b.id = r.root_id"
rows , err := query ( stmt )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
2023-02-14 10:49:03 +08:00
var block Block
var defRootID string
2024-09-04 04:40:50 +03:00
if err := rows . Scan ( & defRootID , & block . ID , & block . ParentID , & block . RootID , & block . Hash , & block . Box , & block . Path , & block . HPath , & block . Name , & block . Alias , & block . Memo , & block . Tag , & block . Content , & block . FContent , & block . Markdown , & block . Length , & block . Type , & block . SubType , & block . IAL , & block . Sort , & block . Created , & block . Updated ) ; err != nil {
2023-02-14 10:49:03 +08:00
logging . LogErrorf ( "query scan field failed: %s\n%s" , err , logging . ShortStack ( ) )
return
}
if nil == ret [ defRootID ] {
2023-02-16 15:06:11 +08:00
ret [ defRootID ] = [ ] * Block { & block }
2023-02-14 10:49:03 +08:00
} else {
ret [ defRootID ] = append ( ret [ defRootID ] , & block )
2022-05-26 15:18:53 +08:00
}
}
return
}
2022-11-20 22:56:51 +08:00
func GetRefText ( defBlockID string ) ( ret string ) {
ret = getRefText ( defBlockID )
ret = strings . ReplaceAll ( ret , search . SearchMarkLeft , "" )
ret = strings . ReplaceAll ( ret , search . SearchMarkRight , "" )
return
}
func getRefText ( defBlockID string ) string {
2022-05-26 15:18:53 +08:00
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
2023-10-05 12:37:34 +08:00
case "av" :
return "Database " + block . Markdown
2022-05-26 15:18:53 +08:00
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 ) ... )
2022-06-23 19:35:59 +08:00
ret = gulu . Str . RemoveDuplicatedElem ( ret )
2022-05-26 15:18:53 +08:00
return
}
func queryDefIDsByDefText ( keyword string , excludeIDs [ ] string ) ( ret [ ] string ) {
ret = [ ] string { }
notIn := "('" + strings . Join ( excludeIDs , "','" ) + "')"
2022-10-13 20:02:18 +08:00
q := "SELECT DISTINCT(def_block_id) FROM refs WHERE content LIKE ? AND def_block_id NOT IN " + notIn
if caseSensitive {
q = "SELECT DISTINCT(def_block_id) FROM refs WHERE content = ? AND def_block_id NOT IN " + notIn
}
rows , err := query ( q , keyword )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
2024-09-04 04:40:50 +03:00
if err = rows . Scan ( & id ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
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 + "%" )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id , name , alias string
2024-09-04 04:40:50 +03:00
if err = rows . Scan ( & id , & name , & alias ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
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
}
2025-01-12 18:46:35 +08:00
func QueryChildRefDefIDsByRootDefID ( rootDefID string ) ( ret map [ string ] [ ] string ) {
ret = map [ string ] [ ] string { }
rows , err := query ( "SELECT block_id, def_block_id FROM refs WHERE def_block_root_id = ?" , rootDefID )
if err != nil {
logging . LogErrorf ( "sql query failed: %s" , err )
return
}
defer rows . Close ( )
for rows . Next ( ) {
var defID , refID string
if err = rows . Scan ( & defID , & refID ) ; err != nil {
logging . LogErrorf ( "query scan field failed: %s" , err )
return
}
if nil == ret [ defID ] {
ret [ defID ] = [ ] string { refID }
} else {
ret [ defID ] = append ( ret [ defID ] , refID )
}
}
return
}
2022-05-26 15:18:53 +08:00
func QueryChildDefIDsByRootDefID ( rootDefID string ) ( ret [ ] string ) {
ret = [ ] string { }
rows , err := query ( "SELECT DISTINCT(def_block_id) FROM refs WHERE def_block_root_id = ?" , rootDefID )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
2024-09-04 04:40:50 +03:00
if err = rows . Scan ( & id ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
ret = append ( ret , id )
}
return
}
2025-01-12 18:46:35 +08:00
func QueryRefIDsByDefID ( defID string , containChildren bool ) ( refIDs [ ] string ) {
2022-05-26 15:18:53 +08:00
refIDs = [ ] string { }
var rows * sql . Rows
var err error
if containChildren {
2025-01-14 21:02:42 +08:00
rows , err = query ( "SELECT DISTINCT block_id FROM refs WHERE def_block_root_id = ?" , defID )
2022-05-26 15:18:53 +08:00
} else {
2025-01-14 21:02:42 +08:00
rows , err = query ( "SELECT DISTINCT block_id FROM refs WHERE def_block_id = ?" , defID )
2022-05-26 15:18:53 +08:00
}
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
2025-01-12 18:46:35 +08:00
var id string
if err = rows . Scan ( & id ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
refIDs = append ( refIDs , id )
}
return
}
2024-07-24 12:07:40 +08:00
func QueryRefsRecent ( onlyDoc bool , typeFilter string , ignoreLines [ ] string ) ( ret [ ] * Ref ) {
stmt := "SELECT r.* FROM refs AS r, blocks AS b WHERE b.id = r.def_block_id AND b.type IN " + typeFilter
2023-04-17 22:47:25 +08:00
if onlyDoc {
2024-07-24 21:08:36 +08:00
stmt = "SELECT r.* FROM refs AS r, blocks AS b WHERE b.id = r.def_block_id AND b.type = 'd'"
2023-04-17 22:47:25 +08:00
}
2024-06-27 10:51:04 +08:00
if 0 < len ( ignoreLines ) {
// Support ignore search results https://github.com/siyuan-note/siyuan/issues/10089
2024-06-28 22:13:23 +08:00
buf := bytes . Buffer { }
2024-06-27 10:51:04 +08:00
for _ , line := range ignoreLines {
2024-06-28 22:13:23 +08:00
buf . WriteString ( " AND " )
buf . WriteString ( line )
2024-06-27 10:51:04 +08:00
}
2024-06-28 22:13:23 +08:00
stmt += buf . String ( )
2024-06-27 10:51:04 +08:00
}
2023-04-17 22:53:07 +08:00
stmt += " GROUP BY r.def_block_id ORDER BY r.id DESC LIMIT 32"
2023-04-17 22:47:25 +08:00
rows , err := query ( stmt )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
ref := scanRefRows ( rows )
ret = append ( ret , ref )
}
return
}
func QueryRefsByDefID ( defBlockID string , containChildren bool ) ( ret [ ] * Ref ) {
var rows * sql . Rows
var err error
2024-10-21 10:32:13 +08:00
if containChildren {
blockIDs := queryBlockChildrenIDs ( defBlockID )
var params [ ] string
for _ , id := range blockIDs {
params = append ( params , "\"" + id + "\"" )
2022-05-26 15:18:53 +08:00
}
2024-10-21 10:32:13 +08:00
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 )
2022-05-26 15:18:53 +08:00
}
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
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 )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
ref := scanRefRows ( rows )
ret = append ( ret , ref )
}
return
}
2024-11-22 23:27:38 +08:00
func DefRefs ( condition string , limit int ) ( ret [ ] map [ * Block ] * Block ) {
2022-05-26 15:18:53 +08:00
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 )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
refs := map [ string ] * Block { }
for rows . Next ( ) {
var ref Block
var rel string
2022-08-28 10:43:56 +08:00
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 ,
2024-09-04 04:40:50 +03:00
& rel ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
refs [ rel ] = & ref
}
2024-11-22 23:27:38 +08:00
rows , err = query ( "SELECT def.* FROM blocks AS def, refs AS r WHERE def.id = r.def_block_id LIMIT ?" , limit )
2024-09-04 04:40:50 +03:00
if err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query failed: %s" , err )
2022-05-26 15:18:53 +08:00
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
2024-09-04 04:40:50 +03:00
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 ) ; err != nil {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
ret = & ref
return
}