forked from organicmaps/organicmaps
[search] Fixed equal street results filtering.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
This commit is contained in:
parent
cd5450b192
commit
e15985307f
14 changed files with 331 additions and 204 deletions
|
@ -244,20 +244,27 @@ string TestVillage::ToDebugString() const
|
|||
|
||||
// TestStreet --------------------------------------------------------------------------------------
|
||||
TestStreet::TestStreet(vector<m2::PointD> const & points, string const & name, string const & lang)
|
||||
: TestFeature(name, lang), m_points(points), m_highwayType("living_street")
|
||||
: TestFeature(name, lang), m_points(points)
|
||||
{
|
||||
SetType({ "highway", "living_street" });
|
||||
}
|
||||
|
||||
TestStreet::TestStreet(vector<m2::PointD> const & points, StringUtf8Multilang const & name)
|
||||
: TestFeature(name), m_points(points), m_highwayType("living_street")
|
||||
: TestFeature(name), m_points(points)
|
||||
{
|
||||
SetType({ "highway", "living_street" });
|
||||
}
|
||||
|
||||
void TestStreet::SetType(base::StringIL const & e)
|
||||
{
|
||||
m_highwayType = classif().GetTypeByPath(e);
|
||||
}
|
||||
|
||||
void TestStreet::Serialize(FeatureBuilder & fb) const
|
||||
{
|
||||
TestFeature::Serialize(fb);
|
||||
|
||||
fb.SetType(classif().GetTypeByPath({string_view("highway"), m_highwayType}));
|
||||
fb.SetType(m_highwayType);
|
||||
|
||||
fb.GetParams().ref = m_roadNumber;
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ public:
|
|||
TestStreet(std::vector<m2::PointD> const & points, std::string const & name, std::string const & lang);
|
||||
TestStreet(std::vector<m2::PointD> const & points, StringUtf8Multilang const & name);
|
||||
|
||||
void SetHighwayType(std::string const & type) { m_highwayType = type; }
|
||||
void SetType(base::StringIL const & e);
|
||||
void SetRoadNumber(std::string const & roadNumber) { m_roadNumber = roadNumber; }
|
||||
|
||||
// TestFeature overrides:
|
||||
|
@ -141,7 +141,7 @@ public:
|
|||
|
||||
private:
|
||||
std::vector<m2::PointD> m_points;
|
||||
std::string m_highwayType;
|
||||
uint32_t m_highwayType;
|
||||
std::string m_roadNumber;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "base/assert.hpp"
|
||||
#include "base/buffer_vector.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
@ -332,32 +331,46 @@ SuburbType IsSuburbChecker::GetType(FeatureType & f) const
|
|||
|
||||
IsWayChecker::IsWayChecker()
|
||||
{
|
||||
// TODO (@y, @m, @vng): this list must be up-to-date with
|
||||
// data/categories.txt, so, it's worth it to generate or parse it
|
||||
// from that file.
|
||||
Classificator const & c = classif();
|
||||
base::StringIL const types[] = { {"highway", "living_street"},
|
||||
{"highway", "footway"},
|
||||
{"highway", "cycleway"},
|
||||
{"highway", "motorway"},
|
||||
{"highway", "motorway_link"},
|
||||
{"highway", "path"},
|
||||
{"highway", "pedestrian"},
|
||||
{"highway", "primary"},
|
||||
{"highway", "primary_link"},
|
||||
{"highway", "residential"},
|
||||
{"highway", "road"},
|
||||
{"highway", "secondary"},
|
||||
{"highway", "secondary_link"},
|
||||
{"highway", "service"},
|
||||
{"highway", "tertiary"},
|
||||
{"highway", "tertiary_link"},
|
||||
{"highway", "track"},
|
||||
{"highway", "trunk"},
|
||||
{"highway", "trunk_link"},
|
||||
{"highway", "unclassified"}};
|
||||
std::pair<char const *, SearchRank> const types[] = {
|
||||
// type rank
|
||||
{"cycleway", Cycleway},
|
||||
{"footway", Pedestrian},
|
||||
{"living_street", Residential},
|
||||
{"motorway", Motorway},
|
||||
{"motorway_link", Motorway},
|
||||
{"path", Outdoor},
|
||||
{"pedestrian", Pedestrian},
|
||||
{"primary", Regular},
|
||||
{"primary_link", Regular},
|
||||
{"residential", Residential},
|
||||
{"road", Outdoor},
|
||||
{"secondary", Regular},
|
||||
{"secondary_link",Regular},
|
||||
{"service", Residential},
|
||||
{"tertiary", Regular},
|
||||
{"tertiary_link", Regular},
|
||||
{"track", Outdoor},
|
||||
{"trunk", Motorway},
|
||||
{"trunk_link", Motorway},
|
||||
{"unclassified", Outdoor},
|
||||
};
|
||||
|
||||
m_ranks.Reserve(std::size(types));
|
||||
for (auto const & e : types)
|
||||
m_types.push_back(c.GetTypeByPath(e));
|
||||
{
|
||||
uint32_t const type = c.GetTypeByPath({"highway", e.first});
|
||||
m_types.push_back(type);
|
||||
m_ranks.Insert(type, e.second);
|
||||
}
|
||||
}
|
||||
|
||||
IsWayChecker::SearchRank IsWayChecker::GetSearchRank(uint32_t type) const
|
||||
{
|
||||
ftype::TruncValue(type, 2);
|
||||
if (auto const * res = m_ranks.Find(type))
|
||||
return *res;
|
||||
return Default;
|
||||
}
|
||||
|
||||
IsStreetOrSquareChecker::IsStreetOrSquareChecker()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "indexer/feature_data.hpp"
|
||||
|
||||
#include "base/base.hpp"
|
||||
#include "base/small_map.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <array>
|
||||
|
@ -207,12 +207,33 @@ public:
|
|||
DECLARE_CHECKER_INSTANCE(IsSuburbChecker);
|
||||
};
|
||||
|
||||
/// @todo Better to rename like IsStreetChecker, as it is used in search context only?
|
||||
class IsWayChecker : public BaseChecker
|
||||
{
|
||||
IsWayChecker();
|
||||
|
||||
public:
|
||||
DECLARE_CHECKER_INSTANCE(IsWayChecker);
|
||||
|
||||
enum SearchRank : uint8_t
|
||||
{
|
||||
Default = 0, // Not a road, other linear way like river, rail ...
|
||||
|
||||
// Bigger is better (more important).
|
||||
Pedestrian,
|
||||
Cycleway,
|
||||
Outdoor,
|
||||
Residential,
|
||||
Regular,
|
||||
Motorway,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
SearchRank GetSearchRank(uint32_t type) const;
|
||||
|
||||
private:
|
||||
base::SmallMap<uint32_t, SearchRank> m_ranks;
|
||||
};
|
||||
|
||||
class IsStreetOrSquareChecker : public BaseChecker
|
||||
|
|
|
@ -1,29 +1,21 @@
|
|||
#include "search/intermediate_result.hpp"
|
||||
|
||||
#include "search/geometry_utils.hpp"
|
||||
#include "search/reverse_geocoder.hpp"
|
||||
|
||||
#include "storage/country_info_getter.hpp"
|
||||
|
||||
#include "indexer/categories_holder.hpp"
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/cuisines.hpp"
|
||||
#include "indexer/feature.hpp"
|
||||
#include "indexer/feature_algo.hpp"
|
||||
#include "indexer/feature_utils.hpp"
|
||||
#include "indexer/ftypes_matcher.hpp"
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "platform/measurement_utils.hpp"
|
||||
|
||||
#include "geometry/angles.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "3party/opening_hours/opening_hours.hpp"
|
||||
|
||||
|
@ -136,9 +128,9 @@ bool PreRankerResult::CategoriesComparator::operator()(PreRankerResult const & l
|
|||
// RankerResult ------------------------------------------------------------------------------------
|
||||
RankerResult::RankerResult(FeatureType & f, m2::PointD const & center, m2::PointD const & pivot,
|
||||
string displayName, string const & fileName)
|
||||
: m_id(f.GetID())
|
||||
, m_types(f)
|
||||
: m_types(f)
|
||||
, m_str(std::move(displayName))
|
||||
, m_id(f.GetID())
|
||||
, m_resultType(ftypes::IsBuildingChecker::Instance()(m_types) ? Type::Building : Type::Feature)
|
||||
, m_geomType(f.GetGeomType())
|
||||
{
|
||||
|
@ -148,7 +140,6 @@ RankerResult::RankerResult(FeatureType & f, m2::PointD const & center, m2::Point
|
|||
m_types.SortBySpec();
|
||||
|
||||
m_region.SetParams(fileName, center);
|
||||
m_distance = PointDistance(center, pivot);
|
||||
|
||||
FillDetails(f, m_details);
|
||||
}
|
||||
|
@ -180,30 +171,28 @@ bool RankerResult::GetCountryId(storage::CountryInfoGetter const & infoGetter, u
|
|||
return m_region.GetCountryId(infoGetter, countryId);
|
||||
}
|
||||
|
||||
bool RankerResult::IsEqualBasic(RankerResult const & r) const
|
||||
{
|
||||
return (m_geomType == r.m_geomType &&
|
||||
GetRankingInfo().m_type == r.GetRankingInfo().m_type &&
|
||||
m_str == r.m_str);
|
||||
}
|
||||
|
||||
bool RankerResult::IsEqualCommon(RankerResult const & r) const
|
||||
{
|
||||
if ((m_geomType != r.m_geomType) || (m_str != r.m_str))
|
||||
return false;
|
||||
|
||||
auto const bestType = GetBestType();
|
||||
auto const rBestType = r.GetBestType();
|
||||
if (bestType == rBestType)
|
||||
return true;
|
||||
|
||||
auto const & checker = ftypes::IsWayChecker::Instance();
|
||||
return checker(bestType) && checker(rBestType);
|
||||
return (IsEqualBasic(r) && GetBestType() == r.GetBestType());
|
||||
}
|
||||
|
||||
bool RankerResult::IsStreet() const { return ftypes::IsStreetOrSquareChecker::Instance()(m_types); }
|
||||
|
||||
uint32_t RankerResult::GetBestType(vector<uint32_t> const & preferredTypes) const
|
||||
uint32_t RankerResult::GetBestType(vector<uint32_t> const * preferredTypes /* = nullptr */) const
|
||||
{
|
||||
ASSERT(is_sorted(preferredTypes.begin(), preferredTypes.end()), ());
|
||||
if (!preferredTypes.empty())
|
||||
if (preferredTypes)
|
||||
{
|
||||
ASSERT(is_sorted(preferredTypes->begin(), preferredTypes->end()), ());
|
||||
for (uint32_t type : m_types)
|
||||
{
|
||||
if (binary_search(preferredTypes.begin(), preferredTypes.end(), type))
|
||||
if (binary_search(preferredTypes->begin(), preferredTypes->end(), type))
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "search/pre_ranking_info.hpp"
|
||||
#include "search/ranking_info.hpp"
|
||||
#include "search/ranking_utils.hpp"
|
||||
#include "search/result.hpp"
|
||||
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
@ -88,9 +87,9 @@ private:
|
|||
class RankerResult
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
LatLon,
|
||||
LatLon = 0,
|
||||
Feature,
|
||||
Building, //!< Buildings are not filtered out in duplicates filter.
|
||||
Postcode
|
||||
|
@ -119,9 +118,8 @@ public:
|
|||
FeatureID const & GetID() const { return m_id; }
|
||||
std::string const & GetName() const { return m_str; }
|
||||
feature::TypesHolder const & GetTypes() const { return m_types; }
|
||||
Type const & GetResultType() const { return m_resultType; }
|
||||
Type GetResultType() const { return m_resultType; }
|
||||
m2::PointD GetCenter() const { return m_region.m_point; }
|
||||
double GetDistance() const { return m_distance; }
|
||||
feature::GeomType GetGeomType() const { return m_geomType; }
|
||||
Result::Details GetDetails() const { return m_details; }
|
||||
|
||||
|
@ -131,9 +129,10 @@ public:
|
|||
bool GetCountryId(storage::CountryInfoGetter const & infoGetter, uint32_t ftype,
|
||||
storage::CountryId & countryId) const;
|
||||
|
||||
bool IsEqualBasic(RankerResult const & r) const;
|
||||
bool IsEqualCommon(RankerResult const & r) const;
|
||||
|
||||
uint32_t GetBestType(std::vector<uint32_t> const & preferredTypes = {}) const;
|
||||
uint32_t GetBestType(std::vector<uint32_t> const * preferredTypes = nullptr) const;
|
||||
|
||||
#ifdef SEARCH_USE_PROVENANCE
|
||||
std::vector<ResultTracer::Branch> const & GetProvenance() const { return m_provenance; }
|
||||
|
@ -161,15 +160,15 @@ private:
|
|||
};
|
||||
|
||||
RegionInfo m_region;
|
||||
FeatureID m_id;
|
||||
feature::TypesHolder m_types;
|
||||
std::string m_str;
|
||||
double m_distance = 0.0;
|
||||
Type m_resultType;
|
||||
RankingInfo m_info = {};
|
||||
feature::GeomType m_geomType = feature::GeomType::Undefined;
|
||||
Result::Details m_details;
|
||||
|
||||
FeatureID m_id;
|
||||
Type m_resultType;
|
||||
feature::GeomType m_geomType = feature::GeomType::Undefined;
|
||||
|
||||
#ifdef SEARCH_USE_PROVENANCE
|
||||
// The call path in the Geocoder that leads to this result.
|
||||
std::vector<ResultTracer::Branch> m_provenance;
|
||||
|
|
|
@ -25,12 +25,12 @@ class Model
|
|||
{
|
||||
public:
|
||||
// WARNING: after modifications to the enum below, re-check all methods in the class.
|
||||
enum Type
|
||||
enum Type : uint8_t
|
||||
{
|
||||
// Low-level features such as amenities, offices, shops, buildings without house number, etc.
|
||||
// Can be stand-alone or located inside COMPLEX_POIs. E.g. cafes/shops inside
|
||||
// airports/universities/museums.
|
||||
TYPE_SUBPOI,
|
||||
TYPE_SUBPOI = 0,
|
||||
|
||||
// Big pois which can contain SUBPOIs. E.g. airports, train stations, malls, parks.
|
||||
TYPE_COMPLEX_POI,
|
||||
|
|
|
@ -193,33 +193,61 @@ void RemoveDuplicatingLinear(vector<RankerResult> & results)
|
|||
{
|
||||
double constexpr kDistSameStreetMeters = 5000.0;
|
||||
|
||||
auto lessCmp = [](RankerResult const & r1, RankerResult const & r2)
|
||||
auto const lessCmp = [](RankerResult const & r1, RankerResult const & r2)
|
||||
{
|
||||
if (r1.GetGeomType() != r2.GetGeomType())
|
||||
return r1.GetGeomType() < r2.GetGeomType();
|
||||
|
||||
auto const & ri1 = r1.GetRankingInfo();
|
||||
auto const & ri2 = r2.GetRankingInfo();
|
||||
|
||||
if (ri1.m_type != ri2.m_type)
|
||||
return ri1.m_type < ri2.m_type;
|
||||
|
||||
if (r1.GetName() != r2.GetName())
|
||||
return r1.GetName() < r2.GetName();
|
||||
|
||||
uint32_t const t1 = r1.GetBestType();
|
||||
uint32_t const t2 = r2.GetBestType();
|
||||
if (t1 != t2)
|
||||
return t1 < t2;
|
||||
if (ri1.m_type == Model::TYPE_STREET)
|
||||
{
|
||||
if (ri1.m_classifType.street != ri2.m_classifType.street)
|
||||
return ri1.m_classifType.street < ri2.m_classifType.street;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t const t1 = r1.GetBestType();
|
||||
uint32_t const t2 = r2.GetBestType();
|
||||
if (t1 != t2)
|
||||
return t1 < t2;
|
||||
}
|
||||
|
||||
// After unique, the better feature should be kept.
|
||||
return r1.GetLinearModelRank() > r2.GetLinearModelRank();
|
||||
};
|
||||
|
||||
auto equalCmp = [](RankerResult const & r1, RankerResult const & r2)
|
||||
auto const equalCmp = [](RankerResult const & r1, RankerResult const & r2)
|
||||
{
|
||||
if (r1.GetGeomType() != feature::GeomType::Line || !r1.IsEqualBasic(r2))
|
||||
return false;
|
||||
|
||||
auto const & ri1 = r1.GetRankingInfo();
|
||||
auto const & ri2 = r2.GetRankingInfo();
|
||||
if (ri1.m_type == Model::TYPE_STREET)
|
||||
{
|
||||
if (ri1.m_classifType.street != ri2.m_classifType.street)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r1.GetBestType() != r2.GetBestType())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note! Do compare for distance when filtering linear objects.
|
||||
// Otherwise we will skip the results for different parts of the map.
|
||||
return r1.GetGeomType() == feature::GeomType::Line && r1.IsEqualCommon(r2) &&
|
||||
PointDistance(r1.GetCenter(), r2.GetCenter()) < kDistSameStreetMeters;
|
||||
return PointDistance(r1.GetCenter(), r2.GetCenter()) < kDistSameStreetMeters;
|
||||
};
|
||||
|
||||
sort(results.begin(), results.end(), lessCmp);
|
||||
results.erase(unique(results.begin(), results.end(), equalCmp), results.end());
|
||||
base::SortUnique(results, lessCmp, equalCmp);
|
||||
}
|
||||
|
||||
ftypes::LocalityType GetLocalityIndex(feature::TypesHolder const & types)
|
||||
|
@ -348,6 +376,8 @@ public:
|
|||
|
||||
search::RankingInfo info;
|
||||
InitRankingInfo(*ft, center, preRankerResult, info);
|
||||
if (info.m_type == Model::TYPE_STREET)
|
||||
info.m_classifType.street = ftypes::IsWayChecker::Instance().GetSearchRank(r.GetBestType());
|
||||
info.m_rank = NormalizeRank(info.m_rank, info.m_type, center, country,
|
||||
ftypes::IsCapitalChecker::Instance()(*ft), !info.m_allTokensUsed);
|
||||
r.SetRankingInfo(info);
|
||||
|
@ -441,7 +471,7 @@ private:
|
|||
info.m_popularity = preInfo.m_popularity;
|
||||
info.m_type = preInfo.m_type;
|
||||
if (Model::IsPoi(info.m_type))
|
||||
info.m_resultType = GetResultType(feature::TypesHolder(ft));
|
||||
info.m_classifType.poi = GetPoiType(feature::TypesHolder(ft));
|
||||
info.m_allTokensUsed = preInfo.m_allTokensUsed;
|
||||
info.m_numTokens = m_params.GetNumTokens();
|
||||
info.m_exactMatch = preInfo.m_exactMatch;
|
||||
|
@ -534,9 +564,7 @@ private:
|
|||
info.m_nameScore = nameScore;
|
||||
info.m_errorsMade = errorsMade;
|
||||
info.m_isAltOrOldName = isAltOrOldName;
|
||||
info.m_matchedFraction =
|
||||
totalLength == 0 ? 1.0
|
||||
: static_cast<double>(matchedLength) / static_cast<double>(totalLength);
|
||||
info.m_matchedFraction = (totalLength == 0) ? 1 : matchedLength / static_cast<float>(totalLength);
|
||||
|
||||
auto const isCountryOrCapital = [](FeatureType & ft) {
|
||||
auto static const countryType = classif().GetTypeByPath({"place", "country"});
|
||||
|
@ -660,7 +688,7 @@ Result Ranker::MakeResult(RankerResult rankerResult, bool needAddress, bool need
|
|||
case RankerResult::Type::Feature:
|
||||
case RankerResult::Type::Building:
|
||||
{
|
||||
auto const type = rankerResult.GetBestType(m_params.m_preferredTypes);
|
||||
auto const type = rankerResult.GetBestType(&m_params.m_preferredTypes);
|
||||
return Result(r.GetID(), r.GetCenter(), move(name), move(address), type, move(r.m_details));
|
||||
}
|
||||
case RankerResult::Type::LatLon: return Result(r.GetCenter(), move(name), move(address));
|
||||
|
@ -923,7 +951,7 @@ void Ranker::ProcessSuggestions(vector<RankerResult> const & vec) const
|
|||
|
||||
string Ranker::GetLocalizedRegionInfoForResult(RankerResult const & result) const
|
||||
{
|
||||
auto const type = result.GetBestType(m_params.m_preferredTypes);
|
||||
auto const type = result.GetBestType(&m_params.m_preferredTypes);
|
||||
|
||||
storage::CountryId id;
|
||||
if (!result.GetCountryId(m_infoGetter, type, id))
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace search
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
// See search/search_quality/scoring_model.py for details. In short,
|
||||
|
@ -50,7 +50,7 @@ double constexpr kType[Model::TYPE_COUNT] = {
|
|||
0 /* SUBPOI */,
|
||||
0 /* COMPLEX_POI */,
|
||||
0 /* Building */,
|
||||
0.005 /* Street */,
|
||||
0 /* Street */,
|
||||
0 /* Unclassified */,
|
||||
-0.0725383 /* Village */,
|
||||
0.0073583 /* City */,
|
||||
|
@ -59,7 +59,7 @@ double constexpr kType[Model::TYPE_COUNT] = {
|
|||
};
|
||||
|
||||
// 0-based factors from General.
|
||||
double constexpr kResultType[base::Underlying(ResultType::Count)] = {
|
||||
double constexpr kPoiType[base::Underlying(PoiType::Count)] = {
|
||||
0.0338794 /* TransportMajor */,
|
||||
0.01 /* TransportLocal */,
|
||||
0.01 /* Eat */,
|
||||
|
@ -69,6 +69,16 @@ double constexpr kResultType[base::Underlying(ResultType::Count)] = {
|
|||
0 /* General */
|
||||
};
|
||||
|
||||
double constexpr kStreetType[base::Underlying(StreetType::Count)] = {
|
||||
0 /* Default */,
|
||||
0 /* Pedestrian */,
|
||||
0 /* Cycleway */,
|
||||
0 /* Outdoor */,
|
||||
0.004 /* Residential */,
|
||||
0.005 /* Regular */,
|
||||
0.006 /* Motorway */,
|
||||
};
|
||||
|
||||
// Coeffs sanity checks.
|
||||
static_assert(kHasName >= 0, "");
|
||||
static_assert(kCategoriesPopularity >= 0, "");
|
||||
|
@ -138,9 +148,6 @@ private:
|
|||
};
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
double const RankingInfo::kMaxDistMeters = 2e6;
|
||||
|
||||
// static
|
||||
void RankingInfo::PrintCSVHeader(ostream & os)
|
||||
{
|
||||
|
@ -164,26 +171,33 @@ void RankingInfo::PrintCSVHeader(ostream & os)
|
|||
string DebugPrint(RankingInfo const & info)
|
||||
{
|
||||
ostringstream os;
|
||||
os << boolalpha;
|
||||
os << boolalpha << "RankingInfo { ";
|
||||
PrintParse(os, info.m_tokenRanges, info.m_numTokens);
|
||||
os << ", RankingInfo [";
|
||||
os << "m_distanceToPivot:" << info.m_distanceToPivot;
|
||||
os << ", m_rank:" << static_cast<int>(info.m_rank);
|
||||
os << ", m_popularity:" << static_cast<int>(info.m_popularity);
|
||||
os << ", m_nameScore:" << DebugPrint(info.m_nameScore);
|
||||
os << ", m_errorsMade:" << DebugPrint(info.m_errorsMade);
|
||||
os << ", m_isAltOrOldName: " << info.m_isAltOrOldName;
|
||||
os << ", m_numTokens:" << info.m_numTokens;
|
||||
os << ", m_matchedFraction:" << info.m_matchedFraction;
|
||||
os << ", m_type:" << DebugPrint(info.m_type);
|
||||
os << ", m_resultType:" << DebugPrint(info.m_resultType);
|
||||
os << ", m_pureCats:" << info.m_pureCats;
|
||||
os << ", m_falseCats:" << info.m_falseCats;
|
||||
os << ", m_allTokensUsed:" << info.m_allTokensUsed;
|
||||
os << ", m_exactCountryOrCapital:" << info.m_exactCountryOrCapital;
|
||||
os << ", m_categorialRequest:" << info.m_categorialRequest;
|
||||
os << ", m_hasName:" << info.m_hasName;
|
||||
os << "]";
|
||||
|
||||
os << ", m_distanceToPivot: " << info.m_distanceToPivot
|
||||
<< ", m_rank: " << static_cast<int>(info.m_rank)
|
||||
<< ", m_popularity: " << static_cast<int>(info.m_popularity)
|
||||
<< ", m_nameScore: " << DebugPrint(info.m_nameScore)
|
||||
<< ", m_errorsMade: " << DebugPrint(info.m_errorsMade)
|
||||
<< ", m_isAltOrOldName: " << info.m_isAltOrOldName
|
||||
<< ", m_numTokens: " << info.m_numTokens
|
||||
<< ", m_matchedFraction: " << info.m_matchedFraction
|
||||
<< ", m_type: " << DebugPrint(info.m_type)
|
||||
<< ", m_resultType: ";
|
||||
|
||||
if (Model::IsPoi(info.m_type))
|
||||
os << DebugPrint(info.m_classifType.poi);
|
||||
else if (info.m_type == Model::TYPE_STREET)
|
||||
os << DebugPrint(info.m_classifType.street);
|
||||
|
||||
os << ", m_pureCats: " << info.m_pureCats
|
||||
<< ", m_falseCats: " << info.m_falseCats
|
||||
<< ", m_allTokensUsed: " << info.m_allTokensUsed
|
||||
<< ", m_exactCountryOrCapital: " << info.m_exactCountryOrCapital
|
||||
<< ", m_categorialRequest: " << info.m_categorialRequest
|
||||
<< ", m_hasName: " << info.m_hasName
|
||||
<< " }";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -197,7 +211,12 @@ void RankingInfo::ToCSV(ostream & os) const
|
|||
os << GetErrorsMadePerToken() << ",";
|
||||
os << m_matchedFraction << ",";
|
||||
os << DebugPrint(m_type) << ",";
|
||||
os << DebugPrint(m_resultType) << ",";
|
||||
|
||||
if (Model::IsPoi(m_type))
|
||||
os << DebugPrint(m_classifType.poi) << ",";
|
||||
else if (m_type == Model::TYPE_STREET)
|
||||
os << DebugPrint(m_classifType.street) << ",";
|
||||
|
||||
os << m_pureCats << ",";
|
||||
os << m_falseCats << ",";
|
||||
os << (m_allTokensUsed ? 1 : 0) << ",";
|
||||
|
@ -236,11 +255,18 @@ double RankingInfo::GetLinearModelRank() const
|
|||
result += kPopularity * popularity;
|
||||
result += m_falseCats * kFalseCats;
|
||||
result += kType[m_type];
|
||||
|
||||
if (Model::IsPoi(m_type))
|
||||
{
|
||||
CHECK_NOT_EQUAL(m_resultType, ResultType::Count, ());
|
||||
result += kResultType[base::Underlying(m_resultType)];
|
||||
CHECK_LESS(m_classifType.poi, PoiType::Count, ());
|
||||
result += kPoiType[base::Underlying(m_classifType.poi)];
|
||||
}
|
||||
else if (m_type == Model::TYPE_STREET)
|
||||
{
|
||||
CHECK_LESS(m_classifType.street, StreetType::Count, ());
|
||||
result += kStreetType[base::Underlying(m_classifType.street)];
|
||||
}
|
||||
|
||||
result += (m_allTokensUsed ? 1 : 0) * kAllTokensUsed;
|
||||
result += (m_exactCountryOrCapital ? 1 : 0) * kExactCountryOrCapital;
|
||||
auto const nameRank = kNameScore[nameScore] + kErrorsMade * GetErrorsMadePerToken() +
|
||||
|
@ -274,20 +300,22 @@ double RankingInfo::GetErrorsMadePerToken() const
|
|||
return static_cast<double>(m_errorsMade.m_errorsMade) / static_cast<double>(m_numTokens);
|
||||
}
|
||||
|
||||
ResultType GetResultType(feature::TypesHolder const & th)
|
||||
PoiType GetPoiType(feature::TypesHolder const & th)
|
||||
{
|
||||
if (ftypes::IsEatChecker::Instance()(th))
|
||||
return ResultType::Eat;
|
||||
if (ftypes::IsHotelChecker::Instance()(th))
|
||||
return ResultType::Hotel;
|
||||
if (ftypes::IsRailwayStationChecker::Instance()(th) ||
|
||||
ftypes::IsSubwayStationChecker::Instance()(th) ||
|
||||
ftypes::IsAirportChecker::Instance()(th))
|
||||
using namespace ftypes;
|
||||
|
||||
if (IsEatChecker::Instance()(th))
|
||||
return PoiType::Eat;
|
||||
if (IsHotelChecker::Instance()(th))
|
||||
return PoiType::Hotel;
|
||||
if (IsRailwayStationChecker::Instance()(th) ||
|
||||
IsSubwayStationChecker::Instance()(th) ||
|
||||
IsAirportChecker::Instance()(th))
|
||||
{
|
||||
return ResultType::TransportMajor;
|
||||
return PoiType::TransportMajor;
|
||||
}
|
||||
if (ftypes::IsPublicTransportStopChecker::Instance()(th))
|
||||
return ResultType::TransportLocal;
|
||||
if (IsPublicTransportStopChecker::Instance()(th))
|
||||
return PoiType::TransportLocal;
|
||||
|
||||
// We have several lists for attractions: short list in search categories for @tourism and long
|
||||
// list in ftypes::AttractionsChecker. We have highway-pedestrian, place-square, historic-tomb,
|
||||
|
@ -297,27 +325,44 @@ ResultType GetResultType(feature::TypesHolder const & th)
|
|||
auto static const attractionTypes =
|
||||
search::GetCategoryTypes("sights", "en", GetDefaultCategories());
|
||||
if (base::AnyOf(attractionTypes, [&th](auto t) { return th.Has(t); }))
|
||||
return ResultType::Attraction;
|
||||
return PoiType::Attraction;
|
||||
|
||||
if (IsServiceTypeChecker::Instance()(th))
|
||||
return ResultType::Service;
|
||||
return PoiType::Service;
|
||||
|
||||
return ResultType::General;
|
||||
return PoiType::General;
|
||||
}
|
||||
|
||||
string DebugPrint(ResultType type)
|
||||
string DebugPrint(PoiType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ResultType::TransportMajor: return "TransportMajor";
|
||||
case ResultType::TransportLocal: return "TransportLocal";
|
||||
case ResultType::Eat: return "Eat";
|
||||
case ResultType::Hotel: return "Hotel";
|
||||
case ResultType::Attraction: return "Attraction";
|
||||
case ResultType::Service: return "Service";
|
||||
case ResultType::General: return "General";
|
||||
case ResultType::Count: return "Count";
|
||||
case PoiType::TransportMajor: return "TransportMajor";
|
||||
case PoiType::TransportLocal: return "TransportLocal";
|
||||
case PoiType::Eat: return "Eat";
|
||||
case PoiType::Hotel: return "Hotel";
|
||||
case PoiType::Attraction: return "Attraction";
|
||||
case PoiType::Service: return "Service";
|
||||
case PoiType::General: return "General";
|
||||
case PoiType::Count: return "Count";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string DebugPrint(StreetType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case StreetType::Default: return "Default";
|
||||
case StreetType::Pedestrian: return "Pedestrian";
|
||||
case StreetType::Cycleway: return "Cycleway";
|
||||
case StreetType::Outdoor: return "Outdoor";
|
||||
case StreetType::Residential: return "Residential";
|
||||
case StreetType::Regular: return "Regular";
|
||||
case StreetType::Motorway: return "Motorway";
|
||||
case StreetType::Count: return "Count";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace search
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "search/model.hpp"
|
||||
#include "search/pre_ranking_info.hpp"
|
||||
#include "search/ranking_utils.hpp"
|
||||
#include "search/token_range.hpp"
|
||||
|
||||
#include "indexer/feature_data.hpp"
|
||||
#include "indexer/ftypes_matcher.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class FeatureType;
|
||||
namespace feature { class TypesHolder; }
|
||||
|
||||
namespace search
|
||||
{
|
||||
enum class ResultType : uint8_t
|
||||
enum class PoiType : uint8_t
|
||||
{
|
||||
// Railway/subway stations, airports.
|
||||
TransportMajor,
|
||||
|
@ -36,9 +33,24 @@ enum class ResultType : uint8_t
|
|||
Count
|
||||
};
|
||||
|
||||
using StreetType = ftypes::IsWayChecker::SearchRank;
|
||||
|
||||
struct RankingInfo
|
||||
{
|
||||
static double const kMaxDistMeters;
|
||||
RankingInfo()
|
||||
: m_isAltOrOldName(false)
|
||||
, m_allTokensUsed(true)
|
||||
, m_exactMatch(true)
|
||||
, m_exactCountryOrCapital(true)
|
||||
, m_pureCats(false)
|
||||
, m_falseCats(false)
|
||||
, m_categorialRequest(false)
|
||||
, m_hasName(false)
|
||||
{
|
||||
m_classifType.street = StreetType::Default;
|
||||
}
|
||||
|
||||
static double constexpr kMaxDistMeters = 2.0E6;
|
||||
|
||||
static void PrintCSVHeader(std::ostream & os);
|
||||
|
||||
|
@ -53,6 +65,20 @@ struct RankingInfo
|
|||
// Distance from the feature to the pivot point.
|
||||
double m_distanceToPivot = kMaxDistMeters;
|
||||
|
||||
// Matched parts of the query.
|
||||
// todo(@m) Using TokenType instead of ModelType here would
|
||||
// allow to distinguish postcodes too.
|
||||
std::array<TokenRange, Model::TYPE_COUNT> m_tokenRanges;
|
||||
|
||||
// Fraction of characters from original query matched to feature.
|
||||
float m_matchedFraction = 0.0;
|
||||
|
||||
// Query tokens number.
|
||||
uint16_t m_numTokens = 0;
|
||||
|
||||
// Number of misprints.
|
||||
ErrorsMade m_errorsMade;
|
||||
|
||||
// Rank of the feature.
|
||||
uint8_t m_rank = 0;
|
||||
|
||||
|
@ -62,58 +88,49 @@ struct RankingInfo
|
|||
// Score for the feature's name.
|
||||
NameScore m_nameScore = NAME_SCORE_ZERO;
|
||||
|
||||
// Number of misprints.
|
||||
ErrorsMade m_errorsMade;
|
||||
|
||||
// alt_name or old_name is used.
|
||||
bool m_isAltOrOldName = false;
|
||||
|
||||
// Query tokens number.
|
||||
size_t m_numTokens = 0;
|
||||
|
||||
// Matched parts of the query.
|
||||
// todo(@m) Using TokenType instead of ModelType here would
|
||||
// allow to distinguish postcodes too.
|
||||
std::array<TokenRange, Model::TYPE_COUNT> m_tokenRanges;
|
||||
|
||||
// Fraction of characters from original query matched to feature.
|
||||
double m_matchedFraction = 0.0;
|
||||
|
||||
// True iff all tokens that are not stop-words
|
||||
// were used when retrieving the feature.
|
||||
bool m_allTokensUsed = true;
|
||||
|
||||
// True iff all tokens retrieved from search index were matched without misprints.
|
||||
bool m_exactMatch = true;
|
||||
|
||||
// True iff feature has country or capital type and matches request: full match with all tokens
|
||||
// used and without misprints.
|
||||
bool m_exactCountryOrCapital = true;
|
||||
|
||||
// Search type for the feature.
|
||||
Model::Type m_type = Model::TYPE_COUNT;
|
||||
|
||||
// Type (food/transport/attraction/etc) for POI results for non-categorial requests.
|
||||
ResultType m_resultType = ResultType::Count;
|
||||
// Used for non-categorial requests.
|
||||
union
|
||||
{
|
||||
PoiType poi; // type (food/transport/attraction/etc) for POI results
|
||||
StreetType street; // type (peddestrian/residential/regular/etc) for Street results
|
||||
} m_classifType;
|
||||
|
||||
// alt_name or old_name is used.
|
||||
bool m_isAltOrOldName : 1;
|
||||
|
||||
// True iff all tokens that are not stop-words
|
||||
// were used when retrieving the feature.
|
||||
bool m_allTokensUsed : 1;
|
||||
|
||||
// True iff all tokens retrieved from search index were matched without misprints.
|
||||
bool m_exactMatch : 1;
|
||||
|
||||
// True iff feature has country or capital type and matches request: full match with all tokens
|
||||
// used and without misprints.
|
||||
bool m_exactCountryOrCapital : 1;
|
||||
|
||||
// True if all of the tokens that the feature was matched by
|
||||
// correspond to this feature's categories.
|
||||
bool m_pureCats = false;
|
||||
bool m_pureCats : 1;
|
||||
|
||||
// True if none of the tokens that the feature was matched by
|
||||
// corresponds to this feature's categories although all of the
|
||||
// tokens are categorial ones.
|
||||
bool m_falseCats = false;
|
||||
bool m_falseCats : 1;
|
||||
|
||||
// True iff the request is categorial.
|
||||
bool m_categorialRequest = false;
|
||||
bool m_categorialRequest : 1;
|
||||
|
||||
// True iff the feature has a name.
|
||||
bool m_hasName = false;
|
||||
bool m_hasName : 1;
|
||||
};
|
||||
|
||||
ResultType GetResultType(feature::TypesHolder const & th);
|
||||
PoiType GetPoiType(feature::TypesHolder const & th);
|
||||
|
||||
std::string DebugPrint(RankingInfo const & info);
|
||||
std::string DebugPrint(ResultType type);
|
||||
std::string DebugPrint(PoiType type);
|
||||
std::string DebugPrint(StreetType type);
|
||||
} // namespace search
|
||||
|
|
|
@ -58,14 +58,10 @@ CategoriesInfo::CategoriesInfo(feature::TypesHolder const & holder, TokenSlice c
|
|||
// ErrorsMade --------------------------------------------------------------------------------------
|
||||
string DebugPrint(ErrorsMade const & errorsMade)
|
||||
{
|
||||
ostringstream os;
|
||||
os << "ErrorsMade [ ";
|
||||
if (errorsMade.IsValid())
|
||||
os << errorsMade.m_errorsMade;
|
||||
return std::to_string(errorsMade.m_errorsMade);
|
||||
else
|
||||
os << "invalid";
|
||||
os << " ]";
|
||||
return os.str();
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
namespace impl
|
||||
|
|
|
@ -48,10 +48,10 @@ private:
|
|||
|
||||
struct ErrorsMade
|
||||
{
|
||||
static size_t constexpr kInfiniteErrors = std::numeric_limits<size_t>::max();
|
||||
static uint16_t constexpr kInfiniteErrors = std::numeric_limits<uint16_t>::max();
|
||||
|
||||
ErrorsMade() = default;
|
||||
explicit ErrorsMade(size_t errorsMade) : m_errorsMade(errorsMade) {}
|
||||
explicit ErrorsMade(uint16_t errorsMade) : m_errorsMade(errorsMade) {}
|
||||
|
||||
bool IsValid() const { return m_errorsMade != kInfiniteErrors; }
|
||||
bool IsZero() const { return m_errorsMade == 0; }
|
||||
|
@ -69,17 +69,17 @@ struct ErrorsMade
|
|||
|
||||
static ErrorsMade Min(ErrorsMade const & lhs, ErrorsMade const & rhs)
|
||||
{
|
||||
return Combine(lhs, rhs, [](size_t u, size_t v) { return std::min(u, v); });
|
||||
return Combine(lhs, rhs, [](uint16_t u, uint16_t v) { return std::min(u, v); });
|
||||
}
|
||||
|
||||
static ErrorsMade Max(ErrorsMade const & lhs, ErrorsMade const & rhs)
|
||||
{
|
||||
return Combine(lhs, rhs, [](size_t u, size_t v) { return std::max(u, v); });
|
||||
return Combine(lhs, rhs, [](uint16_t u, uint16_t v) { return std::max(u, v); });
|
||||
}
|
||||
|
||||
friend ErrorsMade operator+(ErrorsMade const & lhs, ErrorsMade const & rhs)
|
||||
{
|
||||
return Combine(lhs, rhs, [](size_t u, size_t v) { return u + v; });
|
||||
return Combine(lhs, rhs, [](uint16_t u, uint16_t v) { return u + v; });
|
||||
}
|
||||
|
||||
ErrorsMade & operator+=(ErrorsMade const & rhs)
|
||||
|
@ -90,7 +90,7 @@ struct ErrorsMade
|
|||
|
||||
bool operator==(ErrorsMade const & rhs) const { return m_errorsMade == rhs.m_errorsMade; }
|
||||
|
||||
size_t m_errorsMade = kInfiniteErrors;
|
||||
uint16_t m_errorsMade = kInfiniteErrors;
|
||||
};
|
||||
|
||||
std::string DebugPrint(ErrorsMade const & errorsMade);
|
||||
|
@ -105,7 +105,7 @@ ErrorsMade GetPrefixErrorsMade(QueryParams::Token const & token, strings::UniStr
|
|||
|
||||
// The order and numeric values are important here. Please, check all
|
||||
// use-cases before changing this enum.
|
||||
enum NameScore
|
||||
enum NameScore : uint8_t
|
||||
{
|
||||
NAME_SCORE_ZERO = 0,
|
||||
NAME_SCORE_SUBSTRING = 1,
|
||||
|
|
|
@ -1855,18 +1855,30 @@ UNIT_CLASS_TEST(ProcessorTest, RemoveDuplicatingStreets)
|
|||
|
||||
// Distance between centers should be less than 5km.
|
||||
TestStreet street1({{0.0, 0.0}, {0.0, 0.01}}, streetName, "ru");
|
||||
street1.SetHighwayType("primary");
|
||||
street1.SetType({ "highway", "primary" });
|
||||
TestStreet street2({{0.0, 0.01}, {0.0, 0.02}}, streetName, "ru");
|
||||
street1.SetHighwayType("secondary");
|
||||
street2.SetType({ "highway", "secondary" });
|
||||
TestStreet street3({{0.0, 0.02}, {0.0, 0.03}}, streetName, "ru");
|
||||
street3.SetType({ "highway", "footway" });
|
||||
TestStreet street4({{0.0, 0.03}, {0.0, 0.04}}, streetName, "ru");
|
||||
street4.SetType({ "highway", "tertiary_link", "tunnel" });
|
||||
|
||||
auto wonderlandId = BuildCountry("Wonderland", [&](TestMwmBuilder & builder) {
|
||||
auto wonderlandId = BuildCountry("Wonderland", [&](TestMwmBuilder & builder)
|
||||
{
|
||||
builder.Add(street1);
|
||||
builder.Add(street2);
|
||||
builder.Add(street3);
|
||||
builder.Add(street4);
|
||||
});
|
||||
|
||||
SetViewport(m2::RectD(-1, -1, 1, 1));
|
||||
{
|
||||
TEST_EQUAL(GetResultsNumber(streetName, "ru"), 1, ());
|
||||
auto request = MakeRequest(streetName);
|
||||
auto const & results = request->Results();
|
||||
TEST_EQUAL(results.size(), 2, ());
|
||||
|
||||
TEST(ResultsMatch({results[0]}, {ExactMatch(wonderlandId, street1)}), ());
|
||||
TEST(ResultsMatch({results[1]}, {ExactMatch(wonderlandId, street3)}), ());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2427,7 +2439,8 @@ UNIT_CLASS_TEST(ProcessorTest, Suburbs)
|
|||
builder.Add(cafe);
|
||||
});
|
||||
|
||||
auto const testFullMatch = [&](auto const & query, auto const & expected) {
|
||||
auto const testFullMatch = [&](auto const & query, auto const & expected)
|
||||
{
|
||||
auto request = MakeRequest(query);
|
||||
auto const & results = request->Results();
|
||||
TEST_GREATER(results.size(), 0, (results));
|
||||
|
@ -2436,7 +2449,7 @@ UNIT_CLASS_TEST(ProcessorTest, Suburbs)
|
|||
auto const & info = results[0].GetRankingInfo();
|
||||
TEST(info.m_exactMatch, ());
|
||||
TEST(info.m_allTokensUsed, ());
|
||||
TEST_ALMOST_EQUAL_ABS(info.m_matchedFraction, 1.0, 1e-12, ());
|
||||
TEST(fabs(info.m_matchedFraction - 1) < 1.0E-12, (info.m_matchedFraction));
|
||||
};
|
||||
|
||||
SetViewport(m2::RectD(-1.0, -1.0, 1.0, 1.0));
|
||||
|
@ -3014,7 +3027,6 @@ UNIT_CLASS_TEST(ProcessorTest, BurgerStreet)
|
|||
burger.SetTypes({{"amenity", "fast_food"}, {"cuisine", "burger"}});
|
||||
|
||||
TestStreet street({{2.0, 2.0}, {3.0, 3.0}}, "Burger street", "en");
|
||||
street.SetHighwayType("residential");
|
||||
|
||||
auto countryId = BuildCountry("Wonderland", [&](TestMwmBuilder & builder)
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@ void AssignRankingInfo(NameScores const & scores, RankingInfo & info, size_t tot
|
|||
info.m_nameScore = scores.m_nameScore;
|
||||
info.m_errorsMade = scores.m_errorsMade;
|
||||
info.m_isAltOrOldName = scores.m_isAltOrOldName;
|
||||
info.m_matchedFraction = scores.m_matchedLength / static_cast<double>(totalLength);
|
||||
info.m_matchedFraction = scores.m_matchedLength / static_cast<float>(totalLength);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -115,7 +115,7 @@ UNIT_TEST(RankingInfo_PreferCountry)
|
|||
info.m_nameScore = NAME_SCORE_FULL_MATCH;
|
||||
info.m_errorsMade = ErrorsMade(0);
|
||||
info.m_numTokens = 1;
|
||||
info.m_matchedFraction = 1.0;
|
||||
info.m_matchedFraction = 1;
|
||||
info.m_allTokensUsed = true;
|
||||
info.m_exactMatch = false;
|
||||
|
||||
|
@ -124,7 +124,7 @@ UNIT_TEST(RankingInfo_PreferCountry)
|
|||
cafe.m_tokenRanges[Model::TYPE_SUBPOI] = TokenRange(0, 1);
|
||||
cafe.m_exactCountryOrCapital = false;
|
||||
cafe.m_type = Model::TYPE_SUBPOI;
|
||||
cafe.m_resultType = ResultType::Eat;
|
||||
cafe.m_classifType.poi = PoiType::Eat;
|
||||
|
||||
auto country = info;
|
||||
country.m_distanceToPivot = 1e6;
|
||||
|
@ -140,7 +140,7 @@ UNIT_TEST(RankingInfo_PrefixVsFull)
|
|||
{
|
||||
RankingInfo info;
|
||||
info.m_numTokens = 3;
|
||||
info.m_matchedFraction = 1.0;
|
||||
info.m_matchedFraction = 1;
|
||||
info.m_allTokensUsed = true;
|
||||
info.m_exactMatch = false;
|
||||
info.m_exactCountryOrCapital = false;
|
||||
|
|
Loading…
Add table
Reference in a new issue