perf(app-database-service):add keywords field and use it to find applications more accurately.

This commit is contained in:
JunjieBai 2023-11-28 17:15:31 +08:00
parent aee23999ac
commit e49d867075
6 changed files with 56 additions and 19 deletions

View File

@ -474,17 +474,16 @@ bool AppInfoTable::query(ApplicationInfoMap &infoMap, ApplicationProperties prop
return true; return true;
} }
bool AppInfoTable::query(ApplicationInfoMap &infoMap, ApplicationProperties properties, const QStringList &keywords, ApplicationPropertyMap restrictions) bool AppInfoTable::query(ApplicationInfoMap &infoMap, const ApplicationProperties& properties, const QStringList &keywords, const ApplicationPropertyMap& restrictions)
{ {
QString field; QString field;
for(const ApplicationProperty::Property &pro : properties) { for(const ApplicationProperty::Property &pro : properties) {
field.append(ApplicationPropertyHelper(pro).dataBaseField() + ","); field.append(ApplicationPropertyHelper(pro).dataBaseField() + ",");
} }
if(!properties.contains(ApplicationProperty::Property::DesktopFilePath)) { if(!properties.contains(ApplicationProperty::Property::DesktopFilePath)) {
field.append(ApplicationPropertyHelper(ApplicationProperty::Property::DesktopFilePath).dataBaseField()); field.append(ApplicationPropertyHelper(ApplicationProperty::Property::DesktopFilePath).dataBaseField() + ",");
} else if(!field.isEmpty()) {
field.remove(field.length() - 1, 1);
} }
field.append(ApplicationPropertyHelper(ApplicationProperty::Property::LocalKeywords).dataBaseField());
QString condition; QString condition;
for (const ApplicationProperty::Property prop: restrictions.keys()) { for (const ApplicationProperty::Property prop: restrictions.keys()) {
@ -492,18 +491,28 @@ bool AppInfoTable::query(ApplicationInfoMap &infoMap, ApplicationProperties prop
} }
QString keywordCondition; QString keywordCondition;
QString findWithLocalKeywords = "((select count(*) from ("
"WITH RECURSIVE split(str, rest) AS "
"("
"SELECT substr(LOCAL_KEYWORDS || ';', 1, instr(LOCAL_KEYWORDS || ';', ';') - 1),"
"substr(LOCAL_KEYWORDS || ';', instr(LOCAL_KEYWORDS || ';', ';') + 1) "
"UNION ALL "
"SELECT substr(rest, 1, instr(rest, ';') - 1), substr(rest, instr(rest, ';') + 1) "
"FROM split WHERE str <> ''"
")"
"SELECT str FROM split WHERE lower(str)=?)) <> 0)";
for(const QString& keyword : keywords) { for(const QString& keyword : keywords) {
if(keyword.size() < 2) { if(keyword.size() < 2) {
keywordCondition.append("(ifnull(LOCAL_NAME, '') like ? or ifnull(NAME_EN, '') like ? or ifnull(NAME_ZH, '') like ? or ifnull(FIRST_LETTER_OF_PINYIN, '') like ?) AND"); keywordCondition.append(QString("(ifnull(LOCAL_NAME, '') like ? or ifnull(NAME_EN, '') like ? or ifnull(NAME_ZH, '') like ? or ifnull(FIRST_LETTER_OF_PINYIN, '') like ? or %0) AND").arg(findWithLocalKeywords));
} else { } else {
keywordCondition.append("(ifnull(LOCAL_NAME, '') like ? or ifnull(NAME_EN, '') like ? or ifnull(NAME_ZH, '') like ? or ifnull(FIRST_LETTER_OF_PINYIN, '') like ? or ifnull(PINYIN_NAME, '') like ?) AND"); keywordCondition.append(QString("(ifnull(LOCAL_NAME, '') like ? or ifnull(NAME_EN, '') like ? or ifnull(NAME_ZH, '') like ? or ifnull(FIRST_LETTER_OF_PINYIN, '') like ? or ifnull(PINYIN_NAME, '') like ? or %0) AND").arg(findWithLocalKeywords));
} }
} }
if(!keywordCondition.isEmpty()) { if(!keywordCondition.isEmpty()) {
keywordCondition.remove(keywordCondition.length() - 3, 3); keywordCondition.remove(keywordCondition.length() - 3, 3);
} }
QString sql = QString("SELECT %0 FROM APPINFO WHERE %1 %2 ORDER BY LENGTH(LOCAL_NAME)").arg(field).arg(condition).arg(keywordCondition); QString sql = QString("SELECT %0 FROM APPINFO WHERE %1 %2 ORDER BY LENGTH(LOCAL_NAME)").arg(field, condition, keywordCondition);
QSqlQuery query(*d->m_database); QSqlQuery query(*d->m_database);
query.setForwardOnly(true); query.setForwardOnly(true);
query.prepare(sql); query.prepare(sql);
@ -515,12 +524,12 @@ bool AppInfoTable::query(ApplicationInfoMap &infoMap, ApplicationProperties prop
} }
for(const QString &keyword : keywords) { for(const QString &keyword : keywords) {
int i = 5; int i = 6;
if(keyword.size() < 2) { if(keyword.size() < 2) {
i--; i--;
} }
for (int bindCount = 0; bindCount < i; bindCount++) { for (int bindCount = 0; bindCount < i; bindCount++) {
query.bindValue(count, "%" + keyword + "%"); query.bindValue(count, (bindCount - i + 1) ? "%" + keyword + "%" : keyword.toLower());
count++; count++;
} }
} }

View File

@ -43,7 +43,7 @@ public:
bool query(ApplicationPropertyMap &propertyMap, const QString &desktopFile, ApplicationProperties properties); bool query(ApplicationPropertyMap &propertyMap, const QString &desktopFile, ApplicationProperties properties);
bool query(ApplicationInfoMap &infoMap, ApplicationProperties properties); bool query(ApplicationInfoMap &infoMap, ApplicationProperties properties);
bool query(ApplicationInfoMap &infoMap, ApplicationProperties properties, ApplicationPropertyMap restrictions); bool query(ApplicationInfoMap &infoMap, ApplicationProperties properties, ApplicationPropertyMap restrictions);
bool query(ApplicationInfoMap &infoMap, ApplicationProperties properties, const QStringList &keywords, ApplicationPropertyMap restrictions); bool query(ApplicationInfoMap &infoMap, const ApplicationProperties& properties, const QStringList &keywords, const ApplicationPropertyMap& restrictions);
/** /**
* @brief AppInfoTable::setAppFavoritesState * @brief AppInfoTable::setAppFavoritesState

View File

@ -43,7 +43,7 @@ ApplicationPropertyHelper::ApplicationPropertyHelper(ApplicationProperty::Proper
d->m_valueType = QMetaType::QString; d->m_valueType = QMetaType::QString;
break; break;
case ApplicationProperty::NameZh: case ApplicationProperty::NameZh:
d->m_databaseField = "NAME_EN"; d->m_databaseField = "NAME_ZH";
d->m_valueType = QMetaType::QString; d->m_valueType = QMetaType::QString;
break; break;
case ApplicationProperty::PinyinName: case ApplicationProperty::PinyinName:
@ -110,6 +110,18 @@ ApplicationPropertyHelper::ApplicationPropertyHelper(ApplicationProperty::Proper
d->m_databaseField = "AUTO_START"; d->m_databaseField = "AUTO_START";
d->m_valueType = QMetaType::Int; d->m_valueType = QMetaType::Int;
break; break;
case ApplicationProperty::StartUpWMClass:
d->m_databaseField = "START_UP_WMCLASS";
d->m_valueType = QMetaType::QString;
break;
case ApplicationProperty::Keywords:
d->m_databaseField = "KEYWORDS";
d->m_valueType = QMetaType::QString;
break;
case ApplicationProperty::LocalKeywords:
d->m_databaseField = "LOCAL_KEYWORDS";
d->m_valueType = QMetaType::QString;
break;
default: default:
break; break;
} }

View File

@ -54,7 +54,10 @@ enum Property {
Top, //置顶顺序0 未置顶) Top, //置顶顺序0 未置顶)
Lock, //是否锁定(不允许显示或打开) Lock, //是否锁定(不允许显示或打开)
DontDisplay, //是否不需要显示设置了Nodisplay等字段 DontDisplay, //是否不需要显示设置了Nodisplay等字段
AutoStart //是否自启动(位于自启动目录/etc/xdg/autostart下 AutoStart, //是否自启动(位于自启动目录/etc/xdg/autostart下
StartUpWMClass, //应用的className
Keywords, //关键词
LocalKeywords //关键词(本地化)
}; };
} //namespace ApplicationProperty } //namespace ApplicationProperty
typedef QVector<ApplicationProperty::Property> ApplicationProperties; typedef QVector<ApplicationProperty::Property> ApplicationProperties;

View File

@ -217,7 +217,7 @@ void AppDBManager::buildAppInfoDB()
{ {
qDebug() << "I'm going to build app info database."; qDebug() << "I'm going to build app info database.";
QSqlQuery sql(m_database); QSqlQuery sql(m_database);
QString cmd = QString("CREATE TABLE IF NOT EXISTS appInfo(%1, %2, %3, %4, %5, %6, %7, %8,%9, %10, %11, %12, %13, %14, %15, %16, %17, %18, %19, %20, %21, %22, %23)") QString cmd = QString("CREATE TABLE IF NOT EXISTS appInfo(%1, %2, %3, %4, %5, %6, %7, %8,%9, %10, %11, %12, %13, %14, %15, %16, %17, %18, %19, %20, %21, %22, %23, %24, %25)")
// .arg("ID INT")//自增id // .arg("ID INT")//自增id
.arg("DESKTOP_FILE_PATH TEXT PRIMARY KEY NOT NULL")//desktop文件路径 .arg("DESKTOP_FILE_PATH TEXT PRIMARY KEY NOT NULL")//desktop文件路径
.arg("MODIFYED_TIME TEXT")//YYYYMMDDHHmmSS 修改日期 .arg("MODIFYED_TIME TEXT")//YYYYMMDDHHmmSS 修改日期
@ -241,7 +241,9 @@ void AppDBManager::buildAppInfoDB()
.arg("LOCK INT")//应用是否锁定管控0未锁定1锁定 .arg("LOCK INT")//应用是否锁定管控0未锁定1锁定
.arg("DONT_DISPLAY INT")//应用隐藏(NoDisplay, NotShowIn) .arg("DONT_DISPLAY INT")//应用隐藏(NoDisplay, NotShowIn)
.arg("AUTO_START INT")//自启应用 .arg("AUTO_START INT")//自启应用
.arg("START_UP_WMCLASS TEXT");//classname .arg("START_UP_WMCLASS TEXT")//classname
.arg("KEYWORDS TEXT")//应用的关键词
.arg("LOCAL_KEYWORDS TEXT");//应用本地关键词,跟随系统语言
if (!sql.exec(cmd)) { if (!sql.exec(cmd)) {
qWarning() << m_database.lastError() << cmd; qWarning() << m_database.lastError() << cmd;
return; return;
@ -417,10 +419,11 @@ bool AppDBManager::handleLocaleDataUpdate(const QString &desktopFilePath)
QSqlQuery query(m_database); QSqlQuery query(m_database);
query.prepare("UPDATE appInfo SET LOCAL_NAME=:localName, FIRST_LETTER_ALL=:firstOfLetter2All WHERE DESKTOP_FILE_PATH=:desktopFilePath"); query.prepare("UPDATE appInfo SET LOCAL_NAME=:localName, FIRST_LETTER_ALL=:firstOfLetter2All, LOCAL_KEYWORDS=:localKeywords WHERE DESKTOP_FILE_PATH=:desktopFilePath");
query.bindValue(":localName", localName); query.bindValue(":localName", localName);
query.bindValue(":firstOfLetter2All", firstLetter2All); query.bindValue(":firstOfLetter2All", firstLetter2All);
query.bindValue(":desktopFilePath", desktopFilePath); query.bindValue(":desktopFilePath", desktopFilePath);
query.bindValue(":localKeywords", desktopFile.localizedValue("Keywords", "NULL").toString());
if (!this->startTransaction()) { if (!this->startTransaction()) {
return false; return false;
@ -576,9 +579,9 @@ bool AppDBManager::handleDBItemInsert(const QString &desktopFilePath)
"LOCAL_NAME, NAME_EN, NAME_ZH, PINYIN_NAME, " "LOCAL_NAME, NAME_EN, NAME_ZH, PINYIN_NAME, "
"FIRST_LETTER_OF_PINYIN, FIRST_LETTER_ALL, " "FIRST_LETTER_OF_PINYIN, FIRST_LETTER_ALL, "
"ICON, TYPE, CATEGORY, EXEC, COMMENT, MD5, " "ICON, TYPE, CATEGORY, EXEC, COMMENT, MD5, "
"LAUNCH_TIMES, FAVORITES, LAUNCHED, TOP, LOCK, DONT_DISPLAY, AUTO_START, START_UP_WMCLASS) " "LAUNCH_TIMES, FAVORITES, LAUNCHED, TOP, LOCK, DONT_DISPLAY, AUTO_START, START_UP_WMCLASS, KEYWORDS, LOCAL_KEYWORDS) "
"VALUES(:desktopFilePath, '%0', '%1', :localName, :enName, :zhName, :pinyinName, :firstLetterOfPinyin, :firstLetter2All, " "VALUES(:desktopFilePath, '%0', '%1', :localName, :enName, :zhName, :pinyinName, :firstLetterOfPinyin, :firstLetter2All, "
":icon, :type, :categories, :exec, :comment,'%2',%3,%4,%5,%6,%7,%8,%9, :wmClass)") ":icon, :type, :categories, :exec, :comment,'%2',%3,%4,%5,%6,%7,%8,%9, :wmClass, :keywords, :localKeywords)")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")) .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(getAppDesktopMd5(desktopFilePath)) .arg(getAppDesktopMd5(desktopFilePath))
@ -602,6 +605,8 @@ bool AppDBManager::handleDBItemInsert(const QString &desktopFilePath)
sql.bindValue(":exec", desktopfile.value("Exec").toString()); sql.bindValue(":exec", desktopfile.value("Exec").toString());
sql.bindValue(":comment", desktopfile.value("Comment").toString()); sql.bindValue(":comment", desktopfile.value("Comment").toString());
sql.bindValue(":wmClass", desktopfile.value("StartupWMClass").toString()); sql.bindValue(":wmClass", desktopfile.value("StartupWMClass").toString());
sql.bindValue(":keywords", desktopfile.value("Keywords").toString());
sql.bindValue(":localKeywords", desktopfile.localizedValue("Keywords", "NULL").toString());
if (!this->startTransaction()) { if (!this->startTransaction()) {
return false; return false;
@ -779,6 +784,8 @@ bool AppDBManager::handleDBItemUpdate(const QString &desktopFilePath)
"EXEC=:exec," "EXEC=:exec,"
"COMMENT=:comment," "COMMENT=:comment,"
"START_UP_WMCLASS=:wmClass," "START_UP_WMCLASS=:wmClass,"
"KEYWORDS=:keywords,"
"LOCAL_KEYWORDS=:localKeywords,"
"MD5='%1'," "MD5='%1',"
"DONT_DISPLAY=%2," "DONT_DISPLAY=%2,"
"AUTO_START=%3 " "AUTO_START=%3 "
@ -800,6 +807,8 @@ bool AppDBManager::handleDBItemUpdate(const QString &desktopFilePath)
sql.bindValue(":exec", desktopfile.value("Exec").toString()); sql.bindValue(":exec", desktopfile.value("Exec").toString());
sql.bindValue(":comment", desktopfile.value("Comment").toString()); sql.bindValue(":comment", desktopfile.value("Comment").toString());
sql.bindValue(":wmClass", desktopfile.value("StartupWMClass").toString()); sql.bindValue(":wmClass", desktopfile.value("StartupWMClass").toString());
sql.bindValue(":keywords", desktopfile.value("Keywords").toString());
sql.bindValue(":localKeywords", desktopfile.localizedValue("Keywords", "NULL").toString());
if (!this->startTransaction()) { if (!this->startTransaction()) {
return false; return false;

View File

@ -41,8 +41,10 @@
#define CONNECTION_NAME QLatin1String("ukss-appdb-connection") #define CONNECTION_NAME QLatin1String("ukss-appdb-connection")
/** /**
* changelog(1.2 to 1.3): Add the START_UP_WMCLASS field in order to find desktop file path by the windowClassClass. * changelog(1.2 to 1.3): Add the START_UP_WMCLASS field in order to find desktop file path by the windowClassClass.
* changelog(1.3 to 1.4): Add the KEYWORDS & LOCAL_KEYWORDS field in order to find application conveniently.
*/ */
static const QString APP_DATABASE_VERSION = QStringLiteral("1.3");
static const QString APP_DATABASE_VERSION = QStringLiteral("1.4");
namespace UkuiSearch { namespace UkuiSearch {
/** /**
@ -184,7 +186,9 @@ private:
{"LOCK", "INT"}, {"LOCK", "INT"},
{"DONT_DISPLAY", "INT"}, {"DONT_DISPLAY", "INT"},
{"AUTO_START", "INT"}, {"AUTO_START", "INT"},
{"START_UP_WMCLASS", "TEXT"} {"START_UP_WMCLASS", "TEXT"},
{"KEYWORDS", "TEXT"},
{"LOCAL_KEYWORDS", "TEXT"}
}; };
//应用黑名单 //应用黑名单