]> Some of my projects - localmylist.git/commitdiff
Add Anime Search functions.
authorAPTX <marek321@gmail.com>
Sat, 31 Aug 2013 15:43:35 +0000 (17:43 +0200)
committerAPTX <marek321@gmail.com>
Sat, 31 Aug 2013 15:43:35 +0000 (17:43 +0200)
localmylist/database.cpp
localmylist/database.h
localmylist/databaseclasses.h
localmylist/scriptable.cpp
localmylist/scriptable.h
localmylist/share/schema/schema.sql

index 7614c7ff647469de4dd1cdadb76a233c6c2b4314..c2cb4c2be42a2de3d100c83fa3e03dd51e3fbe5c 100644 (file)
@@ -134,6 +134,72 @@ bool Database::rollback()
        return true;
 }
 
+AnimeTitle Database::animeSearchBestMatch(const QString &query)
+{
+       QList<AnimeTitle> ret = animeSearch(query, 1);
+       if (ret.count())
+               return ret[0];
+       return AnimeTitle();
+}
+
+QList<AnimeTitle> Database::animeSearch(const QString &query, int limit)
+{
+       QList<AnimeTitle> ret;
+
+       QSqlQuery &q = prepare(
+       "SELECT at.title_id, at.aid, at.type, at.language, at.title "
+       "       FROM anime_title at "
+       "       WHERE at.title ILIKE :query "
+       "       ORDER BY at.title <-> :query2 ASC "
+       "       LIMIT :limit");
+       q.bindValue(":query", query);
+       q.bindValue(":query2", query);
+       q.bindValue(":limit", limit);
+
+       if (!exec(q))
+               return ret;
+
+       QSqlResultIterator it(q);
+       while (q.next())
+       {
+               AnimeTitle data;
+               readAnimeTitleData(it, data);
+               ret << data;
+       }
+
+       q.finish();
+
+       return ret;
+}
+
+QList<AnimeTitle> Database::animeSearchSimmilar(const QString &query, int limit)
+{
+       QList<AnimeTitle> ret;
+
+       QSqlQuery &q = prepare(
+       "SELECT at.title_id, at.aid, at.type, at.language, at.title "
+       "       FROM anime_title at "
+       "       ORDER BY at.title <-> :query ASC "
+       "       LIMIT :limit");
+       q.bindValue(":query", query);
+       q.bindValue(":limit", limit);
+
+       if (!exec(q))
+               return ret;
+
+       QSqlResultIterator it(q);
+       while (q.next())
+       {
+               AnimeTitle data;
+               readAnimeTitleData(it, data);
+               ret << data;
+       }
+
+       q.finish();
+
+       return ret;
+}
+
 OpenFileData Database::firstUnwatchedByExactTitle(const QString &title)
 {
        QSqlQuery &q = prepare(
@@ -1657,7 +1723,7 @@ void Database::readAnimeTitleData(const SqlResultIteratorInterface &result, Anim
        data.titleId = result.value(offset++).toInt();
        data.aid = result.value(offset++).toInt();
        data.type = AnimeTitle::TitleType(result.value(offset++).toInt());
-       data.language = result.value(offset++).toString();
+       data.language = result.value(offset++).toString().trimmed();
        data.title = result.value(offset++).toString();
 }
 
@@ -2198,4 +2264,13 @@ void RaiiTransaction::commit()
        c = true;
 }
 
+QString toSearchQuery(const QString &string)
+{
+       const static QChar anyChar = QChar('%');
+       QString ret = string.trimmed();
+       ret.replace(QRegExp("\\s+"), anyChar);
+       ret = ret.append(anyChar).prepend(anyChar);
+       return ret;
+}
+
 } // namespace LocalMyList
index 996450b7c2a1d1f818870f080f161b73261d807a..89a8bf8f461d0c08ada1e6a3b8401a82157e080b 100644 (file)
@@ -47,6 +47,10 @@ public slots:
        bool commit();
        bool rollback();
 
+       LocalMyList::AnimeTitle animeSearchBestMatch(const QString &query);
+       QList<LocalMyList::AnimeTitle> animeSearch(const QString &query, int limit = 100);
+       QList<LocalMyList::AnimeTitle> animeSearchSimmilar(const QString &query, int limit = 5);
+
        LocalMyList::OpenFileData firstUnwatchedByExactTitle(const QString &title);
        LocalMyList::OpenFileData firstUnwatchedByTitle(const QString &title);
        LocalMyList::OpenFileData firstUnwatchedByAid(int aid);
@@ -221,6 +225,8 @@ public:
        void commit();
 };
 
+QString toSearchQuery(const QString &string);
+
 } // namespace LocalMyList
 
 Q_DECLARE_METATYPE(LocalMyList::Database*)
index 0366bdab09051a377b439e1e0e978e7bfaba4ea7..75d6c0950cb060fb8672ff446d1f081f80c9c4fa 100644 (file)
@@ -262,6 +262,10 @@ struct LOCALMYLISTSHARED_EXPORT DatabaseConnectionSettings
 
 } // namespace LocalMyList
 
+Q_DECLARE_METATYPE(LocalMyList::AnimeTitle)
+Q_DECLARE_METATYPE(LocalMyList::AnimeTitle*)
+Q_DECLARE_METATYPE(QList<LocalMyList::AnimeTitle>)
+Q_DECLARE_METATYPE(QList<LocalMyList::AnimeTitle*>)
 Q_DECLARE_METATYPE(LocalMyList::Anime)
 Q_DECLARE_METATYPE(LocalMyList::Anime*)
 Q_DECLARE_METATYPE(QList<LocalMyList::Anime>)
index fff61a7ff69fc5f333ebc3f88e24864499258748..c33823cf50d91a94880d7842b98fc2215984b9c7 100644 (file)
@@ -12,6 +12,13 @@ void registerTypes(QScriptEngine *engine)
        qScriptRegisterMetaType<Database*>(engine, Scriptable::toScriptValue<Database*>, Scriptable::fromScriptValue<Database*>);
        qScriptRegisterMetaType<Settings*>(engine, Scriptable::toScriptValue<Settings*>, Scriptable::fromScriptValue<Settings*>);
 
+       engine->globalObject().setProperty("toSearchQuery", engine->newFunction(Scriptable::toSearchQuery));
+
+       Scriptable::AnimeTitle *AnimeTitlePrototype = new Scriptable::AnimeTitle();
+       engine->setDefaultPrototype(qMetaTypeId<AnimeTitle>(), engine->newQObject(AnimeTitlePrototype));
+       engine->setDefaultPrototype(qMetaTypeId<AnimeTitle*>(), engine->newQObject(AnimeTitlePrototype));
+       qScriptRegisterSequenceMetaType<QList<AnimeTitle> >(engine);
+       qScriptRegisterSequenceMetaType<QList<AnimeTitle*> >(engine);
        Scriptable::Anime *AnimePrototype = new Scriptable::Anime();
        engine->setDefaultPrototype(qMetaTypeId<Anime>(), engine->newQObject(AnimePrototype));
        engine->setDefaultPrototype(qMetaTypeId<Anime*>(), engine->newQObject(AnimePrototype));
@@ -82,6 +89,40 @@ void registerTypes(QScriptEngine *engine)
 
 namespace Scriptable {
 
+::LocalMyList::AnimeTitle *AnimeTitle::thisObj() const
+{
+       return qscriptvalue_cast< ::LocalMyList::AnimeTitle*>(thisObject());
+}
+
+AnimeTitle::AnimeTitle(QObject *parent) : QObject(parent), QScriptable()
+{
+}
+
+QString titleTypeToString(int titleType)
+{
+       switch (::LocalMyList::AnimeTitle::TitleType(titleType))
+       {
+               case ::LocalMyList::AnimeTitle::PrimaryTitle:
+                       return "Primary";
+               case ::LocalMyList::AnimeTitle::OfficialTitle:
+                       return "Official";
+               case ::LocalMyList::AnimeTitle::ShortTitle:
+                       return "Short";
+               case ::LocalMyList::AnimeTitle::Synonym:
+                       return "Synonym";
+       }
+       return "Unknown";
+}
+
+QString AnimeTitle::toString() const
+{
+       return QString("AnimeTitle('%1', %2, %3, %4)")
+                       .arg(read_title()).arg(read_language())
+                       .arg(titleTypeToString(read_type())).arg(read_aid());
+}
+
+// --------------------------------------------------------------------------------
+
 ::LocalMyList::Anime *Anime::thisObj() const
 {
        return qscriptvalue_cast< ::LocalMyList::Anime*>(thisObject());
@@ -260,6 +301,64 @@ QString DatabaseConnectionSettings::toString() const
 //                                   Generated
 // --------------------------------------------------------------------------------
 
+int AnimeTitle::read_aid() const
+{
+       auto o = thisObj();
+       if (!o) return int();
+       return o->aid;
+}
+
+void AnimeTitle::write_aid(int val)
+{
+       auto o = thisObj();
+       if (!o) return;
+       o->aid = val;
+}
+
+int AnimeTitle::read_type() const
+{
+       auto o = thisObj();
+       if (!o) return int();
+       return int(o->type);
+}
+
+void AnimeTitle::write_type(int val)
+{
+       auto o = thisObj();
+       if (!o) return;
+       o->type = ::LocalMyList::AnimeTitle::TitleType(val);
+}
+
+QString AnimeTitle::read_language() const
+{
+       auto o = thisObj();
+       if (!o) return QString();
+       return o->language;
+}
+
+void AnimeTitle::write_language(QString val)
+{
+       auto o = thisObj();
+       if (!o) return;
+       o->language = val;
+}
+
+QString AnimeTitle::read_title() const
+{
+       auto o = thisObj();
+       if (!o) return QString();
+       return o->title;
+}
+
+void AnimeTitle::write_title(QString val)
+{
+       auto o = thisObj();
+       if (!o) return;
+       o->title = val;
+}
+
+// --------------------------------------------------------------------------------
+
 int Anime::read_aid() const
 {
        auto o = thisObj();
@@ -2087,5 +2186,12 @@ QScriptValue DatabaseConnectionSettings_ctor(QScriptContext *, QScriptEngine *en
        return engine->toScriptValue(::LocalMyList::DatabaseConnectionSettings());
 }
 
+QScriptValue toSearchQuery(QScriptContext *ctx, QScriptEngine *engine)
+{
+       if (!ctx->argumentCount())
+               return engine->toScriptValue(QString());
+       return engine->toScriptValue(::LocalMyList::toSearchQuery(ctx->argument(0).toString()));
+}
+
 } // namespace Scriptable
 } // namespace LocalMyList
index a3e4976154a48e614dce844d938e16beb30ba39f..ee85499966d2858fd54a5af247eca93a44f7f9ca 100644 (file)
@@ -10,6 +10,7 @@
 
 namespace LocalMyList {
 
+struct AnimeTitle;
 struct Anime;
 struct Episode;
 struct File;
@@ -36,24 +37,35 @@ template<typename T> void fromScriptValue(const QScriptValue &value, T &t)
        t = qobject_cast<T>(value.toQObject());
 }
 
-/*
-struct LOCALMYLISTSHARED_EXPORT AnimeTitle
+struct LOCALMYLISTSHARED_EXPORT AnimeTitle : public QObject, protected QScriptable
 {
-       enum TitleType {
-               PrimaryTitle    = 1,
-               Synonym                 = 2,
-               ShortTitle              = 3,
-               OfficialTitle   = 4
-       };
-
-       int aid;
-       TitleType type;
-       QString language;
-       QString title;
-
-       AnimeTitle(int aid = 0, TitleType type = PrimaryTitle, const QString &language = QString(), const QString &title = QString());
+       Q_OBJECT
+       Q_PROPERTY(int aid READ read_aid WRITE write_aid)
+       Q_PROPERTY(int type READ read_type WRITE write_type)
+       Q_PROPERTY(QString language READ read_language WRITE write_language)
+       Q_PROPERTY(QString title READ read_title WRITE write_title)
+
+       ::LocalMyList::AnimeTitle *thisObj() const;
+
+public:
+       AnimeTitle(QObject *parent = 0);
+
+public slots:
+       QString toString() const;
+
+public:
+       int read_aid() const;
+       void write_aid(int val);
+
+       int read_type() const;
+       void write_type(int val);
+
+       QString read_language() const;
+       void write_language(QString val);
+
+       QString read_title() const;
+       void write_title(QString val);
 };
-*/
 
 class LOCALMYLISTSHARED_EXPORT Anime : public QObject, protected QScriptable
 {
@@ -710,6 +722,7 @@ QScriptValue HostInfo_ctor(QScriptContext *, QScriptEngine *engine);
 QScriptValue OpenFileData_ctor(QScriptContext *, QScriptEngine *engine);
 QScriptValue DatabaseConnectionSettings_ctor(QScriptContext *, QScriptEngine *engine);
 
+QScriptValue toSearchQuery(QScriptContext *, QScriptEngine *engine);
 } // namespace Scriptable
 
 } // namespace LocalMyList
index 258f66a6965bf466c6ee59e313b55744dd924782..9a3ddb1888e02ac6696a6b6625ce39a30a0b59e5 100644 (file)
@@ -45,7 +45,7 @@ CREATE TABLE anime_title (
        CONSTRAINT unique_title UNIQUE (aid, type, language, title)
 );
 CREATE INDEX aid_idx ON anime_title USING btree (aid);
-CREATE INDEX title_idx ON anime_title USING gin (to_tsvector('simple'::regconfig, (title)::text));
+CREATE INDEX title_gist_trgm_idx ON anime_title USING gist (title gist_trgm_ops);
 CREATE INDEX language_idx ON anime_title USING hash (language);
 
 CREATE TABLE episode (