summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarek Kobus <jaroslaw.kobus@vacuumcleaner.com>2015-11-25 11:28:34 +0100
committerJarek Kobus <jaroslaw.kobus@qt.io>2017-03-08 10:04:16 +0000
commitd116ac573d80b6fdfdea4a56b7ae44f84e0ba6ad (patch)
tree829f2e9a994671064358073d6e8dcf1e2f74c8bd
parent76ca291deee980e8952e438abb5a26e6468d0c29 (diff)
downloadqttools-d116ac573d80b6fdfdea4a56b7ae44f84e0ba6ad.tar.gz
Use sqlite's FTS5 for search
This change drops the usage of clucene library. We store the plain text version of html content of all the documentation inside the sqlite database. We transform the html into plain text using QTextDocumentFragment::fromHtml() and QTextDocumentFragment::toPlainText() methods, what causes the indexing a bit slower, however it makes the indexing much more accurate (try e.g. searching for [charset] or ["utf-8"] keywords) and enables showing the highlight (next patch). Dropped usage of local socket in order to detect if the other assistant instance is running. Obsoleted the -rebuild-search-index cmd line option. Comparison for the whole documentation of Qt: Disk usage for index: ~15 MB (5.8), ~85 MB (current) Indexing time: ~40 s (5.8), ~60 s (current). Change-Id: I89f2fa9efddc354f86e5ed0f5b4f5e2790057121 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r--src/assistant/assistant.pro3
-rw-r--r--src/assistant/assistant/cmdlineparser.cpp13
-rw-r--r--src/assistant/assistant/cmdlineparser.h1
-rw-r--r--src/assistant/assistant/helpenginewrapper.cpp4
-rw-r--r--src/assistant/assistant/main.cpp33
-rw-r--r--src/assistant/help/help.pro21
-rw-r--r--src/assistant/help/qclucenefieldnames.cpp55
-rw-r--r--src/assistant/help/qclucenefieldnames_p.h72
-rw-r--r--src/assistant/help/qhelpenginecore.cpp7
-rw-r--r--src/assistant/help/qhelpsearchengine.cpp45
-rw-r--r--src/assistant/help/qhelpsearchengine.h1
-rw-r--r--src/assistant/help/qhelpsearchindex_default.cpp58
-rw-r--r--src/assistant/help/qhelpsearchindex_default_p.h81
-rw-r--r--src/assistant/help/qhelpsearchindexreader.cpp38
-rw-r--r--src/assistant/help/qhelpsearchindexreader_clucene.cpp473
-rw-r--r--src/assistant/help/qhelpsearchindexreader_clucene_p.h112
-rw-r--r--src/assistant/help/qhelpsearchindexreader_default.cpp576
-rw-r--r--src/assistant/help/qhelpsearchindexreader_default_p.h49
-rw-r--r--src/assistant/help/qhelpsearchindexreader_p.h8
-rw-r--r--src/assistant/help/qhelpsearchindexwriter_clucene.cpp870
-rw-r--r--src/assistant/help/qhelpsearchindexwriter_clucene_p.h122
-rw-r--r--src/assistant/help/qhelpsearchindexwriter_default.cpp537
-rw-r--r--src/assistant/help/qhelpsearchindexwriter_default_p.h53
-rw-r--r--src/assistant/help/qhelpsearchquerywidget.cpp399
-rw-r--r--src/assistant/help/qhelpsearchresultwidget.cpp21
25 files changed, 634 insertions, 3018 deletions
diff --git a/src/assistant/assistant.pro b/src/assistant/assistant.pro
index 34590d69e..799582a40 100644
--- a/src/assistant/assistant.pro
+++ b/src/assistant/assistant.pro
@@ -1,13 +1,12 @@
TEMPLATE = subdirs
-SUBDIRS += clucene \
+SUBDIRS += \
help \
assistant \
qhelpgenerator \
qcollectiongenerator \
qhelpconverter
-help.depends = clucene
assistant.depends = help
qhelpgenerator.depends = help
qcollectiongenerator.depends = help
diff --git a/src/assistant/assistant/cmdlineparser.cpp b/src/assistant/assistant/cmdlineparser.cpp
index dff8484b2..b7bf7e49a 100644
--- a/src/assistant/assistant/cmdlineparser.cpp
+++ b/src/assistant/assistant/cmdlineparser.cpp
@@ -60,7 +60,9 @@ static const char helpMessage[] = QT_TRANSLATE_NOOP("CmdLineParser",
" file.\n"
"-setCurrentFilter filter Set the filter as the active filter.\n"
"-remove-search-index Removes the full text search index.\n"
- "-rebuild-search-index Re-builds the full text search index (potentially slow).\n"
+ "-rebuild-search-index Obsolete. Use -remove-search-index instead.\n"
+ " Removes the full text search index.\n"
+ " It will be rebuilt on next Assistant run.\n"
"-quiet Does not display any error or\n"
" status message.\n"
"-help Displays this help.\n"
@@ -76,7 +78,6 @@ CmdLineParser::CmdLineParser(const QStringList &arguments)
m_search(Untouched),
m_register(None),
m_removeSearchIndex(false),
- m_rebuildSearchIndex(false),
m_quiet(false)
{
TRACE_OBJ
@@ -117,7 +118,7 @@ CmdLineParser::Result CmdLineParser::parse()
else if (arg == QLatin1String("-remove-search-index"))
m_removeSearchIndex = true;
else if (arg == QLatin1String("-rebuild-search-index"))
- m_rebuildSearchIndex = true;
+ m_removeSearchIndex = true;
else if (arg == QLatin1String("-help"))
showHelp = true;
else
@@ -342,12 +343,6 @@ bool CmdLineParser::removeSearchIndex() const
return m_removeSearchIndex;
}
-bool CmdLineParser::rebuildSearchIndex() const
-{
- TRACE_OBJ
- return m_rebuildSearchIndex;
-}
-
CmdLineParser::RegisterState CmdLineParser::registerRequest() const
{
TRACE_OBJ
diff --git a/src/assistant/assistant/cmdlineparser.h b/src/assistant/assistant/cmdlineparser.h
index ed16140eb..2a49cbcf4 100644
--- a/src/assistant/assistant/cmdlineparser.h
+++ b/src/assistant/assistant/cmdlineparser.h
@@ -94,7 +94,6 @@ private:
RegisterState m_register;
QString m_currentFilter;
bool m_removeSearchIndex;
- bool m_rebuildSearchIndex;
bool m_quiet;
QString m_error;
};
diff --git a/src/assistant/assistant/helpenginewrapper.cpp b/src/assistant/assistant/helpenginewrapper.cpp
index ea9fd6103..68427562a 100644
--- a/src/assistant/assistant/helpenginewrapper.cpp
+++ b/src/assistant/assistant/helpenginewrapper.cpp
@@ -147,7 +147,7 @@ HelpEngineWrapper::HelpEngineWrapper(const QString &collectionFile)
* called after the new docs have been installed.
*/
disconnect(d->m_helpEngine, SIGNAL(setupFinished()),
- searchEngine(), SLOT(indexDocumentation()));
+ searchEngine(), SLOT(scheduleIndexDocumentation()));
connect(d, SIGNAL(documentationRemoved(QString)),
this, SIGNAL(documentationRemoved(QString)));
@@ -176,7 +176,7 @@ void HelpEngineWrapper::initialDocSetupDone()
{
TRACE_OBJ
connect(d->m_helpEngine, SIGNAL(setupFinished()),
- searchEngine(), SLOT(indexDocumentation()));
+ searchEngine(), SLOT(scheduleIndexDocumentation()));
setupData();
}
diff --git a/src/assistant/assistant/main.cpp b/src/assistant/assistant/main.cpp
index 8ccdb8107..5cf868f8f 100644
--- a/src/assistant/assistant/main.cpp
+++ b/src/assistant/assistant/main.cpp
@@ -42,8 +42,6 @@
#include <QtHelp/QHelpEngine>
#include <QtHelp/QHelpSearchEngine>
-#include <QtNetwork/QLocalSocket>
-
#include <QtSql/QSqlDatabase>
#if defined(BROWSER_QTWEBKIT)
@@ -171,12 +169,8 @@ bool removeSearchIndex(const QString &collectionFile)
QString path = QFileInfo(collectionFile).path();
path += QLatin1Char('/') + indexFilesFolder(collectionFile);
- QLocalSocket localSocket;
- localSocket.connectToServer(QString(QLatin1String("QtAssistant%1"))
- .arg(QLatin1String(QT_VERSION_STR)));
-
- QDir dir(path); // check if there is no other instance ruinning
- if (!dir.exists() || localSocket.waitForConnected())
+ QDir dir(path);
+ if (!dir.exists())
return false;
const QStringList &list = dir.entryList(QDir::Files | QDir::Hidden);
@@ -185,24 +179,6 @@ bool removeSearchIndex(const QString &collectionFile)
return true;
}
-bool rebuildSearchIndex(QCoreApplication *app, const QString &collectionFile,
- CmdLineParser &cmd)
-{
- TRACE_OBJ
- QHelpEngine engine(collectionFile);
- if (!engine.setupData()) {
- cmd.showMessage(QCoreApplication::translate("Assistant", "Error: %1")
- .arg(engine.error()), true);
- return false;
- }
-
- QHelpSearchEngine * const searchEngine = engine.searchEngine();
- QObject::connect(searchEngine, SIGNAL(indexingFinished()), app,
- SLOT(quit()));
- searchEngine->reindexDocumentation();
- return app->exec() == 0;
-}
-
QCoreApplication* createApplication(int &argc, char *argv[])
{
TRACE_OBJ
@@ -388,11 +364,6 @@ int main(int argc, char *argv[])
? EXIT_SUCCESS : EXIT_FAILURE;
}
- if (cmd.rebuildSearchIndex()) {
- return rebuildSearchIndex(a.data(), cachedCollectionFile, cmd)
- ? EXIT_SUCCESS : EXIT_FAILURE;
- }
-
if (!QSqlDatabase::isDriverAvailable(QLatin1String("QSQLITE"))) {
cmd.showMessage(QCoreApplication::translate("Assistant",
"Cannot load sqlite database driver!"),
diff --git a/src/assistant/help/help.pro b/src/assistant/help/help.pro
index 37d5ba416..5b0421ed0 100644
--- a/src/assistant/help/help.pro
+++ b/src/assistant/help/help.pro
@@ -1,17 +1,14 @@
TARGET = QtHelp
-QT = core-private gui widgets
-QT_PRIVATE = network sql clucene-private
+QT = core-private gui widgets sql
+QT_PRIVATE = network
-DEFINES += QHELP_LIB QT_CLUCENE_SUPPORT
+DEFINES += QHELP_LIB
QMAKE_DOCS = $$PWD/doc/qthelp.qdocconf
DEFINES -= QT_ASCII_CAST_WARNINGS
-# impossible to disable exceptions in clucene atm and use help lib without
-CONFIG += exceptions
-
RESOURCES += helpsystem.qrc
SOURCES += qhelpenginecore.cpp \
qhelpengine.cpp \
@@ -25,16 +22,11 @@ SOURCES += qhelpenginecore.cpp \
qhelpsearchengine.cpp \
qhelpsearchquerywidget.cpp \
qhelpsearchresultwidget.cpp \
- qhelpsearchindex_default.cpp \
qhelpsearchindexwriter_default.cpp \
qhelpsearchindexreader_default.cpp \
qhelpsearchindexreader.cpp \
- qclucenefieldnames.cpp \
qhelp_global.cpp
-# access to clucene
-SOURCES += qhelpsearchindexwriter_clucene.cpp \
- qhelpsearchindexreader_clucene.cpp
HEADERS += qhelpenginecore.h \
qhelpengine.h \
qhelpengine_p.h \
@@ -52,11 +44,6 @@ HEADERS += qhelpenginecore.h \
qhelpsearchindex_default_p.h \
qhelpsearchindexwriter_default_p.h \
qhelpsearchindexreader_default_p.h \
- qhelpsearchindexreader_p.h \
- qclucenefieldnames_p.h
-
-# access to clucene
-HEADERS += qhelpsearchindexwriter_clucene_p.h \
- qhelpsearchindexreader_clucene_p.h
+ qhelpsearchindexreader_p.h
load(qt_module)
diff --git a/src/assistant/help/qclucenefieldnames.cpp b/src/assistant/help/qclucenefieldnames.cpp
deleted file mode 100644
index f4ec0c573..000000000
--- a/src/assistant/help/qclucenefieldnames.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qclucenefieldnames_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace fulltextsearch {
-namespace clucene {
-const QString AttributeField(QLatin1String("attribute"));
-const QString ContentField(QLatin1String("content"));
-const QString NamespaceField(QLatin1String("namespace"));
-const QString PathField(QLatin1String("path"));
-const QString TitleField(QLatin1String("title"));
-const QString TitleTokenizedField(QLatin1String("titleTokenized"));
-} // namespace clucene
-} // namespace fulltextsearch
-
-QT_END_NAMESPACE
diff --git a/src/assistant/help/qclucenefieldnames_p.h b/src/assistant/help/qclucenefieldnames_p.h
deleted file mode 100644
index 9bacb27db..000000000
--- a/src/assistant/help/qclucenefieldnames_p.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QCLUCENEFIELDNAMES_P_H
-#define QCLUCENEFIELDNAMES_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QtGlobal>
-#include <QtCore/QString>
-
-QT_BEGIN_NAMESPACE
-
-namespace fulltextsearch {
-namespace clucene {
- extern const QString AttributeField;
- extern const QString ContentField;
- extern const QString NamespaceField;
- extern const QString PathField;
- extern const QString TitleField;
- extern const QString TitleTokenizedField;
-} // namespace clucene
-} // namespace fulltextsearch
-
-QT_END_NAMESPACE
-
-#endif // QCLUCENEFIELDNAMES_P_H
diff --git a/src/assistant/help/qhelpenginecore.cpp b/src/assistant/help/qhelpenginecore.cpp
index 763ad43df..e6cbf5296 100644
--- a/src/assistant/help/qhelpenginecore.cpp
+++ b/src/assistant/help/qhelpenginecore.cpp
@@ -499,12 +499,15 @@ void QHelpEngineCore::setCurrentFilter(const QString &filterName)
*/
QList<QStringList> QHelpEngineCore::filterAttributeSets(const QString &namespaceName) const
{
+ QList<QStringList> ret;
if (d->setup()) {
QHelpDBReader *reader = d->readerMap.value(namespaceName);
if (reader)
- return reader->filterAttributeSets();
+ ret = reader->filterAttributeSets();
}
- return QList<QStringList>();
+ if (ret.isEmpty())
+ ret.append(QStringList());
+ return ret;
}
/*!
diff --git a/src/assistant/help/qhelpsearchengine.cpp b/src/assistant/help/qhelpsearchengine.cpp
index deae8136b..5579c0cc3 100644
--- a/src/assistant/help/qhelpsearchengine.cpp
+++ b/src/assistant/help/qhelpsearchengine.cpp
@@ -43,13 +43,8 @@
#include "qhelpsearchresultwidget.h"
#include "qhelpsearchindexreader_p.h"
-#if defined(QT_CLUCENE_SUPPORT)
-# include "qhelpsearchindexreader_clucene_p.h"
-# include "qhelpsearchindexwriter_clucene_p.h"
-#else
-# include "qhelpsearchindexreader_default_p.h"
-# include "qhelpsearchindexwriter_default_p.h"
-#endif
+#include "qhelpsearchindexreader_default_p.h"
+#include "qhelpsearchindexwriter_default_p.h"
#include <QtCore/QDir>
#include <QtCore/QFile>
@@ -57,14 +52,11 @@
#include <QtCore/QVariant>
#include <QtCore/QThread>
#include <QtCore/QPointer>
+#include <QtCore/QTimer>
QT_BEGIN_NAMESPACE
-#if defined(QT_CLUCENE_SUPPORT)
- using namespace fulltextsearch::clucene;
-#else
- using namespace fulltextsearch::qt;
-#endif
+using namespace fulltextsearch::qt;
class QHelpSearchEnginePrivate : public QObject
{
@@ -122,7 +114,6 @@ private:
connect(indexWriter, SIGNAL(indexingStarted()), this, SIGNAL(indexingStarted()));
connect(indexWriter, SIGNAL(indexingFinished()), this, SIGNAL(indexingFinished()));
- connect(indexWriter, SIGNAL(indexingFinished()), this, SLOT(optimizeIndex()));
}
indexWriter->cancelIndexing();
@@ -145,11 +136,7 @@ private:
return;
if (!indexReader) {
-#if defined(QT_CLUCENE_SUPPORT)
- indexReader = new QHelpSearchIndexReaderClucene();
-#else
indexReader = new QHelpSearchIndexReaderDefault();
-#endif // QT_CLUCENE_SUPPORT
connect(indexReader, SIGNAL(searchingStarted()), this, SIGNAL(searchingStarted()));
connect(indexReader, SIGNAL(searchingFinished(int)), this, SIGNAL(searchingFinished(int)));
}
@@ -177,19 +164,11 @@ private:
return indexFilesFolder;
}
-private slots:
- void optimizeIndex()
- {
-#if defined(QT_CLUCENE_SUPPORT)
- if (indexWriter && !helpEngine.isNull()) {
- indexWriter->optimizeIndex();
- }
-#endif
- }
-
private:
friend class QHelpSearchEngine;
+ bool m_isIndexingScheduled = false;
+
QHelpSearchQueryWidget *queryWidget;
QHelpSearchResultWidget *resultWidget;
@@ -323,7 +302,7 @@ QHelpSearchEngine::QHelpSearchEngine(QHelpEngineCore *helpEngine, QObject *paren
{
d = new QHelpSearchEnginePrivate(helpEngine);
- connect(helpEngine, SIGNAL(setupFinished()), this, SLOT(indexDocumentation()));
+ connect(helpEngine, SIGNAL(setupFinished()), this, SLOT(scheduleIndexDocumentation()));
connect(d, SIGNAL(indexingStarted()), this, SIGNAL(indexingStarted()));
connect(d, SIGNAL(indexingFinished()), this, SIGNAL(indexingFinished()));
@@ -440,8 +419,18 @@ void QHelpSearchEngine::search(const QList<QHelpSearchQuery> &queryList)
d->search(queryList);
}
+void QHelpSearchEngine::scheduleIndexDocumentation()
+{
+ if (d->m_isIndexingScheduled)
+ return;
+
+ d->m_isIndexingScheduled = true;
+ QTimer::singleShot(0, this, &QHelpSearchEngine::indexDocumentation);
+}
+
void QHelpSearchEngine::indexDocumentation()
{
+ d->m_isIndexingScheduled = false;
d->updateIndex();
}
diff --git a/src/assistant/help/qhelpsearchengine.h b/src/assistant/help/qhelpsearchengine.h
index 8521d0dcc..db4876f8d 100644
--- a/src/assistant/help/qhelpsearchengine.h
+++ b/src/assistant/help/qhelpsearchengine.h
@@ -106,6 +106,7 @@ Q_SIGNALS:
void searchingFinished(int hits);
private Q_SLOTS:
+ void scheduleIndexDocumentation();
void indexDocumentation();
private:
diff --git a/src/assistant/help/qhelpsearchindex_default.cpp b/src/assistant/help/qhelpsearchindex_default.cpp
deleted file mode 100644
index d5d470e22..000000000
--- a/src/assistant/help/qhelpsearchindex_default.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qhelpsearchindex_default_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QDataStream &operator>>(QDataStream &s, Document &l)
-{
- s >> l.docNumber;
- s >> l.frequency;
- return s;
-}
-
-QDataStream &operator<<(QDataStream &s, const Document &l)
-{
- s << qint16(l.docNumber);
- s << qint16(l.frequency);
- return s;
-}
-
-QT_END_NAMESPACE
diff --git a/src/assistant/help/qhelpsearchindex_default_p.h b/src/assistant/help/qhelpsearchindex_default_p.h
index a611649cc..9f57612cd 100644
--- a/src/assistant/help/qhelpsearchindex_default_p.h
+++ b/src/assistant/help/qhelpsearchindex_default_p.h
@@ -59,88 +59,19 @@ QT_BEGIN_NAMESPACE
namespace QtHelpInternal {
-struct Document {
- Document(qint16 d, qint16 f)
- : docNumber(d), frequency(f) {}
+struct DocumentInfo {
- Document()
- : docNumber(-1), frequency(0) {}
+ DocumentInfo() = default;
+ DocumentInfo(const QString &title, const QString &url)
+ : m_title(title), m_url(url) {}
- bool operator==(const Document &doc) const {
- return docNumber == doc.docNumber;
- }
- bool operator<(const Document &doc) const {
- return frequency > doc.frequency;
- }
- bool operator<=(const Document &doc) const {
- return frequency >= doc.frequency;
- }
- bool operator>(const Document &doc) const {
- return frequency < doc.frequency;
- }
-
- qint16 docNumber;
- qint16 frequency;
-};
-
-struct DocumentInfo : public Document {
- DocumentInfo()
- : Document(-1, 0), documentTitle(QString()), documentUrl(QString()) {}
-
- DocumentInfo(qint16 d, qint16 f, const QString &title, const QString &url)
- : Document(d, f), documentTitle(title), documentUrl(url) {}
-
- DocumentInfo(const Document &document, const QString &title, const QString &url)
- : Document(document.docNumber, document.frequency), documentTitle(title), documentUrl(url) {}
-
- QString documentTitle;
- QString documentUrl;
-};
-
-struct Entry {
- Entry(qint16 d) { documents.append(Document(d, 1)); }
- Entry(QVector<Document> l) : documents(l) {}
-
- QVector<Document> documents;
-};
-
-struct PosEntry {
- PosEntry(int p) { positions.append(p); }
- QList<uint> positions;
-};
-
-struct Term {
- Term() : frequency(-1) {}
- Term(const QString &t, int f, QVector<Document> l) : term(t), frequency(f), documents(l) {}
- QString term;
- int frequency;
- QVector<Document>documents;
- bool operator<(const Term &i2) const { return frequency < i2.frequency; }
-};
-
-struct TermInfo {
- TermInfo() : frequency(-1) {}
- TermInfo(const QString &t, int f, QVector<DocumentInfo> l)
- : term(t), frequency(f), documents(l) {}
-
- bool operator<(const TermInfo &i2) const { return frequency < i2.frequency; }
-
- QString term;
- int frequency;
- QVector<DocumentInfo>documents;
+ QString m_title;
+ QString m_url;
};
} // namespace QtHelpInternal
-using QtHelpInternal::Document;
using QtHelpInternal::DocumentInfo;
-using QtHelpInternal::Entry;
-using QtHelpInternal::PosEntry;
-using QtHelpInternal::Term;
-using QtHelpInternal::TermInfo;
-
-QDataStream &operator>>(QDataStream &s, Document &l);
-QDataStream &operator<<(QDataStream &s, const Document &l);
QT_END_NAMESPACE
diff --git a/src/assistant/help/qhelpsearchindexreader.cpp b/src/assistant/help/qhelpsearchindexreader.cpp
index 5c21c5151..8037f0120 100644
--- a/src/assistant/help/qhelpsearchindexreader.cpp
+++ b/src/assistant/help/qhelpsearchindexreader.cpp
@@ -43,27 +43,16 @@ QT_BEGIN_NAMESPACE
namespace fulltextsearch {
-QHelpSearchIndexReader::QHelpSearchIndexReader()
- : QThread()
- , m_cancel(false)
-{
- // nothing todo
-}
-
QHelpSearchIndexReader::~QHelpSearchIndexReader()
{
- mutex.lock();
- this->m_cancel = true;
- mutex.unlock();
-
+ cancelSearching();
wait();
}
void QHelpSearchIndexReader::cancelSearching()
{
- mutex.lock();
- this->m_cancel = true;
- mutex.unlock();
+ QMutexLocker lock(&m_mutex);
+ m_cancel = true;
}
void QHelpSearchIndexReader::search(const QString &collectionFile, const QString &indexFilesFolder,
@@ -71,29 +60,26 @@ void QHelpSearchIndexReader::search(const QString &collectionFile, const QString
{
wait();
- this->hitList.clear();
- this->m_cancel = false;
- this->m_query = queryList;
- this->m_collectionFile = collectionFile;
- this->m_indexFilesFolder = indexFilesFolder;
+ m_hitList.clear();
+ m_cancel = false;
+ m_query = queryList;
+ m_collectionFile = collectionFile;
+ m_indexFilesFolder = indexFilesFolder;
start(QThread::NormalPriority);
}
int QHelpSearchIndexReader::hitCount() const
{
- QMutexLocker lock(&mutex);
- return hitList.count();
+ QMutexLocker lock(&m_mutex);
+ return m_hitList.count();
}
QList<QHelpSearchEngine::SearchHit> QHelpSearchIndexReader::hits(int start,
int end) const
{
- QList<QHelpSearchEngine::SearchHit> hits;
- QMutexLocker lock(&mutex);
- for (int i = start; i < end && i < hitList.count(); ++i)
- hits.append(hitList.at(i));
- return hits;
+ QMutexLocker lock(&m_mutex);
+ return m_hitList.mid(start, end - start);
}
diff --git a/src/assistant/help/qhelpsearchindexreader_clucene.cpp b/src/assistant/help/qhelpsearchindexreader_clucene.cpp
deleted file mode 100644
index 7d5bcee1e..000000000
--- a/src/assistant/help/qhelpsearchindexreader_clucene.cpp
+++ /dev/null
@@ -1,473 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "private/qindexreader_p.h"
-#include "private/qqueryparser_p.h"
-#include "private/qsearchable_p.h"
-#include "qclucenefieldnames_p.h"
-#include "qhelpenginecore.h"
-
-#include "qhelpsearchindexreader_clucene_p.h"
-
-#include <QtCore/QDir>
-#include <QtCore/QSet>
-#include <QtCore/QString>
-#include <QtCore/QFileInfo>
-#include <QtCore/QSharedPointer>
-#include <QtCore/QStringList>
-#include <QtCore/QTextStream>
-#include <QtCore/QMutexLocker>
-
-QT_BEGIN_NAMESPACE
-
-namespace fulltextsearch {
-namespace clucene {
-
-QHelpSearchIndexReaderClucene::QHelpSearchIndexReaderClucene()
- : QHelpSearchIndexReader()
-{
- // nothing todo
-}
-
-QHelpSearchIndexReaderClucene::~QHelpSearchIndexReaderClucene()
-{
-}
-
-
-void QHelpSearchIndexReaderClucene::run()
-{
- mutex.lock();
-
- if (m_cancel) {
- mutex.unlock();
- return;
- }
-
- const QString collectionFile(this->m_collectionFile);
- const QList<QHelpSearchQuery> &queryList = this->m_query;
- const QString indexPath(m_indexFilesFolder);
-
- mutex.unlock();
-
- QHelpEngineCore engine(collectionFile, 0);
- if (!engine.setupData())
- return;
-
- QFileInfo fInfo(indexPath);
- if (fInfo.exists() && !fInfo.isWritable()) {
- qWarning("Full Text Search, could not read index (missing permissions).");
- return;
- }
-
- if(QCLuceneIndexReader::indexExists(indexPath)) {
- mutex.lock();
- if (m_cancel) {
- mutex.unlock();
- return;
- }
- mutex.unlock();
-
- emit searchingStarted();
-
- try {
- QCLuceneBooleanQuery booleanQueryTitle;
- QCLuceneBooleanQuery booleanQueryContent;
- QCLuceneStandardAnalyzer analyzer;
- const QStringList& attribList =
- engine.filterAttributes(engine.currentFilter());
- bool titleQueryIsValid = buildQuery(queryList, TitleTokenizedField,
- attribList, booleanQueryTitle, analyzer);
- bool contentQueryIsValid = buildQuery(queryList, ContentField,
- attribList, booleanQueryContent, analyzer);
- if (!titleQueryIsValid && !contentQueryIsValid) {
- emit searchingFinished(0);
- return;
- }
-
- QCLuceneIndexSearcher indexSearcher(indexPath);
-
- // QCLuceneHits object must be allocated on the heap, because
- // there is no default constructor.
- QSharedPointer<QCLuceneHits> titleHits;
- QSharedPointer<QCLuceneHits> contentHits;
- if (titleQueryIsValid) {
- titleHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits(
- indexSearcher.search(booleanQueryTitle)));
- }
- if (contentQueryIsValid) {
- contentHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits(
- indexSearcher.search(booleanQueryContent)));
- }
- bool boost = true;
- if ((titleHits.isNull() || titleHits->length() == 0)
- && (contentHits.isNull() || contentHits->length() == 0)) {
- booleanQueryTitle = QCLuceneBooleanQuery();
- booleanQueryContent = QCLuceneBooleanQuery();
- titleQueryIsValid =
- buildTryHarderQuery(queryList, TitleTokenizedField,
- attribList, booleanQueryTitle, analyzer);
- contentQueryIsValid =
- buildTryHarderQuery(queryList, ContentField, attribList,
- booleanQueryContent, analyzer);
- if (!titleQueryIsValid && !contentQueryIsValid) {
- emit searchingFinished(0);
- return;
- }
- if (titleQueryIsValid) {
- titleHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits(
- indexSearcher.search(booleanQueryTitle)));
- }
- if (contentQueryIsValid) {
- contentHits = QSharedPointer<QCLuceneHits>(new QCLuceneHits(
- indexSearcher.search(booleanQueryContent)));
- }
- boost = false;
- }
- QList<QSharedPointer<QCLuceneHits> > cluceneHitsList;
- if (!titleHits.isNull())
- cluceneHitsList.append(titleHits);
- if (!contentHits.isNull())
- cluceneHitsList.append(contentHits);
-
- QSet<QString> pathSet;
- QCLuceneDocument document;
- const QStringList namespaceList = engine.registeredDocumentations();
-
- for (const QSharedPointer<QCLuceneHits> &hits : qAsConst(cluceneHitsList)) {
- for (qint32 i = 0; i < hits->length(); i++) {
- document = hits->document(i);
- const QString path = document.get(PathField);
- if (!pathSet.contains(path) && namespaceList.contains(
- document.get(NamespaceField), Qt::CaseInsensitive)) {
- pathSet.insert(path);
- hitList.append(qMakePair(path, document.get(TitleField)));
- }
- document.clear();
-
- mutex.lock();
- if (m_cancel) {
- mutex.unlock();
- emit searchingFinished(0);
- return;
- }
- mutex.unlock();
- }
- }
-
- indexSearcher.close();
- const int count = hitList.count();
- if ((count > 0) && boost)
- boostSearchHits(engine, hitList, queryList);
- emit searchingFinished(hitList.count());
- } catch(...) {
- mutex.lock();
- hitList.clear();
- mutex.unlock();
- emit searchingFinished(0);
- }
- }
-}
-
-bool QHelpSearchIndexReaderClucene::buildQuery(
- const QList<QHelpSearchQuery> &queries, const QString &fieldName,
- const QStringList &filterAttributes, QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer)
-{
- bool queryIsValid = false;
- for (const QHelpSearchQuery &query : queries) {
- if (fieldName != ContentField && isNegativeQuery(query)) {
- queryIsValid = false;
- break;
- }
- switch (query.fieldName) {
- case QHelpSearchQuery::FUZZY:
- if (addFuzzyQuery(query, fieldName, booleanQuery, analyzer))
- queryIsValid = true;
- break;
- case QHelpSearchQuery::WITHOUT:
- if (fieldName != ContentField)
- return false;
- if (addWithoutQuery(query, fieldName, booleanQuery))
- queryIsValid = true;
- break;
- case QHelpSearchQuery::PHRASE:
- if (addPhraseQuery(query, fieldName, booleanQuery))
- queryIsValid = true;
- break;
- case QHelpSearchQuery::ALL:
- if (addAllQuery(query, fieldName, booleanQuery))
- queryIsValid = true;
- break;
- case QHelpSearchQuery::DEFAULT:
- if (addDefaultQuery(query, fieldName, true, booleanQuery, analyzer))
- queryIsValid = true;
- break;
- case QHelpSearchQuery::ATLEAST:
- if (addAtLeastQuery(query, fieldName, booleanQuery, analyzer))
- queryIsValid = true;
- break;
- default:
- Q_ASSERT(!"Invalid field name");
- }
- }
-
- if (queryIsValid && !filterAttributes.isEmpty()) {
- queryIsValid =
- addAttributesQuery(filterAttributes, booleanQuery, analyzer);
- }
-
- return queryIsValid;
-}
-
-bool QHelpSearchIndexReaderClucene::buildTryHarderQuery(
- const QList<QHelpSearchQuery> &queries, const QString &fieldName,
- const QStringList &filterAttributes, QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer)
-{
- if (queries.isEmpty())
- return false;
- const QHelpSearchQuery &query = queries.front();
- if (query.fieldName != QHelpSearchQuery::DEFAULT)
- return false;
- if (isNegativeQuery(query))
- return false;
- if (!addDefaultQuery(query, fieldName, !filterAttributes.isEmpty(), booleanQuery, analyzer))
- return false;
- if (filterAttributes.isEmpty())
- return true;
- return addAttributesQuery(filterAttributes, booleanQuery, analyzer);
-}
-
-bool QHelpSearchIndexReaderClucene::isNegativeQuery(const QHelpSearchQuery &query) const
-{
- const QString &search = query.wordList.join(QLatin1Char(' '));
- return search.contains(QLatin1Char('!')) || search.contains(QLatin1Char('-'))
- || search.contains(QLatin1String(" NOT "));
-}
-
-bool QHelpSearchIndexReaderClucene::addFuzzyQuery(const QHelpSearchQuery &query,
- const QString &fieldName, QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer)
-{
- bool queryIsValid = false;
- const QLatin1String fuzzy("~");
- for (const QString &term : query.wordList) {
- if (!term.isEmpty()) {
- QCLuceneQuery *lQuery =
- QCLuceneQueryParser::parse(term + fuzzy, fieldName, analyzer);
- if (lQuery != 0) {
- booleanQuery.add(lQuery, true, false, false);
- queryIsValid = true;
- }
- }
- }
- return queryIsValid;
-}
-
-bool QHelpSearchIndexReaderClucene::addWithoutQuery(const QHelpSearchQuery &query,
- const QString &fieldName, QCLuceneBooleanQuery &booleanQuery)
-{
- bool queryIsValid = false;
- const QStringList &stopWords = QCLuceneStopAnalyzer().englishStopWords();
- for (const QString &term : query.wordList) {
- if (stopWords.contains(term, Qt::CaseInsensitive))
- continue;
- QCLuceneQuery *lQuery = new QCLuceneTermQuery(QCLuceneTerm(
- fieldName, term.toLower()));
- booleanQuery.add(lQuery, true, false, true);
- queryIsValid = true;
- }
- return queryIsValid;
-}
-
-bool QHelpSearchIndexReaderClucene::addPhraseQuery(const QHelpSearchQuery &query,
- const QString &fieldName, QCLuceneBooleanQuery &booleanQuery)
-{
- const QString phrase = query.wordList.at(0).toLower();
- QStringList terms = phrase.split(QChar::Space);
- for (const QString &word : QCLuceneStopAnalyzer().englishStopWords())
- terms.removeAll(word);
-
- if (terms.isEmpty())
- return false;
-
- if (terms.count() == 1) {
- QCLuceneQuery *term = new QCLuceneTermQuery(QCLuceneTerm(fieldName, terms[0].toLower()));
- booleanQuery.add(term, true, true, false);
- return true;
- }
-
- QCLucenePhraseQuery *phraseQuery = new QCLucenePhraseQuery();
- for (const QString &term : qAsConst(terms))
- phraseQuery->addTerm(QCLuceneTerm(fieldName, term.toLower()));
- booleanQuery.add(phraseQuery, true, true, false);
- return true;
-}
-
-bool QHelpSearchIndexReaderClucene::addAllQuery(const QHelpSearchQuery &query,
- const QString &fieldName, QCLuceneBooleanQuery &booleanQuery)
-{
- bool queryIsValid = false;
- const QStringList &stopWords = QCLuceneStopAnalyzer().englishStopWords();
- for (const QString &term : query.wordList) {
- if (stopWords.contains(term, Qt::CaseInsensitive))
- continue;
- QCLuceneQuery *lQuery = new QCLuceneTermQuery(QCLuceneTerm(
- fieldName, term.toLower()));
- booleanQuery.add(lQuery, true, true, false);
- queryIsValid = true;
- }
- return queryIsValid;
-}
-
-bool QHelpSearchIndexReaderClucene::addDefaultQuery(const QHelpSearchQuery &query,
- const QString &fieldName, bool allTermsRequired,
- QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer)
-{
- bool queryIsValid = false;
- for (const QString &term : query.wordList) {
- QCLuceneQuery *lQuery =
- QCLuceneQueryParser::parse(term.toLower(), fieldName, analyzer);
- if (lQuery) {
- booleanQuery.add(lQuery, true, allTermsRequired, false);
- queryIsValid = true;
- }
- }
- return queryIsValid;
-}
-
-bool QHelpSearchIndexReaderClucene::addAtLeastQuery(
- const QHelpSearchQuery &query, const QString &fieldName,
- QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer)
-{
- bool queryIsValid = false;
- for (const QString &term : query.wordList) {
- if (!term.isEmpty()) {
- QCLuceneQuery *lQuery =
- QCLuceneQueryParser::parse(term, fieldName, analyzer);
- if (lQuery) {
- booleanQuery.add(lQuery, true, false, false);
- queryIsValid = true;
- }
- }
- }
- return queryIsValid;
-}
-
-bool QHelpSearchIndexReaderClucene::addAttributesQuery(
- const QStringList &filterAttributes, QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer)
-{
- QCLuceneQuery* lQuery = QCLuceneQueryParser::parse(QLatin1String("+")
- + filterAttributes.join(QLatin1String(" +")), AttributeField, analyzer);
- if (!lQuery)
- return false;
- booleanQuery.add(lQuery, true, true, false);
- return true;
-}
-
-void QHelpSearchIndexReaderClucene::boostSearchHits(const QHelpEngineCore &engine,
- QList<QHelpSearchEngine::SearchHit> &hitList, const QList<QHelpSearchQuery> &queryList)
-{
- for (const QHelpSearchQuery &query : queryList) {
- if (query.fieldName != QHelpSearchQuery::DEFAULT)
- continue;
-
- QString joinedQuery = query.wordList.join(QChar::Space);
-
- QCLuceneStandardAnalyzer analyzer;
- QCLuceneQuery *parsedQuery = QCLuceneQueryParser::parse(
- joinedQuery, ContentField, analyzer);
-
- if (parsedQuery) {
- joinedQuery = parsedQuery->toString();
- delete parsedQuery;
- }
-
- const QString contentString(ContentField + QLatin1String(":"));
- int length = contentString.length();
- int index = joinedQuery.indexOf(contentString);
-
- QString term;
- int nextIndex = 0;
- QStringList searchTerms;
- while (index != -1) {
- nextIndex = joinedQuery.indexOf(contentString, index + 1);
- term = joinedQuery.mid(index + length, nextIndex - (length + index)).simplified();
- if (term.startsWith(QLatin1Char('"'))
- && term.endsWith(QLatin1Char('"'))) {
- searchTerms.append(term.remove(QLatin1Char('"')));
- } else {
- searchTerms += term.split(QChar::Space);
- }
- index = nextIndex;
- }
- searchTerms.removeDuplicates();
-
- int count = qMin(75, hitList.count());
- QMap<int, QHelpSearchEngine::SearchHit> hitMap;
- for (int i = 0; i < count; ++i) {
- const QHelpSearchEngine::SearchHit &hit = hitList.at(i);
- QString data = QString::fromUtf8(engine.fileData(hit.first));
-
- int counter = 0;
- for (const QString &term : qAsConst(searchTerms))
- counter += data.count(term, Qt::CaseInsensitive);
- hitMap.insertMulti(counter, hit);
- }
-
- QList<QHelpSearchEngine::SearchHit> boostedList;
-
- for (auto it = hitMap.cend(), begin = hitMap.cbegin(); it != begin; ) {
- --it;
- boostedList.append(it.value());
- }
-
- boostedList += hitList.mid(count, hitList.count());
- mutex.lock();
- hitList = boostedList;
- mutex.unlock();
- }
-}
-
-} // namespace clucene
-} // namespace fulltextsearch
-
-QT_END_NAMESPACE
diff --git a/src/assistant/help/qhelpsearchindexreader_clucene_p.h b/src/assistant/help/qhelpsearchindexreader_clucene_p.h
deleted file mode 100644
index a9890f0f0..000000000
--- a/src/assistant/help/qhelpsearchindexreader_clucene_p.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QHELPSEARCHINDEXREADERCLUCENE_H
-#define QHELPSEARCHINDEXREADERCLUCENE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the help generator tools. This header file may change from version
-// to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QList>
-#include <QtCore/QString>
-#include <QtCore/QStringList>
-
-#include "private/qanalyzer_p.h"
-#include "private/qquery_p.h"
-#include "qhelpsearchindexreader_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace fulltextsearch {
-namespace clucene {
-
-class QHelpSearchIndexReaderClucene : public QHelpSearchIndexReader
-{
- Q_OBJECT
-
-public:
- QHelpSearchIndexReaderClucene();
- ~QHelpSearchIndexReaderClucene();
-
-private:
- void run() override;
- void boostSearchHits(const QHelpEngineCore &engine, QList<QHelpSearchEngine::SearchHit> &hitList,
- const QList<QHelpSearchQuery> &queryList);
- bool buildQuery(const QList<QHelpSearchQuery> &queries,
- const QString &fieldName,
- const QStringList &filterAttributes,
- QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer);
- bool buildTryHarderQuery(const QList<QHelpSearchQuery> &queries,
- const QString &fieldName,
- const QStringList &filterAttributes,
- QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer);
- bool addFuzzyQuery(const QHelpSearchQuery &query, const QString &fieldName,
- QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer);
- bool addWithoutQuery(const QHelpSearchQuery &query, const QString &fieldName,
- QCLuceneBooleanQuery &booleanQuery);
- bool addPhraseQuery(const QHelpSearchQuery &query, const QString &fieldName,
- QCLuceneBooleanQuery &booleanQuery);
- bool addAllQuery(const QHelpSearchQuery &query, const QString &fieldName,
- QCLuceneBooleanQuery &booleanQuery);
- bool addDefaultQuery(const QHelpSearchQuery &query, const QString &fieldName,
- bool allTermsRequired, QCLuceneBooleanQuery &booleanQuery,
- QCLuceneAnalyzer &analyzer);
- bool addAtLeastQuery(const QHelpSearchQuery &query, const QString &fieldName,
- QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer);
- bool addAttributesQuery(const QStringList &filterAttributes,
- QCLuceneBooleanQuery &booleanQuery, QCLuceneAnalyzer &analyzer);
- bool isNegativeQuery(const QHelpSearchQuery &query) const;
-};
-
-} // namespace clucene
-} // namespace fulltextsearch
-
-QT_END_NAMESPACE
-
-#endif // QHELPSEARCHINDEXREADERCLUCENE_H
diff --git a/src/assistant/help/qhelpsearchindexreader_default.cpp b/src/assistant/help/qhelpsearchindexreader_default.cpp
index 460aad2cb..18e20a382 100644
--- a/src/assistant/help/qhelpsearchindexreader_default.cpp
+++ b/src/assistant/help/qhelpsearchindexreader_default.cpp
@@ -40,461 +40,169 @@
#include "qhelpenginecore.h"
#include "qhelpsearchindexreader_default_p.h"
-#include <QtCore/QDir>
-#include <QtCore/QUrl>
-#include <QtCore/QFile>
-#include <QtCore/QVariant>
-#include <QtCore/QFileInfo>
-#include <QtCore/QDataStream>
-#include <QtCore/QTextStream>
-
-#include <algorithm>
+#include <QtCore/QSet>
+#include <QtSql/QSqlDatabase>
+#include <QtSql/QSqlQuery>
QT_BEGIN_NAMESPACE
namespace fulltextsearch {
namespace qt {
-namespace {
- QStringList split( const QString &str )
- {
- QStringList lst;
- int j = 0;
- int i = str.indexOf(QLatin1Char('*'), j );
-
- if (str.startsWith(QLatin1Char('*')))
- lst << QLatin1String("*");
-
- while ( i != -1 ) {
- if ( i > j && i <= (int)str.length() ) {
- lst << str.mid( j, i - j );
- lst << QLatin1String("*");
- }
- j = i + 1;
- i = str.indexOf(QLatin1Char('*'), j );
- }
-
- const int l = str.length() - 1;
- if ( str.mid( j, l - j + 1 ).length() > 0 )
- lst << str.mid( j, l - j + 1 );
-
- return lst;
- }
-}
-
-
-Reader::Reader()
- : indexPath(QString())
- , indexFile(QString())
- , documentFile(QString())
-{
- termList.clear();
- indexTable.clear();
- searchIndexTable.clear();
-}
-
-Reader::~Reader()
-{
- reset();
- searchIndexTable.clear();
-}
-
-bool Reader::readIndex()
-{
- if (indexTable.contains(indexFile))
- return true;
-
- QFile idxFile(indexFile);
- if (!idxFile.open(QFile::ReadOnly))
- return false;
-
- QString key;
- int numOfDocs;
- EntryTable entryTable;
- QVector<Document> docs;
- QDataStream dictStream(&idxFile);
- while (!dictStream.atEnd()) {
- dictStream >> key;
- dictStream >> numOfDocs;
- docs.resize(numOfDocs);
- dictStream >> docs;
- entryTable.insert(key, new Entry(docs));
- }
- idxFile.close();
-
- if (entryTable.isEmpty())
- return false;
-
- QFile docFile(documentFile);
- if (!docFile.open(QFile::ReadOnly))
- return false;
-
- QString title, url;
- DocumentList documentList;
- QDataStream docStream(&docFile);
- while (!docStream.atEnd()) {
- docStream >> title;
- docStream >> url;
- documentList.append(QStringList(title) << url);
- }
- docFile.close();
-
- if (documentList.isEmpty()) {
- cleanupIndex(entryTable);
- return false;
- }
-
- indexTable.insert(indexFile, Index(entryTable, documentList));
- return true;
-}
-
-bool Reader::initCheck() const
-{
- return !searchIndexTable.isEmpty();
-}
-
void Reader::setIndexPath(const QString &path)
{
- indexPath = path;
-}
-
-void Reader::filterFilesForAttributes(const QStringList &attributes)
-{
- searchIndexTable.clear();
- for (auto it = indexTable.cbegin(), end = indexTable.cend(); it != end; ++it) {
- const QString fileName = it.key();
- bool containsAll = true;
- const QStringList split = fileName.split(QLatin1Char('@'));
- for (const QString &attribute : attributes) {
- if (!split.contains(attribute, Qt::CaseInsensitive)) {
- containsAll = false;
- break;
- }
- }
-
- if (containsAll)
- searchIndexTable.insert(fileName, it.value());
- }
+ m_indexPath = path;
+ m_namespaces.clear();
}
-void Reader::setIndexFile(const QString &namespaceName, const QString &attributes)
+void Reader::addNamespace(const QString &namespaceName, const QStringList &attributes)
{
- const QString extension = namespaceName + QLatin1Char('@') + attributes;
- indexFile = indexPath + QLatin1String("/indexdb40.") + extension;
- documentFile = indexPath + QLatin1String("/indexdoc40.") + extension;
+ m_namespaces.insert(namespaceName, attributes);
}
-bool Reader::splitSearchTerm(const QString &searchTerm, QStringList *terms,
- QStringList *termSeq, QStringList *seqWords)
+static QString namespacePlaceholders(const QMultiMap<QString, QStringList> &namespaces)
{
- QString term = searchTerm;
-
- term = term.simplified();
- term = term.replace(QLatin1Char('\''), QLatin1Char('"'));
- term = term.replace(QLatin1Char('`'), QLatin1Char('"'));
- term.remove(QLatin1Char('-'));
- term = term.replace(QRegExp(QLatin1String("\\s[\\S]?\\s")), QString(QChar::Space));
-
- *terms = term.split(QLatin1Char(' '));
- for (QString &str : *terms) {
- str = str.simplified();
- str = str.toLower();
- str.remove(QLatin1Char('"'));
- }
-
- if (term.contains(QLatin1Char('"'))) {
- if ((term.count(QLatin1Char('"')))%2 == 0) {
- int beg = 0;
- int end = 0;
- QString s;
- beg = term.indexOf(QLatin1Char('"'), beg);
- while (beg != -1) {
- beg++;
- end = term.indexOf(QLatin1Char('"'), beg);
- s = term.mid(beg, end - beg);
- s = s.toLower();
- s = s.simplified();
- if (s.contains(QLatin1Char('*'))) {
- qWarning("Full Text Search, using a wildcard within phrases is not allowed.");
- return false;
+ QString placeholders;
+ const auto namespaceList = namespaces.uniqueKeys();
+ bool firstNS = true;
+ for (const QString &ns : namespaceList) {
+ if (firstNS)
+ firstNS = false;
+ else
+ placeholders += QLatin1String(" OR ");
+ placeholders += QLatin1String("(namespace = ?");
+
+ const QList<QStringList> &attributeSets = namespaces.values(ns);
+ bool firstAS = true;
+ for (const QStringList &attributeSet : attributeSets) {
+ if (!attributeSet.isEmpty()) {
+ if (firstAS) {
+ firstAS = false;
+ placeholders += QLatin1String(" AND (");
+ } else {
+ placeholders += QLatin1String(" OR ");
}
- *seqWords += s.split(QLatin1Char(' '));
- *termSeq << s;
- beg = term.indexOf(QLatin1Char('"'), end + 1);
+ placeholders += QLatin1String("attributes = ?");
}
- } else {
- qWarning("Full Text Search, the closing quotation mark is missing.");
- return false;
}
+ if (!firstAS)
+ placeholders += QLatin1Char(')'); // close "AND ("
+ placeholders += QLatin1Char(')');
}
-
- return true;
+ return placeholders;
}
-void Reader::searchInIndex(const QStringList &terms)
+static void bindNamespacesAndAttributes(QSqlQuery *query, const QMultiMap<QString, QStringList> &namespaces)
{
- for (const QString &term : terms) {
- QVector<Document> documents;
-
- for (const Index &index : qAsConst(searchIndexTable)) {
- const EntryTable &entryTable = index.first;
- const DocumentList &documentList = index.second;
-
- if (term.contains(QLatin1Char('*')))
- documents = setupDummyTerm(getWildcardTerms(term, entryTable), entryTable);
- else if (entryTable.value(term))
- documents = entryTable.value(term)->documents;
- else
- continue;
-
- if (!documents.isEmpty()) {
- DocumentInfo info;
- QVector<DocumentInfo> documentsInfo;
- for (const Document &doc : qAsConst(documents)) {
- info.docNumber = doc.docNumber;
- info.frequency = doc.frequency;
- info.documentUrl = documentList.at(doc.docNumber).at(1);
- info.documentTitle = documentList.at(doc.docNumber).at(0);
- documentsInfo.append(info);
- }
-
- const auto tit = std::find_if(termList.begin(), termList.end(),
- [term] (TermInfo &info) { return info.term == term; } );
- if (tit != termList.end()) {
- tit->documents += documentsInfo;
- tit->frequency += documentsInfo.count();
- } else {
- termList.append(TermInfo(term, documentsInfo.count(), documentsInfo));
- }
- }
+ QString placeholders;
+ const auto namespaceList = namespaces.uniqueKeys();
+ for (const QString &ns : namespaceList) {
+ query->addBindValue(ns);
+
+ const QList<QStringList> &attributeSets = namespaces.values(ns);
+ for (const QStringList &attributeSet : attributeSets) {
+ if (!attributeSet.isEmpty())
+ query->addBindValue(attributeSet.join(QLatin1Char('|')));
}
}
- ::std::sort(termList.begin(), termList.end());
}
-QVector<DocumentInfo> Reader::hits()
+QVector<DocumentInfo> Reader::queryTable(const QSqlDatabase &db,
+ const QString &tableName,
+ const QString &term) const
{
- QVector<DocumentInfo> documents;
- if (!termList.count())
- return documents;
-
- documents = termList.takeFirst().documents;
- for (const TermInfo &info : qAsConst(termList)) {
- const QVector<DocumentInfo> &docs = info.documents;
-
- for (auto minDoc_it = documents.begin(); minDoc_it != documents.end(); ) {
- const int docNumber = minDoc_it->docNumber;
- const auto doc_it = std::find_if(docs.cbegin(), docs.cend(),
- [docNumber] (const DocumentInfo &docInfo)
- { return docInfo.docNumber == docNumber; });
- if (doc_it == docs.cend()) {
- minDoc_it = documents.erase(minDoc_it);
- } else {
- minDoc_it->frequency = doc_it->frequency;
- ++minDoc_it;
- }
- }
+ const QString nsPlaceholders = namespacePlaceholders(m_namespaces);
+ QSqlQuery query(db);
+ query.prepare(QLatin1String("SELECT url, title FROM ") + tableName +
+ QLatin1String(" WHERE (") + nsPlaceholders +
+ QLatin1String(") AND ") + tableName +
+ QLatin1String(" MATCH ? ORDER BY rank"));
+ bindNamespacesAndAttributes(&query, m_namespaces);
+ query.addBindValue(term);
+ query.exec();
+
+ QVector<DocumentInfo> documentsInfo;
+
+ while (query.next()) {
+ const QString url = query.value(QLatin1String("url")).toString();
+ const QString title = query.value(QLatin1String("title")).toString();
+ documentsInfo.append(DocumentInfo(title, url));
}
- ::std::sort(documents.begin(), documents.end());
- return documents;
+ return documentsInfo;
}
-bool Reader::searchForPattern(const QStringList &patterns, const QStringList &words,
- const QByteArray &data)
+void Reader::searchInDB(const QString &term)
{
- if (data.isEmpty())
- return false;
-
- for (const PosEntry *entry : qAsConst(miniIndex))
- delete entry;
-
- miniIndex.clear();
-
- wordNum = 3;
- for (const QString &word : words)
- miniIndex.insert(word, new PosEntry(0));
-
- QTextStream s(data);
- const QString text = s.readAll();
- bool valid = true;
- const QChar *buf = text.unicode();
- QChar str[64];
- QChar c = buf[0];
- int j = 0;
- int i = 0;
- while ( j < text.length() ) {
- if ( c == QLatin1Char('<') || c == QLatin1Char('&') ) {
- valid = false;
- if ( i > 1 )
- buildMiniIndex( QString(str,i) );
- i = 0;
- c = buf[++j];
- continue;
- }
- if ( ( c == QLatin1Char('>') || c == QLatin1Char(';') ) && !valid ) {
- valid = true;
- c = buf[++j];
- continue;
- }
- if ( !valid ) {
- c = buf[++j];
- continue;
- }
- if ( ( c.isLetterOrNumber() || c == QLatin1Char('_') ) && i < 63 ) {
- str[i] = c.toLower();
- ++i;
- } else {
- if ( i > 1 )
- buildMiniIndex( QString(str,i) );
- i = 0;
- }
- c = buf[++j];
- }
- if ( i > 1 )
- buildMiniIndex( QString(str,i) );
-
- QList<uint> a;
- for (const QString &pat : patterns) {
- const QStringList wordList = pat.split(QLatin1Char(' '));
- a = miniIndex.value(wordList.at(0))->positions;
- for (int j = 1; j < wordList.count(); ++j) {
- const QList<uint> &b = miniIndex.value(wordList.at(j))->positions;
-
- for (auto aIt = a.begin(); aIt != a.end(); ) {
- if (b.contains(*aIt + 1)) {
- (*aIt)++;
- ++aIt;
- } else {
- aIt = a.erase(aIt);
+ const QString &uniqueId = QHelpGlobal::uniquifyConnectionName(QLatin1String("QHelpReader"), this);
+ {
+ QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), uniqueId);
+ db.setConnectOptions(QLatin1String("QSQLITE_OPEN_READONLY"));
+ db.setDatabaseName(m_indexPath + QLatin1String("/fts"));
+ if (db.open()) {
+ const QVector<DocumentInfo> titlesInfo = queryTable(db,
+ QLatin1String("titles"), term);
+ const QVector<DocumentInfo> contentsInfo = queryTable(db,
+ QLatin1String("contents"), term);
+
+ // merge results form title and contents searches
+ m_hits = QVector<DocumentInfo>();
+
+ QSet<QString> urls;
+ for (const DocumentInfo &info : titlesInfo) {
+ const QString url = info.m_url;
+ if (!urls.contains(url)) {
+ m_hits.append(info);
+ urls.insert(url);
}
}
- }
- }
- if ( a.count() )
- return true;
- return false;
-}
-QVector<Document> Reader::setupDummyTerm(const QStringList &terms,
- const EntryTable &entryTable)
-{
- QList<Term> termList;
- for (const QString &term : terms) {
- Entry *e = entryTable.value(term);
- if (e)
- termList.append(Term(term, e->documents.count(), e->documents ) );
- }
- QVector<Document> maxList(0);
- if ( !termList.count() )
- return maxList;
- ::std::sort(termList.begin(), termList.end());
-
- maxList = termList.takeLast().documents;
- for (const Term &term : qAsConst(termList)) {
- for (const Document &doc : term.documents) {
- if (maxList.indexOf(doc) == -1)
- maxList.append(doc);
- }
- }
- return maxList;
-}
-
-QStringList Reader::getWildcardTerms(const QString &termStr,
- const EntryTable &entryTable)
-{
- QStringList list;
- const QStringList terms = split(termStr);
-
- for (auto it = entryTable.cbegin(), end = entryTable.cend(); it != end; ++it) {
- int index = 0;
- bool found = false;
- const QString text(it.key());
- for (auto termIt = terms.cbegin(), termItEnd = terms.cend(); termIt != termItEnd; ++termIt) {
- const QString &term = *termIt;
- if (term == QLatin1String("*")) {
- found = true;
- continue;
- }
- if (termIt == terms.cbegin() && term.at(0) != text.at(0)) {
- found = false;
- break;
- }
- index = text.indexOf(term, index);
- if (term == terms.last() && index != text.length() - 1) {
- index = text.lastIndexOf(term);
- if (index != text.length() - term.length()) {
- found = false;
- break;
+ for (const DocumentInfo &info : contentsInfo) {
+ const QString url = info.m_url;
+ if (!urls.contains(url)) {
+ m_hits.append(info);
+ urls.insert(url);
}
}
- if (index != -1) {
- found = true;
- index += term.length();
- continue;
- } else {
- found = false;
- break;
- }
}
- if (found)
- list << text;
}
-
- return list;
+ QSqlDatabase::removeDatabase(uniqueId);
}
-void Reader::buildMiniIndex(const QString &string)
+QVector<DocumentInfo> Reader::hits() const
{
- if (miniIndex[string])
- miniIndex[string]->positions.append(wordNum);
- ++wordNum;
+ return m_hits;
}
-void Reader::reset()
+static bool attributesMatchFilter(const QStringList &attributes,
+ const QStringList &filter)
{
- for (Index &index : indexTable) {
- cleanupIndex(index.first);
- index.second.clear();
+ for (const QString &attribute : filter) {
+ if (!attributes.contains(attribute, Qt::CaseInsensitive))
+ return false;
}
-}
-void Reader::cleanupIndex(EntryTable &entryTable)
-{
- for (const Entry *entry : qAsConst(entryTable))
- delete entry;
-
- entryTable.clear();
-}
-
-
-QHelpSearchIndexReaderDefault::QHelpSearchIndexReaderDefault()
- : QHelpSearchIndexReader()
-{
- // nothing todo
-}
-
-QHelpSearchIndexReaderDefault::~QHelpSearchIndexReaderDefault()
-{
+ return true;
}
void QHelpSearchIndexReaderDefault::run()
{
- mutex.lock();
+ QMutexLocker lock(&m_mutex);
- if (m_cancel) {
- mutex.unlock();
+ if (m_cancel)
return;
- }
- const QList<QHelpSearchQuery> &queryList = this->m_query;
- const QLatin1String key("DefaultSearchNamespaces");
- const QString collectionFile(this->m_collectionFile);
+ const QList<QHelpSearchQuery> queryList = m_query;
+ const QString collectionFile = m_collectionFile;
const QString indexPath = m_indexFilesFolder;
- mutex.unlock();
+ lock.unlock();
QString queryTerm;
+
+ // TODO: we may want to translate the QHelpSearchQuery list into
+ // simple phrase using AND, OR, NOT or quotes in order to
+ // keep working the public interface of QHelpSearchQuery
for (const QHelpSearchQuery &query : queryList) {
if (query.fieldName == QHelpSearchQuery::DEFAULT) {
queryTerm = query.wordList.at(0);
@@ -510,77 +218,39 @@ void QHelpSearchIndexReaderDefault::run()
return;
const QStringList &registeredDocs = engine.registeredDocumentations();
- const QStringList &indexedNamespaces = engine.customValue(key).toString().
- split(QLatin1Char('|'), QString::SkipEmptyParts);
emit searchingStarted();
+ const QStringList &currentFilter = engine.filterAttributes(engine.currentFilter());
+
// setup the reader
m_reader.setIndexPath(indexPath);
for (const QString &namespaceName : registeredDocs) {
- mutex.lock();
- if (m_cancel) {
- mutex.unlock();
- searchingFinished(0); // TODO: check this ???
- return;
- }
- mutex.unlock();
-
const QList<QStringList> &attributeSets =
engine.filterAttributeSets(namespaceName);
for (const QStringList &attributes : attributeSets) {
- // read all index files
- m_reader.setIndexFile(namespaceName, attributes.join(QLatin1Char('@')));
- if (!m_reader.readIndex()) {
- qWarning("Full Text Search, could not read file for namespace: %s.",
- namespaceName.toUtf8().constData());
+ if (attributesMatchFilter(attributes, currentFilter)) {
+ m_reader.addNamespace(namespaceName, attributes);
}
}
}
- // get the current filter attributes and minimize the index files table
- m_reader.filterFilesForAttributes(engine.filterAttributes(engine.currentFilter()));
-
- hitList.clear();
- QStringList terms, termSeq, seqWords;
- if (m_reader.initCheck() && // check if we could read anything
- m_reader.splitSearchTerm(queryTerm, &terms, &termSeq, &seqWords) ) {
-
- // search for term(s)
- m_reader.searchInIndex(terms); // TODO: should this be interruptible as well ???
-
- const QVector<DocumentInfo> &hits = m_reader.hits();
- if (!hits.isEmpty()) {
- if (termSeq.isEmpty()) {
- for (const DocumentInfo &docInfo : hits) {
- mutex.lock();
- if (m_cancel) {
- mutex.unlock();
- searchingFinished(0); // TODO: check this, speed issue while locking???
- return;
- }
- mutex.unlock();
- hitList.append(qMakePair(docInfo.documentTitle, docInfo.documentUrl));
- }
- } else {
- for (const DocumentInfo &docInfo : hits) {
- mutex.lock();
- if (m_cancel) {
- mutex.unlock();
- searchingFinished(0); // TODO: check this, speed issue while locking???
- return;
- }
- mutex.unlock();
-
- if (m_reader.searchForPattern(termSeq, seqWords, engine.fileData(docInfo.documentUrl))) // TODO: should this be interruptible as well ???
- hitList.append(qMakePair(docInfo.documentTitle, docInfo.documentUrl));
- }
- }
- }
+
+ lock.relock();
+ if (m_cancel) {
+ emit searchingFinished(0); // TODO: check this, speed issue while locking???
+ return;
}
+ lock.unlock();
+
+ m_hitList.clear();
+ m_reader.searchInDB(queryTerm); // TODO: should this be interruptible as well ???
+
+ for (const DocumentInfo &docInfo : m_reader.hits())
+ m_hitList.append(qMakePair(docInfo.m_url, docInfo.m_title));
- emit searchingFinished(hitList.count());
+ emit searchingFinished(m_hitList.count());
}
} // namespace std
diff --git a/src/assistant/help/qhelpsearchindexreader_default_p.h b/src/assistant/help/qhelpsearchindexreader_default_p.h
index a4f44a139..0cea34ca2 100644
--- a/src/assistant/help/qhelpsearchindexreader_default_p.h
+++ b/src/assistant/help/qhelpsearchindexreader_default_p.h
@@ -54,8 +54,7 @@
#include "qhelpsearchindex_default_p.h"
#include "qhelpsearchindexreader_p.h"
-#include <QtCore/QHash>
-#include <QtCore/QPair>
+QT_FORWARD_DECLARE_CLASS(QSqlDatabase)
QT_BEGIN_NAMESPACE
@@ -64,45 +63,21 @@ namespace qt {
class Reader
{
- typedef QList<QStringList> DocumentList;
- typedef QHash<QString, Entry*> EntryTable;
- typedef QPair<EntryTable, DocumentList> Index;
- typedef QHash<QString, Index> IndexTable;
-
public:
- Reader();
- ~Reader();
-
- bool readIndex();
- bool initCheck() const;
void setIndexPath(const QString &path);
- void filterFilesForAttributes(const QStringList &attributes);
- void setIndexFile(const QString &namespaceName, const QString &attributes);
- bool splitSearchTerm(const QString &searchTerm, QStringList *terms,
- QStringList *termSeq, QStringList *seqWords);
+ void addNamespace(const QString &namespaceName, const QStringList &attributes);
- void searchInIndex(const QStringList &terms);
- QVector<DocumentInfo> hits();
- bool searchForPattern(const QStringList &patterns,
- const QStringList &words, const QByteArray &data);
+ void searchInDB(const QString &term);
+ QVector<DocumentInfo> hits() const;
private:
- QVector<Document> setupDummyTerm(const QStringList &terms, const EntryTable &entryTable);
- QStringList getWildcardTerms(const QString &termStr, const EntryTable &entryTable);
- void buildMiniIndex(const QString &string);
- void reset();
- void cleanupIndex(EntryTable &entryTable);
+ QVector<DocumentInfo> queryTable(const QSqlDatabase &db,
+ const QString &tableName,
+ const QString &term) const;
-private:
- uint wordNum;
- QString indexPath;
- QString indexFile;
- QString documentFile;
-
- IndexTable indexTable;
- QList<TermInfo> termList;
- IndexTable searchIndexTable;
- QHash<QString, PosEntry*> miniIndex;
+ QString m_indexPath;
+ QMultiMap<QString, QStringList> m_namespaces;
+ QVector<DocumentInfo> m_hits;
};
@@ -110,10 +85,6 @@ class QHelpSearchIndexReaderDefault : public QHelpSearchIndexReader
{
Q_OBJECT
-public:
- QHelpSearchIndexReaderDefault();
- ~QHelpSearchIndexReaderDefault();
-
private:
void run() override;
diff --git a/src/assistant/help/qhelpsearchindexreader_p.h b/src/assistant/help/qhelpsearchindexreader_p.h
index a94085aaf..b75b8dd2b 100644
--- a/src/assistant/help/qhelpsearchindexreader_p.h
+++ b/src/assistant/help/qhelpsearchindexreader_p.h
@@ -71,7 +71,7 @@ class QHelpSearchIndexReader : public QThread
Q_OBJECT
public:
- QHelpSearchIndexReader();
+ QHelpSearchIndexReader() = default;
~QHelpSearchIndexReader();
void cancelSearching();
@@ -86,9 +86,9 @@ signals:
void searchingFinished(int hits);
protected:
- mutable QMutex mutex;
- QList<QHelpSearchEngine::SearchHit> hitList;
- bool m_cancel;
+ mutable QMutex m_mutex;
+ QList<QHelpSearchEngine::SearchHit> m_hitList;
+ bool m_cancel = false;
QString m_collectionFile;
QList<QHelpSearchQuery> m_query;
QString m_indexFilesFolder;
diff --git a/src/assistant/help/qhelpsearchindexwriter_clucene.cpp b/src/assistant/help/qhelpsearchindexwriter_clucene.cpp
deleted file mode 100644
index a281c7d5e..000000000
--- a/src/assistant/help/qhelpsearchindexwriter_clucene.cpp
+++ /dev/null
@@ -1,870 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qclucenefieldnames_p.h"
-#include "qhelpenginecore.h"
-#include "qhelp_global.h"
-#include "private/qhits_p.h"
-#include "private/qquery_p.h"
-#include "private/qanalyzer_p.h"
-#include "private/qdocument_p.h"
-#include "private/qsearchable_p.h"
-#include "private/qindexreader_p.h"
-#include "private/qindexwriter_p.h"
-#include "qhelpsearchindexwriter_clucene_p.h"
-
-#include <QtCore/QDir>
-#include <QtCore/QString>
-#include <QtCore/QFileInfo>
-#include <QtCore/QTextCodec>
-#include <QtCore/QTextStream>
-#include <QtCore/QDataStream>
-
-#include <QtNetwork/QLocalSocket>
-#include <QtNetwork/QLocalServer>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-namespace fulltextsearch {
-namespace clucene {
-
-// taken from qtexthtmlparser
-static const struct QTextHtmlEntity
-{
- const char *name;
- quint16 code;
-} entities[] = {
- { "AElig", 0x00c6 },
- { "AMP", 38 },
- { "Aacute", 0x00c1 },
- { "Acirc", 0x00c2 },
- { "Agrave", 0x00c0 },
- { "Alpha", 0x0391 },
- { "Aring", 0x00c5 },
- { "Atilde", 0x00c3 },
- { "Auml", 0x00c4 },
- { "Beta", 0x0392 },
- { "Ccedil", 0x00c7 },
- { "Chi", 0x03a7 },
- { "Dagger", 0x2021 },
- { "Delta", 0x0394 },
- { "ETH", 0x00d0 },
- { "Eacute", 0x00c9 },
- { "Ecirc", 0x00ca },
- { "Egrave", 0x00c8 },
- { "Epsilon", 0x0395 },
- { "Eta", 0x0397 },
- { "Euml", 0x00cb },
- { "GT", 62 },
- { "Gamma", 0x0393 },
- { "Iacute", 0x00cd },
- { "Icirc", 0x00ce },
- { "Igrave", 0x00cc },
- { "Iota", 0x0399 },
- { "Iuml", 0x00cf },
- { "Kappa", 0x039a },
- { "LT", 60 },
- { "Lambda", 0x039b },
- { "Mu", 0x039c },
- { "Ntilde", 0x00d1 },
- { "Nu", 0x039d },
- { "OElig", 0x0152 },
- { "Oacute", 0x00d3 },
- { "Ocirc", 0x00d4 },
- { "Ograve", 0x00d2 },
- { "Omega", 0x03a9 },
- { "Omicron", 0x039f },
- { "Oslash", 0x00d8 },
- { "Otilde", 0x00d5 },
- { "Ouml", 0x00d6 },
- { "Phi", 0x03a6 },
- { "Pi", 0x03a0 },
- { "Prime", 0x2033 },
- { "Psi", 0x03a8 },
- { "QUOT", 34 },
- { "Rho", 0x03a1 },
- { "Scaron", 0x0160 },
- { "Sigma", 0x03a3 },
- { "THORN", 0x00de },
- { "Tau", 0x03a4 },
- { "Theta", 0x0398 },
- { "Uacute", 0x00da },
- { "Ucirc", 0x00db },
- { "Ugrave", 0x00d9 },
- { "Upsilon", 0x03a5 },
- { "Uuml", 0x00dc },
- { "Xi", 0x039e },
- { "Yacute", 0x00dd },
- { "Yuml", 0x0178 },
- { "Zeta", 0x0396 },
- { "aacute", 0x00e1 },
- { "acirc", 0x00e2 },
- { "acute", 0x00b4 },
- { "aelig", 0x00e6 },
- { "agrave", 0x00e0 },
- { "alefsym", 0x2135 },
- { "alpha", 0x03b1 },
- { "amp", 38 },
- { "and", 0x22a5 },
- { "ang", 0x2220 },
- { "apos", 0x0027 },
- { "aring", 0x00e5 },
- { "asymp", 0x2248 },
- { "atilde", 0x00e3 },
- { "auml", 0x00e4 },
- { "bdquo", 0x201e },
- { "beta", 0x03b2 },
- { "brvbar", 0x00a6 },
- { "bull", 0x2022 },
- { "cap", 0x2229 },
- { "ccedil", 0x00e7 },
- { "cedil", 0x00b8 },
- { "cent", 0x00a2 },
- { "chi", 0x03c7 },
- { "circ", 0x02c6 },
- { "clubs", 0x2663 },
- { "cong", 0x2245 },
- { "copy", 0x00a9 },
- { "crarr", 0x21b5 },
- { "cup", 0x222a },
- { "curren", 0x00a4 },
- { "dArr", 0x21d3 },
- { "dagger", 0x2020 },
- { "darr", 0x2193 },
- { "deg", 0x00b0 },
- { "delta", 0x03b4 },
- { "diams", 0x2666 },
- { "divide", 0x00f7 },
- { "eacute", 0x00e9 },
- { "ecirc", 0x00ea },
- { "egrave", 0x00e8 },
- { "empty", 0x2205 },
- { "emsp", 0x2003 },
- { "ensp", 0x2002 },
- { "epsilon", 0x03b5 },
- { "equiv", 0x2261 },
- { "eta", 0x03b7 },
- { "eth", 0x00f0 },
- { "euml", 0x00eb },
- { "euro", 0x20ac },
- { "exist", 0x2203 },
- { "fnof", 0x0192 },
- { "forall", 0x2200 },
- { "frac12", 0x00bd },
- { "frac14", 0x00bc },
- { "frac34", 0x00be },
- { "frasl", 0x2044 },
- { "gamma", 0x03b3 },
- { "ge", 0x2265 },
- { "gt", 62 },
- { "hArr", 0x21d4 },
- { "harr", 0x2194 },
- { "hearts", 0x2665 },
- { "hellip", 0x2026 },
- { "iacute", 0x00ed },
- { "icirc", 0x00ee },
- { "iexcl", 0x00a1 },
- { "igrave", 0x00ec },
- { "image", 0x2111 },
- { "infin", 0x221e },
- { "int", 0x222b },
- { "iota", 0x03b9 },
- { "iquest", 0x00bf },
- { "isin", 0x2208 },
- { "iuml", 0x00ef },
- { "kappa", 0x03ba },
- { "lArr", 0x21d0 },
- { "lambda", 0x03bb },
- { "lang", 0x2329 },
- { "laquo", 0x00ab },
- { "larr", 0x2190 },
- { "lceil", 0x2308 },
- { "ldquo", 0x201c },
- { "le", 0x2264 },
- { "lfloor", 0x230a },
- { "lowast", 0x2217 },
- { "loz", 0x25ca },
- { "lrm", 0x200e },
- { "lsaquo", 0x2039 },
- { "lsquo", 0x2018 },
- { "lt", 60 },
- { "macr", 0x00af },
- { "mdash", 0x2014 },
- { "micro", 0x00b5 },
- { "middot", 0x00b7 },
- { "minus", 0x2212 },
- { "mu", 0x03bc },
- { "nabla", 0x2207 },
- { "nbsp", 0x00a0 },
- { "ndash", 0x2013 },
- { "ne", 0x2260 },
- { "ni", 0x220b },
- { "not", 0x00ac },
- { "notin", 0x2209 },
- { "nsub", 0x2284 },
- { "ntilde", 0x00f1 },
- { "nu", 0x03bd },
- { "oacute", 0x00f3 },
- { "ocirc", 0x00f4 },
- { "oelig", 0x0153 },
- { "ograve", 0x00f2 },
- { "oline", 0x203e },
- { "omega", 0x03c9 },
- { "omicron", 0x03bf },
- { "oplus", 0x2295 },
- { "or", 0x22a6 },
- { "ordf", 0x00aa },
- { "ordm", 0x00ba },
- { "oslash", 0x00f8 },
- { "otilde", 0x00f5 },
- { "otimes", 0x2297 },
- { "ouml", 0x00f6 },
- { "para", 0x00b6 },
- { "part", 0x2202 },
- { "percnt", 0x0025 },
- { "permil", 0x2030 },
- { "perp", 0x22a5 },
- { "phi", 0x03c6 },
- { "pi", 0x03c0 },
- { "piv", 0x03d6 },
- { "plusmn", 0x00b1 },
- { "pound", 0x00a3 },
- { "prime", 0x2032 },
- { "prod", 0x220f },
- { "prop", 0x221d },
- { "psi", 0x03c8 },
- { "quot", 34 },
- { "rArr", 0x21d2 },
- { "radic", 0x221a },
- { "rang", 0x232a },
- { "raquo", 0x00bb },
- { "rarr", 0x2192 },
- { "rceil", 0x2309 },
- { "rdquo", 0x201d },
- { "real", 0x211c },
- { "reg", 0x00ae },
- { "rfloor", 0x230b },
- { "rho", 0x03c1 },
- { "rlm", 0x200f },
- { "rsaquo", 0x203a },
- { "rsquo", 0x2019 },
- { "sbquo", 0x201a },
- { "scaron", 0x0161 },
- { "sdot", 0x22c5 },
- { "sect", 0x00a7 },
- { "shy", 0x00ad },
- { "sigma", 0x03c3 },
- { "sigmaf", 0x03c2 },
- { "sim", 0x223c },
- { "spades", 0x2660 },
- { "sub", 0x2282 },
- { "sube", 0x2286 },
- { "sum", 0x2211 },
- { "sup", 0x2283 },
- { "sup1", 0x00b9 },
- { "sup2", 0x00b2 },
- { "sup3", 0x00b3 },
- { "supe", 0x2287 },
- { "szlig", 0x00df },
- { "tau", 0x03c4 },
- { "there4", 0x2234 },
- { "theta", 0x03b8 },
- { "thetasym", 0x03d1 },
- { "thinsp", 0x2009 },
- { "thorn", 0x00fe },
- { "tilde", 0x02dc },
- { "times", 0x00d7 },
- { "trade", 0x2122 },
- { "uArr", 0x21d1 },
- { "uacute", 0x00fa },
- { "uarr", 0x2191 },
- { "ucirc", 0x00fb },
- { "ugrave", 0x00f9 },
- { "uml", 0x00a8 },
- { "upsih", 0x03d2 },
- { "upsilon", 0x03c5 },
- { "uuml", 0x00fc },
- { "weierp", 0x2118 },
- { "xi", 0x03be },
- { "yacute", 0x00fd },
- { "yen", 0x00a5 },
- { "yuml", 0x00ff },
- { "zeta", 0x03b6 },
- { "zwj", 0x200d },
- { "zwnj", 0x200c }
-};
-
-static bool operator<(const QTextHtmlEntity &entity, const QString &entityStr)
-{
- return QLatin1String(entity.name) < entityStr;
-}
-
-static QChar resolveEntity(const QString &entity)
-{
- const QTextHtmlEntity *start = &entities[0];
- const QTextHtmlEntity *end = &entities[(sizeof(entities) / sizeof(entities[0]))];
- const QTextHtmlEntity *e = std::lower_bound(start, end, entity);
- if (e == end)
- return QChar();
- return e->code;
-}
-
-static const uint latin1Extended[0xA0 - 0x80] = {
- 0x20ac, // 0x80
- 0x0081, // 0x81 direct mapping
- 0x201a, // 0x82
- 0x0192, // 0x83
- 0x201e, // 0x84
- 0x2026, // 0x85
- 0x2020, // 0x86
- 0x2021, // 0x87
- 0x02C6, // 0x88
- 0x2030, // 0x89
- 0x0160, // 0x8A
- 0x2039, // 0x8B
- 0x0152, // 0x8C
- 0x008D, // 0x8D direct mapping
- 0x017D, // 0x8E
- 0x008F, // 0x8F directmapping
- 0x0090, // 0x90 directmapping
- 0x2018, // 0x91
- 0x2019, // 0x92
- 0x201C, // 0x93
- 0X201D, // 0x94
- 0x2022, // 0x95
- 0x2013, // 0x96
- 0x2014, // 0x97
- 0x02DC, // 0x98
- 0x2122, // 0x99
- 0x0161, // 0x9A
- 0x203A, // 0x9B
- 0x0153, // 0x9C
- 0x009D, // 0x9D direct mapping
- 0x017E, // 0x9E
- 0x0178 // 0x9F
-};
-// end taken from qtexthtmlparser
-
-class DocumentHelper
-{
-public:
- DocumentHelper(const QString &fileName, const QByteArray &data)
- : fileName(fileName) , data(readData(data)) {}
- ~DocumentHelper() {}
-
- bool addFieldsToDocument(QCLuceneDocument *document,
- const QString &namespaceName, const QString &attributes = QString())
- {
- if (!document)
- return false;
-
- if(!data.isEmpty()) {
- QString parsedData = parseData();
- QString parsedTitle = QHelpGlobal::documentTitle(data);
-
- if(!parsedData.isEmpty()) {
- document->add(new QCLuceneField(ContentField,
- parsedData,QCLuceneField::INDEX_TOKENIZED));
- document->add(new QCLuceneField(PathField, fileName,
- QCLuceneField::STORE_YES | QCLuceneField::INDEX_UNTOKENIZED));
- document->add(new QCLuceneField(TitleField, parsedTitle,
- QCLuceneField::STORE_YES | QCLuceneField::INDEX_UNTOKENIZED));
- document->add(new QCLuceneField(TitleTokenizedField, parsedTitle,
- QCLuceneField::STORE_YES | QCLuceneField::INDEX_TOKENIZED));
- document->add(new QCLuceneField(NamespaceField, namespaceName,
- QCLuceneField::STORE_YES | QCLuceneField::INDEX_UNTOKENIZED));
- document->add(new QCLuceneField(AttributeField, attributes,
- QCLuceneField::STORE_YES | QCLuceneField::INDEX_TOKENIZED));
- return true;
- }
- }
-
- return false;
- }
-
-private:
- QString readData(const QByteArray &data)
- {
- QTextStream textStream(data);
- const QByteArray &codec = QHelpGlobal::codecFromData(data).toLatin1();
- textStream.setCodec(QTextCodec::codecForName(codec.constData()));
-
- return textStream.readAll();
- }
-
- QString parseData() const
- {
- const int length = data.length();
- const QChar *buf = data.unicode();
-
- QString parsedContent;
- parsedContent.reserve(length);
-
- bool valid = true;
- int j = 0, count = 0;
-
- QChar c;
- while (j < length) {
- c = buf[j++];
- if (c == QLatin1Char('<') || c == QLatin1Char('&')) {
- if (count > 1 && c != QLatin1Char('&'))
- parsedContent.append(QLatin1Char(' '));
- else if (c == QLatin1Char('&')) {
- // Note: this will modify the counter j, in case we sucessful parsed the entity
- // we will have modified the counter to stay 1 before the closing ';', so
- // the following if condition will be met with if (c == QLatin1Char(';'))
- parsedContent.append(parseEntity(length, buf, j));
- }
-
- count = 0;
- valid = false;
- continue;
- }
- if ((c == QLatin1Char('>') || c == QLatin1Char(';')) && !valid) {
- valid = true;
- continue;
- }
- if (!valid)
- continue;
-
- if (c.isLetterOrNumber() || c.isPrint()) {
- ++count;
- parsedContent.append(c.toLower());
- } else {
- if (count > 1)
- parsedContent.append(QLatin1Char(' '));
- count = 0;
- }
- }
-
- return parsedContent;
- }
-
- // taken from qtexthtmlparser
- // parses an entity after "&", and returns it
- QString parseEntity(int len, const QChar *buf, int &pos) const
- {
- int recover = pos;
- QString entity;
- while (pos < len) {
- QChar c = buf[pos++];
- if (c.isSpace() || pos - recover > 9) {
- goto error;
- }
- if (c == QLatin1Char(';')) {
- pos--;
- break;
- }
- entity += c;
- }
- {
- QChar resolved = resolveEntity(entity);
- if (!resolved.isNull())
- return QString(resolved);
- }
- if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) {
- entity.remove(0, 1); // removing leading #
-
- int base = 10;
- bool ok = false;
-
- if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
- entity.remove(0, 1);
- base = 16;
- }
-
- uint uc = entity.toUInt(&ok, base);
- if (ok) {
- if (uc >= 0x80 && uc < 0x80 + (sizeof(latin1Extended) / sizeof(latin1Extended[0])))
- uc = latin1Extended[uc - 0x80]; // windows latin 1 extended
- QString str;
- if (uc > 0xffff) {
- // surrogate pair
- uc -= 0x10000;
- ushort high = uc/0x400 + 0xd800;
- ushort low = uc%0x400 + 0xdc00;
- str.append(QChar(high));
- str.append(QChar(low));
- } else {
- str.append(QChar(uc));
- }
- return str;
- }
- }
- error:
- pos = recover;
- return QString(QChar::Space);
- }
- // end taken from qtexthtmlparser
-
-private:
- QString fileName;
- QString data;
-};
-
-
-QHelpSearchIndexWriter::QHelpSearchIndexWriter()
- : QThread(0)
- , m_cancel(false)
-{
- // nothing todo
-}
-
-QHelpSearchIndexWriter::~QHelpSearchIndexWriter()
-{
- mutex.lock();
- this->m_cancel = true;
- waitCondition.wakeOne();
- mutex.unlock();
-
- wait();
-}
-
-void QHelpSearchIndexWriter::cancelIndexing()
-{
- mutex.lock();
- this->m_cancel = true;
- mutex.unlock();
-}
-
-void QHelpSearchIndexWriter::updateIndex(const QString &collectionFile,
- const QString &indexFilesFolder, bool reindex)
-{
- wait();
- mutex.lock();
- this->m_cancel = false;
- this->m_reindex = reindex;
- this->m_collectionFile = collectionFile;
- this->m_indexFilesFolder = indexFilesFolder;
- mutex.unlock();
-
- start(QThread::LowestPriority);
-}
-
-void QHelpSearchIndexWriter::optimizeIndex()
-{
- try {
- if (QCLuceneIndexReader::indexExists(m_indexFilesFolder)) {
- if (QCLuceneIndexReader::isLocked(m_indexFilesFolder))
- return;
-
- QCLuceneStandardAnalyzer analyzer;
- QCLuceneIndexWriter writer(m_indexFilesFolder, analyzer, false);
- writer.optimize();
- writer.close();
- }
- } catch (...) {
- qWarning("Full Text Search, could not optimize index.");
- return;
- }
-}
-
-void QHelpSearchIndexWriter::run()
-{
- try {
- QMutexLocker mutexLocker(&mutex);
-
- if (m_cancel)
- return;
-
- const bool reindex = this->m_reindex;
- const QString collectionFile(this->m_collectionFile);
-
- mutexLocker.unlock();
-
- QHelpEngineCore engine(collectionFile, 0);
- if (!engine.setupData())
- return;
-
- const QLatin1String key("CluceneIndexedNamespaces");
- if (reindex)
- engine.setCustomValue(key, QString());
-
- QMap<QString, QDateTime> indexMap;
- const QLatin1String oldKey("CluceneSearchNamespaces");
- if (!engine.customValue(oldKey, QString()).isNull()) {
- // old style qhc file < 4.4.2, need to convert...
- const QStringList &indexedNamespaces
- = engine.customValue(oldKey).toString()
- .split(QLatin1Char('|'), QString::SkipEmptyParts);
- for (const QString &nameSpace : indexedNamespaces)
- indexMap.insert(nameSpace, QDateTime());
- engine.removeCustomValue(oldKey);
- } else {
- QDataStream dataStream(engine.customValue(key).toByteArray());
- dataStream >> indexMap;
- }
-
- QString indexPath = m_indexFilesFolder;
-
- QFileInfo fInfo(indexPath);
- if (fInfo.exists() && !fInfo.isWritable()) {
- qWarning("Full Text Search, could not create index (missing permissions for '%s').",
- qPrintable(indexPath));
- return;
- }
-
- emit indexingStarted();
-
- QCLuceneIndexWriter *writer = 0;
- QCLuceneStandardAnalyzer analyzer;
- const QStringList &registeredDocs = engine.registeredDocumentations();
-
- QLocalSocket localSocket;
- localSocket.connectToServer(QString(QLatin1String("QtAssistant%1"))
- .arg(QLatin1String(QT_VERSION_STR)));
-
- QLocalServer localServer;
- bool otherInstancesRunning = true;
- if (!localSocket.waitForConnected()) {
- otherInstancesRunning = false;
- localServer.listen(QString(QLatin1String("QtAssistant%1"))
- .arg(QLatin1String(QT_VERSION_STR)));
- }
-
- // check if it's locked, and if the other instance is running
- if (!otherInstancesRunning && QCLuceneIndexReader::isLocked(indexPath))
- QCLuceneIndexReader::unlock(indexPath);
-
- if (QCLuceneIndexReader::isLocked(indexPath)) {
- // poll unless indexing finished to fake progress
- while (QCLuceneIndexReader::isLocked(indexPath)) {
- mutexLocker.relock();
- if (m_cancel)
- break;
- mutexLocker.unlock();
- this->sleep(1);
- }
- emit indexingFinished();
- return;
- }
-
- if (QCLuceneIndexReader::indexExists(indexPath) && !reindex) {
- for (const QString &namespaceName : registeredDocs) {
- mutexLocker.relock();
- if (m_cancel) {
- emit indexingFinished();
- return;
- }
- mutexLocker.unlock();
-
- if (!indexMap.contains(namespaceName)) {
- // make sure we remove some partly indexed stuff
- removeDocuments(indexPath, namespaceName);
- } else {
- QString path = engine.documentationFileName(namespaceName);
- if (indexMap.value(namespaceName)
- < QFileInfo(path).lastModified()) {
- // make sure we remove some outdated indexed stuff
- indexMap.remove(namespaceName);
- removeDocuments(indexPath, namespaceName);
- }
-
- if (indexMap.contains(namespaceName)) {
- // make sure we really have content indexed for namespace
- QCLuceneTermQuery query(QCLuceneTerm(NamespaceField, namespaceName));
- QCLuceneIndexSearcher indexSearcher(indexPath);
- QCLuceneHits hits = indexSearcher.search(query);
- if (hits.length() <= 0)
- indexMap.remove(namespaceName);
- }
- }
- }
- writer = new QCLuceneIndexWriter(indexPath, analyzer, false);
- } else {
- indexMap.clear();
- writer = new QCLuceneIndexWriter(indexPath, analyzer, true);
- }
-
- writer->setMergeFactor(100);
- writer->setMinMergeDocs(1000);
- writer->setMaxFieldLength(QCLuceneIndexWriter::DEFAULT_MAX_FIELD_LENGTH);
-
- QStringList namespaces;
- for (const QString &namespaceName : registeredDocs) {
- mutexLocker.relock();
- if (m_cancel) {
- closeIndexWriter(writer);
- emit indexingFinished();
- return;
- }
- mutexLocker.unlock();
-
- namespaces.append(namespaceName);
- if (indexMap.contains(namespaceName))
- continue;
-
- const QList<QStringList> &attributeSets =
- engine.filterAttributeSets(namespaceName);
-
- if (attributeSets.isEmpty()) {
- const QList<QUrl> docFiles = indexableFiles(&engine, namespaceName,
- QStringList());
- if (!addDocuments(docFiles, engine, QStringList(), namespaceName,
- writer, analyzer))
- break;
- } else {
- bool bail = false;
- for (const QStringList &attributes : attributeSets) {
- const QList<QUrl> docFiles = indexableFiles(&engine,
- namespaceName, attributes);
- if (!addDocuments(docFiles, engine, attributes, namespaceName,
- writer, analyzer)) {
- bail = true;
- break;
- }
- }
- if (bail)
- break;
- }
-
- mutexLocker.relock();
- if (!m_cancel) {
- QString path(engine.documentationFileName(namespaceName));
- indexMap.insert(namespaceName, QFileInfo(path).lastModified());
- writeIndexMap(engine, indexMap);
- }
- mutexLocker.unlock();
- }
-
- closeIndexWriter(writer);
-
- mutexLocker.relock();
- if (!m_cancel) {
- mutexLocker.unlock();
-
- const QStringList &indexedNamespaces = indexMap.keys();
- for (const QString &namespaceName : indexedNamespaces) {
- mutexLocker.relock();
- if (m_cancel)
- break;
- mutexLocker.unlock();
-
- if (!namespaces.contains(namespaceName)) {
- indexMap.remove(namespaceName);
- writeIndexMap(engine, indexMap);
- removeDocuments(indexPath, namespaceName);
- }
- }
- }
- } catch (...) {
- qWarning("%s: Failed because of CLucene exception.", Q_FUNC_INFO);
- }
- emit indexingFinished();
-}
-
-bool QHelpSearchIndexWriter::addDocuments(const QList<QUrl> docFiles,
- const QHelpEngineCore &engine, const QStringList &attributes,
- const QString &namespaceName, QCLuceneIndexWriter *writer,
- QCLuceneAnalyzer &analyzer)
-{
- QMutexLocker locker(&mutex);
- const QString attrList = attributes.join(QChar::Space);
-
- locker.unlock();
- for (const QUrl &url : docFiles) {
- QCLuceneDocument document;
- DocumentHelper helper(url.toString(), engine.fileData(url));
- if (helper.addFieldsToDocument(&document, namespaceName, attrList)) {
- try {
- writer->addDocument(document, analyzer);
- } catch (...) {
- qWarning("Full Text Search, could not properly add documents.");
- return false;
- }
- }
- locker.relock();
- if (m_cancel)
- return false;
- locker.unlock();
- }
- return true;
-}
-
-void QHelpSearchIndexWriter::removeDocuments(const QString &indexPath,
- const QString &namespaceName)
-{
- if (namespaceName.isEmpty() || QCLuceneIndexReader::isLocked(indexPath))
- return;
-
- QCLuceneIndexReader reader = QCLuceneIndexReader::open(indexPath);
- reader.deleteDocuments(QCLuceneTerm(NamespaceField, namespaceName));
-
- reader.close();
-}
-
-bool QHelpSearchIndexWriter::writeIndexMap(QHelpEngineCore &engine,
- const QMap<QString, QDateTime> &indexMap)
-{
- QByteArray bArray;
-
- QDataStream data(&bArray, QIODevice::ReadWrite);
- data << indexMap;
-
- return engine.setCustomValue(QLatin1String("CluceneIndexedNamespaces"),
- bArray);
-}
-
-QList<QUrl> QHelpSearchIndexWriter::indexableFiles(QHelpEngineCore *helpEngine,
- const QString &namespaceName, const QStringList &attributes) const
-{
- QList<QUrl> docFiles = helpEngine->files(namespaceName, attributes,
- QLatin1String("html"));
- docFiles += helpEngine->files(namespaceName, attributes, QLatin1String("htm"));
- docFiles += helpEngine->files(namespaceName, attributes, QLatin1String("txt"));
-
- return docFiles;
-}
-
-void QHelpSearchIndexWriter::closeIndexWriter(QCLuceneIndexWriter *writer)
-{
- try {
- writer->close();
- delete writer;
- } catch (...) {
- qWarning("Full Text Search, could not properly close index writer.");
- }
-}
-
-} // namespace clucene
-} // namespace fulltextsearch
-
-QT_END_NAMESPACE
diff --git a/src/assistant/help/qhelpsearchindexwriter_clucene_p.h b/src/assistant/help/qhelpsearchindexwriter_clucene_p.h
deleted file mode 100644
index 668272c7e..000000000
--- a/src/assistant/help/qhelpsearchindexwriter_clucene_p.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Assistant of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QHELPSEARCHINDEXWRITERCLUCENE_H
-#define QHELPSEARCHINDEXWRITERCLUCENE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the help generator tools. This header file may change from version
-// to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qhelpenginecore.h"
-#include "private/qanalyzer_p.h"
-
-#include <QtCore/QUrl>
-#include <QtCore/QThread>
-#include <QtCore/QMutex>
-#include <QtCore/QObject>
-#include <QtCore/QString>
-#include <QtCore/QDateTime>
-#include <QtCore/QStringList>
-#include <QtCore/QWaitCondition>
-
-QT_BEGIN_NAMESPACE
-
-class QCLuceneIndexWriter;
-
-namespace fulltextsearch {
-namespace clucene {
-
-class QHelpSearchIndexWriter : public QThread
-{
- Q_OBJECT
-
-public:
- QHelpSearchIndexWriter();
- ~QHelpSearchIndexWriter();
-
- void cancelIndexing();
- void updateIndex(const QString &collectionFile,
- const QString &indexFilesFolder, bool reindex);
- void optimizeIndex();
-
-signals:
- void indexingStarted();
- void indexingFinished();
-
-private:
- void run() override;
-
- bool addDocuments(const QList<QUrl> docFiles, const QHelpEngineCore &engine,
- const QStringList &attributes, const QString &namespaceName,
- QCLuceneIndexWriter *writer, QCLuceneAnalyzer &analyzer);
- void removeDocuments(const QString &indexPath, const QString &namespaceName);
-
- bool writeIndexMap(QHelpEngineCore& engine,
- const QMap<QString, QDateTime>& indexMap);
-
- QList<QUrl> indexableFiles(QHelpEngineCore *helpEngine,
- const QString &namespaceName, const QStringList &attributes) const;
-
- void closeIndexWriter(QCLuceneIndexWriter *writer);
-
-private:
- QMutex mutex;
- QWaitCondition waitCondition;
-
- bool m_cancel;
- bool m_reindex;
- QString m_collectionFile;
- QString m_indexFilesFolder;
-};
-
-} // namespace clucene
-} // namespace fulltextsearch
-
-
-QT_END_NAMESPACE
-
-#endif // QHELPSEARCHINDEXWRITERCLUCENE_H
diff --git a/src/assistant/help/qhelpsearchindexwriter_default.cpp b/src/assistant/help/qhelpsearchindexwriter_default.cpp
index 8715b0483..ccb97f06c 100644
--- a/src/assistant/help/qhelpsearchindexwriter_default.cpp
+++ b/src/assistant/help/qhelpsearchindexwriter_default.cpp
@@ -41,136 +41,280 @@
#include "qhelp_global.h"
#include "qhelpenginecore.h"
+#include <QtCore/QDataStream>
+#include <QtCore/QDateTime>
#include <QtCore/QDir>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
#include <QtCore/QSet>
#include <QtCore/QUrl>
-#include <QtCore/QFile>
-#include <QtCore/QRegExp>
#include <QtCore/QVariant>
-#include <QtCore/QFileInfo>
-#include <QtCore/QTextCodec>
-#include <QtCore/QTextStream>
+#include <QtSql/QSqlDatabase>
+#include <QtSql/QSqlDriver>
+#include <QtSql/QSqlError>
+#include <QtSql/QSqlQuery>
+
+#include <QTextDocument>
QT_BEGIN_NAMESPACE
namespace fulltextsearch {
namespace qt {
+const char FTS_DB_NAME[] = "fts";
+
Writer::Writer(const QString &path)
- : indexPath(path)
- , indexFile(QString())
- , documentFile(QString())
+ : m_dbDir(path)
{
- // nothing todo
+ clearLegacyIndex();
+ QDir().mkpath(m_dbDir);
+ m_uniqueId = QHelpGlobal::uniquifyConnectionName(QLatin1String("QHelpWriter"), this);
+ m_db = new QSqlDatabase();
+ *m_db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_uniqueId);
+ const QString dbPath = m_dbDir + QLatin1Char('/') + QLatin1String(FTS_DB_NAME);
+ m_db->setDatabaseName(dbPath);
+ if (!m_db->open()) {
+ const QString error = QHelpSearchIndexWriter::tr("Cannot open database \"%1\" using connection \"%2\": %3")
+ .arg(dbPath, m_uniqueId, m_db->lastError().text());
+ qWarning(qPrintable(error));
+ delete m_db;
+ m_db = nullptr;
+ QSqlDatabase::removeDatabase(m_uniqueId);
+ m_uniqueId = QString();
+ } else {
+ startTransaction();
+ }
}
-Writer::~Writer()
+bool Writer::tryInit(bool reindex)
{
- reset();
+ if (!m_db)
+ return true;
+
+ QSqlQuery query(*m_db);
+ // HACK: we try to perform any modifying command just to check if
+ // we don't get SQLITE_BUSY code (SQLITE_BUSY is defined to 5 in sqlite driver)
+ if (!query.exec(QLatin1String("CREATE TABLE foo ();"))) {
+ if (query.lastError().nativeErrorCode() == QLatin1String("5")) // db is locked
+ return false;
+ }
+ // HACK: clear what we have created
+ query.exec(QLatin1String("DROP TABLE foo;"));
+
+ init(reindex);
+ return true;
}
-void Writer::reset()
+bool Writer::hasDB()
{
- for (const Entry *entry : qAsConst(index))
- delete entry;
+ if (!m_db)
+ return false;
+
+ QSqlQuery query(*m_db);
+
+ query.prepare(QLatin1String("SELECT id FROM info LIMIT 1"));
+ query.exec();
- index.clear();
- documentList.clear();
+ return query.next();
}
-bool Writer::writeIndex() const
+void Writer::clearLegacyIndex()
{
- bool status;
- QFile idxFile(indexFile);
- if (!(status = idxFile.open(QFile::WriteOnly)))
- return status;
-
- QDataStream indexStream(&idxFile);
- for (auto it = index.cbegin(), end = index.cend(); it != end; ++it) {
- indexStream << it.key();
- indexStream << it.value()->documents.count();
- indexStream << it.value()->documents;
+ // Clear old legacy clucene index.
+ // More important in case of Creator, since
+ // the index folder is common for all Creator versions
+ QDir dir(m_dbDir);
+ if (!dir.exists())
+ return;
+
+ const QStringList &list = dir.entryList(QDir::Files | QDir::Hidden);
+ if (!list.contains(QLatin1String(FTS_DB_NAME))) {
+ for (const QString &item : list)
+ dir.remove(item);
}
- idxFile.close();
+}
- QFile docFile(documentFile);
- if (!(status = docFile.open(QFile::WriteOnly)))
- return status;
+void Writer::init(bool reindex)
+{
+ if (!m_db)
+ return;
+
+ QSqlQuery query(*m_db);
- QDataStream docStream(&docFile);
- for (const QStringList &list : qAsConst(documentList))
- docStream << list.at(0) << list.at(1);
+ if (reindex && hasDB()) {
+ m_needOptimize = true;
- docFile.close();
+ query.exec(QLatin1String("DROP TABLE titles;"));
+ query.exec(QLatin1String("DROP TABLE contents;"));
+ query.exec(QLatin1String("DROP TABLE info;"));
+ }
- return status;
+ query.exec(QLatin1String("CREATE TABLE info (id INTEGER PRIMARY KEY, namespace, attributes, url, title, data);"));
+
+ query.exec(QLatin1String("CREATE VIRTUAL TABLE titles USING fts5("
+ "namespace UNINDEXED, attributes UNINDEXED, "
+ "url UNINDEXED, title, "
+ "tokenize = 'porter unicode61', content = 'info', content_rowid='id');"));
+ query.exec(QLatin1String("CREATE TRIGGER titles_insert AFTER INSERT ON info BEGIN "
+ "INSERT INTO titles(rowid, namespace, attributes, url, title) "
+ "VALUES(new.id, new.namespace, new.attributes, new.url, new.title); "
+ "END;"));
+ query.exec(QLatin1String("CREATE TRIGGER titles_delete AFTER DELETE ON info BEGIN "
+ "INSERT INTO titles(titles, rowid, namespace, attributes, url, title) "
+ "VALUES('delete', old.id, old.namespace, old.attributes, old.url, old.title); "
+ "END;"));
+ query.exec(QLatin1String("CREATE TRIGGER titles_update AFTER UPDATE ON info BEGIN "
+ "INSERT INTO titles(titles, rowid, namespace, attributes, url, title) "
+ "VALUES('delete', old.id, old.namespace, old.attributes, old.url, old.title); "
+ "INSERT INTO titles(rowid, namespace, attributes, url, title) "
+ "VALUES(new.id, new.namespace, new.attributes, new.url, new.title); "
+ "END;"));
+
+ query.exec(QLatin1String("CREATE VIRTUAL TABLE contents USING fts5("
+ "namespace UNINDEXED, attributes UNINDEXED, "
+ "url UNINDEXED, title, data, "
+ "tokenize = 'porter unicode61', content = 'info', content_rowid='id');"));
+ query.exec(QLatin1String("CREATE TRIGGER contents_insert AFTER INSERT ON info BEGIN "
+ "INSERT INTO contents(rowid, namespace, attributes, url, title, data) "
+ "VALUES(new.id, new.namespace, new.attributes, new.url, new.title, new.data); "
+ "END;"));
+ query.exec(QLatin1String("CREATE TRIGGER contents_delete AFTER DELETE ON info BEGIN "
+ "INSERT INTO contents(contents, rowid, namespace, attributes, url, title, data) "
+ "VALUES('delete', old.id, old.namespace, old.attributes, old.url, old.title, old.data); "
+ "END;"));
+ query.exec(QLatin1String("CREATE TRIGGER contents_update AFTER UPDATE ON info BEGIN "
+ "INSERT INTO contents(contents, rowid, namespace, attributes, url, title, data) "
+ "VALUES('delete', old.id, old.namespace, old.attributes, old.url, old.title, old.data); "
+ "INSERT INTO contents(rowid, namespace, attributes, url, title, data) "
+ "VALUES(new.id, new.namespace, new.attributes, new.url, new.title, new.data); "
+ "END;"));
}
-void Writer::removeIndex() const
+Writer::~Writer()
{
- QFile idxFile(indexFile);
- if (idxFile.exists())
- idxFile.remove();
+ if (m_db) {
+ m_db->close();
+ delete m_db;
+ }
- QFile docFile(documentFile);
- if (docFile.exists())
- docFile.remove();
+ if (!m_uniqueId.isEmpty())
+ QSqlDatabase::removeDatabase(m_uniqueId);
}
-void Writer::setIndexFile(const QString &namespaceName, const QString &attributes)
+void Writer::flush()
{
- const QString extension = namespaceName + QLatin1Char('@') + attributes;
- indexFile = indexPath + QLatin1String("/indexdb40.") + extension;
- documentFile = indexPath + QLatin1String("/indexdoc40.") + extension;
+ if (!m_db)
+ return;
+
+ QSqlQuery query(*m_db);
+
+ query.prepare(QLatin1String("INSERT INTO info (namespace, attributes, url, title, data) VALUES (?, ?, ?, ?, ?)"));
+ query.addBindValue(m_namespaces);
+ query.addBindValue(m_attributes);
+ query.addBindValue(m_urls);
+ query.addBindValue(m_titles);
+ query.addBindValue(m_contents);
+ query.execBatch();
+
+ m_namespaces = QVariantList();
+ m_attributes = QVariantList();
+ m_urls = QVariantList();
+ m_titles = QVariantList();
+ m_contents = QVariantList();
}
-void Writer::insertInIndex(const QString &string, int docNum)
+void Writer::removeNamespace(const QString &namespaceName)
{
- if (string == QLatin1String("amp") || string == QLatin1String("nbsp"))
+ if (!m_db)
return;
- Entry *entry = 0;
- if (index.count())
- entry = index[string];
+ if (!hasNamespace(namespaceName))
+ return; // no data to delete
- if (entry) {
- if (entry->documents.last().docNumber != docNum)
- entry->documents.append(Document(docNum, 1));
- else
- entry->documents.last().frequency++;
- } else {
- index.insert(string, new Entry(docNum));
- }
+ m_needOptimize = true;
+
+ QSqlQuery query(*m_db);
+
+ query.prepare(QLatin1String("DELETE FROM info WHERE namespace = ?"));
+ query.addBindValue(namespaceName);
+ query.exec();
}
-void Writer::insertInDocumentList(const QString &title, const QString &url)
+bool Writer::hasNamespace(const QString &namespaceName)
{
- documentList.append(QStringList(title) << url);
+ if (!m_db)
+ return false;
+
+ QSqlQuery query(*m_db);
+
+ query.prepare(QLatin1String("SELECT id FROM info WHERE namespace = ? LIMIT 1"));
+ query.addBindValue(namespaceName);
+ query.exec();
+
+ return query.next();
+}
+
+void Writer::insertDoc(const QString &namespaceName,
+ const QString &attributes,
+ const QString &url,
+ const QString &title,
+ const QString &contents)
+{
+ m_namespaces.append(namespaceName);
+ m_attributes.append(attributes);
+ m_urls.append(url);
+ m_titles.append(title);
+ m_contents.append(contents);
+}
+
+void Writer::startTransaction()
+{
+ if (!m_db)
+ return;
+
+ m_needOptimize = false;
+ if (m_db && m_db->driver()->hasFeature(QSqlDriver::Transactions))
+ m_db->transaction();
}
+void Writer::endTransaction()
+{
+ if (!m_db)
+ return;
+
+ QSqlQuery query(*m_db);
+
+ if (m_needOptimize) {
+ query.exec(QLatin1String("INSERT INTO titles(titles) VALUES('rebuild')"));
+ query.exec(QLatin1String("INSERT INTO contents(contents) VALUES('rebuild')"));
+ }
+
+ if (m_db && m_db->driver()->hasFeature(QSqlDriver::Transactions))
+ m_db->commit();
+
+ if (m_needOptimize)
+ query.exec(QLatin1String("VACUUM"));
+}
QHelpSearchIndexWriter::QHelpSearchIndexWriter()
: QThread()
, m_cancel(false)
{
- // nothing todo
}
QHelpSearchIndexWriter::~QHelpSearchIndexWriter()
{
- mutex.lock();
+ m_mutex.lock();
this->m_cancel = true;
- waitCondition.wakeOne();
- mutex.unlock();
+ m_mutex.unlock();
wait();
}
void QHelpSearchIndexWriter::cancelIndexing()
{
- mutex.lock();
- this->m_cancel = true;
- mutex.unlock();
+ QMutexLocker lock(&m_mutex);
+ m_cancel = true;
}
void QHelpSearchIndexWriter::updateIndex(const QString &collectionFile,
@@ -178,198 +322,205 @@ void QHelpSearchIndexWriter::updateIndex(const QString &collectionFile,
bool reindex)
{
wait();
- QMutexLocker lock(&mutex);
+ QMutexLocker lock(&m_mutex);
+
+ m_cancel = false;
+ m_reindex = reindex;
+ m_collectionFile = collectionFile;
+ m_indexFilesFolder = indexFilesFolder;
- this->m_cancel = false;
- this->m_reindex = reindex;
- this->m_collectionFile = collectionFile;
- this->m_indexFilesFolder = indexFilesFolder;
+ lock.unlock();
start(QThread::LowestPriority);
}
+static const char IndexedNamespacesKey[] = "FTS5IndexedNamespaces";
+
+static QMap<QString, QDateTime> readIndexMap(const QHelpEngineCore &engine)
+{
+ QMap<QString, QDateTime> indexMap;
+ QDataStream dataStream(engine.customValue(
+ QLatin1String(IndexedNamespacesKey)).toByteArray());
+ dataStream >> indexMap;
+ return indexMap;
+}
+
+static bool writeIndexMap(QHelpEngineCore *engine,
+ const QMap<QString, QDateTime> &indexMap)
+{
+ QByteArray data;
+
+ QDataStream dataStream(&data, QIODevice::ReadWrite);
+ dataStream << indexMap;
+
+ return engine->setCustomValue(
+ QLatin1String(IndexedNamespacesKey), data);
+}
+
+static bool clearIndexMap(QHelpEngineCore *engine)
+{
+ return engine->removeCustomValue(QLatin1String(IndexedNamespacesKey));
+}
+
+static QList<QUrl> indexableFiles(QHelpEngineCore *helpEngine,
+ const QString &namespaceName, const QStringList &attributes)
+{
+ return helpEngine->files(namespaceName, attributes, QLatin1String("html"))
+ + helpEngine->files(namespaceName, attributes, QLatin1String("htm"))
+ + helpEngine->files(namespaceName, attributes, QLatin1String("txt"));
+}
+
void QHelpSearchIndexWriter::run()
{
- mutex.lock();
+ QMutexLocker lock(&m_mutex);
- if (m_cancel) {
- mutex.unlock();
+ if (m_cancel)
return;
- }
- const bool reindex(this->m_reindex);
- const QLatin1String key("DefaultSearchNamespaces");
- const QString collectionFile(this->m_collectionFile);
- const QString indexPath = m_indexFilesFolder;
+ const bool reindex(m_reindex);
+ const QString collectionFile(m_collectionFile);
+ const QString indexPath(m_indexFilesFolder);
- mutex.unlock();
+ lock.unlock();
QHelpEngineCore engine(collectionFile, 0);
if (!engine.setupData())
return;
- if (reindex)
- engine.setCustomValue(key, QString());
+// QFileInfo fInfo(indexPath);
+// if (fInfo.exists() && !fInfo.isWritable()) {
+// qWarning("Full Text Search, could not create index (missing permissions for '%s').",
+// qPrintable(indexPath));
+// return;
+// }
- const QStringList &registeredDocs = engine.registeredDocumentations();
- const QStringList &indexedNamespaces = engine.customValue(key).toString().
- split(QLatin1Char('|'), QString::SkipEmptyParts);
+ if (reindex)
+ clearIndexMap(&engine);
emit indexingStarted();
- QStringList namespaces;
Writer writer(indexPath);
+
+ while (!writer.tryInit(reindex))
+ sleep(1);
+
+ const QStringList &registeredDocs = engine.registeredDocumentations();
+ QMap<QString, QDateTime> indexMap = readIndexMap(engine);
+
+ if (!reindex) {
+ for (const QString &namespaceName : registeredDocs) {
+ if (indexMap.contains(namespaceName)) {
+ const QString path = engine.documentationFileName(namespaceName);
+ if (indexMap.value(namespaceName) < QFileInfo(path).lastModified()) {
+ // Remove some outdated indexed stuff
+ indexMap.remove(namespaceName);
+ writer.removeNamespace(namespaceName);
+ } else if (!writer.hasNamespace(namespaceName)) {
+ // No data in fts db for namespace.
+ // The namespace could have been removed from fts db
+ // or the whole fts db have been removed
+ // without removing it from indexMap.
+ indexMap.remove(namespaceName);
+ }
+ } else {
+ // Needed in case namespaceName was removed from indexMap
+ // without removing it from fts db.
+ // May happen when e.g. qch file was removed manually
+ // without removing fts db.
+ writer.removeNamespace(namespaceName);
+ }
+ // TODO: we may also detect if there are any other data
+ // and remove it
+ }
+ } else {
+ indexMap.clear();
+ }
+
+ for (const QString &namespaceName : indexMap.keys()) {
+ if (!registeredDocs.contains(namespaceName)) {
+ indexMap.remove(namespaceName);
+ writer.removeNamespace(namespaceName);
+ }
+ }
+
for (const QString &namespaceName : registeredDocs) {
- mutex.lock();
+ lock.relock();
if (m_cancel) {
- mutex.unlock();
+ // store what we have done so far
+ writeIndexMap(&engine, indexMap);
+ writer.endTransaction();
+ emit indexingFinished();
return;
}
- mutex.unlock();
+ lock.unlock();
// if indexed, continue
- namespaces.append(namespaceName);
- if (indexedNamespaces.contains(namespaceName))
+ if (indexMap.contains(namespaceName))
continue;
const QList<QStringList> &attributeSets =
engine.filterAttributeSets(namespaceName);
for (const QStringList &attributes : attributeSets) {
- // cleanup maybe old or unfinished files
- writer.setIndexFile(namespaceName, attributes.join(QLatin1Char('@')));
- writer.removeIndex();
-
+ const QString &attributesString = attributes.join(QLatin1Char('|'));
QSet<QString> documentsSet;
- const QList<QUrl> &docFiles = engine.files(namespaceName, attributes);
+ const QList<QUrl> &docFiles = indexableFiles(&engine, namespaceName, attributes);
for (QUrl url : docFiles) {
- if (m_cancel)
- return;
-
// get rid of duplicated files
if (url.hasFragment())
url.setFragment(QString());
- const QString s = url.toString();
+ const QString &s = url.toString();
if (s.endsWith(QLatin1String(".html"))
|| s.endsWith(QLatin1String(".htm"))
|| s.endsWith(QLatin1String(".txt")))
documentsSet.insert(s);
}
- int docNum = 0;
const QStringList documentsList(documentsSet.toList());
for (const QString &url : documentsList) {
- if (m_cancel)
+ lock.relock();
+ if (m_cancel) {
+ // store what we have done so far
+ writeIndexMap(&engine, indexMap);
+ writer.endTransaction();
+ emit indexingFinished();
return;
+ }
+ lock.unlock();
- QByteArray data(engine.fileData(url));
+ const QByteArray data(engine.fileData(url));
if (data.isEmpty())
continue;
QTextStream s(data);
- QString en = QHelpGlobal::codecFromData(data);
+ const QString &en = QHelpGlobal::codecFromData(data);
s.setCodec(QTextCodec::codecForName(en.toLatin1().constData()));
- QString text = s.readAll();
+ const QString &text = s.readAll();
if (text.isEmpty())
continue;
- QString title = QHelpGlobal::documentTitle(text);
-
- int j = 0;
- int i = 0;
- bool valid = true;
- const QChar *buf = text.unicode();
- QChar str[64];
- QChar c = buf[0];
-
- while ( j < text.length() ) {
- if (m_cancel)
- return;
-
- if ( c == QLatin1Char('<') || c == QLatin1Char('&') ) {
- valid = false;
- if ( i > 1 )
- writer.insertInIndex(QString(str,i), docNum);
- i = 0;
- c = buf[++j];
- continue;
- }
- if ( ( c == QLatin1Char('>') || c == QLatin1Char(';') ) && !valid ) {
- valid = true;
- c = buf[++j];
- continue;
- }
- if ( !valid ) {
- c = buf[++j];
- continue;
- }
- if ( ( c.isLetterOrNumber() || c == QLatin1Char('_') ) && i < 63 ) {
- str[i] = c.toLower();
- ++i;
- } else {
- if ( i > 1 )
- writer.insertInIndex(QString(str,i), docNum);
- i = 0;
- }
- c = buf[++j];
- }
- if ( i > 1 )
- writer.insertInIndex(QString(str,i), docNum);
+ QTextDocument doc;
+ doc.setHtml(text);
- docNum++;
- writer.insertInDocumentList(title, url);
- }
+ const QString &title = doc.metaInformation(QTextDocument::DocumentTitle).toHtmlEscaped();
+ const QString &contents = doc.toPlainText().toHtmlEscaped();
- if (writer.writeIndex()) {
- engine.setCustomValue(key, addNamespace(
- engine.customValue(key).toString(), namespaceName));
+ writer.insertDoc(namespaceName, attributesString, url, title, contents);
}
-
- writer.reset();
}
+ writer.flush();
+ const QString &path = engine.documentationFileName(namespaceName);
+ indexMap.insert(namespaceName, QFileInfo(path).lastModified());
}
- for (const QString &namespaceName : indexedNamespaces) {
- if (namespaces.contains(namespaceName))
- continue;
-
- const QList<QStringList> &attributeSets =
- engine.filterAttributeSets(namespaceName);
-
- for (const QStringList &attributes : attributeSets) {
- writer.setIndexFile(namespaceName, attributes.join(QLatin1Char('@')));
- writer.removeIndex();
- }
-
- engine.setCustomValue(key, removeNamespace(
- engine.customValue(key).toString(), namespaceName));
- }
+ writeIndexMap(&engine, indexMap);
+ writer.endTransaction();
emit indexingFinished();
}
-QString QHelpSearchIndexWriter::addNamespace(const QString namespaces,
- const QString &namespaceName)
-{
- QString value = namespaces;
- if (!value.contains(namespaceName))
- value.append(namespaceName).append(QLatin1Char('|'));
-
- return value;
-}
-
-QString QHelpSearchIndexWriter::removeNamespace(const QString namespaces,
- const QString &namespaceName)
-{
- QString value = namespaces;
- if (value.contains(namespaceName))
- value.remove(namespaceName + QLatin1Char('|'));
-
- return value;
-}
-
} // namespace std
} // namespace fulltextsearch
diff --git a/src/assistant/help/qhelpsearchindexwriter_default_p.h b/src/assistant/help/qhelpsearchindexwriter_default_p.h
index 25cfa601a..f63f17d49 100644
--- a/src/assistant/help/qhelpsearchindexwriter_default_p.h
+++ b/src/assistant/help/qhelpsearchindexwriter_default_p.h
@@ -51,15 +51,10 @@
// We mean it.
//
-#include "qhelpsearchindex_default_p.h"
-
-#include <QtCore/QHash>
#include <QtCore/QMutex>
-#include <QtCore/QObject>
-#include <QtCore/QString>
#include <QtCore/QThread>
-#include <QtCore/QStringList>
-#include <QtCore/QWaitCondition>
+
+QT_FORWARD_DECLARE_CLASS(QSqlDatabase)
QT_BEGIN_NAMESPACE
@@ -72,20 +67,33 @@ public:
Writer(const QString &path);
~Writer();
- void reset();
- bool writeIndex() const;
- void removeIndex() const;
- void setIndexFile(const QString &namespaceName, const QString &attributes);
- void insertInIndex(const QString &string, int docNum);
- void insertInDocumentList(const QString &title, const QString &url);
-
+ bool tryInit(bool reindex);
+ void flush();
+
+ void removeNamespace(const QString &namespaceName);
+ bool hasNamespace(const QString &namespaceName);
+ void insertDoc(const QString &namespaceName,
+ const QString &attributes,
+ const QString &url,
+ const QString &title,
+ const QString &contents);
+ void startTransaction();
+ void endTransaction();
private:
- QString indexPath;
- QString indexFile;
- QString documentFile;
-
- QHash<QString, Entry*> index;
- QList<QStringList> documentList;
+ void init(bool reindex);
+ bool hasDB();
+ void clearLegacyIndex();
+
+ const QString m_dbDir;
+ QString m_uniqueId;
+
+ bool m_needOptimize = false;
+ QSqlDatabase *m_db = nullptr;
+ QVariantList m_namespaces;
+ QVariantList m_attributes;
+ QVariantList m_urls;
+ QVariantList m_titles;
+ QVariantList m_contents;
};
@@ -107,12 +115,9 @@ signals:
private:
void run() override;
- QString addNamespace(const QString namespaces, const QString &namespaceName);
- QString removeNamespace(const QString namespaces, const QString &namespaceName);
private:
- QMutex mutex;
- QWaitCondition waitCondition;
+ QMutex m_mutex;
bool m_cancel;
bool m_reindex;
diff --git a/src/assistant/help/qhelpsearchquerywidget.cpp b/src/assistant/help/qhelpsearchquerywidget.cpp
index 8cc4cb194..332d8a431 100644
--- a/src/assistant/help/qhelpsearchquerywidget.cpp
+++ b/src/assistant/help/qhelpsearchquerywidget.cpp
@@ -99,19 +99,11 @@ private:
QHelpSearchQueryWidgetPrivate()
: QObject()
- , compactMode(false)
- , simpleSearch(true)
- , searchCompleter(new CompleterModel(this), this)
+ , m_compactMode(false)
+ , m_searchCompleter(new CompleterModel(this), this)
{
- searchButton = 0;
- advancedSearchWidget = 0;
- showHideAdvancedSearchButton = 0;
- defaultQuery = 0;
- exactQuery = 0;
- similarQuery = 0;
- withoutQuery = 0;
- allQuery = 0;
- atLeastQuery = 0;
+ m_searchButton = 0;
+ m_lineEdit = 0;
}
~QHelpSearchQueryWidgetPrivate()
@@ -121,70 +113,10 @@ private:
void retranslate()
{
- simpleSearchLabel->setText(QHelpSearchQueryWidget::tr("Search for:"));
- prevQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Previous search"));
- nextQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Next search"));
- searchButton->setText(QHelpSearchQueryWidget::tr("Search"));
-#ifdef QT_CLUCENE_SUPPORT
- advancedSearchLabel->setText(QHelpSearchQueryWidget::tr("Advanced search"));
- similarLabel->setText(QHelpSearchQueryWidget::tr("words <B>similar</B> to:"));
- withoutLabel->setText(QHelpSearchQueryWidget::tr("<B>without</B> the words:"));
- exactLabel->setText(QHelpSearchQueryWidget::tr("with <B>exact phrase</B>:"));
- allLabel->setText(QHelpSearchQueryWidget::tr("with <B>all</B> of the words:"));
- atLeastLabel->setText(QHelpSearchQueryWidget::tr("with <B>at least one</B> of the words:"));
-#endif
- }
-
- QList<QHelpSearchQuery> escapeQueries(const QList<QHelpSearchQuery> &queries)
- {
- static const char charsToEscapeList[] = "\\+-!():^[]{}~";
- static const QString escapeChar(QLatin1Char('\\'));
- static const QRegExp regExp(QLatin1String("[\\+\\-\\!\\(\\)\\^\\[\\]\\{\\}~:]"));
-
- QList<QHelpSearchQuery> escapedQueries;
- for (const QHelpSearchQuery &query : queries) {
- QHelpSearchQuery escapedQuery;
- escapedQuery.fieldName = query.fieldName;
- for (QString word : query.wordList) {
- if (word.contains(regExp)) {
- for (const char &charToEscape : charsToEscapeList)
- word.replace(charToEscape, escapeChar + QLatin1Char(charToEscape));
- }
- escapedQuery.wordList.append(word);
- }
- escapedQueries.append(escapedQuery);
- }
- return escapedQueries;
- }
-
- QStringList buildTermList(const QString query)
- {
- bool s = false;
- QString phrase;
- QStringList wordList;
- QString searchTerm = query;
-
- for (int i = 0; i < searchTerm.length(); ++i) {
- if (searchTerm[i] == QLatin1Char('"') && !s) {
- s = true;
- phrase = searchTerm[i];
- continue;
- }
- if (searchTerm[i] != QLatin1Char('"') && s)
- phrase += searchTerm[i];
- if (searchTerm[i] == QLatin1Char('"') && s) {
- s = false;
- phrase += searchTerm[i];
- wordList.append(phrase);
- searchTerm.remove(phrase);
- }
- }
- if (s)
- searchTerm.replace(phrase, phrase.mid(1));
-
- QRegExp exp(QLatin1String("\\s+"));
- wordList += searchTerm.split(exp, QString::SkipEmptyParts);
- return wordList;
+ m_searchLabel->setText(QHelpSearchQueryWidget::tr("Search for:"));
+ m_prevQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Previous search"));
+ m_nextQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Next search"));
+ m_searchButton->setText(QHelpSearchQueryWidget::tr("Search"));
}
void saveQuery(const QList<QHelpSearchQuery> &query, QueryHistory &queryHist)
@@ -210,7 +142,7 @@ private:
if (insert) {
queryHist.queries.append(query);
for (const QHelpSearchQuery &queryPart : query) {
- static_cast<CompleterModel *>(searchCompleter.model())->
+ static_cast<CompleterModel *>(m_searchCompleter.model())->
addTerm(queryPart.wordList.join(QLatin1Char(' ')));
}
}
@@ -220,17 +152,8 @@ private:
QToolButton *otherButton)
{
QueryHistory *queryHist;
- QList<QLineEdit *> lineEdits;
- if (simpleSearch) {
- queryHist = &simpleQueries;
- lineEdits << defaultQuery;
- } else {
- queryHist = &complexQueries;
- lineEdits << allQuery << atLeastQuery << similarQuery
- << withoutQuery << exactQuery;
- }
- for (QLineEdit *lineEdit : qAsConst(lineEdits))
- lineEdit->clear();
+ queryHist = &m_queries;
+ m_lineEdit->clear();
// Otherwise, the respective button would be disabled.
Q_ASSERT(queryHist->curQuery != maxOrMinIndex);
@@ -238,44 +161,19 @@ private:
queryHist->curQuery += addend;
const QList<QHelpSearchQuery> &query =
queryHist->queries.at(queryHist->curQuery);
- for (const QHelpSearchQuery &queryPart : query) {
- if (QLineEdit *lineEdit = lineEditFor(queryPart.fieldName))
- lineEdit->setText(queryPart.wordList.join(QLatin1Char(' ')));
- }
+ for (const QHelpSearchQuery &queryPart : query)
+ m_lineEdit->setText(queryPart.wordList.join(QLatin1Char(' ')));
if (queryHist->curQuery == maxOrMinIndex)
thisButton->setEnabled(false);
otherButton->setEnabled(true);
}
- QLineEdit* lineEditFor(const QHelpSearchQuery::FieldName &fieldName) const
- {
- switch (fieldName) {
- case QHelpSearchQuery::DEFAULT:
- return defaultQuery;
- case QHelpSearchQuery::ALL:
- return allQuery;
- case QHelpSearchQuery::ATLEAST:
- return atLeastQuery;
- case QHelpSearchQuery::FUZZY:
- return similarQuery;
- case QHelpSearchQuery::WITHOUT:
- return withoutQuery;
- case QHelpSearchQuery::PHRASE:
- return exactQuery;
- default:
- Q_ASSERT(0);
- }
- return 0;
- }
-
void enableOrDisableToolButtons()
{
- const QueryHistory &queryHist = simpleSearch ? simpleQueries
- : complexQueries;
- prevQueryButton->setEnabled(queryHist.curQuery > 0);
- nextQueryButton->setEnabled(queryHist.curQuery
- < queryHist.queries.size() - 1);
+ m_prevQueryButton->setEnabled(m_queries.curQuery > 0);
+ m_nextQueryButton->setEnabled(m_queries.curQuery
+ < m_queries.queries.size() - 1);
}
private slots:
@@ -284,12 +182,12 @@ private slots:
if (event->type() == QEvent::KeyPress) {
QKeyEvent *const keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Down) {
- if (simpleQueries.curQuery + 1 < simpleQueries.queries.size())
+ if (m_queries.curQuery + 1 < m_queries.queries.size())
nextQuery();
return true;
}
if (keyEvent->key() == Qt::Key_Up) {
- if (simpleQueries.curQuery > 0)
+ if (m_queries.curQuery > 0)
prevQuery();
return true;
}
@@ -298,123 +196,41 @@ private slots:
return QObject::eventFilter(ob, event);
}
- void showHideAdvancedSearch()
- {
- if (simpleSearch) {
- advancedSearchWidget->show();
- showHideAdvancedSearchButton->setText((QLatin1String("-")));
- } else {
- advancedSearchWidget->hide();
- showHideAdvancedSearchButton->setText((QLatin1String("+")));
- }
-
- simpleSearch = !simpleSearch;
- defaultQuery->setEnabled(simpleSearch);
- enableOrDisableToolButtons();
- }
-
void searchRequested()
{
QList<QHelpSearchQuery> queryList;
-#if !defined(QT_CLUCENE_SUPPORT)
queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
- QStringList(defaultQuery->text())));
-#else
- if (defaultQuery->isEnabled()) {
- queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
- buildTermList(defaultQuery->text())));
- } else {
- QRegExp exp(QLatin1String("\\s+"));
- QStringList list = similarQuery->text().split(exp,
- QString::SkipEmptyParts);
- if (!list.isEmpty()) {
- QStringList fuzzy;
- for (const QString &term : qAsConst(list))
- fuzzy += buildTermList(term);
- queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY,
- fuzzy));
- }
-
- list = withoutQuery->text().split(exp, QString::SkipEmptyParts);
- if (!list.isEmpty()) {
- QStringList without;
- for (const QString &term : qAsConst(list))
- without.append(term);
- queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT,
- without));
- }
-
- if (!exactQuery->text().isEmpty()) {
- QString phrase = exactQuery->text().remove(QLatin1Char('"'));
- phrase = phrase.simplified();
- queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE,
- QStringList(phrase)));
- }
-
- list = allQuery->text().split(exp, QString::SkipEmptyParts);
- if (!list.isEmpty()) {
- QStringList all;
- for (const QString &term : qAsConst(list))
- all.append(term);
- queryList.append(QHelpSearchQuery(QHelpSearchQuery::ALL, all));
- }
-
- list = atLeastQuery->text().split(exp, QString::SkipEmptyParts);
- if (!list.isEmpty()) {
- QStringList atLeast;
- for (const QString &term : qAsConst(list))
- atLeast += buildTermList(term);
- queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST,
- atLeast));
- }
- }
-#endif
- QueryHistory &queryHist = simpleSearch ? simpleQueries : complexQueries;
+ QStringList(m_lineEdit->text())));
+ QueryHistory &queryHist = m_queries;
saveQuery(queryList, queryHist);
queryHist.curQuery = queryHist.queries.size() - 1;
if (queryHist.curQuery > 0)
- prevQueryButton->setEnabled(true);
- nextQueryButton->setEnabled(false);
+ m_prevQueryButton->setEnabled(true);
+ m_nextQueryButton->setEnabled(false);
}
void nextQuery()
{
- nextOrPrevQuery((simpleSearch ? simpleQueries
- : complexQueries).queries.size() - 1, 1, nextQueryButton,
- prevQueryButton);
+ nextOrPrevQuery(m_queries.queries.size() - 1, 1, m_nextQueryButton,
+ m_prevQueryButton);
}
void prevQuery()
{
- nextOrPrevQuery(0, -1, prevQueryButton, nextQueryButton);
+ nextOrPrevQuery(0, -1, m_prevQueryButton, m_nextQueryButton);
}
private:
friend class QHelpSearchQueryWidget;
- bool compactMode;
- bool simpleSearch;
- QLabel *simpleSearchLabel;
- QLabel *advancedSearchLabel;
- QLabel *similarLabel;
- QLabel *withoutLabel;
- QLabel *exactLabel;
- QLabel *allLabel;
- QLabel *atLeastLabel;
- QPushButton *searchButton;
- QWidget* advancedSearchWidget;
- QToolButton *showHideAdvancedSearchButton;
- QLineEdit *defaultQuery;
- QLineEdit *exactQuery;
- QLineEdit *similarQuery;
- QLineEdit *withoutQuery;
- QLineEdit *allQuery;
- QLineEdit *atLeastQuery;
- QToolButton *nextQueryButton;
- QToolButton *prevQueryButton;
- QueryHistory simpleQueries;
- QueryHistory complexQueries;
- QCompleter searchCompleter;
+ bool m_compactMode;
+ QLabel *m_searchLabel;
+ QPushButton *m_searchButton;
+ QLineEdit *m_lineEdit;
+ QToolButton *m_nextQueryButton;
+ QToolButton *m_prevQueryButton;
+ QueryHistory m_queries;
+ QCompleter m_searchCompleter;
};
#include "qhelpsearchquerywidget.moc"
@@ -450,97 +266,31 @@ QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent)
vLayout->setMargin(0);
QHBoxLayout* hBoxLayout = new QHBoxLayout();
- d->simpleSearchLabel = new QLabel(this);
- d->defaultQuery = new QLineEdit(this);
- d->defaultQuery->setCompleter(&d->searchCompleter);
- d->defaultQuery->installEventFilter(d);
- d->prevQueryButton = new QToolButton(this);
- d->prevQueryButton->setArrowType(Qt::LeftArrow);
- d->prevQueryButton->setEnabled(false);
- d->nextQueryButton = new QToolButton(this);
- d->nextQueryButton->setArrowType(Qt::RightArrow);
- d->nextQueryButton->setEnabled(false);
- d->searchButton = new QPushButton(this);
- hBoxLayout->addWidget(d->simpleSearchLabel);
- hBoxLayout->addWidget(d->defaultQuery);
- hBoxLayout->addWidget(d->prevQueryButton);
- hBoxLayout->addWidget(d->nextQueryButton);
- hBoxLayout->addWidget(d->searchButton);
+ d->m_searchLabel = new QLabel(this);
+ d->m_lineEdit = new QLineEdit(this);
+ d->m_lineEdit->setCompleter(&d->m_searchCompleter);
+ d->m_lineEdit->installEventFilter(d);
+ d->m_prevQueryButton = new QToolButton(this);
+ d->m_prevQueryButton->setArrowType(Qt::LeftArrow);
+ d->m_prevQueryButton->setEnabled(false);
+ d->m_nextQueryButton = new QToolButton(this);
+ d->m_nextQueryButton->setArrowType(Qt::RightArrow);
+ d->m_nextQueryButton->setEnabled(false);
+ d->m_searchButton = new QPushButton(this);
+ hBoxLayout->addWidget(d->m_searchLabel);
+ hBoxLayout->addWidget(d->m_lineEdit);
+ hBoxLayout->addWidget(d->m_prevQueryButton);
+ hBoxLayout->addWidget(d->m_nextQueryButton);
+ hBoxLayout->addWidget(d->m_searchButton);
vLayout->addLayout(hBoxLayout);
- connect(d->prevQueryButton, SIGNAL(clicked()), d, SLOT(prevQuery()));
- connect(d->nextQueryButton, SIGNAL(clicked()), d, SLOT(nextQuery()));
- connect(d->searchButton, SIGNAL(clicked()), this, SIGNAL(search()));
- connect(d->defaultQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
-
-#if defined(QT_CLUCENE_SUPPORT)
- hBoxLayout = new QHBoxLayout();
- d->showHideAdvancedSearchButton = new QToolButton(this);
- d->showHideAdvancedSearchButton->setText(QLatin1String("+"));
- d->showHideAdvancedSearchButton->setMinimumSize(25, 20);
-
- d->advancedSearchLabel = new QLabel(this);
- QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
- sizePolicy.setHeightForWidth(d->advancedSearchLabel->sizePolicy().hasHeightForWidth());
- d->advancedSearchLabel->setSizePolicy(sizePolicy);
-
- QFrame* hLine = new QFrame(this);
- hLine->setFrameStyle(QFrame::HLine);
- hBoxLayout->addWidget(d->showHideAdvancedSearchButton);
- hBoxLayout->addWidget(d->advancedSearchLabel);
- hBoxLayout->addWidget(hLine);
-
- vLayout->addLayout(hBoxLayout);
-
- // setup advanced search layout
- d->advancedSearchWidget = new QWidget(this);
- QGridLayout *gLayout = new QGridLayout(d->advancedSearchWidget);
- gLayout->setMargin(0);
-
- d->similarLabel = new QLabel(this);
- gLayout->addWidget(d->similarLabel, 0, 0);
- d->similarQuery = new QLineEdit(this);
- d->similarQuery->setCompleter(&d->searchCompleter);
- gLayout->addWidget(d->similarQuery, 0, 1);
-
- d->withoutLabel = new QLabel(this);
- gLayout->addWidget(d->withoutLabel, 1, 0);
- d->withoutQuery = new QLineEdit(this);
- d->withoutQuery->setCompleter(&d->searchCompleter);
- gLayout->addWidget(d->withoutQuery, 1, 1);
-
- d->exactLabel = new QLabel(this);
- gLayout->addWidget(d->exactLabel, 2, 0);
- d->exactQuery = new QLineEdit(this);
- d->exactQuery->setCompleter(&d->searchCompleter);
- gLayout->addWidget(d->exactQuery, 2, 1);
-
- d->allLabel = new QLabel(this);
- gLayout->addWidget(d->allLabel, 3, 0);
- d->allQuery = new QLineEdit(this);
- d->allQuery->setCompleter(&d->searchCompleter);
- gLayout->addWidget(d->allQuery, 3, 1);
-
- d->atLeastLabel = new QLabel(this);
- gLayout->addWidget(d->atLeastLabel, 4, 0);
- d->atLeastQuery = new QLineEdit(this);
- d->atLeastQuery->setCompleter(&d->searchCompleter);
- gLayout->addWidget(d->atLeastQuery, 4, 1);
-
- vLayout->addWidget(d->advancedSearchWidget);
- d->advancedSearchWidget->hide();
+ connect(d->m_prevQueryButton, SIGNAL(clicked()), d, SLOT(prevQuery()));
+ connect(d->m_nextQueryButton, SIGNAL(clicked()), d, SLOT(nextQuery()));
+ connect(d->m_searchButton, SIGNAL(clicked()), this, SIGNAL(search()));
+ connect(d->m_lineEdit, SIGNAL(returnPressed()), this, SIGNAL(search()));
d->retranslate();
-
- connect(d->exactQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
- connect(d->similarQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
- connect(d->withoutQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
- connect(d->allQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
- connect(d->atLeastQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
- connect(d->showHideAdvancedSearchButton, SIGNAL(clicked()),
- d, SLOT(showHideAdvancedSearch()));
-#endif
connect(this, SIGNAL(search()), d, SLOT(searchRequested()));
setCompactMode(true);
}
@@ -558,8 +308,7 @@ QHelpSearchQueryWidget::~QHelpSearchQueryWidget()
*/
void QHelpSearchQueryWidget::expandExtendedSearch()
{
- if (d->simpleSearch)
- d->showHideAdvancedSearch();
+ // TODO: no extended search anymore, deprecate it?
}
/*!
@@ -568,8 +317,7 @@ void QHelpSearchQueryWidget::expandExtendedSearch()
*/
void QHelpSearchQueryWidget::collapseExtendedSearch()
{
- if (!d->simpleSearch)
- d->showHideAdvancedSearch();
+ // TODO: no extended search anymore, deprecate it?
}
/*!
@@ -578,11 +326,9 @@ void QHelpSearchQueryWidget::collapseExtendedSearch()
*/
QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const
{
- const QHelpSearchQueryWidgetPrivate::QueryHistory &queryHist =
- d->simpleSearch ? d->simpleQueries : d->complexQueries;
- if (queryHist.queries.isEmpty())
+ if (d->m_queries.queries.isEmpty())
return QList<QHelpSearchQuery>();
- return d->escapeQueries(queryHist.queries.last());
+ return d->m_queries.queries.last();
}
/*!
@@ -593,31 +339,26 @@ QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const
*/
void QHelpSearchQueryWidget::setQuery(const QList<QHelpSearchQuery> &queryList)
{
- QList<QLineEdit *> lineEdits;
- lineEdits << d->defaultQuery << d->allQuery << d->atLeastQuery
- << d->similarQuery << d->withoutQuery << d->exactQuery;
- for (QLineEdit *lineEdit : qAsConst(lineEdits))
- lineEdit->clear();
-
- for (const QHelpSearchQuery &q : queryList) {
- if (QLineEdit *lineEdit = d->lineEditFor(q.fieldName))
- lineEdit->setText(lineEdit->text() + q.wordList.join(QChar::Space) + QChar::Space);
- }
+ d->m_lineEdit->clear();
+
+ for (const QHelpSearchQuery &q : queryList)
+ d->m_lineEdit->setText(d->m_lineEdit->text() + q.wordList.join(QChar::Space) + QChar::Space);
+
d->searchRequested();
}
bool QHelpSearchQueryWidget::isCompactMode() const
{
- return d->compactMode;
+ return d->m_compactMode;
}
void QHelpSearchQueryWidget::setCompactMode(bool on)
{
- if (d->compactMode != on) {
- d->compactMode = on;
- d->prevQueryButton->setVisible(!on);
- d->nextQueryButton->setVisible(!on);
- d->simpleSearchLabel->setVisible(!on);
+ if (d->m_compactMode != on) {
+ d->m_compactMode = on;
+ d->m_prevQueryButton->setVisible(!on);
+ d->m_nextQueryButton->setVisible(!on);
+ d->m_searchLabel->setVisible(!on);
}
}
@@ -627,8 +368,8 @@ void QHelpSearchQueryWidget::setCompactMode(bool on)
void QHelpSearchQueryWidget::focusInEvent(QFocusEvent *focusEvent)
{
if (focusEvent->reason() != Qt::MouseFocusReason) {
- d->defaultQuery->selectAll();
- d->defaultQuery->setFocus();
+ d->m_lineEdit->selectAll();
+ d->m_lineEdit->setFocus();
}
}
diff --git a/src/assistant/help/qhelpsearchresultwidget.cpp b/src/assistant/help/qhelpsearchresultwidget.cpp
index 50d8f04f4..4c5f1b365 100644
--- a/src/assistant/help/qhelpsearchresultwidget.cpp
+++ b/src/assistant/help/qhelpsearchresultwidget.cpp
@@ -154,13 +154,8 @@ private slots:
void setResults(int hitsCount)
{
if (!searchEngine.isNull()) {
-#if defined(QT_CLUCENE_SUPPORT)
showFirstResultPage();
updateNextButtonState(((hitsCount > 20) ? true : false));
-#else
- resultTreeWidget->clear();
- resultTreeWidget->showResultPage(searchEngine->hits(0, hitsCount));
-#endif
}
}
@@ -351,7 +346,6 @@ QHelpSearchResultWidget::QHelpSearchResultWidget(QHelpSearchEngine *engine)
vLayout->setMargin(0);
vLayout->setSpacing(0);
-#if defined(QT_CLUCENE_SUPPORT)
QHBoxLayout *hBoxLayout = new QHBoxLayout();
#ifndef Q_OS_MAC
hBoxLayout->setMargin(0);
@@ -396,13 +390,6 @@ QHelpSearchResultWidget::QHelpSearchResultWidget(QHelpSearchEngine *engine)
connect(d->nextResultPage, SIGNAL(clicked()), d, SLOT(updatePrevButtonState()));
connect(d->lastResultPage, SIGNAL(clicked()), d, SLOT(updatePrevButtonState()));
-#else
- d->resultTreeWidget = new QDefaultResultWidget(this);
- vLayout->addWidget(d->resultTreeWidget);
- connect(d->resultTreeWidget, SIGNAL(requestShowLink(QUrl)), this,
- SIGNAL(requestShowLink(QUrl)));
-#endif
-
connect(engine, SIGNAL(searchingFinished(int)), d, SLOT(setResults(int)));
}
@@ -429,16 +416,8 @@ QHelpSearchResultWidget::~QHelpSearchResultWidget()
QUrl QHelpSearchResultWidget::linkAt(const QPoint &point)
{
QUrl url;
-#if defined(QT_CLUCENE_SUPPORT)
if (d->resultTextBrowser)
url = d->resultTextBrowser->anchorAt(point);
-#else
- if (d->resultTreeWidget) {
- QTreeWidgetItem *item = d->resultTreeWidget->itemAt(point);
- if (item)
- url = item->data(1, Qt::DisplayRole).toString();
- }
-#endif
return url;
}