forked from organicmaps/organicmaps
[search] Brand new search :) For now, only name matching is used (i.e. no viewport intersection) and ranking is far from perfect.
This commit is contained in:
parent
6e9b2757f5
commit
3a0cb52832
21 changed files with 514 additions and 219 deletions
|
@ -133,7 +133,8 @@ private:
|
|||
ReadFeatureFunctor<F> f1(fv, f, offsets);
|
||||
for (size_t i = 0; i < intervals[ind].size(); ++i)
|
||||
{
|
||||
index.ForEachInIntervalAndScale(f1, intervals[ind][i].first, intervals[ind][i].second, scale);
|
||||
index.ForEachInIntervalAndScale(f1, intervals[ind][i].first, intervals[ind][i].second,
|
||||
scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,22 +48,4 @@ struct EdgeValueReader
|
|||
search::trie::ValueReader::ValueType,
|
||||
search::trie::EdgeValueReader::ValueType> TrieIterator;
|
||||
|
||||
class SearchInfo
|
||||
{
|
||||
FeaturesVector m_features;
|
||||
scoped_ptr<TrieIterator> m_iterator;
|
||||
|
||||
public:
|
||||
SearchInfo(FilesContainerR const & cont, feature::DataHeader const & header)
|
||||
: m_features(cont, header),
|
||||
m_iterator(::trie::reader::ReadTrie(
|
||||
cont.GetReader(SEARCH_INDEX_FILE_TAG),
|
||||
trie::ValueReader(),
|
||||
trie::EdgeValueReader()))
|
||||
{
|
||||
}
|
||||
|
||||
TrieIterator * GetTrie() { return m_iterator.get(); }
|
||||
FeaturesVector * GetFeatures() { return &m_features; }
|
||||
};
|
||||
} // namespace search
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#include "../std/vector.hpp"
|
||||
#include "../std/shared_ptr.hpp"
|
||||
|
||||
#include "../search/engine.hpp"
|
||||
|
||||
struct BenchmarkRectProvider;
|
||||
class WindowHandle;
|
||||
class PaintEvent;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "../platform/settings.hpp"
|
||||
|
||||
#include "../search/engine.hpp"
|
||||
#include "../search/search_engine.hpp"
|
||||
#include "../search/result.hpp"
|
||||
#include "../search/categories_holder.hpp"
|
||||
|
||||
|
@ -224,8 +224,6 @@ void Framework<TModel>::Clean()
|
|||
template <typename TModel>
|
||||
void Framework<TModel>::PrepareToShutdown()
|
||||
{
|
||||
if (m_pSearchEngine)
|
||||
m_pSearchEngine->StopEverything();
|
||||
}
|
||||
|
||||
template <typename TModel>
|
||||
|
@ -634,11 +632,9 @@ void Framework<TModel>::Search(string const & text, SearchCallbackT callback)
|
|||
|
||||
if (!m_pSearchEngine.get())
|
||||
{
|
||||
search::CategoriesHolder holder;
|
||||
string buffer;
|
||||
ReaderT(GetPlatform().GetReader(SEARCH_CATEGORIES_FILE_NAME)).ReadAsString(buffer);
|
||||
holder.LoadFromStream(buffer);
|
||||
m_pSearchEngine.reset(new search::Engine(&m_model.GetIndex(), holder));
|
||||
scoped_ptr<Reader> pReader(GetPlatform().GetReader(SEARCH_CATEGORIES_FILE_NAME));
|
||||
m_pSearchEngine.reset(
|
||||
new search::Engine(&m_model.GetIndex(), new search::CategoriesHolder(*pReader)));
|
||||
}
|
||||
|
||||
m_pSearchEngine->Search(text, m_navigator.Screen().GlobalRect(), callback);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "../std/shared_ptr.hpp"
|
||||
#include "../std/target_os.hpp"
|
||||
|
||||
#include "../search/engine.hpp"
|
||||
#include "../search/search_engine.hpp"
|
||||
|
||||
//#define DRAW_TOUCH_POINTS
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ size_t CategoriesHolder::LoadFromStream(string const & buffer)
|
|||
if (!cat.m_synonyms.empty() && !cat.m_types.empty())
|
||||
m_categories.push_back(cat);
|
||||
|
||||
LOG(LINFO, ("Categories loaded: ", m_categories.size()));
|
||||
return m_categories.size();
|
||||
}
|
||||
|
||||
|
@ -114,4 +115,15 @@ void CategoriesHolder::swap(CategoriesHolder & o)
|
|||
m_categories.swap(o.m_categories);
|
||||
}
|
||||
|
||||
CategoriesHolder::CategoriesHolder()
|
||||
{
|
||||
}
|
||||
|
||||
CategoriesHolder::CategoriesHolder(Reader const & reader)
|
||||
{
|
||||
string buffer;
|
||||
reader.ReadAsString(buffer);
|
||||
LoadFromStream(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../std/string.hpp"
|
||||
#include "../std/algorithm.hpp"
|
||||
|
||||
class Reader;
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
@ -33,6 +34,9 @@ class CategoriesHolder
|
|||
public:
|
||||
typedef ContainerT::const_iterator const_iterator;
|
||||
|
||||
CategoriesHolder();
|
||||
explicit CategoriesHolder(Reader const & reader);
|
||||
|
||||
/// @return number of loaded categories or 0 if something goes wrong
|
||||
size_t LoadFromStream(string const & buffer);
|
||||
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
#include "categories_holder.hpp"
|
||||
#include "engine.hpp"
|
||||
#include "query.hpp"
|
||||
#include "result.hpp"
|
||||
|
||||
#include "../platform/concurrent_runner.hpp"
|
||||
|
||||
#include "../base/logging.hpp"
|
||||
|
||||
#include "../std/bind.hpp"
|
||||
#include "../std/function.hpp"
|
||||
#include "../std/string.hpp"
|
||||
#include "../std/vector.hpp"
|
||||
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
||||
Engine::Engine(IndexType const * pIndex,
|
||||
CategoriesHolder & categories)
|
||||
: m_pIndex(pIndex), m_pSearchInfo(NULL),
|
||||
m_pCategories(new CategoriesHolder()),
|
||||
m_pRunner(new threads::ConcurrentRunner), m_pLastQuery(NULL),
|
||||
m_queriesActive(0)
|
||||
{
|
||||
m_pCategories->swap(categories);
|
||||
}
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
LOG(LDEBUG, (m_queriesActive));
|
||||
ASSERT_EQUAL(m_queriesActive, 0, ());
|
||||
}
|
||||
|
||||
void Engine::Search(string const & queryText,
|
||||
m2::RectD const & rect,
|
||||
function<void (Result const &)> const & f)
|
||||
{
|
||||
LOG(LDEBUG, (queryText, rect));
|
||||
|
||||
impl::Query * pQuery =
|
||||
new impl::Query(queryText, rect, m_pIndex, this, m_pCategories.get(),
|
||||
m_pSearchInfo->GetTrie(), m_pSearchInfo->GetFeatures());
|
||||
|
||||
{
|
||||
threads::MutexGuard mutexGuard(m_mutex);
|
||||
UNUSED_VALUE(mutexGuard);
|
||||
|
||||
ASSERT_GREATER_OR_EQUAL(m_queriesActive, 0, ());
|
||||
|
||||
if (m_pLastQuery)
|
||||
{
|
||||
LOG(LDEBUG, ("Stopping previous", m_pLastQuery->GetQueryText(), m_pLastQuery->GetViewport()));
|
||||
m_pLastQuery->SetTerminateFlag();
|
||||
}
|
||||
|
||||
m_pLastQuery = pQuery;
|
||||
++m_queriesActive;
|
||||
LOG(LDEBUG, ("Queries active", m_queriesActive));
|
||||
}
|
||||
|
||||
m_pRunner->Run(bind(&impl::Query::SearchAndDestroy, pQuery, f));
|
||||
}
|
||||
|
||||
void Engine::OnQueryDelete(impl::Query * pQuery)
|
||||
{
|
||||
threads::MutexGuard mutexGuard(m_mutex);
|
||||
UNUSED_VALUE(mutexGuard);
|
||||
|
||||
ASSERT_GREATER_OR_EQUAL(m_queriesActive, 1, ());
|
||||
|
||||
--m_queriesActive;
|
||||
LOG(LDEBUG, ("Queries active", m_queriesActive));
|
||||
LOG(LDEBUG, ("Query destroyed", pQuery->GetQueryText(), pQuery->GetViewport()));
|
||||
|
||||
if (m_pLastQuery == pQuery)
|
||||
{
|
||||
LOG(LDEBUG, ("Last query destroyed"));
|
||||
m_pLastQuery = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::StopEverything()
|
||||
{
|
||||
threads::MutexGuard mutexGuard(m_mutex);
|
||||
UNUSED_VALUE(mutexGuard);
|
||||
|
||||
ASSERT_GREATER_OR_EQUAL(m_queriesActive, 0, ());
|
||||
LOG(LINFO, (m_queriesActive, m_pLastQuery));
|
||||
|
||||
if (m_pLastQuery)
|
||||
{
|
||||
LOG(LDEBUG, ("Stopping previous", m_pLastQuery->GetQueryText(), m_pLastQuery->GetViewport()));
|
||||
m_pLastQuery->SetTerminateFlag();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace search
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../indexer/index.hpp"
|
||||
#include "../indexer/search_trie.hpp"
|
||||
|
||||
#include "../geometry/rect2d.hpp"
|
||||
|
||||
#include "../base/base.hpp"
|
||||
#include "../base/mutex.hpp"
|
||||
#include "../base/runner.hpp"
|
||||
|
||||
#include "../std/function.hpp"
|
||||
#include "../std/scoped_ptr.hpp"
|
||||
#include "../std/string.hpp"
|
||||
|
||||
|
||||
class Reader;
|
||||
class ModelReaderPtr;
|
||||
class FeatureType;
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
||||
namespace impl { class Query; }
|
||||
class CategoriesHolder;
|
||||
class Result;
|
||||
|
||||
class Engine
|
||||
{
|
||||
public:
|
||||
typedef Index IndexType;
|
||||
|
||||
/// Doesn't take ownership of @pIndex and @pTrieIterator. Modifies @categories.
|
||||
Engine(IndexType const * pIndex,
|
||||
CategoriesHolder & categories);
|
||||
~Engine();
|
||||
|
||||
void Search(string const & query,
|
||||
m2::RectD const & rect,
|
||||
function<void (Result const &)> const & f);
|
||||
|
||||
void StopEverything();
|
||||
|
||||
void OnQueryDelete(impl::Query *);
|
||||
|
||||
private:
|
||||
IndexType const * m_pIndex;
|
||||
SearchInfo * m_pSearchInfo;
|
||||
scoped_ptr<CategoriesHolder> m_pCategories;
|
||||
scoped_ptr<threads::IRunner> m_pRunner;
|
||||
threads::Mutex m_mutex;
|
||||
impl::Query * volatile m_pLastQuery;
|
||||
int volatile m_queriesActive;
|
||||
};
|
||||
|
||||
} // namespace search
|
176
search/feature_match.hpp
Normal file
176
search/feature_match.hpp
Normal file
|
@ -0,0 +1,176 @@
|
|||
#pragma once
|
||||
#include "../indexer/search_trie.hpp"
|
||||
#include "../base/string_utils.hpp"
|
||||
#include "../base/base.hpp"
|
||||
#include "../std/algorithm.hpp"
|
||||
#include "../std/bind.hpp"
|
||||
#include "../std/queue.hpp"
|
||||
#include "../std/scoped_ptr.hpp"
|
||||
#include "../std/unordered_map.hpp"
|
||||
#include "../std/utility.hpp"
|
||||
#include "../std/vector.hpp"
|
||||
|
||||
namespace search
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
template <class SrcIterT, class CompIterT>
|
||||
size_t CalcEqualLength(SrcIterT b, SrcIterT e, CompIterT bC, CompIterT eC)
|
||||
{
|
||||
size_t count = 0;
|
||||
while ((b != e) && (bC != eC) && (*b++ == *bC++))
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
TrieIterator * MoveTrieIteratorToString(TrieIterator const & trieRoot,
|
||||
strings::UniString const & queryS,
|
||||
size_t & symbolsMatched)
|
||||
{
|
||||
scoped_ptr<search::TrieIterator> pIter(trieRoot.Clone());
|
||||
symbolsMatched = 0;
|
||||
size_t const szQuery = queryS.size();
|
||||
while (symbolsMatched < szQuery)
|
||||
{
|
||||
bool bMatched = false;
|
||||
|
||||
for (size_t i = 0; i < pIter->m_edge.size(); ++i)
|
||||
{
|
||||
size_t const szEdge = pIter->m_edge[i].m_str.size();
|
||||
|
||||
size_t const count = CalcEqualLength(pIter->m_edge[i].m_str.begin(),
|
||||
pIter->m_edge[i].m_str.end(),
|
||||
queryS.begin() + symbolsMatched,
|
||||
queryS.end());
|
||||
|
||||
if ((count > 0) && (count == szEdge || szQuery == count + symbolsMatched))
|
||||
{
|
||||
scoped_ptr<search::TrieIterator>(pIter->GoToEdge(i)).swap(pIter);
|
||||
symbolsMatched += count;
|
||||
bMatched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bMatched)
|
||||
return NULL;
|
||||
}
|
||||
return pIter->Clone();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void FullMatchInTrie(TrieIterator const & trieRoot,
|
||||
strings::UniString const & s,
|
||||
F & f)
|
||||
{
|
||||
size_t symbolsMatched = 0;
|
||||
scoped_ptr<search::TrieIterator> pIter(MoveTrieIteratorToString(trieRoot, s, symbolsMatched));
|
||||
if (!pIter || symbolsMatched != s.size())
|
||||
return;
|
||||
for (size_t i = 0; i < pIter->m_value.size(); ++i)
|
||||
f(pIter->m_value[i].m_featureId, pIter->m_value[i].m_rank);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void PrefixMatchInTrie(TrieIterator const & trieRoot,
|
||||
strings::UniString const & s,
|
||||
F & f)
|
||||
{
|
||||
size_t symbolsMatched = 0;
|
||||
scoped_ptr<search::TrieIterator> pIter(MoveTrieIteratorToString(trieRoot, s, symbolsMatched));
|
||||
if (!pIter)
|
||||
return;
|
||||
|
||||
priority_queue<pair<uint8_t, search::TrieIterator *> > trieQueue;
|
||||
trieQueue.push(make_pair(uint8_t(-1), pIter->Clone()));
|
||||
|
||||
uint8_t maxRank = 0;
|
||||
for (size_t i = 0; i < pIter->m_edge.size(); ++i)
|
||||
maxRank = max(maxRank, pIter->m_edge[i].m_value);
|
||||
|
||||
int featuresStillToMatch = 100000;
|
||||
while (!trieQueue.empty() && (featuresStillToMatch > 0 || trieQueue.top().first == maxRank))
|
||||
{
|
||||
scoped_ptr<search::TrieIterator> pIter(trieQueue.top().second);
|
||||
trieQueue.pop();
|
||||
for (size_t i = 0; i < pIter->m_value.size(); ++i)
|
||||
f(pIter->m_value[i].m_featureId, pIter->m_value[i].m_rank);
|
||||
featuresStillToMatch -= pIter->m_value.size();
|
||||
for (size_t i = 0; i < pIter->m_edge.size(); ++i)
|
||||
trieQueue.push(make_pair(pIter->m_edge[i].m_value, pIter->GoToEdge(i)));
|
||||
}
|
||||
}
|
||||
|
||||
struct OffsetIntersecter
|
||||
{
|
||||
typedef unordered_map<uint32_t, uint16_t> MapType;
|
||||
|
||||
MapType m_prevMap;
|
||||
MapType m_map;
|
||||
bool m_bFirstStep;
|
||||
|
||||
void operator() (uint32_t offset, uint8_t rank)
|
||||
{
|
||||
uint16_t prevRankSum = 0;
|
||||
if (!m_bFirstStep)
|
||||
{
|
||||
MapType::const_iterator it = m_prevMap.find(offset);
|
||||
if (it == m_prevMap.end())
|
||||
return;
|
||||
prevRankSum = it->second;
|
||||
}
|
||||
|
||||
uint16_t & mappedRank = m_map[offset];
|
||||
mappedRank = max(mappedRank, static_cast<uint16_t>(prevRankSum + rank));
|
||||
}
|
||||
|
||||
void NextStep()
|
||||
{
|
||||
m_prevMap.swap(m_map);
|
||||
m_map.clear();
|
||||
m_bFirstStep = false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace search::impl
|
||||
|
||||
template <typename F>
|
||||
void MatchFeaturesInTrie(strings::UniString const * tokens, size_t tokenCount,
|
||||
strings::UniString const & prefix,
|
||||
TrieIterator const & trieRoot,
|
||||
F & f,
|
||||
size_t resultsNeeded)
|
||||
{
|
||||
impl::OffsetIntersecter intersecter;
|
||||
|
||||
// Match tokens.
|
||||
for (size_t i = 0; i < tokenCount; ++i)
|
||||
{
|
||||
impl::FullMatchInTrie(trieRoot, tokens[i], intersecter);
|
||||
intersecter.NextStep();
|
||||
}
|
||||
|
||||
// Match prefix.
|
||||
if (prefix.size() > 0)
|
||||
{
|
||||
impl::PrefixMatchInTrie(trieRoot, prefix, intersecter);
|
||||
intersecter.NextStep();
|
||||
}
|
||||
|
||||
typedef vector<pair<uint32_t, uint16_t> > ResType;
|
||||
ResType res(intersecter.m_prevMap.begin(), intersecter.m_prevMap.end());
|
||||
|
||||
if (res.size() > resultsNeeded)
|
||||
{
|
||||
partial_sort(res.begin(), res.begin() + resultsNeeded, res.end(),
|
||||
bind(&ResType::value_type::second, _1) > bind(&ResType::value_type::second, _2));
|
||||
res.resize(resultsNeeded);
|
||||
}
|
||||
|
||||
for (ResType::const_iterator it = res.begin(); it != res.end(); ++it)
|
||||
f(it->first);
|
||||
}
|
||||
|
||||
|
||||
} // namespace search
|
|
@ -15,7 +15,7 @@
|
|||
#include "../std/algorithm.hpp"
|
||||
#include "../std/scoped_ptr.hpp"
|
||||
|
||||
|
||||
/*
|
||||
namespace search
|
||||
{
|
||||
namespace impl
|
||||
|
@ -68,7 +68,7 @@ struct FeatureMatcher
|
|||
explicit FeatureMatcher(KeywordMatcher & keywordMatcher)
|
||||
: m_keywordMatcher(keywordMatcher), m_minScore(keywordMatcher.MAX_SCORE) {}
|
||||
|
||||
bool operator () (int /*lang*/, string const & name)
|
||||
bool operator () (int , string const & name)
|
||||
{
|
||||
uint32_t const score = m_keywordMatcher.Score(name);
|
||||
if (score < m_minScore)
|
||||
|
@ -131,16 +131,18 @@ struct FeatureProcessor
|
|||
} // unnamed namespace
|
||||
|
||||
Query::Query(string const & query, m2::RectD const & viewport, IndexType const * pIndex,
|
||||
Engine * pEngine, CategoriesHolder * pCategories,
|
||||
CategoriesHolder * pCategories,
|
||||
TrieIterator * pTrieRoot, FeaturesVector * pFeatures)
|
||||
: m_queryText(query), m_queryUniText(NormalizeAndSimplifyString(query)),
|
||||
m_viewport(viewport),
|
||||
m_pCategories(pCategories),
|
||||
m_pTrieRoot(pTrieRoot),
|
||||
m_pFeatures(pFeatures),
|
||||
m_pIndex(/*pIndex ? new IndexType(*pIndex) : */NULL),
|
||||
m_pIndex(
|
||||
// pIndex ? new IndexType(*pIndex) :
|
||||
NULL),
|
||||
m_resultsRemaining(10),
|
||||
m_pEngine(pEngine), m_bTerminate(false)
|
||||
m_bTerminate(false)
|
||||
{
|
||||
search::Delimiters delims;
|
||||
SplitUniString(m_queryUniText, MakeBackInsertFunctor(m_keywords), delims);
|
||||
|
@ -157,8 +159,6 @@ Query::Query(string const & query, m2::RectD const & viewport, IndexType const *
|
|||
|
||||
Query::~Query()
|
||||
{
|
||||
if (m_pEngine)
|
||||
m_pEngine->OnQueryDelete(this);
|
||||
}
|
||||
|
||||
void Query::Search(function<void (Result const &)> const & f)
|
||||
|
@ -289,19 +289,6 @@ void Query::Search(function<void (Result const &)> const & f)
|
|||
f(Result(string(), string())); // Send last search result marker.
|
||||
}
|
||||
|
||||
void Query::FlushResults(const function<void (const Result &)> &f)
|
||||
{
|
||||
vector<Result> results;
|
||||
results.reserve(m_results.size());
|
||||
while (!m_results.empty())
|
||||
{
|
||||
results.push_back(m_results.top().GenerateFinalResult());
|
||||
m_results.pop();
|
||||
}
|
||||
for (vector<Result>::const_reverse_iterator it = results.rbegin(); it != results.rend(); ++it)
|
||||
f(*it);
|
||||
m_resultsRemaining = max(0, m_resultsRemaining - static_cast<int>(results.size()));
|
||||
}
|
||||
|
||||
void Query::SearchAndDestroy(function<void (const Result &)> const & f)
|
||||
{
|
||||
|
@ -331,3 +318,4 @@ uint32_t Query::GetKeywordsToSkipForType(uint32_t const type) const
|
|||
|
||||
} // namespace search::impl
|
||||
} // namespace search
|
||||
*/
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "engine.hpp"
|
||||
/*
|
||||
|
||||
#include "search_engine.hpp"
|
||||
#include "intermediate_result.hpp"
|
||||
#include "keyword_matcher.hpp"
|
||||
#include "result.hpp"
|
||||
#include "../indexer/search_trie.hpp"
|
||||
#include "../geometry/rect2d.hpp"
|
||||
#include "../base/string_utils.hpp"
|
||||
#include "../std/function.hpp"
|
||||
|
@ -27,7 +30,7 @@ public:
|
|||
typedef Engine::IndexType IndexType;
|
||||
|
||||
Query(string const & query, m2::RectD const & viewport, IndexType const * pIndex,
|
||||
Engine * pEngine, CategoriesHolder * pCategories,
|
||||
CategoriesHolder * pCategories,
|
||||
TrieIterator * pTrieRoot, FeaturesVector * pFeatures);
|
||||
~Query();
|
||||
|
||||
|
@ -71,9 +74,9 @@ private:
|
|||
priority_queue<IntermediateResult> m_results;
|
||||
int m_resultsRemaining;
|
||||
|
||||
Engine * m_pEngine;
|
||||
bool volatile m_bTerminate;
|
||||
};
|
||||
|
||||
} // namespace search::impl
|
||||
} // namespace search
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,13 @@ Result::Result(string const & str, string const & suggestionStr)
|
|||
{
|
||||
}
|
||||
|
||||
Result Result::GetEndResult()
|
||||
{
|
||||
Result result("", "");
|
||||
ASSERT(result.IsEndMarker(), ());
|
||||
return result;
|
||||
}
|
||||
|
||||
Result::ResultType Result::GetResultType() const
|
||||
{
|
||||
if (!m_suggestionStr.empty())
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
double distanceFromCenter, double directionFromCenter);
|
||||
Result(string const & str, string const & suggestionStr);
|
||||
|
||||
static Result GetEndResult();
|
||||
bool IsEndMarker() const { return m_str.empty(); }
|
||||
|
||||
// String that is displayed in the GUI.
|
||||
|
|
|
@ -11,23 +11,40 @@ include($$ROOT_DIR/common.pri)
|
|||
|
||||
HEADERS += \
|
||||
search_common.hpp \
|
||||
engine.hpp \
|
||||
search_engine.hpp \
|
||||
intermediate_result.hpp \
|
||||
keyword_matcher.hpp \
|
||||
query.hpp \
|
||||
search_query.hpp \
|
||||
result.hpp \
|
||||
latlon_match.hpp \
|
||||
categories_holder.hpp \
|
||||
search_trie_matching.hpp \
|
||||
approximate_string_match.hpp \
|
||||
feature_match.hpp \
|
||||
|
||||
SOURCES += \
|
||||
engine.cpp \
|
||||
search_engine.cpp \
|
||||
intermediate_result.cpp \
|
||||
keyword_matcher.cpp \
|
||||
query.cpp \
|
||||
search_query.cpp \
|
||||
result.cpp \
|
||||
latlon_match.cpp \
|
||||
categories_holder.cpp \
|
||||
search_trie_matching.cpp \
|
||||
approximate_string_match.cpp \
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
35
search/search_engine.cpp
Normal file
35
search/search_engine.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "search_engine.hpp"
|
||||
#include "categories_holder.hpp"
|
||||
#include "result.hpp"
|
||||
#include "search_query.hpp"
|
||||
|
||||
#include "../base/logging.hpp"
|
||||
|
||||
#include "../std/function.hpp"
|
||||
#include "../std/string.hpp"
|
||||
#include "../std/vector.hpp"
|
||||
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
||||
Engine::Engine(IndexType const * pIndex, CategoriesHolder * pCategories)
|
||||
: m_pIndex(pIndex), m_pCategories(pCategories)
|
||||
{
|
||||
}
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
}
|
||||
|
||||
void Engine::Search(string const & queryText,
|
||||
m2::RectD const & viewport,
|
||||
function<void (Result const &)> const & f)
|
||||
{
|
||||
LOG(LDEBUG, (queryText, viewport));
|
||||
search::Query query(m_pIndex, m_pCategories.get());
|
||||
query.Search(queryText, viewport, f);
|
||||
f(Result::GetEndResult());
|
||||
}
|
||||
|
||||
} // namespace search
|
39
search/search_engine.hpp
Normal file
39
search/search_engine.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "../indexer/index.hpp"
|
||||
|
||||
#include "../geometry/rect2d.hpp"
|
||||
|
||||
#include "../base/base.hpp"
|
||||
|
||||
#include "../std/function.hpp"
|
||||
#include "../std/scoped_ptr.hpp"
|
||||
#include "../std/string.hpp"
|
||||
|
||||
class Index;
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
||||
class CategoriesHolder;
|
||||
class Result;
|
||||
|
||||
class Engine
|
||||
{
|
||||
public:
|
||||
typedef Index IndexType;
|
||||
|
||||
// Doesn't take ownership of @pIndex. Takes ownership of pCategories
|
||||
Engine(IndexType const * pIndex, CategoriesHolder * pCategories);
|
||||
~Engine();
|
||||
|
||||
void Search(string const & query,
|
||||
m2::RectD const & rect,
|
||||
function<void (Result const &)> const & f);
|
||||
|
||||
private:
|
||||
Index const * m_pIndex;
|
||||
scoped_ptr<CategoriesHolder> m_pCategories;
|
||||
};
|
||||
|
||||
} // namespace search
|
140
search/search_query.cpp
Normal file
140
search/search_query.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include "search_query.hpp"
|
||||
#include "categories_holder.hpp"
|
||||
#include "feature_match.hpp"
|
||||
#include "latlon_match.hpp"
|
||||
#include "result.hpp"
|
||||
#include "../indexer/features_vector.hpp"
|
||||
#include "../indexer/index.hpp"
|
||||
#include "../indexer/search_delimiters.hpp"
|
||||
#include "../indexer/search_string_utils.hpp"
|
||||
#include "../base/logging.hpp"
|
||||
#include "../base/string_utils.hpp"
|
||||
#include "../base/stl_add.hpp"
|
||||
#include "../std/algorithm.hpp"
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
||||
Query::Query(Index const * pIndex, search::CategoriesHolder const * pCategories)
|
||||
: m_pIndex(pIndex), m_pCategories(pCategories)
|
||||
{
|
||||
}
|
||||
|
||||
Query::~Query()
|
||||
{}
|
||||
|
||||
void Query::Search(string const & query,
|
||||
m2::RectD const & viewport,
|
||||
function<void (Result const &)> const & f,
|
||||
unsigned int resultsNeeded)
|
||||
{
|
||||
// Initialize.
|
||||
{
|
||||
m_rawQuery = query;
|
||||
m_viewport = viewport;
|
||||
|
||||
m_uniQuery = strings::MakeUniString(m_rawQuery);
|
||||
|
||||
search::Delimiters delims;
|
||||
SplitUniString(m_uniQuery, MakeBackInsertFunctor(m_tokens), delims);
|
||||
if (!m_tokens.empty() && !delims(strings::LastUniChar(m_rawQuery)))
|
||||
{
|
||||
m_prefix.swap(m_tokens.back());
|
||||
m_tokens.pop_back();
|
||||
}
|
||||
if (m_tokens.size() > 31)
|
||||
m_tokens.resize(31);
|
||||
|
||||
m_results = my::limited_priority_queue<impl::IntermediateResult>(resultsNeeded);
|
||||
}
|
||||
|
||||
// Match (lat, lon).
|
||||
{
|
||||
double lat, lon, latPrec, lonPrec;
|
||||
if (search::MatchLatLon(m_rawQuery, lat, lon, latPrec, lonPrec))
|
||||
{
|
||||
double const precision = 5.0 * max(0.0001, min(latPrec, lonPrec)); // Min 55 meters
|
||||
AddResult(impl::IntermediateResult(m_viewport, lat, lon, precision));
|
||||
}
|
||||
}
|
||||
|
||||
SearchFeatures();
|
||||
|
||||
FlushResults(f);
|
||||
}
|
||||
|
||||
void Query::AddResult(impl::IntermediateResult const & result)
|
||||
{
|
||||
m_results.push(result);
|
||||
}
|
||||
|
||||
void Query::FlushResults(function<void (Result const &)> const & f)
|
||||
{
|
||||
vector<impl::IntermediateResult> v(m_results.begin(), m_results.end());
|
||||
sort_heap(v.begin(), v.end());
|
||||
for (vector<impl::IntermediateResult>::const_iterator it = v.begin(); it != v.end(); ++it)
|
||||
f(it->GenerateFinalResult());
|
||||
}
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
struct FeatureLoader
|
||||
{
|
||||
uint32_t m_count;
|
||||
FeaturesVector & m_featuresVector;
|
||||
Query & m_query;
|
||||
|
||||
FeatureLoader(FeaturesVector & featuresVector, Query & query)
|
||||
: m_count(0), m_featuresVector(featuresVector), m_query(query)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() (uint32_t offset)
|
||||
{
|
||||
++m_count;
|
||||
FeatureType feature;
|
||||
m_featuresVector.Get(offset, feature);
|
||||
m_query.AddResult(impl::IntermediateResult(m_query.m_viewport, feature,
|
||||
feature.GetPreferredDrawableName(), 0, 0));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace search::impl
|
||||
|
||||
void Query::SearchFeatures()
|
||||
{
|
||||
if (!m_pIndex)
|
||||
return;
|
||||
|
||||
vector<MwmInfo> mwmInfo;
|
||||
m_pIndex->GetMwmInfo(mwmInfo);
|
||||
|
||||
m2::RectD extendedViewport = m_viewport;
|
||||
extendedViewport.Scale(3);
|
||||
|
||||
for (MwmSet::MwmId mwmId = 0; mwmId < mwmInfo.size(); ++mwmId)
|
||||
{
|
||||
// Search only mwms that intersect with viewport (world always does).
|
||||
if (extendedViewport.IsIntersect(mwmInfo[mwmId].m_limitRect))
|
||||
{
|
||||
Index::MwmLock mwmLock(*m_pIndex, mwmId);
|
||||
if (MwmValue * pMwmValue = mwmLock.GetValue())
|
||||
{
|
||||
scoped_ptr<TrieIterator> pTrieRoot(::trie::reader::ReadTrie(
|
||||
pMwmValue->m_cont.GetReader(SEARCH_INDEX_FILE_TAG),
|
||||
::search::trie::ValueReader(),
|
||||
::search::trie::EdgeValueReader()));
|
||||
if (pTrieRoot)
|
||||
{
|
||||
FeaturesVector featuresVector(pMwmValue->m_cont, pMwmValue->GetHeader());
|
||||
impl::FeatureLoader f(featuresVector, *this);
|
||||
MatchFeaturesInTrie(m_tokens.data(), m_tokens.size(), m_prefix, *pTrieRoot, f, 10);
|
||||
LOG(LINFO, ("Matched: ", f.m_count));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace search
|
48
search/search_query.hpp
Normal file
48
search/search_query.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include "intermediate_result.hpp"
|
||||
#include "../geometry/rect2d.hpp"
|
||||
#include "../base/buffer_vector.hpp"
|
||||
#include "../base/limited_priority_queue.hpp"
|
||||
#include "../std/function.hpp"
|
||||
#include "../std/string.hpp"
|
||||
|
||||
class Index;
|
||||
|
||||
namespace search
|
||||
{
|
||||
|
||||
class CategoriesHolder;
|
||||
namespace impl { class IntermediateResult; class FeatureLoader; }
|
||||
|
||||
class Query
|
||||
{
|
||||
public:
|
||||
Query(Index const * pIndex, CategoriesHolder const * pCategories);
|
||||
~Query();
|
||||
|
||||
void Search(string const & query,
|
||||
m2::RectD const & viewport,
|
||||
function<void (Result const &)> const & f,
|
||||
unsigned int resultsNeeded = 10);
|
||||
|
||||
private:
|
||||
|
||||
friend class impl::FeatureLoader;
|
||||
|
||||
void AddResult(impl::IntermediateResult const & result);
|
||||
void FlushResults(function<void (Result const &)> const & f);
|
||||
void SearchFeatures();
|
||||
|
||||
Index const * m_pIndex;
|
||||
CategoriesHolder const * m_pCategories;
|
||||
|
||||
string m_rawQuery;
|
||||
strings::UniString m_uniQuery;
|
||||
buffer_vector<strings::UniString, 32> m_tokens;
|
||||
strings::UniString m_prefix;
|
||||
m2::RectD m_viewport;
|
||||
|
||||
my::limited_priority_queue<impl::IntermediateResult> m_results;
|
||||
};
|
||||
|
||||
} // namespace search
|
|
@ -3,7 +3,7 @@
|
|||
#include "../../base/string_utils.hpp"
|
||||
#include "../../std/memcpy.hpp"
|
||||
#include "../../std/string.hpp"
|
||||
|
||||
/*
|
||||
using search::impl::Query;
|
||||
using strings::MakeUniString;
|
||||
using strings::UniString;
|
||||
|
@ -13,16 +13,17 @@ UNIT_TEST(QueryParseKeywords_Smoke)
|
|||
vector<UniString> expected;
|
||||
expected.push_back(MakeUniString("minsk"));
|
||||
expected.push_back(MakeUniString("belarus"));
|
||||
TEST_EQUAL(expected, Query("minsk belarus ", m2::RectD(), 0, 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString(""), Query("minsk belarus ", m2::RectD(), 0, 0, 0, 0, 0).GetPrefix(), ());
|
||||
TEST_EQUAL(expected, Query("minsk belarus ma", m2::RectD(), 0, 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString("ma"), Query("minsk belarus ma", m2::RectD(), 0, 0, 0, 0, 0).GetPrefix(), ());
|
||||
TEST_EQUAL(expected, Query("minsk belarus ", m2::RectD(), 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString(""), Query("minsk belarus ", m2::RectD(), 0, 0, 0, 0).GetPrefix(), ());
|
||||
TEST_EQUAL(expected, Query("minsk belarus ma", m2::RectD(), 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString("ma"), Query("minsk belarus ma", m2::RectD(), 0, 0, 0, 0).GetPrefix(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(QueryParseKeywords_Empty)
|
||||
{
|
||||
TEST_EQUAL(vector<UniString>(), Query("", m2::RectD(), 0, 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString(""), Query("", m2::RectD(), 0, 0, 0, 0, 0).GetPrefix(), ());
|
||||
TEST_EQUAL(vector<UniString>(), Query("Z", m2::RectD(), 0, 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString("z"), Query("Z", m2::RectD(), 0, 0, 0, 0, 0).GetPrefix(), ());
|
||||
TEST_EQUAL(vector<UniString>(), Query("", m2::RectD(), 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString(""), Query("", m2::RectD(), 0, 0, 0, 0).GetPrefix(), ());
|
||||
TEST_EQUAL(vector<UniString>(), Query("Z", m2::RectD(), 0, 0, 0, 0).GetKeywords(), ());
|
||||
TEST_EQUAL(MakeUniString("z"), Query("Z", m2::RectD(), 0, 0, 0, 0).GetPrefix(), ());
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "../std/utility.hpp"
|
||||
#include "../std/vector.hpp"
|
||||
|
||||
|
||||
/*
|
||||
namespace
|
||||
{
|
||||
template <class SrcIterT, class CompIterT>
|
||||
|
@ -120,3 +120,4 @@ void search::MatchAgainstTrie(search::impl::Query & query, search::TrieIterator
|
|||
featureQueue.pop();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue