@ -67,25 +67,26 @@ local function match(str, query, case_insensitive)
end
end
-- get books that exactly match the search tag
local function getBooksBy Tag( t , tag )
-- get books that exactly match the search in a specific flat field (series or title)
local function getBooksBy Field( t , field , query )
local result = { }
for _ , book in ipairs ( t ) do
for __ , _tag in ipairs ( book.tags ) do
if tag == _tag then
local data = book [ field ]
-- We can compare nil & rapidjson.null (light userdata) to a string safely
if data == query then
table.insert ( result , book )
end
end
end
return result
end
-- get books that exactly match the search series
local function getBooksBy Series( t , series )
-- get books that exactly match the search in a specific array (tags or authors)
local function getBooksBy NestedField( t , field , query )
local result = { }
for _ , book in ipairs ( t ) do
if book.series and book.series ~= rapidjson.null then
if book.series == series then
local array = book [ field ]
for __ , data in ipairs ( array ) do
if data == query then
table.insert ( result , book )
end
end
@ -93,28 +94,29 @@ local function getBooksBySeries(t, series)
return result
end
-- ge t tags that match the search criteria and their frequency
local function searchBy Tag( t , query , case_insensitive )
-- ge neric search in a specific flat field (series or title), matching the search criteria and their frequency
local function searchBy Field( t , field , query , case_insensitive )
local freq = { }
for _ , book in ipairs ( t ) do
if type ( book.tags ) == " table " then
for __ , tag in ipairs ( book.tags ) do
if match ( tag , query , case_insensitive ) then
freq [ tag ] = ( freq [ tag ] or 0 ) + 1
end
local data = book [ field ]
-- We have to make sure we only pass strings to match
if data and data ~= rapidjson.null then
if match ( data , query , case_insensitive ) then
freq [ data ] = ( freq [ data ] or 0 ) + 1
end
end
end
return freq
end
-- ge t series that match the search criteria and their frequency
local function searchBy Series( t , query , case_insensitive )
-- ge neric search in a specific array (tags or authors), matching the search criteria and their frequency
local function searchBy NestedField( t , field , query , case_insensitive )
local freq = { }
for _ , book in ipairs ( t ) do
if book.series and book.series ~= rapidjson.null then
if match ( book.series , query , case_insensitive ) then
freq [ book.series ] = ( freq [ book.series ] or 0 ) + 1
local array = book [ field ]
for __ , data in ipairs ( array ) do
if match ( data , query , case_insensitive ) then
freq [ data ] = ( freq [ data ] or 0 ) + 1
end
end
end
@ -162,11 +164,17 @@ local CalibreSearch = WidgetContainer:extend{
libraries = { } ,
natsort_cache = { } ,
last_scan = { } ,
search_options = {
-- These are enabled by default
default_search_options = {
" cache_metadata " ,
" case_insensitive " ,
" find_by_title " ,
" find_by_authors " ,
} ,
-- These aren't
extra_search_options = {
" find_by_series " ,
" find_by_tag " ,
" find_by_path " ,
} ,
@ -205,6 +213,26 @@ function CalibreSearch:ShowSearch()
end ,
} ,
} ,
{
{
text = _ ( " Browse authors " ) ,
enabled = true ,
callback = function ( )
self.search_value = self.search_dialog : getInputText ( )
self.lastsearch = " authors "
self : close ( )
end ,
} ,
{
text = _ ( " Browse titles " ) ,
enabled = true ,
callback = function ( )
self.search_value = self.search_dialog : getInputText ( )
self.lastsearch = " title "
self : close ( )
end ,
} ,
} ,
{
{
text = _ ( " Cancel " ) ,
@ -300,11 +328,14 @@ function CalibreSearch:bookCatalog(t, option)
return catalog
end
-- find books, series or tag s
-- find books, series , tags, authors or title s
function CalibreSearch : find ( option )
for _ , opt in ipairs ( self. search_options) do
for _ , opt in ipairs ( self. default_ search_options) do
self [ opt ] = G_reader_settings : nilOrTrue ( " calibre_search_ " .. opt )
end
for _ , opt in ipairs ( self.extra_search_options ) do
self [ opt ] = G_reader_settings : isTrue ( " calibre_search_ " .. opt )
end
if # self.libraries == 0 then
local libs , err = self.cache_libs : load ( )
@ -336,6 +367,8 @@ function CalibreSearch:find(option)
" case sensitive: " .. tostring ( not self.case_insensitive ) ,
" title: " .. tostring ( self.find_by_title ) ,
" authors: " .. tostring ( self.find_by_authors ) ,
" series: " .. tostring ( self.find_by_series ) ,
" tag: " .. tostring ( self.find_by_tag ) ,
" path: " .. tostring ( self.find_by_path ) ) )
end
@ -362,6 +395,16 @@ function CalibreSearch:findBooks(query)
end
end
end
if self.find_by_series and bookMatch ( book.series , pattern ) then
return true
end
if self.find_by_tag then
for _ , tag in ipairs ( book.tags ) do
if bookMatch ( tag , pattern ) then
return true
end
end
end
if self.find_by_path and bookMatch ( book.lpath , pattern ) then
return true
end
@ -393,16 +436,24 @@ function CalibreSearch:browse(option)
local source
if option == " tags " then
name = _ ( " Browse by tags " )
source = searchBy Tag( self.books , search_value , self.case_insensitive )
source = searchBy NestedField( self.books , option , search_value , self.case_insensitive )
elseif option == " series " then
name = _ ( " Browse by series " )
source = searchBySeries ( self.books , search_value , self.case_insensitive )
source = searchByField ( self.books , option , search_value , self.case_insensitive )
elseif option == " authors " then
name = _ ( " Browse by authors " )
source = searchByNestedField ( self.books , option , search_value , self.case_insensitive )
elseif option == " title " then
name = _ ( " Browse by titles " )
-- This is admittedly only midly useful in the face of the generic search above,
-- but makes finding duplicate titles easy, at least ;).
source = searchByField ( self.books , option , search_value , self.case_insensitive )
end
for k , v in pairs ( source ) do
local entry = { }
entry.text = string.format ( " %s (%d) " , k , v )
entry.callback = function ( )
self : expandTagOrSeries ( option , k )
self : expand SearchResults( option , k )
end
table.insert ( menu_entries , entry )
end
@ -430,13 +481,13 @@ function CalibreSearch:browse(option)
UIManager : show ( self.search_menu )
end
function CalibreSearch : expand TagOrSerie s( option , chosen_item )
function CalibreSearch : expand SearchResult s( option , chosen_item )
local results
if option == " tags " then
results = getBooksBy Tag( self.books , chosen_item )
else if option == " series " then
results = getBooksBy Series( self.books , chosen_item )
if option == " tags " or option == " authors " then
results = getBooksBy NestedField( self.books , option , chosen_item )
else
results = getBooksBy Field( self.books , option , chosen_item )
end
if results then
local catalog = self : bookCatalog ( results , option )