mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-06 06:07:37 +02:00
* Fixed TWK-474: proper fulltext search.
This commit is contained in:
@@ -81,11 +81,9 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
|
|||||||
typedef QPair<int, float> scorepair_t;
|
typedef QPair<int, float> scorepair_t;
|
||||||
|
|
||||||
// STEP 1
|
// STEP 1
|
||||||
QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist(), false );
|
QList< QPair<int, float> > tracks = lib->search( m_query );
|
||||||
QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track(), false );
|
|
||||||
QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album(), false );
|
|
||||||
|
|
||||||
if ( artists.length() == 0 || tracks.length() == 0 )
|
if ( tracks.length() == 0 )
|
||||||
{
|
{
|
||||||
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
|
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
|
||||||
emit results( m_query->id(), res );
|
emit results( m_query->id(), res );
|
||||||
@@ -95,13 +93,10 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
|
|||||||
// STEP 2
|
// STEP 2
|
||||||
TomahawkSqlQuery files_query = lib->newquery();
|
TomahawkSqlQuery files_query = lib->newquery();
|
||||||
|
|
||||||
QStringList artsl, trksl;
|
QStringList trksl;
|
||||||
for ( int k = 0; k < artists.count(); k++ )
|
|
||||||
artsl.append( QString::number( artists.at( k ).first ) );
|
|
||||||
for ( int k = 0; k < tracks.count(); k++ )
|
for ( int k = 0; k < tracks.count(); k++ )
|
||||||
trksl.append( QString::number( tracks.at( k ).first ) );
|
trksl.append( QString::number( tracks.at( k ).first ) );
|
||||||
|
|
||||||
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
|
|
||||||
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
|
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
|
||||||
|
|
||||||
QString sql = QString( "SELECT "
|
QString sql = QString( "SELECT "
|
||||||
@@ -124,8 +119,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
|
|||||||
"artist.id = file_join.artist AND "
|
"artist.id = file_join.artist AND "
|
||||||
"track.id = file_join.track AND "
|
"track.id = file_join.track AND "
|
||||||
"file.id = file_join.file AND "
|
"file.id = file_join.file AND "
|
||||||
"(%1 AND %2)" )
|
"(%1)" )
|
||||||
.arg( artsToken )
|
|
||||||
.arg( trksToken );
|
.arg( trksToken );
|
||||||
|
|
||||||
files_query.prepare( sql );
|
files_query.prepare( sql );
|
||||||
@@ -201,34 +195,9 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
|
|||||||
typedef QPair<int, float> scorepair_t;
|
typedef QPair<int, float> scorepair_t;
|
||||||
|
|
||||||
// STEP 1
|
// STEP 1
|
||||||
QList< QPair<int, float> > artistPairs = lib->searchTable( "artist", m_query->fullTextQuery(), false, 20 );
|
QList< QPair<int, float> > trackPairs = lib->search( m_query );
|
||||||
QList< QPair<int, float> > albumPairs = lib->searchTable( "album", m_query->fullTextQuery(), false, 20 );
|
QList< QPair<int, float> > albumPairs = lib->searchAlbum( m_query, 20 );
|
||||||
QList< QPair<int, float> > trackArtistPairs = lib->searchTable( "trackartist", m_query->fullTextQuery(), true, 20 );
|
|
||||||
|
|
||||||
if ( artistPairs.length() == 0 && albumPairs.length() == 0 && trackArtistPairs.length() == 0 )
|
|
||||||
{
|
|
||||||
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
|
|
||||||
emit results( m_query->id(), res );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ( const scorepair_t& artistPair, artistPairs )
|
|
||||||
{
|
|
||||||
TomahawkSqlQuery query = lib->newquery();
|
|
||||||
|
|
||||||
QString sql = QString( "SELECT name FROM artist WHERE id = %1" ).arg( artistPair.first );
|
|
||||||
query.prepare( sql );
|
|
||||||
query.exec();
|
|
||||||
|
|
||||||
QList<Tomahawk::artist_ptr> artistList;
|
|
||||||
while ( query.next() )
|
|
||||||
{
|
|
||||||
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistPair.first, query.value( 0 ).toString() );
|
|
||||||
artistList << artist;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit artists( m_query->id(), artistList );
|
|
||||||
}
|
|
||||||
foreach ( const scorepair_t& albumPair, albumPairs )
|
foreach ( const scorepair_t& albumPair, albumPairs )
|
||||||
{
|
{
|
||||||
TomahawkSqlQuery query = lib->newquery();
|
TomahawkSqlQuery query = lib->newquery();
|
||||||
@@ -248,12 +217,19 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
|
|||||||
emit albums( m_query->id(), albumList );
|
emit albums( m_query->id(), albumList );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( trackPairs.length() == 0 )
|
||||||
|
{
|
||||||
|
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->fullTextQuery();
|
||||||
|
emit results( m_query->id(), res );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// STEP 2
|
// STEP 2
|
||||||
TomahawkSqlQuery files_query = lib->newquery();
|
TomahawkSqlQuery files_query = lib->newquery();
|
||||||
|
|
||||||
QStringList trksl;
|
QStringList trksl;
|
||||||
for ( int k = 0; k < trackArtistPairs.count(); k++ )
|
for ( int k = 0; k < trackPairs.count(); k++ )
|
||||||
trksl.append( QString::number( trackArtistPairs.at( k ).first ) );
|
trksl.append( QString::number( trackPairs.at( k ).first ) );
|
||||||
|
|
||||||
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
|
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
|
||||||
QString sql = QString( "SELECT "
|
QString sql = QString( "SELECT "
|
||||||
@@ -325,11 +301,11 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
|
|||||||
result->setAlbumPos( files_query.value( 17 ).toUInt() );
|
result->setAlbumPos( files_query.value( 17 ).toUInt() );
|
||||||
result->setTrackId( files_query.value( 9 ).toUInt() );
|
result->setTrackId( files_query.value( 9 ).toUInt() );
|
||||||
|
|
||||||
for ( int k = 0; k < trackArtistPairs.count(); k++ )
|
for ( int k = 0; k < trackPairs.count(); k++ )
|
||||||
{
|
{
|
||||||
if ( trackArtistPairs.at( k ).first == (int)result->trackId() )
|
if ( trackPairs.at( k ).first == (int)result->trackId() )
|
||||||
{
|
{
|
||||||
result->setScore( trackArtistPairs.at( k ).second );
|
result->setScore( trackPairs.at( k ).second );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,40 +44,40 @@ DatabaseCommand_UpdateSearchIndex::~DatabaseCommand_UpdateSearchIndex()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
DatabaseCommand_UpdateSearchIndex::indexTable( DatabaseImpl* db, const QString& table, const QString& query )
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
|
|
||||||
TomahawkSqlQuery q = db->newquery();
|
|
||||||
qDebug() << "Building index for" << table;
|
|
||||||
q.exec( QString( "SELECT %1" ).arg( query ) );
|
|
||||||
|
|
||||||
QMap< unsigned int, QString > fields;
|
|
||||||
QString value;
|
|
||||||
while ( q.next() )
|
|
||||||
{
|
|
||||||
value = "";
|
|
||||||
for ( int v = 1; v < q.record().count(); v++ )
|
|
||||||
value += q.value( v ).toString() + " ";
|
|
||||||
|
|
||||||
fields.insert( q.value( 0 ).toUInt(), value.trimmed() );
|
|
||||||
}
|
|
||||||
|
|
||||||
db->m_fuzzyIndex->appendFields( table, fields );
|
|
||||||
qDebug() << "Building index for" << table << "finished.";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
|
DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
|
||||||
{
|
{
|
||||||
db->m_fuzzyIndex->beginIndexing();
|
db->m_fuzzyIndex->beginIndexing();
|
||||||
|
|
||||||
indexTable( db, "artist", "id, name FROM artist" );
|
QMap< unsigned int, QMap< QString, QString > > data;
|
||||||
indexTable( db, "album", "id, name FROM album" );
|
TomahawkSqlQuery q = db->newquery();
|
||||||
indexTable( db, "track", "id, name FROM track" );
|
|
||||||
indexTable( db, "trackartist", "track.id, artist.name, track.name FROM track, artist WHERE artist.id = track.artist" );
|
q.exec( "SELECT track.id, track.name, artist.name, artist.id FROM track, artist WHERE artist.id = track.artist" );
|
||||||
|
while ( q.next() )
|
||||||
|
{
|
||||||
|
QMap< QString, QString > track;
|
||||||
|
track.insert( "track", q.value( 1 ).toString() );
|
||||||
|
track.insert( "artist", q.value( 2 ).toString() );
|
||||||
|
track.insert( "artistid", q.value( 3 ).toString() );
|
||||||
|
|
||||||
|
data.insert( q.value( 0 ).toUInt(), track );
|
||||||
|
}
|
||||||
|
|
||||||
|
db->m_fuzzyIndex->appendFields( data );
|
||||||
|
data.clear();
|
||||||
|
|
||||||
|
q.exec( "SELECT album.id, album.name FROM album" );
|
||||||
|
while ( q.next() )
|
||||||
|
{
|
||||||
|
QMap< QString, QString > album;
|
||||||
|
album.insert( "album", q.value( 1 ).toString() );
|
||||||
|
|
||||||
|
data.insert( q.value( 0 ).toUInt(), album );
|
||||||
|
}
|
||||||
|
|
||||||
|
db->m_fuzzyIndex->appendFields( data );
|
||||||
|
|
||||||
|
qDebug() << "Building index finished.";
|
||||||
|
|
||||||
db->m_fuzzyIndex->endIndexing();
|
db->m_fuzzyIndex->endIndexing();
|
||||||
}
|
}
|
||||||
|
@@ -35,13 +35,7 @@ public:
|
|||||||
virtual bool doesMutates() const { return true; }
|
virtual bool doesMutates() const { return true; }
|
||||||
virtual void exec( DatabaseImpl* db );
|
virtual void exec( DatabaseImpl* db );
|
||||||
|
|
||||||
signals:
|
|
||||||
void indexUpdated();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void indexTable( DatabaseImpl* db, const QString& table, const QString& query );
|
|
||||||
|
|
||||||
QString table;
|
|
||||||
IndexingJobItem* m_statusJob;
|
IndexingJobItem* m_statusJob;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -78,7 +78,7 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
|
|||||||
// in case of unclean shutdown last time:
|
// in case of unclean shutdown last time:
|
||||||
query.exec( "UPDATE source SET isonline = 'false'" );
|
query.exec( "UPDATE source SET isonline = 'false'" );
|
||||||
|
|
||||||
// schemaUpdated = true; // REMOVE ME
|
schemaUpdated = true; // REMOVE ME
|
||||||
m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated );
|
m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated );
|
||||||
if ( schemaUpdated )
|
if ( schemaUpdated )
|
||||||
QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
|
QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
|
||||||
@@ -409,13 +409,36 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool autoCreate )
|
|||||||
|
|
||||||
|
|
||||||
QList< QPair<int, float> >
|
QList< QPair<int, float> >
|
||||||
DatabaseImpl::searchTable( const QString& table, const QString& name, bool fulltext, uint limit )
|
DatabaseImpl::search( const Tomahawk::query_ptr& query, uint limit )
|
||||||
{
|
{
|
||||||
QList< QPair<int, float> > resultslist;
|
QList< QPair<int, float> > resultslist;
|
||||||
if ( table != "artist" && table != "track" && table != "album" && table != "trackartist" )
|
|
||||||
|
QMap< int, float > resultsmap = m_fuzzyIndex->search( query );
|
||||||
|
foreach ( int i, resultsmap.keys() )
|
||||||
|
{
|
||||||
|
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
|
||||||
|
}
|
||||||
|
qSort( resultslist.begin(), resultslist.end(), DatabaseImpl::scorepairSorter );
|
||||||
|
|
||||||
|
if ( !limit )
|
||||||
return resultslist;
|
return resultslist;
|
||||||
|
|
||||||
QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name, fulltext );
|
QList< QPair<int, float> > resultscapped;
|
||||||
|
for ( int i = 0; i < (int)limit && i < resultsmap.count(); i++ )
|
||||||
|
{
|
||||||
|
resultscapped << resultslist.at( i );
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultscapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QList< QPair<int, float> >
|
||||||
|
DatabaseImpl::searchAlbum( const Tomahawk::query_ptr& query, uint limit )
|
||||||
|
{
|
||||||
|
QList< QPair<int, float> > resultslist;
|
||||||
|
|
||||||
|
QMap< int, float > resultsmap = m_fuzzyIndex->searchAlbum( query );
|
||||||
foreach ( int i, resultsmap.keys() )
|
foreach ( int i, resultsmap.keys() )
|
||||||
{
|
{
|
||||||
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
|
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
|
||||||
|
@@ -56,7 +56,8 @@ public:
|
|||||||
int trackId( int artistid, const QString& name_orig, bool autoCreate );
|
int trackId( int artistid, const QString& name_orig, bool autoCreate );
|
||||||
int albumId( int artistid, const QString& name_orig, bool autoCreate );
|
int albumId( int artistid, const QString& name_orig, bool autoCreate );
|
||||||
|
|
||||||
QList< QPair<int, float> > searchTable( const QString& table, const QString& name, bool fulltext, uint limit = 0 );
|
QList< QPair<int, float> > search( const Tomahawk::query_ptr& query, uint limit = 0 );
|
||||||
|
QList< QPair<int, float> > searchAlbum( const Tomahawk::query_ptr& query, uint limit = 0 );
|
||||||
QList< int > getTrackFids( int tid );
|
QList< int > getTrackFids( int tid );
|
||||||
|
|
||||||
static QString sortname( const QString& str, bool replaceArticle = false );
|
static QString sortname( const QString& str, bool replaceArticle = false );
|
||||||
@@ -79,8 +80,6 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void indexReady();
|
void indexReady();
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateIndex();
|
void updateIndex();
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
#include <CLucene.h>
|
#include <CLucene.h>
|
||||||
|
#include <CLucene/queryParser/MultiFieldQueryParser.h>
|
||||||
|
|
||||||
#include "databaseimpl.h"
|
#include "databaseimpl.h"
|
||||||
#include "utils/tomahawkutils.h"
|
#include "utils/tomahawkutils.h"
|
||||||
@@ -84,7 +85,7 @@ FuzzyIndex::beginIndexing()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Creating new index writer.";
|
qDebug() << "Creating new index writer.";
|
||||||
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, true );
|
IndexWriter luceneWriter( m_luceneDir, m_analyzer, true );
|
||||||
}
|
}
|
||||||
catch( CLuceneError& error )
|
catch( CLuceneError& error )
|
||||||
{
|
{
|
||||||
@@ -103,31 +104,49 @@ FuzzyIndex::endIndexing()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
FuzzyIndex::appendFields( const QString& table, const QMap< unsigned int, QString >& fields )
|
FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tDebug() << "Appending to index:" << table << fields.count();
|
tDebug() << "Appending to index:" << trackData.count();
|
||||||
bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
|
bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
|
||||||
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, create );
|
IndexWriter luceneWriter( m_luceneDir, m_analyzer, create );
|
||||||
Document doc;
|
Document doc;
|
||||||
|
|
||||||
QMapIterator< unsigned int, QString > it( fields );
|
QMapIterator< unsigned int, QMap< QString, QString > > it( trackData );
|
||||||
while ( it.hasNext() )
|
while ( it.hasNext() )
|
||||||
{
|
{
|
||||||
it.next();
|
it.next();
|
||||||
unsigned int id = it.key();
|
unsigned int id = it.key();
|
||||||
QString name = it.value();
|
QMap< QString, QString > values = it.value();
|
||||||
{
|
|
||||||
Field* field = _CLNEW Field( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str(),
|
|
||||||
Field::STORE_YES | Field::INDEX_TOKENIZED );
|
|
||||||
doc.add( *field );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if ( values.contains( "track" ) )
|
||||||
{
|
{
|
||||||
Field* field = _CLNEW Field( _T( "id" ), QString::number( id ).toStdWString().c_str(), Field::STORE_YES | Field::INDEX_NO );
|
doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( values.value( "artist" ) ).arg( values.value( "track" ) ) ).toStdWString().c_str(),
|
||||||
doc.add( *field );
|
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
|
||||||
|
|
||||||
|
doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( values.value( "track" ) ).toStdWString().c_str(),
|
||||||
|
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
|
||||||
|
|
||||||
|
doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( values.value( "artist" ) ).toStdWString().c_str(),
|
||||||
|
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
|
||||||
|
|
||||||
|
doc.add( *( _CLNEW Field( _T( "artistid" ), values.value( "artistid" ).toStdWString().c_str(),
|
||||||
|
Field::STORE_YES | Field::INDEX_NO ) ) );
|
||||||
|
|
||||||
|
doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( id ).toStdWString().c_str(),
|
||||||
|
Field::STORE_YES | Field::INDEX_NO ) ) );
|
||||||
}
|
}
|
||||||
|
else if ( values.contains( "album" ) )
|
||||||
|
{
|
||||||
|
doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( values.value( "album" ) ).toStdWString().c_str(),
|
||||||
|
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
|
||||||
|
|
||||||
|
doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( id ).toStdWString().c_str(),
|
||||||
|
Field::STORE_YES | Field::INDEX_NO ) ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Q_ASSERT( false );
|
||||||
|
|
||||||
luceneWriter.addDocument( &doc );
|
luceneWriter.addDocument( &doc );
|
||||||
doc.clear();
|
doc.clear();
|
||||||
@@ -152,7 +171,7 @@ FuzzyIndex::loadLuceneIndex()
|
|||||||
|
|
||||||
|
|
||||||
QMap< int, float >
|
QMap< int, float >
|
||||||
FuzzyIndex::search( const QString& table, const QString& name, bool fulltext )
|
FuzzyIndex::search( const Tomahawk::query_ptr& query )
|
||||||
{
|
{
|
||||||
QMutexLocker lock( &m_mutex );
|
QMutexLocker lock( &m_mutex );
|
||||||
|
|
||||||
@@ -171,47 +190,112 @@ FuzzyIndex::search( const QString& table, const QString& name, bool fulltext )
|
|||||||
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
|
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( name.isEmpty() )
|
float minScore;
|
||||||
return resultsmap;
|
const TCHAR** fields = 0;
|
||||||
|
MultiFieldQueryParser parser( fields, m_analyzer );
|
||||||
|
BooleanQuery* qry = _CLNEW BooleanQuery();
|
||||||
|
|
||||||
Hits* hits = 0;
|
if ( query->isFullTextQuery() )
|
||||||
Query* qry = 0;
|
|
||||||
QueryParser parser( table.toStdWString().c_str(), m_analyzer );
|
|
||||||
|
|
||||||
if ( fulltext )
|
|
||||||
{
|
{
|
||||||
QString escapedName = QString::fromWCharArray( parser.escape( name.toStdWString().c_str() ) );
|
QString escapedQuery = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
|
||||||
|
|
||||||
QStringList sl = DatabaseImpl::sortname( escapedName ).split( " ", QString::SkipEmptyParts );
|
Term* term = _CLNEW Term( _T( "track" ), escapedQuery.toStdWString().c_str() );
|
||||||
qry = parser.parse( QString( "%1:%2~" ).arg( table ).arg( sl.join( "~ " ) ).toStdWString().c_str() );
|
Query* fqry = _CLNEW FuzzyQuery( term );
|
||||||
|
qry->add( fqry, true, BooleanClause::SHOULD );
|
||||||
|
|
||||||
|
term = _CLNEW Term( _T( "artist" ), escapedQuery.toStdWString().c_str() );
|
||||||
|
fqry = _CLNEW FuzzyQuery( term );
|
||||||
|
qry->add( fqry, true, BooleanClause::SHOULD );
|
||||||
|
|
||||||
|
term = _CLNEW Term( _T( "fulltext" ), escapedQuery.toStdWString().c_str() );
|
||||||
|
fqry = _CLNEW FuzzyQuery( term );
|
||||||
|
qry->add( fqry, true, BooleanClause::SHOULD );
|
||||||
|
|
||||||
|
minScore = 0.00;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// qry = _CLNEW FuzzyQuery( _CLNEW Term( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str() ) );
|
QString track = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->track() ).toStdWString().c_str() ) );
|
||||||
QString escapedName = QString::fromWCharArray( parser.escape( name.toStdWString().c_str() ) );
|
QString artist = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->artist() ).toStdWString().c_str() ) );
|
||||||
|
// QString album = QString::fromWCharArray( parser.escape( query->album().toStdWString().c_str() ) );
|
||||||
|
|
||||||
QStringList sl = DatabaseImpl::sortname( escapedName ).split( " ", QString::SkipEmptyParts );
|
Term* term = _CLNEW Term( _T( "track" ), track.toStdWString().c_str() );
|
||||||
qry = parser.parse( QString( "%1:\"%2\"~" ).arg( table ).arg( sl.join( " " ) ).toStdWString().c_str() );
|
Query* fqry = _CLNEW FuzzyQuery( term );
|
||||||
|
qry->add( fqry, true, BooleanClause::MUST );
|
||||||
|
|
||||||
|
term = _CLNEW Term( _T( "artist" ), artist.toStdWString().c_str() );
|
||||||
|
fqry = _CLNEW FuzzyQuery( term );
|
||||||
|
qry->add( fqry, true, BooleanClause::MUST );
|
||||||
|
|
||||||
|
minScore = 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
hits = m_luceneSearcher->search( qry );
|
Hits* hits = m_luceneSearcher->search( qry );
|
||||||
for ( uint i = 0; i < hits->length(); i++ )
|
for ( uint i = 0; i < hits->length(); i++ )
|
||||||
{
|
{
|
||||||
Document* d = &hits->doc( i );
|
Document* d = &hits->doc( i );
|
||||||
|
|
||||||
float score = hits->score( i );
|
float score = hits->score( i );
|
||||||
int id = QString::fromWCharArray( d->get( _T( "id" ) ) ).toInt();
|
int id = QString::fromWCharArray( d->get( _T( "trackid" ) ) ).toInt();
|
||||||
QString result = QString::fromWCharArray( d->get( table.toStdWString().c_str() ) );
|
|
||||||
|
|
||||||
if ( DatabaseImpl::sortname( result ) == DatabaseImpl::sortname( name ) )
|
if ( score > minScore )
|
||||||
score = 1.0;
|
|
||||||
else
|
|
||||||
score = qMin( score, (float)0.99 );
|
|
||||||
|
|
||||||
if ( score > 0.20 )
|
|
||||||
{
|
{
|
||||||
resultsmap.insert( id, score );
|
resultsmap.insert( id, score );
|
||||||
// qDebug() << "Hitres:" << result << id << score << table << name;
|
// tDebug() << "Index hit:" << id << score << QString::fromWCharArray( ((Query*)qry)->toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete hits;
|
||||||
|
delete qry;
|
||||||
|
}
|
||||||
|
catch( CLuceneError& error )
|
||||||
|
{
|
||||||
|
tDebug() << "Caught CLucene error:" << error.what();
|
||||||
|
Q_ASSERT( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultsmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QMap< int, float >
|
||||||
|
FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query )
|
||||||
|
{
|
||||||
|
Q_ASSERT( query->isFullTextQuery() );
|
||||||
|
|
||||||
|
QMutexLocker lock( &m_mutex );
|
||||||
|
|
||||||
|
QMap< int, float > resultsmap;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ( !m_luceneReader )
|
||||||
|
{
|
||||||
|
if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << "index didn't exist.";
|
||||||
|
return resultsmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_luceneReader = IndexReader::open( m_luceneDir );
|
||||||
|
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryParser parser( _T( "album" ), m_analyzer );
|
||||||
|
QString escapedName = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
|
||||||
|
|
||||||
|
Query* qry = _CLNEW FuzzyQuery( _CLNEW Term( _T( "album" ), escapedName.toStdWString().c_str() ) );
|
||||||
|
Hits* hits = m_luceneSearcher->search( qry );
|
||||||
|
for ( uint i = 0; i < hits->length(); i++ )
|
||||||
|
{
|
||||||
|
Document* d = &hits->doc( i );
|
||||||
|
|
||||||
|
float score = hits->score( i );
|
||||||
|
int id = QString::fromWCharArray( d->get( _T( "albumid" ) ) ).toInt();
|
||||||
|
|
||||||
|
if ( score > 0.30 )
|
||||||
|
{
|
||||||
|
resultsmap.insert( id, score );
|
||||||
|
// tDebug() << "Index hit:" << id << score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,6 +25,8 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
|
||||||
|
#include "query.h"
|
||||||
|
|
||||||
namespace lucene
|
namespace lucene
|
||||||
{
|
{
|
||||||
namespace analysis
|
namespace analysis
|
||||||
@@ -58,7 +60,7 @@ public:
|
|||||||
|
|
||||||
void beginIndexing();
|
void beginIndexing();
|
||||||
void endIndexing();
|
void endIndexing();
|
||||||
void appendFields( const QString& table, const QMap< unsigned int, QString >& fields );
|
void appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData );
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void indexReady();
|
void indexReady();
|
||||||
@@ -66,7 +68,8 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void loadLuceneIndex();
|
void loadLuceneIndex();
|
||||||
|
|
||||||
QMap< int, float > search( const QString& table, const QString& name, bool fulltext );
|
QMap< int, float > search( const Tomahawk::query_ptr& query );
|
||||||
|
QMap< int, float > searchAlbum( const Tomahawk::query_ptr& query );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DatabaseImpl& m_db;
|
DatabaseImpl& m_db;
|
||||||
|
@@ -304,7 +304,18 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
|
|||||||
if ( m_overwriteOnAdd )
|
if ( m_overwriteOnAdd )
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
if ( !albums.count() )
|
QList<Tomahawk::album_ptr> trimmedAlbums;
|
||||||
|
foreach ( const album_ptr& album, albums )
|
||||||
|
{
|
||||||
|
if ( !album.isNull() && album->name().length() )
|
||||||
|
{
|
||||||
|
if ( findItem( album ) || trimmedAlbums.contains( album ) )
|
||||||
|
continue;
|
||||||
|
trimmedAlbums << album;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !trimmedAlbums.count() )
|
||||||
{
|
{
|
||||||
emit itemCountChanged( rowCount( QModelIndex() ) );
|
emit itemCountChanged( rowCount( QModelIndex() ) );
|
||||||
return;
|
return;
|
||||||
@@ -313,12 +324,12 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
|
|||||||
int c = rowCount( QModelIndex() );
|
int c = rowCount( QModelIndex() );
|
||||||
QPair< int, int > crows;
|
QPair< int, int > crows;
|
||||||
crows.first = c;
|
crows.first = c;
|
||||||
crows.second = c + albums.count() - 1;
|
crows.second = c + trimmedAlbums.count() - 1;
|
||||||
|
|
||||||
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
|
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
|
||||||
|
|
||||||
AlbumItem* albumitem;
|
AlbumItem* albumitem;
|
||||||
foreach( const album_ptr& album, albums )
|
foreach( const album_ptr& album, trimmedAlbums )
|
||||||
{
|
{
|
||||||
albumitem = new AlbumItem( album, m_rootItem );
|
albumitem = new AlbumItem( album, m_rootItem );
|
||||||
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
|
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
|
||||||
@@ -339,7 +350,18 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
|
|||||||
if ( m_overwriteOnAdd )
|
if ( m_overwriteOnAdd )
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
if ( !artists.count() )
|
QList<Tomahawk::artist_ptr> trimmedArtists;
|
||||||
|
foreach ( const artist_ptr& artist, artists )
|
||||||
|
{
|
||||||
|
if ( !artist.isNull() && artist->name().length() )
|
||||||
|
{
|
||||||
|
if ( findItem( artist ) || trimmedArtists.contains( artist ) )
|
||||||
|
continue;
|
||||||
|
trimmedArtists << artist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !trimmedArtists.count() )
|
||||||
{
|
{
|
||||||
emit itemCountChanged( rowCount( QModelIndex() ) );
|
emit itemCountChanged( rowCount( QModelIndex() ) );
|
||||||
return;
|
return;
|
||||||
@@ -348,12 +370,12 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
|
|||||||
int c = rowCount( QModelIndex() );
|
int c = rowCount( QModelIndex() );
|
||||||
QPair< int, int > crows;
|
QPair< int, int > crows;
|
||||||
crows.first = c;
|
crows.first = c;
|
||||||
crows.second = c + artists.count() - 1;
|
crows.second = c + trimmedArtists.count() - 1;
|
||||||
|
|
||||||
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
|
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
|
||||||
|
|
||||||
AlbumItem* albumitem;
|
AlbumItem* albumitem;
|
||||||
foreach( const artist_ptr& artist, artists )
|
foreach ( const artist_ptr& artist, trimmedArtists )
|
||||||
{
|
{
|
||||||
albumitem = new AlbumItem( artist, m_rootItem );
|
albumitem = new AlbumItem( artist, m_rootItem );
|
||||||
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
|
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
|
||||||
@@ -396,3 +418,35 @@ AlbumModel::onDataChanged()
|
|||||||
AlbumItem* p = (AlbumItem*)sender();
|
AlbumItem* p = (AlbumItem*)sender();
|
||||||
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
|
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AlbumItem*
|
||||||
|
AlbumModel::findItem( const artist_ptr& artist ) const
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
|
||||||
|
{
|
||||||
|
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
|
||||||
|
if ( !item->artist().isNull() && item->artist() == artist )
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AlbumItem*
|
||||||
|
AlbumModel::findItem( const album_ptr& album ) const
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
|
||||||
|
{
|
||||||
|
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
|
||||||
|
if ( !item->album().isNull() && item->album() == album )
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -71,6 +71,9 @@ public:
|
|||||||
virtual void setTitle( const QString& title ) { m_title = title; }
|
virtual void setTitle( const QString& title ) { m_title = title; }
|
||||||
virtual void setDescription( const QString& description ) { m_description = description; }
|
virtual void setDescription( const QString& description ) { m_description = description; }
|
||||||
|
|
||||||
|
AlbumItem* findItem( const Tomahawk::artist_ptr& artist ) const;
|
||||||
|
AlbumItem* findItem( const Tomahawk::album_ptr& album ) const;
|
||||||
|
|
||||||
AlbumItem* itemFromIndex( const QModelIndex& index ) const
|
AlbumItem* itemFromIndex( const QModelIndex& index ) const
|
||||||
{
|
{
|
||||||
if ( index.isValid() )
|
if ( index.isValid() )
|
||||||
|
@@ -130,6 +130,9 @@ SearchWidget::jumpToCurrentTrack()
|
|||||||
void
|
void
|
||||||
SearchWidget::onResultsFound( const QList<Tomahawk::result_ptr>& results )
|
SearchWidget::onResultsFound( const QList<Tomahawk::result_ptr>& results )
|
||||||
{
|
{
|
||||||
|
QList<Tomahawk::artist_ptr> artists;
|
||||||
|
QList<Tomahawk::album_ptr> albums;
|
||||||
|
|
||||||
foreach( const Tomahawk::result_ptr& result, results )
|
foreach( const Tomahawk::result_ptr& result, results )
|
||||||
{
|
{
|
||||||
if ( !result->collection().isNull() && !result->isOnline() )
|
if ( !result->collection().isNull() && !result->isOnline() )
|
||||||
@@ -143,7 +146,13 @@ SearchWidget::onResultsFound( const QList<Tomahawk::result_ptr>& results )
|
|||||||
q->addResults( rl );
|
q->addResults( rl );
|
||||||
|
|
||||||
m_resultsModel->append( q );
|
m_resultsModel->append( q );
|
||||||
|
|
||||||
|
artists << result->artist();
|
||||||
|
albums << result->album();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_artistsModel->addArtists( artists );
|
||||||
|
m_albumsModel->addAlbums( albums );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -32,7 +32,7 @@ ZeroconfFactory::createPlugin( const QString& pluginId )
|
|||||||
return new ZeroconfPlugin( pluginId.isEmpty() ? generateId() : pluginId );
|
return new ZeroconfPlugin( pluginId.isEmpty() ? generateId() : pluginId );
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroconfPlugin::ZeroconfPlugin() : SipPlugin( "") {}
|
ZeroconfPlugin::ZeroconfPlugin() : SipPlugin( "" ) {}
|
||||||
|
|
||||||
ZeroconfPlugin::ZeroconfPlugin ( const QString& pluginId )
|
ZeroconfPlugin::ZeroconfPlugin ( const QString& pluginId )
|
||||||
: SipPlugin( pluginId )
|
: SipPlugin( pluginId )
|
||||||
|
Reference in New Issue
Block a user