mirror of https://github.com/fmartingr/shiori.git
Add search command
This commit is contained in:
parent
155d0f646d
commit
fa6f0eebc3
|
@ -1,3 +1,6 @@
|
|||
# Exclude sample
|
||||
sample.txt
|
||||
|
||||
# Exclude config file
|
||||
.vscode/
|
||||
*.toml
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
searchCmd = &cobra.Command{
|
||||
Use: "search keyword",
|
||||
Short: "Search bookmarks by submitted keyword.",
|
||||
Long: "Search bookmarks by looking for matching keyword in bookmark's title and content. " +
|
||||
"If no keyword submitted, print all saved bookmarks. " +
|
||||
"Search results will be different depending on DBMS that used by shiori :\n" +
|
||||
"- sqlite3, search works using fts4 method: https://www.sqlite.org/fts3.html.\n" +
|
||||
"- mysql or mariadb, search works using natural language mode: https://dev.mysql.com/doc/refman/5.5/en/fulltext-natural-language.html.",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Read flags
|
||||
useJSON, _ := cmd.Flags().GetBool("json")
|
||||
tags, _ := cmd.Flags().GetStringSlice("tags")
|
||||
|
||||
// Fetch keyword
|
||||
keyword := ""
|
||||
if len(args) > 0 {
|
||||
keyword = args[0]
|
||||
}
|
||||
|
||||
// Read bookmarks from database
|
||||
bookmarks, err := DB.SearchBookmarks(keyword, tags...)
|
||||
if err != nil {
|
||||
cError.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(bookmarks) == 0 {
|
||||
cError.Println("No matching bookmarks found")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print data
|
||||
if useJSON {
|
||||
bt, err := json.MarshalIndent(&bookmarks, "", " ")
|
||||
if err != nil {
|
||||
cError.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(bt))
|
||||
} else {
|
||||
printBookmark(bookmarks...)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
searchCmd.Flags().BoolP("json", "j", false, "Output data in JSON format")
|
||||
searchCmd.Flags().StringSliceP("tags", "t", []string{}, "Search bookmarks with specified tag(s)")
|
||||
rootCmd.AddCommand(searchCmd)
|
||||
}
|
|
@ -10,6 +10,7 @@ type Database interface {
|
|||
SaveBookmark(article readability.Article, tags ...string) (model.Bookmark, error)
|
||||
GetBookmarks(indices ...string) ([]model.Bookmark, error)
|
||||
DeleteBookmarks(indices ...string) ([]int, []int, error)
|
||||
SearchBookmarks(keyword string, tags ...string) ([]model.Bookmark, error)
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
|
|
|
@ -210,7 +210,7 @@ func (db *SQLiteDatabase) GetBookmarks(indices ...string) ([]model.Bookmark, err
|
|||
whereClause = " WHERE id IN ("
|
||||
for _, idx := range listIndex {
|
||||
args = append(args, idx)
|
||||
whereClause += fmt.Sprintf("%d,", idx)
|
||||
whereClause += "?,"
|
||||
}
|
||||
|
||||
whereClause = whereClause[:len(whereClause)-1]
|
||||
|
@ -293,7 +293,7 @@ func (db *SQLiteDatabase) DeleteBookmarks(indices ...string) (oldIndices, newInd
|
|||
whereClause = " WHERE id IN ("
|
||||
for _, idx := range listIndex {
|
||||
args = append(args, idx)
|
||||
whereClause += fmt.Sprintf("%d,", idx)
|
||||
whereClause += "?,"
|
||||
}
|
||||
|
||||
whereClause = whereClause[:len(whereClause)-1]
|
||||
|
@ -361,3 +361,68 @@ func (db *SQLiteDatabase) DeleteBookmarks(indices ...string) (oldIndices, newInd
|
|||
|
||||
return oldIndices, newIndices, err
|
||||
}
|
||||
|
||||
func (db *SQLiteDatabase) SearchBookmarks(keyword string, tags ...string) ([]model.Bookmark, error) {
|
||||
// Create initial variable
|
||||
keyword = strings.TrimSpace(keyword)
|
||||
whereClause := "WHERE 1"
|
||||
args := []interface{}{}
|
||||
|
||||
// Create where clause for keyword
|
||||
if keyword != "" {
|
||||
whereClause += ` AND id IN (
|
||||
SELECT docid id FROM bookmark_content
|
||||
WHERE bookmark_content MATCH ?)`
|
||||
args = append(args, keyword)
|
||||
}
|
||||
|
||||
// Create where clause for tags
|
||||
if len(tags) > 0 {
|
||||
whereTagClause := ` AND id IN (
|
||||
SELECT DISTINCT bookmark_id FROM bookmark_tag
|
||||
WHERE tag_id IN (SELECT id FROM tag WHERE name IN (`
|
||||
|
||||
for _, tag := range tags {
|
||||
args = append(args, tag)
|
||||
whereTagClause += "?,"
|
||||
}
|
||||
|
||||
whereTagClause = whereTagClause[:len(whereTagClause)-1]
|
||||
whereTagClause += ")))"
|
||||
|
||||
whereClause += whereTagClause
|
||||
}
|
||||
|
||||
// Search bookmarks
|
||||
query := `SELECT id,
|
||||
url, title, image_url, excerpt, author,
|
||||
language, min_read_time, max_read_time, modified
|
||||
FROM bookmark ` + whereClause
|
||||
|
||||
bookmarks := []model.Bookmark{}
|
||||
err := db.Select(&bookmarks, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetch tags for each bookmarks
|
||||
stmtGetTags, err := db.Preparex(`SELECT t.id, t.name
|
||||
FROM bookmark_tag bt LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE bt.bookmark_id = ? ORDER BY t.name`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmtGetTags.Close()
|
||||
|
||||
for i := range bookmarks {
|
||||
tags := []model.Tag{}
|
||||
err = stmtGetTags.Select(&tags, bookmarks[i].ID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bookmarks[i].Tags = tags
|
||||
}
|
||||
|
||||
return bookmarks, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue