Skip to content

Commit

Permalink
registry: Add cancellation and caching to docsets
Browse files Browse the repository at this point in the history
Add query cancellation at a docset level.
This allows the query to be cancelled midway a large docset run.
Add caching to docset results.
This should limit the number of items to search through for longer
queries.
  • Loading branch information
drognanar authored and mrhota committed Jul 18, 2016
1 parent 415bda0 commit 0f1b69c
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 11 deletions.
96 changes: 96 additions & 0 deletions src/registry/cachingsearchstrategy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2015 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
** Contact: http://zealdocs.org/contact.html
**
** This file is part of Zeal.
**
** Zeal is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Zeal is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Zeal. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#include "cachingsearchstrategy.h"
#include "docset.h"

#include "searchresult.h"
#include "searchquery.h"

using namespace Zeal;

CachingSearchStrategy::CachingSearchStrategy(std::unique_ptr<DocsetSearchStrategy> strategy)
: m_search(std::move(strategy)),
m_cache(CachingSearchStrategy::CacheSize)
{
}

QList<SearchResult> CachingSearchStrategy::search(const QString &query, CancellationToken &token)
{
QString candidate = getCacheEntry(query);
if (!candidate.isEmpty()) {
return searchWithCache(query, candidate);
} else {
return searchWithoutCache(query, token);
}
}

bool CachingSearchStrategy::validResult(
const QString &query, const SearchResult &previousResult,
SearchResult &result)
{
return m_search->validResult(query, previousResult, result);
}

QString CachingSearchStrategy::getCacheEntry(const QString &query) const
{
const SearchQuery searchQuery = SearchQuery::fromString(query);

for (int i = searchQuery.query().size(); i > 0; i--) {
QString candidate = searchQuery.query().mid(0, i);

/// Use the cache only if the cache entry contains all results.
/// Also handle cases where prefix is a docset query `std:`.
if (m_cache.contains(candidate)
&& m_cache[candidate]->size() < Docset::MaxDocsetResultsCount
&& SearchQuery::fromString(candidate).query().size() == i)
return candidate;
}
return QString();
}

QList<SearchResult> CachingSearchStrategy::searchWithCache(const QString &query, const QString &prefix)
{
QList<SearchResult> *prefixResults = m_cache[prefix];
QList<SearchResult> results;
QListIterator<SearchResult> prefixResultsIterator(*prefixResults);
while (prefixResultsIterator.hasNext()) {
SearchResult previousResult = prefixResultsIterator.next();
SearchResult result;
if (validResult(query, previousResult, result))
results.append(result);
}

m_cache.insert(query, new QList<SearchResult>(results));
return results;
}

QList<SearchResult> CachingSearchStrategy::searchWithoutCache(const QString &query, CancellationToken token)
{
QList<SearchResult> results = m_search->search(query, token);

/// Only cache the results if they are not partial.
if (!token.isCancelled())
m_cache.insert(query, new QList<SearchResult>(results));
return results;
}
65 changes: 65 additions & 0 deletions src/registry/cachingsearchstrategy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2015 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
** Contact: http://zealdocs.org/contact.html
**
** This file is part of Zeal.
**
** Zeal is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Zeal is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Zeal. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#ifndef CACHINGSEARCHSTRATEGY_H
#define CACHINGSEARCHSTRATEGY_H

#include "cancellationtoken.h"
#include "docsetsearchstrategy.h"

#include <QCache>
#include <QList>
#include <QString>

#include <memory>

namespace Zeal {

struct SearchResult;

/// A search strategy that uses a cache of previous searches.
/// If a search was made for a prefix then then all results
/// must appear in the cache. In this case instead of searching
/// entire docset only cache is searched.
class CachingSearchStrategy : public DocsetSearchStrategy
{
public:
CachingSearchStrategy(std::unique_ptr<DocsetSearchStrategy> strategy);
QList<SearchResult> search(const QString &query, CancellationToken &token) override;
bool validResult(const QString &query, const SearchResult &previousResult,
SearchResult &result)override;

private:
QString getCacheEntry(const QString &query) const;
QList<SearchResult> searchWithCache(const QString &query, const QString &prefix);
QList<SearchResult> searchWithoutCache(const QString &query, CancellationToken token);

const static int CacheSize = 10;

std::unique_ptr<DocsetSearchStrategy> m_search;
QCache<QString, QList<SearchResult> > m_cache;
};

}

#endif // CACHINGSEARCHSTRATEGY_H
23 changes: 23 additions & 0 deletions src/registry/cancellationtoken.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
/****************************************************************************
**
** Copyright (C) 2015 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
** Contact: http://zealdocs.org/contact.html
**
** This file is part of Zeal.
**
** Zeal is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Zeal is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Zeal. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#include "cancellationtoken.h"

using namespace Zeal;
Expand Down
23 changes: 23 additions & 0 deletions src/registry/cancellationtoken.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
/****************************************************************************
**
** Copyright (C) 2015 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
** Contact: http://zealdocs.org/contact.html
**
** This file is part of Zeal.
**
** Zeal is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Zeal is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Zeal. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#ifndef CANCELLATIONTOKEN_H
#define CANCELLATIONTOKEN_H

Expand Down
37 changes: 29 additions & 8 deletions src/registry/docset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
****************************************************************************/

#include "docset.h"
#include "cachingsearchstrategy.h"
#include "docsetsearchstrategy.h"
#include "searchresult.h"

Expand Down Expand Up @@ -65,7 +66,9 @@ class DashSearchStrategy : public DocsetSearchStrategy
{
public:
explicit DashSearchStrategy(Docset *docset);
QList<SearchResult> search(const QString &query) const override;
QList<SearchResult> search(const QString &query, CancellationToken &token) override;
bool validResult(const QString &query, const SearchResult &previousResult,
SearchResult &result) override;

private:
Docset *m_docset;
Expand All @@ -78,7 +81,7 @@ DashSearchStrategy::DashSearchStrategy(Docset *docset)
{
}

QList<SearchResult> DashSearchStrategy::search(const QString &rawQuery) const
QList<SearchResult> DashSearchStrategy::search(const QString &rawQuery, CancellationToken &token)
{
QList<SearchResult> results;
int resultCount = 0;
Expand Down Expand Up @@ -114,7 +117,7 @@ QList<SearchResult> DashSearchStrategy::search(const QString &rawQuery) const
query.bindValue(":query", QString("%1%").arg(curQuery));
query.exec();

while (query.next() && resultCount < Docset::MaxDocsetResultsCount) {
while (query.next() && !token.isCancelled() && resultCount < Docset::MaxDocsetResultsCount) {
const QString itemName = query.value(0).toString();
QString path = query.value(2).toString();
if (m_docset->type() == Docset::Type::ZDash) {
Expand All @@ -137,15 +140,33 @@ QList<SearchResult> DashSearchStrategy::search(const QString &rawQuery) const
return results;
}

bool DashSearchStrategy::validResult(
const QString &query, const SearchResult &previousResult,
SearchResult &result)
{
SearchQuery searchQuery = SearchQuery::fromString(query);
bool docsetEnabled = !searchQuery.hasKeywords() || searchQuery.hasKeywords(m_docset->keywords());

if (previousResult.name.contains(searchQuery.query(), Qt::CaseInsensitive)
&& docsetEnabled) {
result = previousResult.withScore(Docset::scoreSubstringResult(searchQuery, previousResult.name));
return true;

} else {
return false;
}
}

Docset::Docset(const QString &path) :
m_path(path),
m_searchStrategy(new DashSearchStrategy(this))
m_path(path)
{
QDir dir(m_path);
if (!dir.exists())
return;

loadMetadata();
std::unique_ptr<DocsetSearchStrategy> strategy(new DashSearchStrategy(this));
m_searchStrategy = std::unique_ptr<DocsetSearchStrategy>(new CachingSearchStrategy(std::move(strategy)));

// Attempt to find the icon in any supported format
for (const QString &iconFile : dir.entryList({QStringLiteral("icon.*")}, QDir::Files)) {
Expand Down Expand Up @@ -342,7 +363,7 @@ int Docset::scoreSubstringResult(const SearchQuery &query, const QString result)
{
int score = TotalBuckets - 1;

int index = result.indexOf(query.query());
int index = result.indexOf(query.query(), 0, Qt::CaseInsensitive);
if (index == 0 || Docset::endsWithSeparator(result, index)) {
score -= result.size() - query.query().size() - index + Docset::separators(result, index);
} else {
Expand All @@ -352,9 +373,9 @@ int Docset::scoreSubstringResult(const SearchQuery &query, const QString result)
return std::max(1, score);
}

QList<SearchResult> Docset::search(const QString &rawQuery) const
QList<SearchResult> Docset::search(const QString &rawQuery, CancellationToken token) const
{
return m_searchStrategy->search(rawQuery);
return m_searchStrategy->search(rawQuery, token);
}

QList<SearchResult> Docset::relatedLinks(const QUrl &url) const
Expand Down
3 changes: 2 additions & 1 deletion src/registry/docset.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

namespace Zeal {

struct CancellationToken;
class DocsetSearchStrategy;
class SearchQuery;
struct SearchResult;
Expand Down Expand Up @@ -61,7 +62,7 @@ class Docset

const QMap<QString, QString> &symbols(const QString &symbolType) const;

QList<SearchResult> search(const QString &query) const;
QList<SearchResult> search(const QString &query, CancellationToken token) const;
QList<SearchResult> relatedLinks(const QUrl &url) const;

// FIXME: This is an ugly workaround before we have a proper docset sources implementation
Expand Down
2 changes: 1 addition & 1 deletion src/registry/docsetregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ void DocsetRegistry::_runQueryAsync(const QString &query, const CancellationToke
for (Docset *docset : docsets()) {
if (token.isCancelled())
return;
results += docset->search(query);
results += docset->search(query, token);
}

std::sort(results.begin(), results.end());
Expand Down
23 changes: 23 additions & 0 deletions src/registry/docsetsearchstrategy.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
/****************************************************************************
**
** Copyright (C) 2015 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
** Contact: http://zealdocs.org/contact.html
**
** This file is part of Zeal.
**
** Zeal is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Zeal is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Zeal. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#include "docsetsearchstrategy.h"

using namespace Zeal;
Expand Down
Loading

0 comments on commit 0f1b69c

Please sign in to comment.