Add search command

This commit is contained in:
Radhi Fadlillah 2018-01-29 20:45:27 +07:00
parent 155d0f646d
commit fa6f0eebc3
4 changed files with 133 additions and 2 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# Exclude sample
sample.txt
# Exclude config file
.vscode/
*.toml

62
cmd/search.go Normal file
View File

@ -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)
}

View File

@ -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) {

View File

@ -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
}