[search] [assessment-tool] Implemented searching in the background.

This commit is contained in:
Maxim Pimenov 2019-03-22 20:00:01 +03:00 committed by Tatiana Yan
parent aff6a26b56
commit ed850339be
12 changed files with 437 additions and 107 deletions

View file

@ -30,6 +30,8 @@ set(
sample_view.hpp
samples_view.cpp
samples_view.hpp
search_request_runner.cpp
search_request_runner.hpp
view.hpp
)

View file

@ -19,6 +19,13 @@ class FeatureLoader;
struct Context
{
enum class SearchState
{
Untouched,
InQueue,
Completed
};
Context(Edits::OnUpdate onFoundResultsUpdate, Edits::OnUpdate onNonFoundResultsUpdate)
: m_foundResultsEdits(onFoundResultsUpdate), m_nonFoundResultsEdits(onNonFoundResultsUpdate)
{
@ -60,6 +67,8 @@ struct Context
std::vector<search::Sample::Result> m_nonFoundResults;
Edits m_nonFoundResultsEdits;
SearchState m_searchState = SearchState::Untouched;
bool m_initialized = false;
};
@ -81,6 +90,11 @@ public:
bool IsChanged(size_t index) const { return (*m_contexts)[index].HasChanges(); }
Context::SearchState GetSearchState(size_t index) const
{
return (*m_contexts)[index].m_searchState;
}
size_t Size() const { return m_contexts->Size(); }
private:

View file

@ -37,6 +37,12 @@ MainModel::MainModel(Framework & framework)
[this](size_t sampleIndex, Edits::Update const & update) {
OnUpdate(View::ResultType::NonFound, sampleIndex, update);
})
, m_runner(m_framework, m_dataSource, m_contexts,
[this](search::Results const & results) { UpdateViewOnResults(results); },
[this](size_t index) {
// The second parameter does not matter, we only change SearchStatus.
m_view->OnSampleChanged(index, false /* hasEdits */);
})
{
}
@ -63,7 +69,8 @@ void MainModel::Open(string const & path)
return;
}
ResetSearch();
m_runner.ResetForegroundSearch();
m_runner.ResetBackgroundSearch();
m_view->Clear();
@ -108,6 +115,11 @@ void MainModel::SaveAs(string const & path)
m_path = path;
}
void MainModel::InitiateBackgroundSearch(size_t from, size_t to)
{
m_runner.InitiateBackgroundSearch(from, to);
}
void MainModel::OnSampleSelected(int index)
{
CHECK(m_threadChecker.CalledOnOriginalThread(), ());
@ -120,55 +132,19 @@ void MainModel::OnSampleSelected(int index)
auto & context = m_contexts[index];
auto const & sample = context.m_sample;
m_view->ShowSample(index, sample, sample.m_pos, context.HasChanges());
ResetSearch();
auto const timestamp = m_queryTimestamp;
m_runner.ResetForegroundSearch();
m_numShownResults = 0;
if (context.m_initialized)
{
OnResults(timestamp, index, context.m_foundResults, context.m_foundResultsEdits.GetRelevances(),
context.m_goldenMatching, context.m_actualMatching);
UpdateViewOnResults(context.m_foundResults);
return;
}
auto & engine = m_framework.GetSearchAPI().GetEngine();
{
search::SearchParams params;
sample.FillSearchParams(params);
params.m_onResults = [this, index, sample, timestamp](search::Results const & results) {
vector<boost::optional<Edits::Relevance>> relevances;
vector<size_t> goldenMatching;
vector<size_t> actualMatching;
if (results.IsEndedNormal())
{
// Can't use m_loader here due to thread-safety issues.
search::FeatureLoader loader(m_dataSource);
search::Matcher matcher(loader);
vector<search::Result> const actual(results.begin(), results.end());
matcher.Match(sample.m_results, actual, goldenMatching, actualMatching);
relevances.resize(actual.size());
for (size_t i = 0; i < goldenMatching.size(); ++i)
{
auto const j = goldenMatching[i];
if (j != search::Matcher::kInvalidId)
{
CHECK_LESS(j, relevances.size(), ());
relevances[j] = sample.m_results[i].m_relevance;
}
}
}
GetPlatform().RunTask(Platform::Thread::Gui, bind(&MainModel::OnResults, this, timestamp, index, results,
relevances, goldenMatching, actualMatching));
};
m_queryHandle = engine.Search(params);
m_view->OnSearchStarted();
}
InitiateForegroundSearch(index);
}
void MainModel::OnResultSelected(int index)
@ -294,6 +270,16 @@ void MainModel::AddNonFoundResult(FeatureID const & id)
context.AddNonFoundResult(result);
}
void MainModel::InitiateForegroundSearch(size_t index)
{
auto & context = m_contexts[index];
auto const & sample = context.m_sample;
m_view->ShowSample(index, sample, sample.m_pos, context.HasChanges());
m_runner.InitiateForegroundSearch(index);
m_view->OnSearchStarted();
}
void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update)
{
using Type = Edits::Update::Type;
@ -320,82 +306,34 @@ void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Updat
{
CHECK(context.m_initialized, ());
CHECK_EQUAL(type, View::ResultType::NonFound, ());
ShowMarks(context);
m_view->ShowMarks(context);
}
}
void MainModel::OnResults(uint64_t timestamp, size_t sampleIndex, search::Results const & results,
vector<boost::optional<Edits::Relevance>> const & relevances,
vector<size_t> const & goldenMatching,
vector<size_t> const & actualMatching)
void MainModel::UpdateViewOnResults(search::Results const & results)
{
CHECK(m_threadChecker.CalledOnOriginalThread(), ());
if (timestamp != m_queryTimestamp)
return;
CHECK_LESS_OR_EQUAL(m_numShownResults, results.GetCount(), ());
m_view->AddFoundResults(results.begin() + m_numShownResults, results.end());
m_numShownResults = results.GetCount();
auto & context = m_contexts[sampleIndex];
context.m_foundResults = results;
if (!results.IsEndedNormal())
return;
if (!context.m_initialized)
{
context.m_foundResultsEdits.Reset(relevances);
context.m_goldenMatching = goldenMatching;
context.m_actualMatching = actualMatching;
{
vector<boost::optional<Edits::Relevance>> relevances;
auto & nonFound = context.m_nonFoundResults;
CHECK(nonFound.empty(), ());
for (size_t i = 0; i < context.m_goldenMatching.size(); ++i)
{
auto const j = context.m_goldenMatching[i];
if (j != search::Matcher::kInvalidId)
continue;
nonFound.push_back(context.m_sample.m_results[i]);
relevances.emplace_back(nonFound.back().m_relevance);
}
context.m_nonFoundResultsEdits.Reset(relevances);
}
context.m_initialized = true;
}
auto & context = m_contexts[m_selectedSample];
m_view->ShowNonFoundResults(context.m_nonFoundResults,
context.m_nonFoundResultsEdits.GetEntries());
ShowMarks(context);
m_view->OnResultChanged(sampleIndex, View::ResultType::Found,
Edits::Update::MakeAll());
m_view->OnResultChanged(sampleIndex, View::ResultType::NonFound,
Edits::Update::MakeAll());
m_view->OnSampleChanged(sampleIndex, context.HasChanges());
m_view->SetEdits(sampleIndex, context.m_foundResultsEdits, context.m_nonFoundResultsEdits);
m_view->ShowMarks(context);
m_view->OnResultChanged(m_selectedSample, View::ResultType::Found, Edits::Update::MakeAll());
m_view->OnResultChanged(m_selectedSample, View::ResultType::NonFound, Edits::Update::MakeAll());
m_view->OnSampleChanged(m_selectedSample, context.HasChanges());
m_view->SetEdits(m_selectedSample, context.m_foundResultsEdits, context.m_nonFoundResultsEdits);
m_view->OnSearchCompleted();
}
void MainModel::ResetSearch()
{
++m_queryTimestamp;
if (auto handle = m_queryHandle.lock())
handle->Cancel();
}
void MainModel::ShowMarks(Context const & context)
{
m_view->ClearSearchResultMarks();
m_view->ShowFoundResultsMarks(context.m_foundResults.begin(), context.m_foundResults.end());
m_view->ShowNonFoundResultsMarks(context.m_nonFoundResults,
context.m_nonFoundResultsEdits.GetEntries());
}
void MainModel::OnChangeAllRelevancesClicked(Edits::Relevance relevance)
{
CHECK_GREATER_OR_EQUAL(m_selectedSample, 0, ());

View file

@ -5,6 +5,7 @@
#include "search/search_quality/assessment_tool/context.hpp"
#include "search/search_quality/assessment_tool/edits.hpp"
#include "search/search_quality/assessment_tool/model.hpp"
#include "search/search_quality/assessment_tool/search_request_runner.hpp"
#include "search/search_quality/assessment_tool/view.hpp"
#include "search/search_quality/sample.hpp"
@ -34,6 +35,7 @@ public:
void Open(std::string const & path) override;
void Save() override;
void SaveAs(std::string const & path) override;
void InitiateBackgroundSearch(size_t const from, size_t const to) override;
void OnSampleSelected(int index) override;
void OnResultSelected(int index) override;
@ -49,14 +51,11 @@ public:
private:
static int constexpr kInvalidIndex = -1;
void InitiateForegroundSearch(size_t index);
void OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update);
void OnResults(uint64_t timestamp, size_t sampleIndex, search::Results const & results,
std::vector<boost::optional<Edits::Relevance>> const & relevances,
std::vector<size_t> const & goldenMatching,
std::vector<size_t> const & actualMatching);
void ResetSearch();
void UpdateViewOnResults(search::Results const & results);
void ShowMarks(Context const & context);
void OnChangeAllRelevancesClicked(Edits::Relevance relevance);
@ -73,10 +72,10 @@ private:
// Path to the last file search samples were loaded from or saved to.
std::string m_path;
std::weak_ptr<search::ProcessorHandle> m_queryHandle;
uint64_t m_queryTimestamp = 0;
int m_selectedSample = kInvalidIndex;
size_t m_numShownResults = 0;
SearchRequestRunner m_runner;
ThreadChecker m_threadChecker;
};

View file

@ -18,15 +18,25 @@
#include "geometry/mercator.hpp"
#include "base/assert.hpp"
#include "base/checked_cast.hpp"
#include "base/string_utils.hpp"
#include <limits>
#include <QtCore/Qt>
#include <QtGui/QCloseEvent>
#include <QtGui/QIntValidator>
#include <QtGui/QKeySequence>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QDialog>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QDockWidget>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QToolBar>
@ -125,6 +135,13 @@ void MainView::ShowNonFoundResults(std::vector<search::Sample::Result> const & r
m_sampleView->ShowNonFoundResults(results, entries);
}
void MainView::ShowMarks(Context const & context)
{
ClearSearchResultMarks();
ShowFoundResultsMarks(context.m_foundResults.begin(), context.m_foundResults.end());
ShowNonFoundResultsMarks(context.m_nonFoundResults, context.m_nonFoundResultsEdits.GetEntries());
}
void MainView::ShowFoundResultsMarks(search::Results::ConstIter begin,
search::Results::ConstIter end)
@ -165,6 +182,7 @@ void MainView::OnResultChanged(size_t sampleIndex, ResultType type, Edits::Updat
if (!m_samplesView->IsSelected(sampleIndex))
return;
switch (type)
{
case ResultType::Found: m_sampleView->GetFoundResultsView().Update(update); break;
@ -174,6 +192,7 @@ void MainView::OnResultChanged(size_t sampleIndex, ResultType type, Edits::Updat
void MainView::OnSampleChanged(size_t sampleIndex, bool hasEdits)
{
m_samplesView->OnUpdate(sampleIndex);
if (!m_samplesView->IsSelected(sampleIndex))
return;
SetSampleDockTitle(hasEdits);
@ -276,6 +295,17 @@ void MainView::InitMenuBar()
fileMenu->addAction(m_saveAs);
}
{
m_initiateBackgroundSearch = new QAction(tr("Initiate background search"), this /* parent */);
m_initiateBackgroundSearch->setShortcut(Qt::CTRL | Qt::Key_I);
m_initiateBackgroundSearch->setStatusTip(
tr("Search in the background for the queries from a selected range"));
m_initiateBackgroundSearch->setEnabled(false);
connect(m_initiateBackgroundSearch, &QAction::triggered, this,
&MainView::InitiateBackgroundSearch);
fileMenu->addAction(m_initiateBackgroundSearch);
}
fileMenu->addSeparator();
{
@ -379,6 +409,7 @@ void MainView::Open()
return;
m_model->Open(file);
m_initiateBackgroundSearch->setEnabled(true);
}
void MainView::Save() { m_model->Save(); }
@ -392,6 +423,52 @@ void MainView::SaveAs()
m_model->SaveAs(file);
}
void MainView::InitiateBackgroundSearch()
{
QDialog dialog(this);
QFormLayout form(&dialog);
form.addRow(new QLabel("Queries range"));
QValidator * validator = new QIntValidator(0, std::numeric_limits<int>::max(), this);
QLineEdit * lineEditFrom = new QLineEdit(&dialog);
form.addRow(new QLabel("First"), lineEditFrom);
lineEditFrom->setValidator(validator);
QLineEdit * lineEditTo = new QLineEdit(&dialog);
form.addRow(new QLabel("Last"), lineEditTo);
lineEditTo->setValidator(validator);
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal,
&dialog);
form.addRow(&buttonBox);
connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
if (dialog.exec() != QDialog::Accepted)
return;
std::string const strFrom = lineEditFrom->text().toStdString();
std::string const strTo = lineEditTo->text().toStdString();
uint64_t from = 0;
uint64_t to = 0;
if (!strings::to_uint64(strFrom, from))
{
LOG(LERROR, ("Could not parse number from", strFrom));
return;
}
if (!strings::to_uint64(strTo, to))
{
LOG(LERROR, ("Could not parse number from", strTo));
return;
}
m_model->InitiateBackgroundSearch(base::checked_cast<size_t>(from),
base::checked_cast<size_t>(to));
}
void MainView::SetSamplesDockTitle(bool hasEdits)
{
CHECK(m_samplesDock, ());

View file

@ -39,6 +39,7 @@ public:
void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
std::vector<Edits::Entry> const & entries) override;
void ShowMarks(Context const & context) override;
void ShowFoundResultsMarks(search::Results::ConstIter begin,
search::Results::ConstIter end) override;
void ShowNonFoundResultsMarks(std::vector<search::Sample::Result> const & results,
@ -101,6 +102,7 @@ private:
void Open();
void Save();
void SaveAs();
void InitiateBackgroundSearch();
void SetSamplesDockTitle(bool hasEdits);
void SetSampleDockTitle(bool hasEdits);
@ -120,6 +122,7 @@ private:
QAction * m_save = nullptr;
QAction * m_saveAs = nullptr;
QAction * m_initiateBackgroundSearch = nullptr;
State m_state = State::BeforeSearch;
FeatureID m_selectedFeature;

View file

@ -16,6 +16,14 @@ public:
virtual void Save() = 0;
virtual void SaveAs(std::string const & path) = 0;
// Initiates the search in the background on all samples
// in the 1-based range [|from|, |to|], both ends included.
// Another background search that may currently be running will be cancelled
// but the results for already completed requests will not be discarded.
//
// Does nothing if the range is invalid.
virtual void InitiateBackgroundSearch(size_t from, size_t to) = 0;
virtual void OnSampleSelected(int index) = 0;
virtual void OnResultSelected(int index) = 0;
virtual void OnNonFoundResultSelected(int index) = 0;

View file

@ -22,7 +22,14 @@ QVariant SamplesView::Model::data(QModelIndex const & index, int role) const
if (role == Qt::BackgroundRole && m_samples.IsValid())
{
if (m_samples.IsChanged(row))
return QBrush(QColor(255, 255, 200));
return QBrush(QColor(0xFF, 0xFF, 0xC8));
if (m_samples.GetSearchState(row) == Context::SearchState::InQueue)
return QBrush(QColor(0xFF, 0xCC, 0x66));
if (m_samples.GetSearchState(row) == Context::SearchState::Completed)
return QBrush(QColor(0xCA, 0xFE, 0xDB));
return QBrush(Qt::transparent);
}
return QStandardItemModel::data(index, role);

View file

@ -0,0 +1,207 @@
#include "search/search_quality/assessment_tool/search_request_runner.hpp"
#include "search/feature_loader.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
#include <utility>
using namespace std;
SearchRequestRunner::SearchRequestRunner(Framework & framework, DataSource const & dataSource,
ContextList & contexts,
UpdateViewOnResults && updateViewOnResults,
UpdateSampleSearchState && updateSampleSearchState)
: m_framework(framework)
, m_dataSource(dataSource)
, m_contexts(contexts)
, m_updateViewOnResults(move(updateViewOnResults))
, m_updateSampleSearchState(move(updateSampleSearchState))
{
}
void SearchRequestRunner::InitiateForegroundSearch(size_t index)
{
RunRequest(index, false /* background */, m_foregroundTimestamp);
}
void SearchRequestRunner::InitiateBackgroundSearch(size_t from, size_t to)
{
// 1 <= from <= to <= m_contexts.Size().
if (from < 1 || from > to || to > m_contexts.Size())
{
LOG(LINFO,
("Could not initiate search in the range", from, to, "Total samples:", m_contexts.Size()));
return;
}
ResetBackgroundSearch();
// Convert to 0-based.
--from;
--to;
m_backgroundFirstIndex = from;
m_backgroundLastIndex = to;
m_numProcessedRequests = 0;
for (size_t index = from; index <= to; ++index)
{
if (m_contexts[index].m_searchState == Context::SearchState::Untouched)
{
m_contexts[index].m_searchState = Context::SearchState::InQueue;
m_backgroundQueue.push(index);
m_updateSampleSearchState(index);
}
else
{
CHECK(m_contexts[index].m_searchState == Context::SearchState::Completed, ());
}
}
RunNextBackgroundRequest(m_backgroundTimestamp);
}
void SearchRequestRunner::RunNextBackgroundRequest(size_t timestamp)
{
// todo(@m) Process in batches instead?
if (m_backgroundQueue.empty())
{
LOG(LINFO, ("All requests from", m_backgroundFirstIndex + 1, "to", m_backgroundLastIndex + 1,
"have been processed"));
return;
}
size_t index = m_backgroundQueue.front();
m_backgroundQueue.pop();
RunRequest(index, true /* background */, timestamp);
}
void SearchRequestRunner::RunRequest(size_t index, bool background, size_t timestamp)
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
auto const & context = m_contexts[index];
auto const & sample = context.m_sample;
// todo(@m) What if we want multiple threads in engine?
auto & engine = m_framework.GetSearchAPI().GetEngine();
search::SearchParams params;
sample.FillSearchParams(params);
params.m_onResults = [=](search::Results const & results) {
vector<boost::optional<Edits::Relevance>> relevances;
vector<size_t> goldenMatching;
vector<size_t> actualMatching;
if (results.IsEndedNormal())
{
// Can't use MainModel's m_loader here due to thread-safety issues.
search::FeatureLoader loader(m_dataSource);
search::Matcher matcher(loader);
vector<search::Result> const actual(results.begin(), results.end());
matcher.Match(sample.m_results, actual, goldenMatching, actualMatching);
relevances.resize(actual.size());
for (size_t i = 0; i < goldenMatching.size(); ++i)
{
auto const j = goldenMatching[i];
if (j != search::Matcher::kInvalidId)
{
CHECK_LESS(j, relevances.size(), ());
relevances[j] = sample.m_results[i].m_relevance;
}
}
LOG(LINFO, ("Request number", index + 1, "has been processed in the",
background ? "background" : "foreground"));
}
GetPlatform().RunTask(Platform::Thread::Gui, [this, background, timestamp, index, results,
relevances, goldenMatching, actualMatching] {
size_t const latestTimestamp = background ? m_backgroundTimestamp : m_foregroundTimestamp;
if (timestamp != latestTimestamp)
return;
auto & context = m_contexts[index];
context.m_foundResults = results;
if (results.IsEndMarker())
{
if (results.IsEndedNormal())
context.m_searchState = Context::SearchState::Completed;
else
context.m_searchState = Context::SearchState::Untouched;
m_updateSampleSearchState(index);
}
if (results.IsEndedNormal() && !context.m_initialized)
{
context.m_foundResultsEdits.Reset(relevances);
context.m_goldenMatching = goldenMatching;
context.m_actualMatching = actualMatching;
{
vector<boost::optional<Edits::Relevance>> relevances;
auto & nonFound = context.m_nonFoundResults;
CHECK(nonFound.empty(), ());
for (size_t i = 0; i < context.m_goldenMatching.size(); ++i)
{
auto const j = context.m_goldenMatching[i];
if (j != search::Matcher::kInvalidId)
continue;
nonFound.push_back(context.m_sample.m_results[i]);
relevances.emplace_back(nonFound.back().m_relevance);
}
context.m_nonFoundResultsEdits.Reset(relevances);
}
context.m_initialized = true;
}
if (background)
RunNextBackgroundRequest(timestamp);
else
m_updateViewOnResults(results);
});
};
if (background)
m_backgroundQueryHandle = engine.Search(params);
else
m_foregroundQueryHandle = engine.Search(params);
}
void SearchRequestRunner::ResetForegroundSearch()
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
++m_foregroundTimestamp;
if (auto handle = m_foregroundQueryHandle.lock())
handle->Cancel();
}
void SearchRequestRunner::ResetBackgroundSearch()
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
++m_backgroundTimestamp;
auto handle = m_backgroundQueryHandle.lock();
if (!handle)
return;
handle->Cancel();
for (size_t index = m_backgroundFirstIndex; index <= m_backgroundLastIndex; ++index)
{
if (m_contexts[index].m_searchState == Context::SearchState::InQueue)
{
m_contexts[index].m_searchState = Context::SearchState::Untouched;
m_updateSampleSearchState(index);
}
}
queue<size_t>().swap(m_backgroundQueue);
}

View file

@ -0,0 +1,68 @@
#pragma once
#include "search/search_quality/assessment_tool/context.hpp"
#include "map/framework.hpp"
#include "base/thread_checker.hpp"
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <queue>
#include <vector>
// A proxy for SearchAPI/SearchEngine.
// This class updates the Model's |m_contexts| directly (from the main thread) and updates
// the View via the |m_updateViewOnResults| and |m_updateSampleSearchState| callbacks.
class SearchRequestRunner
{
public:
using UpdateViewOnResults = std::function<void(search::Results const & results)>;
using UpdateSampleSearchState = std::function<void(size_t index)>;
SearchRequestRunner(Framework & framework, DataSource const & dataSource, ContextList & contexts,
UpdateViewOnResults && updateViewOnResults,
UpdateSampleSearchState && updateSampleSearchState);
void InitiateForegroundSearch(size_t index);
void InitiateBackgroundSearch(size_t from, size_t to);
void ResetForegroundSearch();
void ResetBackgroundSearch();
private:
static size_t constexpr kInvalidIndex = std::numeric_limits<size_t>::max();
void RunNextBackgroundRequest(size_t timestamp);
void RunRequest(size_t index, bool background, size_t timestamp);
Framework & m_framework;
DataSource const & m_dataSource;
ContextList & m_contexts;
UpdateViewOnResults m_updateViewOnResults;
UpdateSampleSearchState m_updateSampleSearchState;
std::weak_ptr<search::ProcessorHandle> m_backgroundQueryHandle;
std::weak_ptr<search::ProcessorHandle> m_foregroundQueryHandle;
size_t m_foregroundTimestamp = 0;
size_t m_backgroundTimestamp = 0;
size_t m_backgroundFirstIndex = kInvalidIndex;
size_t m_backgroundLastIndex = kInvalidIndex;
std::queue<size_t> m_backgroundQueue;
size_t m_numProcessedRequests = 0;
ThreadChecker m_threadChecker;
};

View file

@ -41,6 +41,7 @@ public:
virtual void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
std::vector<Edits::Entry> const & entries) = 0;
virtual void ShowMarks(Context const & context) = 0;
virtual void ShowFoundResultsMarks(search::Results::ConstIter begin,
search::Results::ConstIter end) = 0;
virtual void ShowNonFoundResultsMarks(std::vector<search::Sample::Result> const & results,

View file

@ -20,6 +20,12 @@ public:
explicit Matcher(FeatureLoader & loader);
// Matches the |golden| results loaded from a Sample with |actual| results
// found by the search engine using the params from the Sample.
// goldenMatching[i] is the index of the result in |actual| that matches
// the sample result number i.
// actualMatching[j] is the index of the sample in |golden| that matches
// the golden result number j.
void Match(std::vector<Sample::Result> const & golden, std::vector<Result> const & actual,
std::vector<size_t> & goldenMatching, std::vector<size_t> & actualMatching);