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 committed Jul 20, 2016
1 parent 0f747fe commit 6a65374
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 7 deletions.
98 changes: 98 additions & 0 deletions src/registry/cachingsearchstrategy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/****************************************************************************
**
** 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 "searchquery.h"
#include "searchresult.h"

using namespace Zeal;

const int CacheSize = 10;

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

QList<SearchResult> CachingSearchStrategy::search(const QString &query, const 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) const
{
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.isCanceled())
m_cache.insert(query, new QList<SearchResult>(results));
return results;
}
63 changes: 63 additions & 0 deletions src/registry/cachingsearchstrategy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/****************************************************************************
**
** 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, const CancellationToken &token) override;
bool validResult(const QString &query, const SearchResult &previousResult,
SearchResult &result) const 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);

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

}

#endif // CACHINGSEARCHSTRATEGY_H
33 changes: 27 additions & 6 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 @@ -66,7 +67,9 @@ class DashSearchStrategy : public DocsetSearchStrategy
{
public:
explicit DashSearchStrategy(Docset *docset);
QList<SearchResult> search(const QString &query, const CancellationToken &token) const;
QList<SearchResult> search(const QString &query, const CancellationToken &token);
bool validResult(const QString &query, const SearchResult &previousResult,
SearchResult &result) const override;

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

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

while (query.next() && resultCount < Docset::MaxDocsetResultsCount) {
while (query.next() && !token.isCanceled() && 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 @@ -138,15 +141,33 @@ QList<SearchResult> DashSearchStrategy::search(const QString &rawQuery, const Ca
return results;
}

bool DashSearchStrategy::validResult(
const QString &query, const SearchResult &previousResult,
SearchResult &result) const
{
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 @@ -343,7 +364,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 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
31 changes: 30 additions & 1 deletion src/registry/docsetsearchstrategy.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
/****************************************************************************
**
** 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 DOCSETSEARCHSTRATEGY_H
#define DOCSETSEARCHSTRATEGY_H

#include "cancellationtoken.h"

#include <QString>
#include <QList>

Expand All @@ -14,7 +39,11 @@ class DocsetSearchStrategy
{
public:
DocsetSearchStrategy();
virtual QList<SearchResult> search(const QString &query, const CancellationToken &token) const = 0;
virtual QList<SearchResult> search(const QString &query, const CancellationToken &token) = 0;

/// Used to filter out cached results.
virtual bool validResult(const QString &query, const SearchResult &previousResult,
SearchResult &result) const = 0;
};

}
Expand Down
7 changes: 7 additions & 0 deletions src/registry/searchresult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@ bool SearchResult::operator<(const SearchResult &r) const

return QString::compare(parentName, r.parentName, Qt::CaseInsensitive) < 0;
}

SearchResult SearchResult::withScore(int newScore) const
{
return SearchResult({name, parentName, type,
docset, path, query,
newScore});
}
2 changes: 2 additions & 0 deletions src/registry/searchresult.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ struct SearchResult
int score;

bool operator<(const SearchResult &r) const;

SearchResult withScore(int newScore) const;
};

} // namespace Zeal
Expand Down

0 comments on commit 6a65374

Please sign in to comment.