2019-04-20 23:31:14 +02:00
# include <QStack>
# include <QRegularExpression>
2019-04-22 21:07:41 +02:00
# include <QSqlQuery>
# include <QSqlError>
2019-04-25 10:27:54 +02:00
# include <QStringList>
2019-04-22 21:07:41 +02:00
# include <QDebug>
2019-04-20 23:31:14 +02:00
# include "sqlitesearch.h"
2019-04-22 21:07:41 +02:00
# include "qssgeneralexception.h"
2019-04-20 23:31:14 +02:00
2019-04-22 21:07:41 +02:00
SqliteSearch : : SqliteSearch ( QSqlDatabase & db )
2019-04-20 23:31:14 +02:00
{
2019-04-22 21:07:41 +02:00
this - > db = & db ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
QString SqliteSearch : : fieldToColumn ( QueryField field )
2019-04-20 23:31:14 +02:00
{
2019-08-17 11:06:35 +02:00
if ( field = = FILE_MTIME )
2019-04-20 23:31:14 +02:00
{
2019-08-17 11:06:35 +02:00
return " file.mtime " ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
else if ( field = = FILE_PATH )
2019-04-20 23:31:14 +02:00
{
2019-08-17 11:06:35 +02:00
return " file.path " ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
else if ( field = = FILE_SIZE )
2019-04-25 10:27:54 +02:00
{
2019-08-17 11:06:35 +02:00
return " file.size " ;
2019-04-25 10:27:54 +02:00
}
2019-08-17 11:06:35 +02:00
else if ( field = = CONTENT_TEXT_PAGE )
2019-04-25 10:27:54 +02:00
{
return " content.page " ;
}
2019-08-17 11:06:35 +02:00
else if ( field = = CONTENT_TEXT )
2019-04-25 10:27:54 +02:00
{
2019-08-17 11:06:35 +02:00
return " content.text " ;
2019-04-25 10:27:54 +02:00
}
return " " ;
}
2019-08-17 11:06:35 +02:00
QString SqliteSearch : : createSortSql ( const QVector < SortCondition > sortConditions )
2019-04-25 10:27:54 +02:00
{
2019-04-27 21:23:06 +02:00
QString sortsql ;
2019-08-17 11:06:35 +02:00
for ( const SortCondition & sc : sortConditions )
2019-04-25 10:27:54 +02:00
{
2019-08-17 11:06:35 +02:00
QString order ;
QString field = fieldToColumn ( sc . field ) ;
2019-05-04 20:40:43 +02:00
if ( field = = " " )
{
throw QSSGeneralException ( " Unknown sort field supplied " ) ;
}
2019-08-17 11:06:35 +02:00
if ( sc . order = = DESC )
2019-04-25 10:27:54 +02:00
{
2019-08-17 11:06:35 +02:00
order = " DESC " ;
2019-04-25 10:27:54 +02:00
}
else
{
2019-05-04 20:40:43 +02:00
order = " ASC " ;
2019-04-25 10:27:54 +02:00
}
2019-08-17 11:06:35 +02:00
sortsql + = field + " " + order + " , " ;
}
sortsql . chop ( 2 ) ;
if ( ! sortsql . isEmpty ( ) )
{
return " ORDER BY " + sortsql ;
2019-04-25 10:27:54 +02:00
}
2019-08-17 11:06:35 +02:00
return " " ;
}
2019-05-04 20:40:43 +02:00
2019-08-17 11:06:35 +02:00
QPair < QString , QVector < QString > > createNonArgPair ( QString key )
{
return { " " + key + " " , QVector < QString > ( ) } ;
2019-04-25 10:27:54 +02:00
}
2019-04-20 23:31:14 +02:00
2019-08-17 11:06:35 +02:00
QPair < QString , QVector < QString > > SqliteSearch : : createSql ( const Token & token )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
QPair < QString , QVector < QString > > result ;
2019-04-20 23:31:14 +02:00
QString value = token . value ;
value = value . replace ( " ' " , " \\ ' " ) ;
2019-08-17 11:06:35 +02:00
if ( token . type = = BOOL_AND )
{
return createNonArgPair ( " AND " ) ;
}
if ( token . type = = BOOL_OR )
{
return createNonArgPair ( " OR " ) ;
}
if ( token . type = = NEGATION )
{
return createNonArgPair ( " NOT " ) ;
}
if ( token . type = = BRACKET_OPEN )
2019-04-20 23:31:14 +02:00
{
2019-08-17 11:06:35 +02:00
return createNonArgPair ( " ( " ) ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
if ( token . type = = BRACKET_CLOSE )
2019-04-20 23:31:14 +02:00
{
2019-08-17 11:06:35 +02:00
return createNonArgPair ( " ) " ) ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
if ( token . type = = FILTER_PATH_STARTS )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
return { " file.path LIKE ? || '%' " , { value } } ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
if ( token . type = = FILTER_PATH_ENDS )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
return { " file.path LIKE '%' || ? " , { value } } ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
if ( token . type = = FILTER_PATH_CONTAINS )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
return { " file.path LIKE '%' || ? || '%' " , { value } } ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
if ( token . type = = FILTER_CONTENT_PAGE )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
return { " content.page = ? " , { value } } ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
if ( token . type = = FILTER_CONTENT_CONTAINS )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
return { " content.id IN (SELECT content_fts.ROWID FROM content_fts WHERE content_fts.content MATCH ?) " , { value } } ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
throw QSSGeneralException ( " Unknown token passed (should not happen) " ) ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
QSqlQuery SqliteSearch : : makeSqlQuery ( const QSSQuery & query )
2019-04-20 23:31:14 +02:00
{
2019-04-25 10:27:54 +02:00
QString whereSql ;
QVector < QString > bindValues ;
2019-08-17 11:06:35 +02:00
bool isContentSearch = query . getTokensMask ( ) & FILTER_CONTENT = = FILTER_CONTENT ;
if ( query . getTokens ( ) . isEmpty ( ) )
2019-04-20 23:31:14 +02:00
{
2019-08-17 11:06:35 +02:00
throw QSSGeneralException ( " Nothing to search for supplied " ) ;
2019-04-20 23:31:14 +02:00
}
2019-08-17 11:06:35 +02:00
for ( const Token & token : query . getTokens ( ) )
2019-05-04 20:40:43 +02:00
{
2019-08-17 11:06:35 +02:00
auto sql = createSql ( token ) ;
whereSql + = sql . first ;
bindValues . append ( sql . second ) ;
2019-05-04 20:40:43 +02:00
}
2019-08-17 11:06:35 +02:00
QString prepSql ;
QString sortSql = createSortSql ( query . getSortConditions ( ) ) ;
2019-04-25 10:27:54 +02:00
if ( isContentSearch )
2019-04-22 21:07:41 +02:00
{
2019-04-26 21:41:20 +02:00
if ( sortSql . isEmpty ( ) )
{
sortSql = " ORDER BY file.mtime DESC, content.page ASC " ;
}
2019-04-27 21:23:06 +02:00
prepSql = " SELECT file.path AS path, group_concat(content.page) AS pages, file.mtime AS mtime, file.size AS size, file.filetype AS filetype FROM file INNER JOIN content ON file.id = content.fileid WHERE 1=1 AND " + whereSql + " GROUP BY file.path " + sortSql ;
2019-04-22 21:07:41 +02:00
}
else
{
2019-04-26 21:41:20 +02:00
if ( sortSql . isEmpty ( ) )
{
sortSql = " ORDER BY file.mtime DESC " ;
}
2019-04-26 15:31:42 +02:00
prepSql = " SELECT file.path AS path, '0' as pages, file.mtime AS mtime, file.size AS size, file.filetype AS filetype FROM file WHERE 1=1 AND " + whereSql + " " + sortSql ;
2019-04-22 21:07:41 +02:00
}
2019-04-25 10:27:54 +02:00
2019-04-22 21:07:41 +02:00
QSqlQuery dbquery ( * db ) ;
2019-04-25 10:27:54 +02:00
dbquery . prepare ( prepSql ) ;
for ( const QString & value : bindValues )
{
if ( value ! = " " )
{
dbquery . addBindValue ( value ) ;
}
}
return dbquery ;
}
2019-08-17 11:06:35 +02:00
QVector < SearchResult > SqliteSearch : : search ( const QSSQuery & query )
2019-04-25 10:27:54 +02:00
{
QVector < SearchResult > results ;
2019-08-17 11:06:35 +02:00
QSqlQuery dbQuery = makeSqlQuery ( query ) ;
2019-04-25 10:27:54 +02:00
bool success = dbQuery . exec ( ) ;
2019-04-22 21:07:41 +02:00
if ( ! success )
{
2019-04-25 10:27:54 +02:00
qDebug ( ) < < dbQuery . lastError ( ) ;
2019-04-27 21:23:06 +02:00
qDebug ( ) < < dbQuery . executedQuery ( ) ;
2019-04-25 10:27:54 +02:00
throw QSSGeneralException ( " SQL Error: " + dbQuery . lastError ( ) . text ( ) ) ;
2019-04-22 21:07:41 +02:00
}
2019-04-20 23:31:14 +02:00
2019-04-25 10:27:54 +02:00
while ( dbQuery . next ( ) )
2019-04-22 21:07:41 +02:00
{
SearchResult result ;
2019-04-25 10:27:54 +02:00
result . fileData . absPath = dbQuery . value ( " path " ) . toString ( ) ;
result . fileData . mtime = dbQuery . value ( " mtime " ) . toUInt ( ) ;
result . fileData . size = dbQuery . value ( " size " ) . toUInt ( ) ;
result . fileData . filetype = dbQuery . value ( " filetype " ) . toChar ( ) ;
2019-04-26 15:31:42 +02:00
QString pages = dbQuery . value ( " pages " ) . toString ( ) ;
QStringList pagesList = pages . split ( " , " ) ;
for ( QString & page : pagesList )
{
if ( page ! = " " )
{
result . pages . append ( page . toUInt ( ) ) ;
}
}
2019-04-22 21:07:41 +02:00
results . append ( result ) ;
}
return results ;
2019-04-20 23:31:14 +02:00
}