diff -Nru kdebase-runtime-4.3.2/nepomuk/kioslaves/search/CMakeLists.txt kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/CMakeLists.txt --- kdebase-runtime-4.3.2/nepomuk/kioslaves/search/CMakeLists.txt 2009-07-21 17:18:35.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/CMakeLists.txt 2009-10-26 09:52:22.000000000 +0100 @@ -12,7 +12,7 @@ ${nepomuk_SOURCE_DIR}/libnepomukquery ) -add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) +add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS} -DDISABLE_NEPOMUK_LEGACY) set(kio_nepomuksearch_PART_SRCS kio_nepomuksearch.cpp @@ -25,16 +25,29 @@ ${nepomuk_SOURCE_DIR}/libnepomukquery/dbusoperators.cpp ) -soprano_add_ontology(kio_nepomuksearch_PART_SRCS - ${nepomukontologies_SOURCE_DIR}/nfo.trig - "NFO" - "Nepomuk::Vocabulary" - "trig") -soprano_add_ontology(kio_nepomuksearch_PART_SRCS - ${nepomukontologies_SOURCE_DIR}/nie.trig - "NIE" - "Nepomuk::Vocabulary" - "trig") +# find the nfo.trig file from the kdebase installation +#find_file(NIE_TRIG_SOURCE +# nie.trig +# PATHS "${KDE4_DATA_INSTALL_DIR}/.." "${KDE4_DATA_INSTALL_DIR}" ENV XDG_DATA_DIRS +# PATH_SUFFIXES "apps/nepomuk/ontologies" +# NO_DEFAULT_PATH +# ) +#find_file(NFO_TRIG_SOURCE +# nfo.trig +# PATHS "${KDE4_DATA_INSTALL_DIR}/.." "${KDE4_DATA_INSTALL_DIR}" ENV XDG_DATA_DIRS +# PATH_SUFFIXES "apps/nepomuk/ontologies" +# NO_DEFAULT_PATH +# ) +#find_file(PIMO_TRIG_SOURCE +# pimo.trig +# PATHS "${KDE4_DATA_INSTALL_DIR}/.." "${KDE4_DATA_INSTALL_DIR}" ENV XDG_DATA_DIRS +# PATH_SUFFIXES "apps/nepomuk/ontologies" +# NO_DEFAULT_PATH +# ) + +soprano_add_ontology(kio_nepomuksearch_PART_SRCS ${nepomukontologies_SOURCE_DIR}/nfo.trig "NFO" "Nepomuk::Vocabulary" "trig") +soprano_add_ontology(kio_nepomuksearch_PART_SRCS ${nepomukontologies_SOURCE_DIR}/nie.trig "NIE" "Nepomuk::Vocabulary" "trig") +soprano_add_ontology(kio_nepomuksearch_PART_SRCS ${nepomukontologies_SOURCE_DIR}/pimo.trig "PIMO" "Nepomuk::Vocabulary" "trig") set_source_files_properties( ../../interfaces/org.kde.nepomuk.QueryService.xml diff -Nru kdebase-runtime-4.3.2/nepomuk/kioslaves/search/kio_nepomuksearch.cpp kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/kio_nepomuksearch.cpp --- kdebase-runtime-4.3.2/nepomuk/kioslaves/search/kio_nepomuksearch.cpp 2009-08-27 10:17:34.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/kio_nepomuksearch.cpp 2009-10-26 10:35:59.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2009 by Sebastian Trueg <trueg at kde.org> + Copyright (C) 2008 by Sebastian Trueg <trueg at kde.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +20,8 @@ #include "searchfolder.h" #include "nfo.h" #include "nie.h" +#include "pimo.h" +#include "queryserviceclient.h" #include <QtCore/QFile> @@ -27,10 +29,13 @@ #include <KDebug> #include <KAboutData> #include <KApplication> +#include <KConfig> +#include <KConfigGroup> #include <KCmdLineArgs> #include <kio/global.h> #include <kio/job.h> #include <KMimeType> +#include <KStandardDirs> #include <Nepomuk/Resource> #include <Nepomuk/ResourceManager> @@ -38,7 +43,10 @@ #include "queryparser.h" #include <Soprano/Vocabulary/RDF> -#include <Soprano/Vocabulary/Xesam> +#include <Soprano/Vocabulary/RDFS> +#include <Soprano/Vocabulary/NRL> +#include <Soprano/Vocabulary/NAO> +#include <Soprano/Vocabulary/XMLSchema> #include <sys/types.h> #include <unistd.h> @@ -55,6 +63,53 @@ return uds; } + + //if type != 0, also retreives the type of the query and stores it into type + QString queryNameFromURL( const KUrl& url, Nepomuk::Search::Query::Type* type = 0 ) { + if(url.queryItems().contains( "sparql" ) ) { + if( type ) { + *type = Nepomuk::Search::Query::SPARQLQuery; + } + return url.queryItem( "sparql" ); + } + else if(url.queryItems().contains( "query" ) ) { + if( type ) { + *type = Nepomuk::Search::Query::PlainQuery; + } + return url.queryItem( "query" ); + } + else { + if( type ) { + *type = Nepomuk::Search::Query::PlainQuery; + } + return url.path().section( '/', 0, 0, QString::SectionSkipEmpty ); + } + } + + /** + * Empty if the path only contains the query. + */ + QString fileNameFromUrl( const KUrl& url ) { + QString fn; + if ( url.hasQueryItem( QLatin1String( "sparql" ) ) || + url.hasQueryItem( QLatin1String( "query" ) ) || + url.directory() != QLatin1String( "/" ) ) { + return url.fileName(); + } + else { + return QString(); + } + } + + Nepomuk::Search::Query createQuery( const QString& name, Nepomuk::Search::Query::Type type ) { + if( type == Nepomuk::Search::Query::PlainQuery ) { + return Nepomuk::Search::QueryParser::parseQuery( name ); + } + else /*type == Search::Query::SPARQLQuery*/ { + return Nepomuk::Search::Query( name ); + } + } + // do not cache more than SEARCH_CACHE_MAX search folders at the same time const int SEARCH_CACHE_MAX = 5; } @@ -63,26 +118,21 @@ Nepomuk::SearchProtocol::SearchProtocol( const QByteArray& poolSocket, const QByteArray& appSocket ) : KIO::ForwardingSlaveBase( "nepomuksearch", poolSocket, appSocket ) { - // FIXME: load default searches from config - // FIXME: allow icons + // FIXME: trueg: install a file watch on this file and update it whenever the queries change. + // FIXME: trueg: also emit a KDirNotify signal to inform KIO about that change + KConfig config("kionepomukuserqueriesrc" ); + + foreach( QString search, config.group("Searches").readEntry("All searches", QStringList() ) ) + { + search = search.simplified(); + KConfigGroup grp = config.group(search); + KUrl url( QUrl( QString("nepomuksearch:/") + grp.readEntry("Query",QString() ) ) ); + + Search::Query::Type type; + QString name = queryNameFromURL(url, &type ); - // all music files - Search::Term musicOrTerm; - musicOrTerm.setType( Search::Term::OrTerm ); - musicOrTerm.addSubTerm( Search::Term( Soprano::Vocabulary::RDF::type(), - Soprano::Vocabulary::Xesam::Music() ) ); - musicOrTerm.addSubTerm( Search::Term( Soprano::Vocabulary::RDF::type(), - Nepomuk::Vocabulary::NFO::Audio() ) ); - addDefaultSearch( i18n( "All Music Files" ), musicOrTerm ); - - // select the 10 most recent files: - addDefaultSearch( i18n( "Recent Files" ), - Search::Query( QString( "select distinct ?r where { " - "?r a %1 . " - "?r %2 ?date . " - "} ORDER BY DESC(?date) LIMIT 10" ) - .arg(Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::FileDataObject() )) - .arg(Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::fileLastModified() )) ) ); + addDefaultSearch(search, createQuery( name, type ) ); + } } @@ -91,10 +141,25 @@ } +bool Nepomuk::SearchProtocol::ensureNepomukRunning() +{ + if ( Nepomuk::ResourceManager::instance()->init() ) { + error( KIO::ERR_SLAVE_DEFINED, i18n( "The Nepomuk system is not activated. Unable to answer queries without it." ) ); + return false; + } + else if ( !Nepomuk::Search::QueryServiceClient::serviceAvailable() ) { + error( KIO::ERR_SLAVE_DEFINED, i18n( "The Nepomuk query service is not running. Unable to answer queries without it." ) ); + return false; + } + else { + return true; + } +} + + void Nepomuk::SearchProtocol::addDefaultSearch( const QString& name, const Search::Query& q ) { Search::Query query( q ); - query.addRequestProperty( Soprano::Vocabulary::Xesam::url(), true ); query.addRequestProperty( Nepomuk::Vocabulary::NIE::url(), true ); m_defaultSearches.insert( name, query ); } @@ -102,13 +167,14 @@ Nepomuk::SearchFolder* Nepomuk::SearchProtocol::extractSearchFolder( const KUrl& url ) { - QString name = url.path().section( '/', 0, 0, QString::SectionSkipEmpty ); + Search::Query::Type type; + QString name = queryNameFromURL( url, &type ); kDebug() << url << name; if ( SearchFolder* sf = getDefaultQueryFolder( name ) ) { kDebug() << "-----> is default search folder"; return sf; } - else if ( SearchFolder* sf = getQueryResults( name ) ) { + else if ( SearchFolder* sf = getQueryResults( name, type ) ) { kDebug() << "-----> is on-the-fly search folder"; return sf; } @@ -121,7 +187,12 @@ void Nepomuk::SearchProtocol::listDir( const KUrl& url ) { - kDebug() << url; + Search::Query::Type type; + QString name = queryNameFromURL( url, &type ); + kDebug() << url << name; + + if ( !ensureNepomukRunning() ) + return; // // Root dir: * list default searches: "all music files", "recent files" @@ -134,17 +205,16 @@ // * Look for a default search and execute that // - if ( url.path() == "/" ) { + if ( name.isEmpty() ) { listRoot(); } - else if ( url.directory() == "/" && - m_defaultSearches.contains( url.fileName() ) ) { + else if ( m_defaultSearches.contains( name ) ) { // the default search name is the folder name - listDefaultSearch( url.fileName() ); + listDefaultSearch( name ); } else { // lets create an on-the-fly search - listQuery( url.fileName() ); + listQuery( name, type ); } } @@ -152,6 +222,10 @@ void Nepomuk::SearchProtocol::get( const KUrl& url ) { kDebug() << url; + + if ( !ensureNepomukRunning() ) + return; + ForwardingSlaveBase::get( url ); } @@ -159,6 +233,10 @@ void Nepomuk::SearchProtocol::put( const KUrl& url, int permissions, KIO::JobFlags flags ) { kDebug() << url << permissions << flags; + + if ( !ensureNepomukRunning() ) + return; + // this will work only for existing files (ie. overwrite to allow saving of opened files) ForwardingSlaveBase::put( url, permissions, flags ); } @@ -168,6 +246,9 @@ { kDebug() << url; + if ( !ensureNepomukRunning() ) + return; + if ( url.path() == "/" ) { mimeType( QString::fromLatin1( "inode/directory" ) ); finished(); @@ -187,52 +268,84 @@ { kDebug() << url; - if ( url.path() == "/" ) { - if ( url.queryItems().isEmpty() ) { - kDebug() << "/"; - // - // stat the root path - // - KIO::UDSEntry uds; - uds.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1( "/" ) ); - uds.insert( KIO::UDSEntry::UDS_ICON_NAME, QString::fromLatin1( "nepomuk" ) ); - uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); - uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1( "inode/directory" ) ); + Search::Query::Type type; + QString name = queryNameFromURL( url, &type ); + kDebug() << url << name; - statEntry( uds ); - finished(); - } - else { - kDebug() << "query folder:" << url.queryItemValue("query"); + if ( !ensureNepomukRunning() ) + return; + + // + // Root dir: * list default searches: "all music files", "recent files" + // * list configuration entries: "create new default search" + // + // Root dir with query: + // * execute the query (cached) and list its results + // + // some folder: + // * Look for a default search and execute that + // - // - // stat a query folder - // - KIO::UDSEntry uds; - uds.insert( KIO::UDSEntry::UDS_NAME, url.fileName() ); - uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); - uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1( "inode/directory" ) ); + if ( name.isEmpty() ) { + kDebug() << "Stating root" << url; + // + // stat the root path + // + KIO::UDSEntry uds; + uds.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1( "/" ) ); + uds.insert( KIO::UDSEntry::UDS_ICON_NAME, QString::fromLatin1( "nepomuk" ) ); + uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); + uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1( "inode/directory" ) ); - statEntry( uds ); - finished(); - } + statEntry( uds ); + finished(); } - else if ( url.directory() == "/" ) { - if ( SearchFolder* sf = extractSearchFolder( url ) ) { - KIO::UDSEntry uds = statDefaultSearchFolder( sf->name() ); - Q_ASSERT( !uds.stringValue( KIO::UDSEntry::UDS_NAME ).isEmpty() ); - statEntry( uds ); + else if ( m_defaultSearches.contains( name ) ) { + kDebug() << "Stating default search" << url; + statEntry( statDefaultSearchFolder( name ) ); + finished(); + } + else if ( fileNameFromUrl(url).isEmpty() ) { + kDebug() << "Stating search folder" << url; + statEntry( statDefaultSearchFolder( name ) ); + finished(); + } + else { + kDebug() << "Stating file" << url; + ForwardingSlaveBase::stat(url); + } +} + + +void Nepomuk::SearchProtocol::del(const KUrl& url, bool isFile) +{ + if ( !ensureNepomukRunning() ) + return; + + Nepomuk::SearchFolder* folder = extractSearchFolder( url ); + + if (folder) { + if ( SearchEntry* entry = folder->findEntry( url.fileName() ) ) { + kDebug() << "findEntry returned something"; + + if ( entry->isFile() ) { + kDebug() << entry->resource() << "is file"; + KIO::ForwardingSlaveBase::del(entry->entry().stringValue( KIO::UDSEntry::UDS_TARGET_URL ), isFile); + } + else { + kDebug() << entry->resource() << "is non file"; + Nepomuk::Resource(entry->resource()).remove(); + } finished(); } else { - error( KIO::ERR_DOES_NOT_EXIST, url.url() ); + kDebug() << "findEntry returned nothing"; + error( KIO::ERR_DOES_NOT_EXIST, url.fileName() ); // not in m_entries } } - else if ( SearchFolder* folder = extractSearchFolder( url ) ) { - folder->stat( url.fileName() ); - } else { - error( KIO::ERR_DOES_NOT_EXIST, url.url() ); + kDebug() << "ERROR : extractSearchFolder returned NOTHING"; + error( KIO::ERR_DOES_NOT_EXIST, url.fileName() ); } } @@ -244,7 +357,7 @@ if ( SearchFolder* folder = extractSearchFolder( url ) ) { if ( SearchEntry* entry = folder->findEntry( url.fileName() ) ) { QString localPath = entry->entry().stringValue( KIO::UDSEntry::UDS_LOCAL_PATH ); - if ( localPath.isEmpty() ) { + if ( !localPath.isEmpty() ) { newURL = localPath; } else { @@ -276,7 +389,7 @@ } -Nepomuk::SearchFolder* Nepomuk::SearchProtocol::getQueryResults( const QString& query ) +Nepomuk::SearchFolder* Nepomuk::SearchProtocol::getQueryResults( const QString& query, Search::Query::Type type ) { if ( m_searchCache.contains( query ) ) { return m_searchCache[query]; @@ -287,8 +400,8 @@ delete m_searchCache.take( oldestQuery ); } - Search::Query q = Nepomuk::Search::QueryParser::parseQuery( query ); - q.addRequestProperty( Soprano::Vocabulary::Xesam::url(), true ); + Search::Query q = createQuery( query, type ); + q.addRequestProperty( Nepomuk::Vocabulary::NIE::url(), true ); SearchFolder* folder = new SearchFolder( query, q, this ); m_searchCacheNameQueue.enqueue( query ); @@ -314,10 +427,10 @@ } -void Nepomuk::SearchProtocol::listQuery( const QString& query ) +void Nepomuk::SearchProtocol::listQuery( const QString& query, Search::Query::Type type ) { kDebug() << query; - getQueryResults( query )->list(); + getQueryResults( query, type )->list(); } @@ -350,11 +463,6 @@ KComponentData comp( "kio_nepomuksearch" ); QCoreApplication app( argc, argv ); - if ( Nepomuk::ResourceManager::instance()->init() ) { - kError() << "Unable to initialized Nepomuk."; - return -1; - } - kDebug(7102) << "Starting nepomuksearch slave " << getpid(); Nepomuk::SearchProtocol slave( argv[2], argv[3] ); diff -Nru kdebase-runtime-4.3.2/nepomuk/kioslaves/search/kio_nepomuksearch.h kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/kio_nepomuksearch.h --- kdebase-runtime-4.3.2/nepomuk/kioslaves/search/kio_nepomuksearch.h 2008-09-26 16:55:58.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/kio_nepomuksearch.h 2009-10-26 09:52:22.000000000 +0100 @@ -40,7 +40,7 @@ virtual ~SearchProtocol(); /** - * + * */ void listDir( const KUrl& url ); @@ -66,6 +66,11 @@ */ void stat( const KUrl& url ); + /** + * Delete resources + */ + void del(const KUrl&, bool); + protected: /** * reimplemented from ForwardingSlaveBase @@ -73,8 +78,9 @@ bool rewriteUrl( const KUrl& url, KUrl& newURL ); private: + bool ensureNepomukRunning(); void listRoot(); - void listQuery( const QString& query ); + void listQuery( const QString& query, Search::Query::Type type ); void listActions(); void listDefaultSearches(); void listDefaultSearch( const QString& path ); @@ -85,7 +91,7 @@ /** * Get (possibly cached) query results */ - SearchFolder* getQueryResults( const QString& query ); + SearchFolder* getQueryResults( const QString& query, Search::Query::Type type ); SearchFolder* getDefaultQueryFolder( const QString& name ); // the default search folders diff -Nru kdebase-runtime-4.3.2/nepomuk/kioslaves/search/nepomuksearch.protocol kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/nepomuksearch.protocol --- kdebase-runtime-4.3.2/nepomuk/kioslaves/search/nepomuksearch.protocol 2008-09-26 16:55:58.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/nepomuksearch.protocol 2009-10-26 09:52:22.000000000 +0100 @@ -5,7 +5,7 @@ output=filesystem reading=true writing=false -deleting=false +deleting=true linking=false makedir=false moving=false diff -Nru kdebase-runtime-4.3.2/nepomuk/kioslaves/search/searchfolder.cpp kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/searchfolder.cpp --- kdebase-runtime-4.3.2/nepomuk/kioslaves/search/searchfolder.cpp 2009-08-27 10:17:34.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/searchfolder.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -19,6 +19,7 @@ #include "searchfolder.h" #include "nfo.h" #include "nie.h" +#include "pimo.h" #include "queryserviceclient.h" @@ -37,6 +38,7 @@ #include <KIO/NetAccess> #include <KUser> #include <kdirnotify.h> +#include <kdeversion.h> namespace { @@ -59,9 +61,11 @@ Nepomuk::SearchEntry::SearchEntry( const QUrl& res, - const KIO::UDSEntry& uds ) + bool isFile, + const KIO::UDSEntry& uds) : m_resource( res ), - m_entry( uds ) + m_entry( uds ), + m_isFile(isFile) { } @@ -427,6 +431,19 @@ uds.insert( KIO::UDSEntry::UDS_USER, KUser().loginName() ); // uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, "application/x-nepomuk-resource" ); } +#if KDE_IS_VERSION( 4, 3, 61 ) + // + // We always want the display type, even for pimo thing linked files, in the + // end showing "Invoice" or "Letter" is better than "text file" + // However, mimetypes are better than generic stuff like pimo:Thing and pimo:Document + // + Nepomuk::Types::Class type( res.pimoThing().isValid() ? res.pimoThing().resourceType() : res.resourceType() ); + if ( !isPimoThingLinkedFile || + type.uri() != Nepomuk::Vocabulary::PIMO::Thing() ) { + if (!type.label().isEmpty()) + uds.insert( KIO::UDSEntry::UDS_DISPLAY_TYPE, type.label() ); + } +#endif // // Although in KDE 4.3 the target url is sort of deprecated, we still set it. @@ -461,7 +478,7 @@ uds.insert( KIO::UDSEntry::UDS_NAME, name ); uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, name ); - SearchEntry* entry = new SearchEntry( result.resourceUri(), uds ); + SearchEntry* entry = new SearchEntry( result.resourceUri(), isFile, uds ); m_entries.insert( name, entry ); m_resourceNameMap.insert( result.resourceUri(), name ); diff -Nru kdebase-runtime-4.3.2/nepomuk/kioslaves/search/searchfolder.h kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/searchfolder.h --- kdebase-runtime-4.3.2/nepomuk/kioslaves/search/searchfolder.h 2009-08-27 10:17:34.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/kioslaves/search/searchfolder.h 2009-10-26 09:52:22.000000000 +0100 @@ -48,14 +48,17 @@ { public: SearchEntry( const QUrl& uri, + bool isFile, const KIO::UDSEntry& = KIO::UDSEntry() ); QUrl resource() const { return m_resource; } KIO::UDSEntry entry() const { return m_entry; } + bool isFile() const { return m_isFile; } private: QUrl m_resource; KIO::UDSEntry m_entry; + bool m_isFile; friend class SearchFolder; }; diff -Nru kdebase-runtime-4.3.2/nepomuk/libnepomukquery/dbusoperators.cpp kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/dbusoperators.cpp --- kdebase-runtime-4.3.2/nepomuk/libnepomukquery/dbusoperators.cpp 2008-11-28 16:33:44.000000000 +0100 +++ kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/dbusoperators.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -99,8 +99,9 @@ QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Term& term ) { // - // Signature: (ii(isss)sss) + // Signature: (ibi(isss)sss) // i -> type + // b -> positive/negative term // i -> comparator type // (isss) -> Soprano::LiteralValue encoded as a Soprano::Node for simplicity // s -> resource @@ -110,6 +111,7 @@ arg.beginStructure(); arg << ( int )term.type() + << term.positive() << ( int )term.comparator() << Soprano::Node( term.value() ) << QString::fromAscii( term.resource().toEncoded() ) @@ -124,8 +126,9 @@ const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Term& term ) { // - // Signature: (ii(isss)sss) + // Signature: (ibi(isss)sss) // i -> type + // b -> positive/negative term // i -> comparator type // (isss) -> Soprano::LiteralValue encoded as a Soprano::Node for simplicity // s -> resource @@ -135,16 +138,19 @@ arg.beginStructure(); int type = Nepomuk::Search::Term::InvalidTerm; + bool positive = true; int comparator = Nepomuk::Search::Term::Equal; Soprano::Node valueNode; QString resource, field, property; arg >> type + >> positive >> comparator >> valueNode >> resource >> field >> property; term.setType( Nepomuk::Search::Term::Type( type ) ); + term.setPositive( positive ); term.setComparator( Nepomuk::Search::Term::Comparator( comparator ) ); if ( valueNode.isLiteral() ) term.setValue( valueNode.literal() ); @@ -183,13 +189,14 @@ QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Query& query ) { // - // Signature: (isa(ii(isss)sss)a{iai}ia{sb}) + // Signature: (isa(ii(isss)sss)a{iai}ia{sb}a{s}) // i -> type // s -> sparql query // a(ii(isss)sss) -> array of terms (first is root term) // a{iai} -> hash of term relations // i -> limit // a{sb} -> request properties + // a{sb} -> folder limits // arg.beginStructure(); @@ -222,6 +229,15 @@ arg.endMapEntry(); } arg.endMap(); + + arg.beginMap( QVariant::String, QVariant::Bool ); + QList<Nepomuk::Search::Query::FolderLimit> folderLimits = query.folderLimits(); + foreach( const Nepomuk::Search::Query::FolderLimit& fl, folderLimits ) { + arg.beginMapEntry(); + arg << QString::fromAscii( fl.first.toEncoded() ) << fl.second; + arg.endMapEntry(); + } + arg.endMap(); arg.endStructure(); @@ -244,13 +260,14 @@ const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Query& query ) { // - // Signature: (isa(ii(isss)sss)a{iai}ia{sb}) + // Signature: (isa(ii(isss)sss)a{iai}ia{sb}a{s}) // i -> type // s -> sparql query // a(ii(isss)sss) -> array of terms (first is root term) // a{iai} -> hash of term relations // i -> limit // a{sb} -> request properties + // a{sb} -> folder limits // arg.beginStructure(); @@ -288,6 +305,17 @@ query.addRequestProperty( QUrl::fromEncoded( prop.toAscii() ), optional ); } arg.endMap(); + + arg.beginMap(); + while( !arg.atEnd() ) { + QString prop; + bool include = true; + arg.beginMapEntry(); + arg >> prop >> include; + arg.endMapEntry(); + query.addFolderLimit( QUrl::fromEncoded( prop.toAscii() ), include ); + } + arg.endMap(); arg.endStructure(); diff -Nru kdebase-runtime-4.3.2/nepomuk/libnepomukquery/query.cpp kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/query.cpp --- kdebase-runtime-4.3.2/nepomuk/libnepomukquery/query.cpp 2008-09-26 16:55:58.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/query.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -38,6 +38,7 @@ int limit; QList<RequestProperty> requestProperties; + QList<FolderLimit> folderLimits; }; @@ -145,14 +146,15 @@ namespace { - bool compareRequestProperties( const QList<Nepomuk::Search::Query::RequestProperty>& rp1, const QList<Nepomuk::Search::Query::RequestProperty>& rp2 ) { + template<typename T> + bool compareQList( const QList<T>& rp1, const QList<T>& rp2 ) { // brute force - foreach( const Nepomuk::Search::Query::RequestProperty& rp, rp1 ) { + foreach( const T& rp, rp1 ) { if ( !rp2.contains( rp ) ) { return false; } } - foreach( const Nepomuk::Search::Query::RequestProperty& rp, rp2 ) { + foreach( const T& rp, rp2 ) { if ( !rp1.contains( rp ) ) { return false; } @@ -167,11 +169,13 @@ d->limit == other.d->limit ) { if ( d->type == SPARQLQuery ) { return( d->sparqlQuery == other.d->sparqlQuery && - compareRequestProperties( d->requestProperties, other.d->requestProperties ) ); + compareQList( d->requestProperties, other.d->requestProperties ) && + compareQList( d->folderLimits, other.d->folderLimits ) ); } else { return( d->term == other.d->term && - compareRequestProperties( d->requestProperties, other.d->requestProperties ) ); + compareQList( d->requestProperties, other.d->requestProperties ) && + compareQList( d->folderLimits, other.d->folderLimits ) ); } } @@ -179,6 +183,24 @@ } +void Nepomuk::Search::Query::addFolderLimit( const QUrl& folder, bool include ) +{ + d->folderLimits.append( qMakePair( folder, include ) ); +} + + +void Nepomuk::Search::Query::clearFolderLimits() +{ + d->folderLimits.clear(); +} + + +QList<Nepomuk::Search::Query::FolderLimit> Nepomuk::Search::Query::folderLimits() const +{ + return d->folderLimits; +} + + QDebug operator<<( QDebug dbg, const Nepomuk::Search::Query& query ) { dbg << "(Query" << query.term() << query.limit() << ")"; diff -Nru kdebase-runtime-4.3.2/nepomuk/libnepomukquery/query.h kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/query.h --- kdebase-runtime-4.3.2/nepomuk/libnepomukquery/query.h 2008-09-26 16:55:58.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/query.h 2009-10-26 09:52:22.000000000 +0100 @@ -107,6 +107,20 @@ QList<RequestProperty> requestProperties() const; bool operator==( const Query& ) const; + + /** + * Add a folder to the search space. If no folder limits are set, will + * search the entire Nepomuk database + * \param folder the folder to search + * \param include true if we should search this folder, false + * if we should exclude this folder from search results + */ + void addFolderLimit( const QUrl& folder, bool include ); + void clearFolderLimits(); + + typedef QPair<QUrl, bool> FolderLimit; + + QList<FolderLimit> folderLimits() const; private: class Private; diff -Nru kdebase-runtime-4.3.2/nepomuk/libnepomukquery/queryparser.cpp kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/queryparser.cpp --- kdebase-runtime-4.3.2/nepomuk/libnepomukquery/queryparser.cpp 2009-02-04 19:18:10.000000000 +0100 +++ kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/queryparser.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -121,6 +121,17 @@ return Soprano::LiteralValue( d ); return s; } + + bool positiveTerm( const QString& s) { + if(s.isEmpty()) + return true; + else if(s == "+") + return true; + else if(s == "-") + return false; + else //unrecognized capture + return true; + } } @@ -164,6 +175,7 @@ // TODO: a "real" parser which can handle all of the Xesam user language // This one for example does not handle nesting at all. + Nepomuk::Search::Query final; QList<Term> terms; bool inOrBlock = false; @@ -181,15 +193,15 @@ if ( pos < query.length() ) { if ( s_resourceRx.indexIn( query, pos ) == pos ) { - // FIXME: honour the +- kDebug() << "matched resource term at" << pos << s_resourceRx.cap( 0 ); term = Term( tryToBeIntelligentAboutParsingUrl( s_resourceRx.cap( 2 ) ), - tryToBeIntelligentAboutParsingUrl( s_resourceRx.cap( 3 ) ) ); + tryToBeIntelligentAboutParsingUrl( s_resourceRx.cap( 3 ) ), + positiveTerm(s_resourceRx.cap( 1 ) ) ); pos += s_resourceRx.matchedLength(); } else if ( s_propertyRx.indexIn( query, pos ) == pos ) { - // FIXME: honour the +- kDebug() << "matched property term at" << pos << s_propertyRx.cap( 0 ); + term.setPositive( positiveTerm(s_propertyRx.cap( 1 ) ) ); term.setProperty( tryToBeIntelligentAboutParsingUrl( s_propertyRx.cap( 2 ) ) ); term.addSubTerm( Term( createLiteral( stripQuotes( s_propertyRx.cap( 4 ) ) ) ) ); QString comparator = s_propertyRx.cap( 3 ); @@ -213,17 +225,24 @@ pos += s_fieldFieldRx.matchedLength(); } else if ( s_fieldRx.indexIn( query, pos ) == pos ) { - // FIXME: honour the +- kDebug() << "matched field term at" << pos << s_fieldRx.cap( 0 ) << s_fieldRx.cap( 2 ) << s_fieldRx.cap( 4 ) << s_fieldRx.cap( 5 ); - term.setField( stripQuotes( s_fieldRx.cap( 2 ) ) ); - term.addSubTerm( Term( createLiteral( stripQuotes( s_fieldRx.cap( 5 ) ) ) ) ); - QString comparator = s_fieldRx.cap( 4 ); - term.setType( Term::ComparisonTerm ); - term.setComparator( fieldTypeRelationFromString( comparator ) ); - pos += s_fieldRx.matchedLength(); + if( stripQuotes ( s_fieldRx.cap( 2 ) ).compare( QString( "inFolder" ), Qt::CaseInsensitive ) == 0 ) { + QUrl path = QUrl::fromLocalFile( s_fieldRx.cap( 5 ) ); + kDebug() << "found include path" << path; + final.addFolderLimit( path, positiveTerm( s_fieldRx.cap( 1 ) ) ); + pos += s_fieldRx.matchedLength(); + } + else { + term.setField( stripQuotes( s_fieldRx.cap( 2 ) ) ); + term.setPositive( positiveTerm( s_fieldRx.cap( 1 ) ) ); + term.addSubTerm( Term( createLiteral( stripQuotes( s_fieldRx.cap( 5 ) ) ) ) ); + QString comparator = s_fieldRx.cap( 4 ); + term.setType( Term::ComparisonTerm ); + term.setComparator( fieldTypeRelationFromString( comparator ) ); + pos += s_fieldRx.matchedLength(); + } } else if ( s_plainTermRx.indexIn( query, pos ) == pos ) { - // FIXME: honour the +- QString value = stripQuotes( s_plainTermRx.cap( 2 ) ); if ( d->orKeywords.contains( value.toLower() ) ) { inOrBlock = true; @@ -233,7 +252,7 @@ } else { kDebug() << "matched literal at" << pos << value; - term = Term( Soprano::LiteralValue( value ) ); + term = Term( Soprano::LiteralValue( value ), positiveTerm(s_plainTermRx.cap( 1 ) ) ); } pos += s_plainTermRx.matchedLength(); } @@ -265,15 +284,17 @@ } if ( terms.count() == 1 ) { - return terms[0]; + final.setTerm( terms[0] ); + return final; } else if ( terms.count() > 0 ) { Term t; t.setType( Term::AndTerm ); t.setSubTerms( terms ); - return t; + final.setTerm( t ); + return final; } else { - return Term(); + return final; } } diff -Nru kdebase-runtime-4.3.2/nepomuk/libnepomukquery/term.cpp kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/term.cpp --- kdebase-runtime-4.3.2/nepomuk/libnepomukquery/term.cpp 2009-02-04 19:18:10.000000000 +0100 +++ kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/term.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -28,12 +28,14 @@ class Nepomuk::Search::Term::Private : public QSharedData { public: - Private( Type t = InvalidTerm, Comparator c = Equal ) + Private( Type t = InvalidTerm, bool p = true, Comparator c = Equal ) : type( t ), + positive( p ), comparator( c ) { } Type type; + bool positive; Comparator comparator; Soprano::LiteralValue value; QUrl resource; @@ -55,8 +57,8 @@ } -Nepomuk::Search::Term::Term( const Soprano::LiteralValue& value ) - : d( new Private( LiteralTerm ) ) +Nepomuk::Search::Term::Term( const Soprano::LiteralValue& value, bool isPositive ) + : d( new Private( LiteralTerm, isPositive ) ) { d->value = value; } @@ -69,24 +71,24 @@ } -Nepomuk::Search::Term::Term( const QString& field, const Soprano::LiteralValue& value, Comparator c ) - : d( new Private( ComparisonTerm, c ) ) +Nepomuk::Search::Term::Term( const QString& field, const Soprano::LiteralValue& value, bool isPositive, Comparator c ) + : d( new Private( ComparisonTerm, isPositive, c ) ) { d->field = field; d->subTerms.append( Term( value ) ); } -Nepomuk::Search::Term::Term( const QUrl& field, const Soprano::LiteralValue& value, Comparator c ) - : d( new Private( ComparisonTerm, c ) ) +Nepomuk::Search::Term::Term( const QUrl& field, const Soprano::LiteralValue& value, bool isPositive, Comparator c ) + : d( new Private( ComparisonTerm, isPositive, c ) ) { d->property = field; d->subTerms.append( Term( value ) ); } -Nepomuk::Search::Term::Term( const QUrl& field, const QUrl& resource ) - : d( new Private( ComparisonTerm ) ) +Nepomuk::Search::Term::Term( const QUrl& field, const QUrl& resource, bool isPositive ) + : d( new Private( ComparisonTerm, isPositive ) ) { d->property = field; d->subTerms.append( Term( resource ) ); @@ -139,6 +141,12 @@ } +bool Nepomuk::Search::Term::positive() const +{ + return d->positive; +} + + Nepomuk::Search::Term::Type Nepomuk::Search::Term::type() const { return d->type; @@ -181,6 +189,12 @@ } +void Nepomuk::Search::Term::setPositive( bool positive ) +{ + d->positive = positive; +} + + void Nepomuk::Search::Term::setType( Type type ) { d->type = type; @@ -312,6 +326,8 @@ default: break; } + if( !term.positive() ) + dbg << "( negative )"; if ( term.type() == Nepomuk::Search::Term::ComparisonTerm ) { if ( term.property().isValid() ) { dbg << "Property" << term.property(); @@ -340,12 +356,13 @@ { switch( term.type() ) { case Nepomuk::Search::Term::LiteralTerm: - return qHash( term.value().toString() ); + return( qHash( term.value().toString() )<<8 | ( uint )term.positive() ); case Nepomuk::Search::Term::ComparisonTerm: - return( qHash( term.property().isValid() ? term.property().toString() : term.field() )<<16 | - qHash( term.subTerms().first() )<<8 | - ( uint )term.comparator() ); + return( qHash( term.property().isValid() ? term.property().toString() : term.field() )<<24 | + qHash( term.subTerms().first() )<<16 | + ( uint )term.comparator()<<8 | + ( uint )term.positive() ); case Nepomuk::Search::Term::AndTerm: case Nepomuk::Search::Term::OrTerm: { diff -Nru kdebase-runtime-4.3.2/nepomuk/libnepomukquery/term.h kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/term.h --- kdebase-runtime-4.3.2/nepomuk/libnepomukquery/term.h 2008-10-30 15:13:02.000000000 +0100 +++ kdebase-runtime-4.3.2-new/nepomuk/libnepomukquery/term.h 2009-10-26 09:52:22.000000000 +0100 @@ -107,7 +107,7 @@ /** * Construct a literal term. */ - Term( const Soprano::LiteralValue& value ); + Term( const Soprano::LiteralValue& value, bool isPositive = true ); /** * Construct a resource term. @@ -121,7 +121,7 @@ * types are converted to string. * \param comparator The Comparator to use */ - Term( const QString& field, const Soprano::LiteralValue& value, Comparator c = Contains ); + Term( const QString& field, const Soprano::LiteralValue& value, bool isPositive = true, Comparator c = Contains ); /** * Construct a Contains ComparisonTerm term. @@ -130,14 +130,14 @@ * types are converted to string. * \param comparator The Comparator to use */ - Term( const QUrl& field, const Soprano::LiteralValue& value, Comparator c = Contains ); + Term( const QUrl& field, const Soprano::LiteralValue& value, bool isPositive = true, Comparator c = Contains ); /** * Construct an EqualityTerm term. * \param field The exact field to match * \param value The resource that should be matched. */ - Term( const QUrl& field, const QUrl& resource ); + Term( const QUrl& field, const QUrl& resource, bool isPositive = true ); /** * Destructor @@ -158,6 +158,11 @@ * \return \p true if the Term is valid. */ bool isValid() const; + + /** + * \return \p true is the Term is positive + */ + bool positive() const; /** * \return the Term type. @@ -208,6 +213,11 @@ * \sa setSubTerms, addSubTerm */ QList<Term> subTerms() const; + + /** + * Sets whether or not this term is positive + */ + void setPositive( bool ); /** * Set the type of the Term diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/CMakeLists.txt kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/CMakeLists.txt --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/CMakeLists.txt 2009-07-21 17:18:36.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/CMakeLists.txt 2009-10-26 09:52:22.000000000 +0100 @@ -2,8 +2,6 @@ include(SopranoAddOntology) -add_definitions(-DKDE_DEFAULT_DEBUG_AREA=300104) - include_directories( ${QT_INCLUDES} ${KDE4_INCLUDES} @@ -17,6 +15,7 @@ searchthread.cpp folder.cpp folderconnection.cpp + dateparser.cpp ${nepomuk_SOURCE_DIR}/libnepomukquery/result.cpp ${nepomuk_SOURCE_DIR}/libnepomukquery/query.cpp ${nepomuk_SOURCE_DIR}/libnepomukquery/term.cpp @@ -24,6 +23,12 @@ ${nepomuk_SOURCE_DIR}/libnepomukquery/dbusoperators.cpp ) +#find_file(NFO_TRIG_SOURCE +# nfo.trig +# PATHS "${KDE4_DATA_INSTALL_DIR}/.." "${KDE4_DATA_INSTALL_DIR}" ENV XDG_DATA_DIRS +# PATH_SUFFIXES "apps/nepomuk/ontologies" +# NO_DEFAULT_PATH +# ) soprano_add_ontology(queryservice_SRCS ${nepomukontologies_SOURCE_DIR}/nfo.trig "NFO" diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/dateparser.cpp kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/dateparser.cpp --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/dateparser.cpp 1970-01-01 01:00:00.000000000 +0100 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/dateparser.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -0,0 +1,356 @@ +/* +* This file is part of the Nepomuk KDE project. +* Copyright (c) 2009 Adam Kidder <thekidder@gmail.com> +* Copyright (c) 2009 Sebastian Trueg <trueg@kde.org> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; see the file COPYING.LIB. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#include "dateparser.h" + +#include <KDebug> + +#include <QtCore/QLocale> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVector> + + +namespace { + //represents a time difference from the current time, to represent relative dates + struct time_difference + { + time_difference() : seconds(0), minutes(0), hours(0), days(0), weeks(0), months(0), years(0) {} + int seconds, minutes, hours, days, weeks, months, years; + }; + + struct format + { + format() : pos(0) {} + format(const QRegExp& r, const QStringList& f) : regex(r), pos(0), useRelativeDate(false), formats(f) {} + format(const QRegExp& r, const time_difference& d, bool dr = false) : regex(r), pos(0), useRelativeDate(true), difference(d), dynamicRelative(dr) {} + QRegExp regex; + int pos; + + bool useRelativeDate; + time_difference difference; + bool dynamicRelative; + QStringList formats; + }; + + struct date_string + { + QDate date; + unsigned int pos; + unsigned int length; + }; +} + +class Nepomuk::Search::DateParser::Private +{ +public: + Private(const QString& text, unsigned int flags) : + m_text(text), m_locale(QLocale::English), m_flags(flags) { + //TODO: we are english-only here! + QStringList longMonthNames; + QStringList shortMonthNames; + for ( int i = 1; i <= 12; ++i ) { + longMonthNames << m_locale.monthName( i, QLocale::LongFormat ); + shortMonthNames << m_locale.monthName( i, QLocale::ShortFormat ); + } + + // DD.MM.YYYY + format date1( QRegExp( "\\b\\d{1,2}\\.\\d{1,2}\\.\\d{4,4}\\b" ), QStringList("d.M.yyyy") ); + + // DD.MM.YY + format date2( QRegExp( "\\b\\d{1,2}\\.\\d{1,2}\\.\\d{2,2}\\b" ), QStringList("d.M.yy") ); + + // MM/DD/YYYY + format date3( QRegExp( "\\b\\d{1,2}/\\d{1,2}/\\d{4,4}\\b" ), QStringList("M/d/yyyy") ); + + // YYYY-MM-DD + format date13(QRegExp( "\\b\\d{4,4}-\\d{1,2}-\\d{1,2}\\b" ), QStringList("yyyy-M-d") ); + + // MM/DD/YY + format date4( QRegExp( "\\b\\d{1,2}/\\d{1,2}/\\d{2,2}\\b" ), QStringList("M/d/yy") ); + + // January MM [YYYY] (no word boundry at the end for 'st' or 'nd' or 'th') (also excluding ranges) + format date5( QRegExp( QString( "\\b(%1)\\s\\d{1,2}(?!(\\d|\\s?-\\s?\\d))(\\s\\d{4,4})?" ).arg( longMonthNames.join( "|" ) ) ), + QStringList("MMMM d") << QString("MMMM d yyyy") ); + + // January, MM [YYYY] (no word boundry at the end for 'st' or 'nd' or 'th') (also excluding ranges) + format date6( QRegExp( QString( "\\b(%1),\\s?\\d{1,2}(?!(\\d|\\s?-\\s?\\d))(\\s\\d{4,4})?" ).arg( longMonthNames.join( "|" ) ) ), + QStringList("MMMM, d") << QString("MMMM,d") << QString("MMMM, d yyyy") << QString("MMMM,d yyyy") ); + + // FIXME: trueg: IMHO something like "yesterday" should result in a range if not used with < or >. + + //TODO: english only again! + time_difference days; + days.days = -1; + format date7( QRegExp( QString( "\\b(yesterday)\\b" ) ), days ); + + format date8( QRegExp( QString( "\\b(\\d{1,3}) (day)s? ago\\b" ) ), days, true ); + + time_difference weeks; + weeks.weeks = -1; + format date9( QRegExp( QString( "\\ba week ago\\b" ) ), weeks ); + + format date10( QRegExp( QString( "\\b(\\d{1,3}) (week)s? ago\\b" ) ), weeks, true ); + + time_difference months; + months.months = -1; + format date11( QRegExp( QString( "\\ba month ago\\b" ) ), months ); + + format date12( QRegExp( QString( "\\b(\\d{1,3}) (month)s? ago\\b" ) ), months, true ); + + m_regexes.push_back( date1 ); + m_regexes.push_back( date2 ); + m_regexes.push_back( date3 ); + m_regexes.push_back( date4 ); + m_regexes.push_back( date5 ); + m_regexes.push_back( date6 ); + m_regexes.push_back( date7 ); + m_regexes.push_back( date8 ); + m_regexes.push_back( date9 ); + m_regexes.push_back( date10); + m_regexes.push_back( date11); + m_regexes.push_back( date12); + m_regexes.push_back( date13); + } + + + bool hasDate() { + if(!m_dates.empty()) return true; + + while(m_dates.empty() && !finishedParsing()) + { + parseAllRegexes(); + } + + if(!m_dates.empty()) return true; + return false; + } + + + QDate getDate() { + if( !m_dates.isEmpty() ) + return m_dates.first().date; + else + return QDate(); + } + + void next() { + m_dates.pop_front(); + } + + unsigned int length() const { + if( !m_dates.isEmpty() ) + return m_dates.first().length; + else + return 0; + } + + unsigned int pos() const { + if( !m_dates.isEmpty() ) + return m_dates.first().pos; + else + return 0; + } + int dateObject; + +private: + bool finishedParsing() { + foreach(format r, m_regexes) { + if(r.pos != -1) return false; + } + return true; + } + + + void parseAllRegexes() { + QVector<format>::iterator it ; + for(it = m_regexes.begin(); it != m_regexes.end(); ++it) { + it->pos = it->regex.indexIn(m_text, it->pos); + if( it->pos == -1 ) + continue; + if( !it->useRelativeDate && (m_flags & AbsoluteDates) ) { + foreach(QString format, it->formats) { + QDate date = m_locale.toDate( it->regex.cap( 0 ), format ); + if(date.isValid()) { + if(!format.contains( "yy" ) ) + date.setDate( QDate::currentDate().year(), date.month(), date.day() ); + kDebug() << "Found absolute date:" << date; + date_string dateObject; + dateObject.date = date; + dateObject.pos = it->pos; + dateObject.length = it->regex.matchedLength(); + m_dates.append( dateObject ); + break; + } + } + } + else if( m_flags & RelativeDates) { + int amount = 1; + if( it->dynamicRelative ) { + amount = it->regex.cap( 1 ).toInt(); + kDebug() << "dynamic relative date, amount is" << amount << it->regex.cap( 1 ); + } + QDate current( QDate::currentDate() ); + current = current.addDays( it->difference.days * amount ); + current = current.addDays( it->difference.weeks * 7 * amount ); + current = current.addMonths( it->difference.months * amount ); + current = current.addYears( it->difference.years * amount ); + + kDebug() << "Found relative date:" << current << it->regex.pattern(); + date_string dateObject; + dateObject.date = current; + dateObject.pos = it->pos; + dateObject.length = it->regex.matchedLength(); + m_dates.append( dateObject ); + } + } + } + + + const QString& m_text; + QLocale m_locale; + QVector<format> m_regexes; + QList<date_string> m_dates; + unsigned int m_flags; +}; + + + +Nepomuk::Search::DateParser::DateParser(const QString& text, unsigned int flags) : + d( new Private(text, flags) ) { +} + + +Nepomuk::Search::DateParser::~DateParser() { + delete d; +} + +bool Nepomuk::Search::DateParser::hasDate() { + return d->hasDate(); +} + +QDate Nepomuk::Search::DateParser::getDate() { + return d->getDate(); +} + +void Nepomuk::Search::DateParser::next() { + d->next(); +} + +unsigned int Nepomuk::Search::DateParser::pos() const { + return d->pos(); +} + +unsigned int Nepomuk::Search::DateParser::length() const { + return d->length(); +} + +class Nepomuk::Search::TimeParser::Private +{ +public: + Private(const QString& text) : m_text(text), m_locale(QLocale::English) { + // hh:mm[pm|am] + format time1( QRegExp( "\\b\\d{1,2}\\:\\d{2,2}\\s?(pm|am|AM|PM)?\\b" ), QStringList("h:map") << QString("h:m ap") ); + + // hh:mm + format time2( QRegExp( "\\b\\d{1,2}\\:\\d{2,2}\\b(?!\\s?(pm|am|AM|PM))\\b" ), QStringList("h:m") ); + + m_regexes.push_back( time1 ); + m_regexes.push_back( time2 ); + } + + + bool hasTime() { + if(!m_times.empty()) return true; + + while(m_times.empty() && !finishedParsing()) + { + parseAllRegexes(); + } + + if(!m_times.empty()) return true; + return false; + } + + + QTime next() { + return m_times.takeFirst(); + } +private: + bool finishedParsing() { + foreach(format r, m_regexes) { + if(r.pos != -1) return false; + } + return true; + } + + + void parseAllRegexes() { + QVector<format>::iterator it ; + for(it = m_regexes.begin(); it != m_regexes.end(); ++it) { + it->pos = it->regex.indexIn(m_text, it->pos); + if( !it->useRelativeDate ) { + foreach(QString format, it->formats) { + QTime time = m_locale.toTime( it->regex.cap( 0 ), format ); + if(time.isValid()) { + kDebug() << "Found time:" << time; + m_times.append( time ); + break; + } + } + } + else { + QTime current( QTime::currentTime() ); + current.addSecs( it->difference.seconds ); + current.addSecs( it->difference.minutes * 60 ); + current.addSecs( it->difference.hours * 60 * 60 ); + + kDebug() << "Found time:" << current; + m_times.append( current ); + break; + } + } + } + + + const QString& m_text; + QLocale m_locale; + QVector<format> m_regexes; + QList<QTime> m_times; +}; + + + +Nepomuk::Search::TimeParser::TimeParser(const QString& text) : d( new Private(text) ) { +} + + +Nepomuk::Search::TimeParser::~TimeParser() { + delete d; +} + +bool Nepomuk::Search::TimeParser::hasTime() { + return d->hasTime(); +} + +QTime Nepomuk::Search::TimeParser::next() { + return d->next(); +} diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/dateparser.h kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/dateparser.h --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/dateparser.h 1970-01-01 01:00:00.000000000 +0100 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/dateparser.h 2009-10-26 09:52:22.000000000 +0100 @@ -0,0 +1,83 @@ +/* +* This file is part of the Nepomuk KDE project. +* Copyright (c) 2009 Adam Kidder <thekidder@gmail.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; see the file COPYING.LIB. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#include <QtCore/QDate> +#include <QtCore/QTime> + + +namespace Nepomuk { + namespace Search { + /*DateTimeParser parses a string to extract QDateTime object(s). It is (will be) capable of extracting: + * fully qualified date times + * times qualified using a specified date + * dates qualified with a specified time + */ + class DateTimeParser + { + public: + DateTimeParser(const QString& dateTimeString); + + + private: + const QString& text; + }; + + + class DateParser + { + public: + //flags + const static unsigned int AbsoluteDates = 0x1; + const static unsigned int RelativeDates = 0x2; + + DateParser(const QString& dateString, unsigned int flags = AbsoluteDates | RelativeDates); + ~DateParser(); + + //true if another date has been found + bool hasDate(); + //advances to the next date + void next(); + + //next 3 functions only valid if hasDate() + QDate getDate(); + //position of current extracted date + unsigned int pos() const; + //length of current extracted date string + unsigned int length() const; + private: + class Private; + Private* const d; + }; + + //FIXME: code duplication in DateParser and TimeParser + class TimeParser + { + public: + TimeParser(const QString& timeString); + ~TimeParser(); + + bool hasTime(); + QTime next(); + private: + class Private; + Private* const d; + }; + } +} \ No newline at end of file diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchcore.cpp kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchcore.cpp --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchcore.cpp 2009-08-27 10:17:34.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchcore.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -31,7 +31,7 @@ { public: Private() - : cutOffScore( 0.0 ), + : cutOffScore( 0.0 ), // TODO: make this configurable through the service API active( false ), canceled( false ) { } diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchcore.h kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchcore.h --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchcore.h 2009-08-27 10:17:34.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchcore.h 2009-10-26 09:52:22.000000000 +0100 @@ -54,7 +54,7 @@ public Q_SLOTS: void query( const Query& query ); - + void cancel(); /** diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchthread.cpp kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchthread.cpp --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchthread.cpp 2009-07-21 17:18:36.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchthread.cpp 2009-10-26 09:52:22.000000000 +0100 @@ -17,6 +17,7 @@ Boston, MA 02110-1301, USA. */ +#include "dateparser.h" #include "searchthread.h" #include "term.h" #include "nfo.h" @@ -43,9 +44,12 @@ #include <Soprano/Vocabulary/Xesam> #include <KDebug> +#include <KDateTime> #include <KRandom> #include <QtCore/QTime> +#include <QLatin1String> +#include <QStringList> @@ -94,6 +98,24 @@ return es; } + QString createFolderFilterStringSparql( const Nepomuk::Search::SearchNode& node, const QString& varName = QString( "?r" ) ) + { + QStringList positive, negative; + QString filter; + + QList<Nepomuk::Search::Query::FolderLimit>::const_iterator it; + for( it = node.folderLimits.constBegin(); it != node.folderLimits.constEnd(); ++it ) { + QStringList& ref = it->second ? positive : negative; + ref.append( QString::fromAscii( it->first.toEncoded( QUrl::StripTrailingSlash ) ) ); + } + + if( !positive.isEmpty() ) + filter += QString( " FILTER(REGEX(STR(%1), \"^%2/\")) ." ).arg( varName ).arg( positive.join( "|" ) ); + if( !negative.isEmpty() ) + filter += QString( " FILTER(!REGEX(STR(%1), \"^%2/\")) ." ).arg( varName ).arg( negative.join( "|" ) ); + return filter; + } + QString luceneQueryEscape( const QUrl& s ) { return luceneQueryEscape( QString::fromAscii( s.toEncoded() ) ); } @@ -107,12 +129,35 @@ } } + QString createFolderFilterStringLucene( const Nepomuk::Search::SearchNode& node ) + { + QStringList positive, negative; + QString filter; + + QList<Nepomuk::Search::Query::FolderLimit>::const_iterator it; + for( it = node.folderLimits.constBegin(); it != node.folderLimits.constEnd(); ++it ) { + QStringList& ref = it->second ? positive : negative; + QString notTerm = it->second ? QString() : QString( "NOT" ); + ref.append( QString( "%1 id:%2/*" ).arg( notTerm ).arg( luceneQueryEscape( it->first ) ) ); + } + + if( !positive.isEmpty() ) + filter += QString( " AND ( %1) " ).arg( positive.join( " OR " ) ); + if( !negative.isEmpty() ) + filter += QString( " AND %1 " ).arg( negative.join( " AND " ) ); + return filter; + } + QString createLuceneQuery( const Nepomuk::Search::SearchNode& node ) { + const QString notTerm = node.term.positive() ? QLatin1String("") : QLatin1String( "NOT " ); + + QString filterString = createFolderFilterStringLucene( node ); + if ( node.term.type() == Nepomuk::Search::Term::LiteralTerm ) { - return createLuceneLiteralQuery( luceneQueryEscape( node.term.value().toString() ) ); + return notTerm + createLuceneLiteralQuery( luceneQueryEscape( node.term.value().toString() ) ) + filterString; } else if ( node.term.type() == Nepomuk::Search::Term::ComparisonTerm ) { - return luceneQueryEscape( node.term.property() ) + ':' + createLuceneLiteralQuery( luceneQueryEscape( node.term.subTerms().first().value().toString() ) ); + return notTerm + luceneQueryEscape( node.term.property() ) + ':' + createLuceneLiteralQuery( luceneQueryEscape( node.term.subTerms().first().value().toString() ) ) + filterString; } else { Q_ASSERT( node.term.type() == Nepomuk::Search::Term::AndTerm || @@ -151,6 +196,24 @@ } + Nepomuk::Search::Term::Comparator oppositeComparator( Nepomuk::Search::Term::Comparator c ) { + switch( c ) { + case Nepomuk::Search::Term::Greater: + return Nepomuk::Search::Term::SmallerOrEqual; + case Nepomuk::Search::Term::Smaller: + return Nepomuk::Search::Term::GreaterOrEqual; + case Nepomuk::Search::Term::GreaterOrEqual: + return Nepomuk::Search::Term::Smaller; + case Nepomuk::Search::Term::SmallerOrEqual: + return Nepomuk::Search::Term::Greater; + default: + kDebug() << "Unknown or invalid comparator:" << comparatorString(c); + //to remove warnings we return Contains as default + return Nepomuk::Search::Term::Contains; + } + } + + bool isNumberLiteralValue( const Soprano::LiteralValue& value ) { return value.isInt() || value.isInt64() || value.isUnsignedInt() || value.isUnsignedInt64() || value.isDouble(); } @@ -168,11 +231,14 @@ QString createGraphPattern( const Nepomuk::Search::SearchNode& node, int& varCnt, const QString& varName = QString( "?r" ) ) { + //TODO: ugly code, refactor :( switch( node.term.type() ) { case Nepomuk::Search::Term::ComparisonTerm: { Nepomuk::Search::Term subTerm( node.term.subTerms().first() ); + QString filterString = createFolderFilterStringSparql( node ); + // // is the subterm (we only support one ATM) a final term (no further subterms) // -> actually match the literal or resource @@ -180,11 +246,12 @@ if ( subTerm.type() == Nepomuk::Search::Term::ResourceTerm || subTerm.type() == Nepomuk::Search::Term::LiteralTerm ) { if( node.term.comparator() != Nepomuk::Search::Term::Equal ) { + Nepomuk::Search::Term::Comparator c = node.term.positive() ? node.term.comparator() : oppositeComparator( node.term.comparator() ); // For numbers there is no need for quotes + this way we can handle all the xsd decimal types // FIXME: it may be necessary to escape stuff QString filter = QString( "?var%1 %2 " ) .arg( ++varCnt ) - .arg( comparatorString( node.term.comparator() ) ); + .arg( comparatorString( c ) ); if ( isNumberLiteralValue( subTerm.value() ) ) { filter += subTerm.value().toString(); } @@ -195,19 +262,31 @@ filter += QString( "^^<%1>" ).arg( prop.literalRangeType().dataTypeUri().toString() ); } - return wrapInInstanceBaseGraphQuery( QString( "%1 <%2> ?var%3 . FILTER(%4) . " ) + return wrapInInstanceBaseGraphQuery( QString( "%1 <%2> ?var%3 . FILTER(%4) . %5" ) .arg( varName ) .arg( QString::fromAscii( node.term.property().toEncoded() ) ) .arg( varCnt ) - .arg( filter ) ); + .arg( filter ) + .arg( filterString ) ); } else { if ( subTerm.type() == Nepomuk::Search::Term::ResourceTerm ) { - return wrapInInstanceBaseGraphQuery( QString( "%1 <%2> <%3> . " ) - .arg( varName ) - .arg( QString::fromAscii( node.term.property().toEncoded() ) ) - .arg( QString::fromAscii( subTerm.resource().toEncoded() ) ) ); + QString resourceLiteral( QString::fromAscii( subTerm.resource().toEncoded() ) ); + QString resource = node.term.positive() ? Soprano::Node::resourceToN3( resourceLiteral ) : QString( "?v" ); + QString baseTerm = QString( "%1 <%2> %3 . %4" ) + .arg( varName ) + .arg( QString::fromAscii( node.term.property().toEncoded() ) ) + .arg( resource ) + .arg( filterString ); + if( node.term.positive() ) + return wrapInInstanceBaseGraphQuery( baseTerm ); + else { + return QString( " %1 OPTIONAL { %2 } FILTER(!BOUND(?v))" ) + .arg( wrapInInstanceBaseGraphQuery( QString ( "%1 a ?type . " ).arg( varName ) ) ) + .arg( wrapInInstanceBaseGraphQuery( baseTerm + QString(" FILTER(?v = <%1>) . ").arg( resourceLiteral ) ) ); + } } + //TODO: negation, folder filters here else if ( Nepomuk::Types::Property( node.term.property() ).range().isValid() ) { return wrapInInstanceBaseGraphQuery( QString( "%1 %2 ?x . " ) .arg( varName ) @@ -230,13 +309,14 @@ // property is defined to range to a plain one. // Nepomuk::Types::Property p( node.term.property() ); - return wrapInInstanceBaseGraphQuery( QString( "%1 <%2> \"%3\"^^<%4> . " ) + return wrapInInstanceBaseGraphQuery( QString( "%1 <%2> \"%3\"^^<%4> . %5" ) .arg( varName ) .arg( QString::fromAscii( node.term.property().toEncoded() ) ) .arg( subTerm.value().toString() ) .arg( p.literalRangeType().dataTypeUri() == Soprano::Vocabulary::RDFS::Literal() ? Soprano::Vocabulary::XMLSchema::string().toString() - : p.literalRangeType().dataTypeUri().toString() ) ); + : p.literalRangeType().dataTypeUri().toString() ) + .arg( filterString ) ); } } } @@ -279,6 +359,57 @@ return QString(); } + + + QDateTime parseDateTime( const Soprano::LiteralValue& literal ) { + //TODO: change to DateTime parser once complete + Nepomuk::Search::DateParser date( literal.toString() ); + if( date.hasDate() ) + return QDateTime( date.getDate() ); + else + { + Nepomuk::Search::TimeParser time( literal.toString() ); + if(time.hasTime() ) + return QDateTime(QDate::currentDate(), time.next() ); + else + return QDateTime(); //return invalid datetime + } + } + + + Soprano::LiteralValue parseSizeType( const Soprano::LiteralValue& literal ) { + const double KiB = 1024.0; + const double MiB = KiB * 1024.0; + const double GiB = MiB * 1024.0; + const double TiB = GiB * 1024.0; + + const double KB = 1000.0; + const double MB = KB * 1000.0; + const double GB = MB * 1000.0; + const double TB = GB * 1000.0; + + QHash<QString, double> sizes; + sizes.insert( "KiB", KiB ); + sizes.insert( "MiB", MiB ); + sizes.insert( "GiB", GiB ); + sizes.insert( "TiB", TiB ); + sizes.insert( "KB", KB ); + sizes.insert( "MB", MB ); + sizes.insert( "GB", GB ); + sizes.insert ("TB", TB ); + + QHash<QString, double>::const_iterator i; + for (i = sizes.constBegin(); i != sizes.constEnd(); ++i) { + QRegExp cur( QString("^([\\d]+.?[\\d]*)[\\s]*%1$").arg( i.key() ) ); + if( cur.indexIn( literal.toString() ) != -1 ) { + double value = cur.cap( 1 ).toDouble(); + double newValue = value * i.value(); + kDebug() << "Found value" << value << i.key() << "->" << newValue; + return Soprano::LiteralValue( newValue ); + } + } + return literal; + } } @@ -331,12 +462,14 @@ t = optimize( t ); kDebug() << "Optimized query:" << t; - search( splitLuceneSparql( t ) /*optimize( resolveValues( resolveFields( m_searchTerm ) ) )*/, 1.0, true ); + Nepomuk::Search::SearchNode temp = splitLuceneSparql( t, m_searchTerm.folderLimits() ); + + search( temp /*optimize( resolveValues( resolveFields( m_searchTerm ) ) )*/, 1.0, true ); } else { // FIXME: once we have the Soprano query API it should be simple to add the requestProperties here // for now we do it the hacky way - QString query = m_searchTerm.sparqlQuery(); + QString query = tuneQuery( m_searchTerm.sparqlQuery() ); int pos = query.indexOf( QLatin1String( "where" ) ); if ( pos > 0 ) { query.insert( pos, buildRequestPropertyVariableList() + ' ' ); @@ -346,6 +479,15 @@ } } + //do relative date parsing here + DateParser date(query, DateParser::RelativeDates); + while( date.hasDate() ) { + //TODO: this will likely not work with multiple dates in a query due to changing string length + QString replaced = Soprano::Node::literalToN3( QDateTime(date.getDate()) ); + query.replace( date.pos(), date.length(), replaced ); + date.next(); + } + sparqlQuery( query, 1.0, true ); } @@ -480,7 +622,7 @@ QUrl hit = hits.binding( 0 ).uri(); if ( prop.range().uri() == Soprano::Vocabulary::RDFS::Resource() || Nepomuk::Resource( hit ).hasType( prop.range().uri() ) ) { - orTerm.addSubTerm( Term( term.property(), hit ) ); + orTerm.addSubTerm( Term( term.property(), hit, term.positive() ) ); if ( orTerm.subTerms().count() == MAX_RESOURCES ) { break; } @@ -504,11 +646,43 @@ } } - // non-literal term or non-contains term -> handled in SPARQL query + else { - Term newTerm( term ); - newTerm.setSubTerms( QList<Term>() << resolveValues( term.subTerms().first() ) ); - return newTerm; + //modify the value for specific ranges + QString dateTimeRange = QString("ASK {%1 %2 %3}") + .arg( Soprano::Node::resourceToN3( term.property().toString() ) ) + .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::RDFS::range().toString() ) ) + .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::XMLSchema::dateTime().toString() ) ); + QString integerRange = QString("ASK {%1 %2 %3}") + .arg( Soprano::Node::resourceToN3( term.property().toString() ) ) + .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::RDFS::range().toString() ) ) + .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::XMLSchema::integer().toString() ) ); + + Soprano::QueryResultIterator dtRange = ResourceManager::instance()->mainModel()->executeQuery( dateTimeRange, Soprano::Query::QUERY_LANGUAGE_SPARQL ); + Soprano::QueryResultIterator intRange = ResourceManager::instance()->mainModel()->executeQuery( integerRange , Soprano::Query::QUERY_LANGUAGE_SPARQL ); + //look for date properties to parse + if( dtRange.boolValue() ) { + QDateTime dateTime = parseDateTime( term.subTerms().first().value() ); + kDebug() << "datetime is" << dateTime; + Term newTerm( term.property(), dateTime, term.positive(), term.comparator() ); + if( dateTime.isValid() ) + return newTerm; + else { + Term newTerm( term ); + newTerm.setSubTerms( QList<Term>() << resolveValues( term.subTerms().first() ) ); + return newTerm; + } + } + //check for sizes + else if( intRange.boolValue() ) { + return Term( term.property(), parseSizeType( term.subTerms().first().value() ), term.positive(), term.comparator() ); + } + // non-literal term or non-contains term -> handled in SPARQL query + else { + Term newTerm( term ); + newTerm.setSubTerms( QList<Term>() << resolveValues( term.subTerms().first() ) ); + return newTerm; + } } } @@ -549,7 +723,8 @@ } -Nepomuk::Search::SearchNode Nepomuk::Search::SearchThread::splitLuceneSparql( const Term& term ) +Nepomuk::Search::SearchNode Nepomuk::Search::SearchThread::splitLuceneSparql( const Nepomuk::Search::Term& term, + const QList<Nepomuk::Search::Query::FolderLimit>& folderLimits ) { // Goal: separate the terms into 2 groups: literal and resource which are // merged with only one AND or OR action. Is that possible? @@ -562,18 +737,18 @@ switch( term.type() ) { case Term::LiteralTerm: - return SearchNode( term, SearchNode::Lucene ); + return SearchNode( term, SearchNode::Lucene, folderLimits ); case Term::ComparisonTerm: if ( term.comparator() == Term::Contains && term.subTerms().first().type() == Term::LiteralTerm ) { // no need for subnides here - we only use the subterm's value - return SearchNode( term, SearchNode::Lucene ); + return SearchNode( term, SearchNode::Lucene, folderLimits ); } else { // all subnodes are resolved and can be handled in a SPARQL query - SearchNode node( term, SearchNode::Sparql ); - node.subNodes += splitLuceneSparql( term.subTerms().first() ); + SearchNode node( term, SearchNode::Sparql, folderLimits ); + node.subNodes += splitLuceneSparql( term.subTerms().first(), folderLimits ); return node; } @@ -585,7 +760,7 @@ QList<Term>::const_iterator end( subTerms.constEnd() ); for ( QList<Term>::const_iterator it = subTerms.constBegin(); it != end; ++it ) { - SearchNode node = splitLuceneSparql( *it ); + SearchNode node = splitLuceneSparql( *it, folderLimits ); if ( node.type == SearchNode::Lucene ) { luceneNodes += node; } @@ -598,24 +773,24 @@ } if ( luceneNodes.count() && !sparqlNodes.count() && !unknownNodes.count() ) { - return SearchNode( term, SearchNode::Lucene, luceneNodes ); + return SearchNode( term, SearchNode::Lucene, folderLimits, luceneNodes ); } else if ( !luceneNodes.count() && sparqlNodes.count() && !unknownNodes.count() ) { - return SearchNode( term, SearchNode::Sparql, sparqlNodes ); + return SearchNode( term, SearchNode::Sparql, folderLimits, sparqlNodes ); } else if ( !luceneNodes.count() && !sparqlNodes.count() && unknownNodes.count() ) { - return SearchNode( term, SearchNode::Unknown, unknownNodes ); + return SearchNode( term, SearchNode::Unknown, folderLimits, unknownNodes ); } else { Term newTerm; newTerm.setType( term.type() ); SearchNode andNode( newTerm ); if ( luceneNodes.count() ) - andNode.subNodes += SearchNode( term, SearchNode::Lucene, luceneNodes ); + andNode.subNodes += SearchNode( term, SearchNode::Lucene, folderLimits, luceneNodes ); if ( sparqlNodes.count() ) - andNode.subNodes += SearchNode( term, SearchNode::Sparql, sparqlNodes ); + andNode.subNodes += SearchNode( term, SearchNode::Sparql, folderLimits, sparqlNodes ); if ( unknownNodes.count() ) - andNode.subNodes += SearchNode( term, SearchNode::Unknown, unknownNodes ); + andNode.subNodes += SearchNode( term, SearchNode::Unknown, folderLimits, unknownNodes ); return andNode; } } @@ -755,9 +930,10 @@ // we do not search the data itself and do not have to filter // BUT: What about inference? - query = QString( "select ?p where { " + query = QString( "select distinct ?p where { " "?p <%1> <%2> . " "?p <%3> ?label . " + "?x ?p ?y . " "FILTER(REGEX(STR(?label),'%4','i')) . }" ) .arg( Soprano::Vocabulary::RDF::type().toString() ) .arg( Soprano::Vocabulary::RDF::Property().toString() ) @@ -775,8 +951,9 @@ if ( results.isEmpty() ) { - query = QString( "select ?p where { " + query = QString( "select distinct ?p where { " "?p <%1> <%2> . " + "?x ?p ?y . " "FILTER(REGEX(STR(?p),'%3','i')) . }" ) .arg( Soprano::Vocabulary::RDF::type().toString() ) .arg( Soprano::Vocabulary::RDF::Property().toString() ) @@ -799,7 +976,7 @@ QString Nepomuk::Search::SearchThread::createSparqlQuery( const Nepomuk::Search::SearchNode& node ) { int varCnt = 0; - return QString( "select distinct ?r %1 where { %3 %4 }" ) + return QString( "select distinct ?r %1 where { %3 %2 }" ) .arg( buildRequestPropertyVariableList() ) .arg( createGraphPattern( node, varCnt ) ) .arg( buildRequestPropertyPatterns() ); @@ -965,4 +1142,55 @@ } } + +void Nepomuk::Search::SearchThread::buildPrefixMap() +{ + // fixed prefixes + m_prefixes.insert( "rdf", Soprano::Vocabulary::RDF::rdfNamespace() ); + m_prefixes.insert( "rdfs", Soprano::Vocabulary::RDFS::rdfsNamespace() ); + m_prefixes.insert( "xsd", Soprano::Vocabulary::XMLSchema::xsdNamespace() ); + + // get prefixes from nepomuk + Soprano::QueryResultIterator it = + ResourceManager::instance()->mainModel()->executeQuery( QString( "select ?ns ?ab where { " + "?g %1 ?ns . " + "?g %2 ?ab . }" ) + .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::NAO::hasDefaultNamespace() ) ) + .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::NAO::hasDefaultNamespaceAbbreviation() ) ), + Soprano::Query::QueryLanguageSparql ); + while ( it.next() ) { + QString ab = it["ab"].toString(); + QUrl ns = it["ns"].toString(); + if ( !m_prefixes.contains( ab ) ) { + m_prefixes.insert( ab, ns ); + } + } +} + + +QString Nepomuk::Search::SearchThread::tuneQuery( const QString& query_ ) +{ + QString query( query_ ); + + buildPrefixMap(); + + for ( QHash<QString, QUrl>::const_iterator it = m_prefixes.constBegin(); + it != m_prefixes.constEnd(); ++it ) { + QString prefix = it.key(); + QUrl ns = it.value(); + + // very stupid check for the prefix usage + if ( query.contains( prefix + ':' ) ) { + // if the prefix is not defined add it + if ( !query.contains( QRegExp( QString( "[pP][rR][eE][fF][iI][xX]\\s*%1\\s*:\\s*<%2>" ) + .arg( prefix ) + .arg( QRegExp::escape( ns.toString() ) ) ) ) ) { + query.prepend( QString( "prefix %1: <%2> " ).arg( prefix ).arg( ns.toString() ) ); + } + } + } + + return query; +} + #include "searchthread.moc" diff -Nru kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchthread.h kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchthread.h --- kdebase-runtime-4.3.2/nepomuk/services/queryservice/searchthread.h 2008-09-18 14:25:44.000000000 +0200 +++ kdebase-runtime-4.3.2-new/nepomuk/services/queryservice/searchthread.h 2009-10-26 09:52:22.000000000 +0100 @@ -47,14 +47,19 @@ Sparql }; - SearchNode( const Term& t, Type tt = Unknown, const QList<SearchNode>& sub = QList<SearchNode>() ) + SearchNode( const Term& t, + Type tt = Unknown, + const QList<Nepomuk::Search::Query::FolderLimit>& l = QList<Nepomuk::Search::Query::FolderLimit>(), + const QList<SearchNode>& sub = QList<SearchNode>() ) : term(t), type(tt), + folderLimits(l), subNodes(sub) { } - + Term term; Type type; + QList<Nepomuk::Search::Query::FolderLimit> folderLimits; QList<SearchNode> subNodes; }; @@ -112,7 +117,7 @@ * Try to split the query into two (or more) subqueries, one of which will be * executed against the lucene index and one against the soprano store. */ - SearchNode splitLuceneSparql( const Term& term ); + SearchNode splitLuceneSparql( const Term& term, const QList<Nepomuk::Search::Query::FolderLimit>& folderLimits ); QList<QUrl> matchFieldName( const QString& field ); QHash<QUrl, Result> search( const SearchNode& node, double baseScore, bool reportResults = false ); @@ -128,12 +133,25 @@ QString createSparqlQuery( const SearchNode& node ); + /** + * Fill m_prefixes with values from the Nepomuk db. + */ + void buildPrefixMap(); + + /** + * Insert missing prefix statements into the query. + */ + QString tuneQuery( const QString& query_ ); + Query m_searchTerm; double m_cutOffScore; // status int m_numResults; bool m_canceled; + + // cache of all prefixes that are supported + QHash<QString, QUrl> m_prefixes; }; } }