[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/calibre d8c85ae51b 8/9: Enable fuzzy searching
|
From: |
ELPA Syncer |
|
Subject: |
[elpa] externals/calibre d8c85ae51b 8/9: Enable fuzzy searching |
|
Date: |
Tue, 16 Jan 2024 12:57:41 -0500 (EST) |
branch: externals/calibre
commit d8c85ae51bafde748c512f980dd54077d0ef7511
Author: Kjartan Oli Agustsson <kjartanoli@disroot.org>
Commit: Kjartan Oli Agustsson <kjartanoli@disroot.org>
Enable fuzzy searching
* calibre-cli.el: (calibre-cli--search-operation): Add a new argument
docstring, specifying the docstring of the generated function.
Add a new argument fuzzy-match to the generated function.
Condition the search query of the generated function on the value of
fuzzy-match.
* calibre-db.el (calibre-db--get-title-books):
(calibre-db--query): New macro to generate SQL queries with
potentially fuzzy matching and handling their output.
(calibre-db--search-function): New macro to generate search functions.
(calibre-db--get-title-books):
(calibre-db--get-author-books):
(calibre-db--get-tag-books):
(calibre-db--get-publisher-books):
(calibre-db--get-series-books):
(calibre-db--get-format-books):
Define using calibre-db--search-function.
* calibre-search.el (calibre-search--fuzzy-search-p): New predicate
function.
(calibre-search--make-filter):
(calibre-search--make-composite-filter-component):
New functions.
(calibre-library--search-function):
(calibre-search): Add option for fuzzy searching.
(calibre-search--composition-function): Handle transient arguments
consistently with calibre-library--search-function.
* doc/calibre.texi (Virtual Libraries): Document fuzzy filters.
* etc/NEWS: Mention the new fuzzy search functionality.
---
calibre-cli.el | 25 +++++++++++---------
calibre-core.el | 19 ++++++++-------
calibre-db.el | 70 +++++++++++++++++++++++++++++++++++--------------------
calibre-search.el | 55 +++++++++++++++++++++++++++++++++----------
doc/calibre.texi | 19 ++++++++++++---
etc/NEWS | 7 +++++-
6 files changed, 134 insertions(+), 61 deletions(-)
diff --git a/calibre-cli.el b/calibre-cli.el
index 2be09163fc..8f948b1e29 100644
--- a/calibre-cli.el
+++ b/calibre-cli.el
@@ -1,6 +1,6 @@
;;; calibre-cli.el --- Fallback CLI interface when SQLite is not available
-*- lexical-binding: t; -*-
-;; Copyright (C) 2023 Free Software Foundation, Inc.
+;; Copyright (C) 2023,2024 Free Software Foundation, Inc.
;; This file is part of calibre.el.
@@ -97,9 +97,12 @@ AUTHORS should be a comma separated string."
"Return the File Name of the book whose files are FILES."
(file-name-base (car files)))
-(defmacro calibre-cli--search-operation (field)
- "Create a function to search for books matching FIELD."
- `(defun ,(intern (format "calibre-cli--get-%s-books" field)) (,field)
+(defmacro calibre-cli--search-operation (field docstring)
+ "Create a function to search for books matching FIELD.
+
+DOCSTRING is the docstring of the created function."
+ `(defun ,(intern (format "calibre-cli--get-%s-books" field)) (,field
&optional fuzzy-match)
+ ,docstring
(with-temp-buffer
(call-process calibre-calibredb-executable
nil
@@ -108,18 +111,18 @@ AUTHORS should be a comma separated string."
"search"
"--with-library"
(calibre--library)
- (format ,(format "%s:=%%s" field) ,field))
+ (format ,(format "%s%%s%%s" field ) (if fuzzy-match ":"
":=") ,field))
(mapcar #'cl-parse-integer
(string-split
(buffer-substring-no-properties (point-min) (point-max))
",")))))
-(calibre-cli--search-operation title)
-(calibre-cli--search-operation series)
-(calibre-cli--search-operation publisher)
-(calibre-cli--search-operation format)
-(calibre-cli--search-operation tag)
-(calibre-cli--search-operation author)
+(calibre-cli--search-operation title "Return the id's of books whose title is
TITLE.")
+(calibre-cli--search-operation series "Return the id's of books that are part
of SERIES.")
+(calibre-cli--search-operation publisher "Return the id's of books published
by PUBLISHER.")
+(calibre-cli--search-operation format "Return the id's of books available in
FORMAT.")
+(calibre-cli--search-operation tag "Return the id's of books tagged with TAG.")
+(calibre-cli--search-operation author "Return the id's of books written by
AUTHOR.")
(defun calibre-cli--get-titles ()
"Return a list of the titles in the active library."
diff --git a/calibre-core.el b/calibre-core.el
index 4e5ecf2623..bc8721b112 100644
--- a/calibre-core.el
+++ b/calibre-core.el
@@ -1,6 +1,6 @@
;;; calibre-core.el --- Abstract interface for the Calibre Library -*-
lexical-binding: t; -*-
-;; Copyright (C) 2023 Free Software Foundation, Inc.
+;; Copyright (C) 2023,2024 Free Software Foundation, Inc.
;; This file is part of calibre.el.
@@ -219,14 +219,15 @@ BOOK is a `calibre-book'."
(if (eq op '+)
'()
(calibre--books))))
- (seq-let (_ field value) filter
- (cl-case field
- (title (calibre-core--interface get-title-books value))
- (author (calibre-core--interface get-author-books value))
- (tag (calibre-core--interface get-tag-books value))
- (publisher (calibre-core--interface get-publisher-books value))
- (series (calibre-core--interface get-series-books value))
- (format (calibre-core--interface get-format-books value))))))
+ (seq-let (_ field value &rest params) filter
+ (let ((fuzzy-match (seq-contains-p params '~)))
+ (cl-case field
+ (title (calibre-core--interface get-title-books value fuzzy-match))
+ (author (calibre-core--interface get-author-books value fuzzy-match))
+ (tag (calibre-core--interface get-tag-books value fuzzy-match))
+ (publisher (calibre-core--interface get-publisher-books value
fuzzy-match))
+ (series (calibre-core--interface get-series-books value fuzzy-match))
+ (format (calibre-core--interface get-format-books value
fuzzy-match)))))))
(defun calibre-library--filter (filters books)
"Return those books in BOOKS that match FILTERS.
diff --git a/calibre-db.el b/calibre-db.el
index 20ffc031f8..5e49d702d4 100644
--- a/calibre-db.el
+++ b/calibre-db.el
@@ -1,6 +1,6 @@
;;; calibre-db.el --- Interact with the Calibre database -*- lexical-binding:t
-*-
-;; Copyright (C) 2023 Free Software Foundation, Inc.
+;; Copyright (C) 2023,2024 Free Software Foundation, Inc.
;; Author: Kjartan Oli Agustsson <kjartanoli@disroot.org>
;; Maintainer: Kjartan Oli Agustsson <kjartanoli@disroot.org>
@@ -151,50 +151,70 @@ FROM books
LEFT JOIN books_series_link sl ON books.id = sl.book
LEFT JOIN series ON sl.series = series.id;"))))
-(defun calibre-db--get-title-books (title)
+(defmacro calibre-db--query (query arg fuzzy)
+ "Run QUERY on the Calibre database.
+
+ARG is a value to passed to a WHERE clause in QUERY. FUZZY
+determines whether the WHERE clause matches using LIKE or exact
+matching. If FUZZY is non-nil LIKE will be used. QUERY must contain exactly
one %s where the matching against ARG should be substituted.
+
+This means QUERY should probably end:
+WHERE column %s
+with column being the name of some column."
+ `(flatten-list (sqlite-select (calibre--db)
+ (format ,query (if ,fuzzy "LIKE ?" "= ?"))
+ (if fuzzy-match
+ (vector (format "%%%s%%" ,arg))
+ (vector ,arg)))))
+
+(defmacro calibre-db--search-function (field docstring query)
+ "Create a search function for FIELD with DOCSTRING as docstring.
+
+QUERY is the SQL query used to perform the search, suitable as an
+argument to `calibre-db--query'."
+ (declare (indent 1))
+ `(defun ,(intern (format "calibre-db--get-%s-books" field))
+ (,field &optional fuzzy-match)
+ ,docstring
+ (calibre-db--query ,query ,field fuzzy-match)))
+
+(calibre-db--search-function title
"Return the id's of books whose title is TITLE."
- (flatten-list (sqlite-select (calibre--db)
- "SELECT id FROM books WHERE title = ?"
- `[,title])))
+ "SELECT id FROM books WHERE title %s")
-(defun calibre-db--get-author-books (author)
+(calibre-db--search-function author
"Return the id's of books written by AUTHOR."
- (flatten-list (sqlite-select (calibre--db)
- "SELECT book
+ "SELECT book
FROM books_authors_link al
LEFT JOIN authors a ON al.author = a.id
-WHERE a.name = ?" `[,author])))
+WHERE a.name %s")
-(defun calibre-db--get-tag-books (tag)
+(calibre-db--search-function tag
"Return the id's of books tagged with TAG."
- (flatten-list (sqlite-select (calibre--db)
- "SELECT book
+ "SELECT book
FROM books_tags_link tl
LEFT JOIN tags t ON tl.tag = t.id
-WHERE t.name = ?" `[,tag])))
+WHERE t.name %s")
-(defun calibre-db--get-publisher-books (publisher)
+(calibre-db--search-function publisher
"Return the id's of books published by PUBLISHER."
- (flatten-list (sqlite-select (calibre--db)
- "SELECT book
+ "SELECT book
FROM books_publishers_link pl
LEFT JOIN publishers p ON pl.publisher = p.id
-WHERE p.name = ?" `[,publisher])))
+WHERE p.name %s")
-(defun calibre-db--get-series-books (series)
+(calibre-db--search-function series
"Return the id's of books that are part of SERIES."
- (flatten-list (sqlite-select (calibre--db)
- "SELECT book
+ "SELECT book
FROM books_series_link sl
LEFT JOIN series s ON sl.series = s.id
-WHERE s.name = ?" `[,series])))
+WHERE s.name %s")
-(defun calibre-db--get-format-books (format)
+(calibre-db--search-function format
"Return the id's of books available in FORMAT."
- (flatten-list (sqlite-select (calibre--db)
- "SELECT book
+ "SELECT book
FROM data
-WHERE format = ?" `[,format])))
+WHERE format %s")
(provide 'calibre-db)
;;; calibre-db.el ends here
diff --git a/calibre-search.el b/calibre-search.el
index 4e22fb84aa..5f32fe9468 100644
--- a/calibre-search.el
+++ b/calibre-search.el
@@ -1,5 +1,5 @@
;;; calibre-search.el --- Filter books based on search criteria. -*-
lexical-binding: t; -*-
-;; Copyright (C) 2023 Free Software Foundation, Inc.
+;; Copyright (C) 2023,2024 Free Software Foundation, Inc.
;; This file is part of calibre.el.
@@ -59,16 +59,32 @@ ARGS is the argument list of a transient command."
'-
'+))
+(defun calibre-search--fuzzy-search-p (args)
+ "Return t if the filter operation should use fuzzy matching.
+ARGS is the argument list of a transient command."
+ (seq-contains-p args "--fuzzy"))
+
+(defun calibre-search--make-filter (op field val fuzzy)
+ "Create a search filter.
+
+OP is a symbol, either + or - for inclusive or exclusive. FIELD
+is a symbol identifying the field to search. VAL is the value to
+search for. If FUZZY is non-nil create a fuzzy filter."
+ (if fuzzy
+ `[,op ,field ,val ~]
+ `[,op ,field ,val]))
+
(defmacro calibre-library--search-function (field)
"Create a function adding a filter for FIELD."
`(defun ,(intern (format "calibre-library-search-%s" field)) (val &optional
args)
(interactive (list (,(intern (format "calibre-search-chose-%s" field)))
(transient-args 'calibre-search)))
- (setf calibre-library--filters (cons
- (vector (calibre-search--operation args)
- (quote ,(intern field))
- val)
- calibre-library--filters))
+ (push (calibre-search--make-filter
+ (calibre-search--operation args)
+ (quote ,(intern field))
+ val
+ (calibre-search--fuzzy-search-p args))
+ calibre-library--filters)
(calibre-library--refresh)))
(calibre-library--search-function "title")
@@ -89,7 +105,8 @@ ARGS is the argument list of a transient command."
"Filter the library view."
:transient-suffix 'transient--do-call
["Arguments"
- ("-e" "Exclude" "--exclude")]
+ ("e" "Exclude" "--exclude")
+ ("~" "Fuzzy" "--fuzzy")]
["Search"
("T" "Title" calibre-library-search-title)
("a" "Author" calibre-library-search-author)
@@ -121,13 +138,26 @@ ARGS is the argument list of a transient command."
(interactive)
(setf calibre-search-composing-filter nil))
+(defun calibre-search--make-composite-filter-component (field val fuzzy)
+ "Create a component of a composite search filter.
+
+FIELD is a symbol identifying the field to search. VAL is the
+value to search for. If FUZZY is non-nil create a fuzzy filter."
+ (if fuzzy
+ `[,field ,val ~]
+ `[,field ,val]))
+
(defmacro calibre-search--composition-function (field)
"Create a function adding a filter for FIELD to a composite filter."
- `(defun ,(intern (format "calibre-search-compose-%s" field)) ()
+ `(defun ,(intern (format "calibre-search-compose-%s" field)) (val &optional
args)
,(format "Add a filter for %s to the composite filter under
construction." field)
- (interactive)
- (setf calibre-search-composing-filter
- (cons (vector ',(intern field) (,(intern (format
"calibre-search-chose-%s" field)))) calibre-search-composing-filter))))
+ (interactive (list (,(intern (format "calibre-search-chose-%s" field)))
+ (transient-args 'calibre-search-compose)))
+ (push (calibre-search--make-composite-filter-component
+ (quote ,(intern field))
+ val
+ (calibre-search--fuzzy-search-p args))
+ calibre-search-composing-filter)))
(calibre-search--composition-function "title")
(calibre-search--composition-function "author")
@@ -140,7 +170,8 @@ ARGS is the argument list of a transient command."
"Create a composite filter."
:transient-suffix 'transient--do-call
["Arguments"
- ("-e" "Exclude" "--exclude")]
+ ("-e" "Exclude" "--exclude")
+ ("~" "Fuzzy" "--fuzzy")]
["Compose"
("T" "Title" calibre-search-compose-title)
("a" "Author" calibre-search-compose-author)
diff --git a/doc/calibre.texi b/doc/calibre.texi
index a705fef6d1..fb148d0d9e 100644
--- a/doc/calibre.texi
+++ b/doc/calibre.texi
@@ -9,7 +9,7 @@ This manual is for calibre.el (version @value{VERSION},
@value{UPDATED}), a package for interacting with Calibre libraries from
Emacs.
-Copyright @copyright{} 2023 Free Software Foundation, Inc.
+Copyright @copyright{} 2023,2024 Free Software Foundation, Inc.
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -241,11 +241,24 @@ it must be one of:
@end itemize
@var{VALUE} is the string to compare @var{FIELD} to.
+@cindex fuzzy filter
+For a given book to match against a basic filter the value of
+@var{FIELD} for that book must be exactly equal to @var{VALUE}.
+Sometimes this exact matching is undesirable and you would like to find
+all books where @var{FIELD} contains @var{VALUE} instead. This is
+exactly what fuzzy filters do.
+
+A fuzzy filter is a vector @code{[@var{OP} @var{FIELD} @var{VALUE} ~]}.
+@var{OP}, @var{FIELD}, and @var{VALUE} have the same meaning as in a
+basic filter. The @code{~} is what identifies the filter as fuzzy.
+
@cindex composite filter
A composite filter, is a vector @code{[@var{OP} @var{FILTERS}]} where
@var{OP} has the same meaning as for basic filters and @var{FILTERS} is
-a list of vectors @code{[@var{FIELD} @var{VALUE}]}. In each such vector
-@var{FIELD} and @var{VALUE} have the same meaning as for a basic filter.
+a list of vectors @code{[@var{FIELD} @var{VALUE}]} or @code{[@var{FIELD}
+@var{VALUE} ~]}. In each such vector @var{FIELD} and @var{VALUE} have
+the same meaning as for a basic filter and a @code{~} at the end marks
+that particular component as fuzzy.
@node Modifying your library
@chapter Modifying your library
diff --git a/etc/NEWS b/etc/NEWS
index 228914094e..4fd68f6f5d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1,6 +1,6 @@
calibre.el NEWS -*- outline -*-
-Copyright (C) 2023 Free Software Foundation, Inc.
+Copyright (C) 2023,2024 Free Software Foundation, Inc.
See the end of the file for license conditions.
This file is about changes in calibre.el, the Emacs client for
@@ -8,6 +8,11 @@ Calibre.
* Changes in calibre.el 1.4.0
+** Enable fuzzy searching
+The search interface now supports 'fuzzy' searching, which matches any
+book whose value for the search field contains the search term as a
+substring.
+
** Allow opening books in external programs
The new calibre-library-open-book-external command allows opening
books using an external program.
- [elpa] externals/calibre updated (2c4221ecfa -> 7fe464d218), ELPA Syncer, 2024/01/16
- [elpa] externals/calibre 828be28570 4/9: Allow opening books in an external program, ELPA Syncer, 2024/01/16
- [elpa] externals/calibre d8c85ae51b 8/9: Enable fuzzy searching,
ELPA Syncer <=
- [elpa] externals/calibre 7fe464d218 9/9: Bump version number, ELPA Syncer, 2024/01/16
- [elpa] externals/calibre 5b2b8a878d 2/9: Move some index commands, ELPA Syncer, 2024/01/16
- [elpa] externals/calibre 1d23ae29ff 1/9: Use pop instead of (setf lst (cdr lst)), ELPA Syncer, 2024/01/16
- [elpa] externals/calibre c350125e48 5/9: Fix docstring, ELPA Syncer, 2024/01/16
- [elpa] externals/calibre 5a92273025 6/9: Fix punctuation, ELPA Syncer, 2024/01/16
- [elpa] externals/calibre 6446221d20 7/9: Reword description of composite filters, ELPA Syncer, 2024/01/16
- [elpa] externals/calibre 339258061e 3/9: Expand file name before opening files, ELPA Syncer, 2024/01/16