Skip to content
This repository has been archived by the owner on Sep 4, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
[libcontacts] Use libphonenumber for number matching. Contributes to …
…JB#38835

This commit yields a significant improvement to how our phone number
matching works. It gets rid of a lot of false positive matches.

Previously we used a "minimized" number which we stored in the cache
and used for matching. This lead to false positives between numbers
for which the "minimized" number was the same when the full wasn't,
for example when the area code of the number differs in only one digit.

Examples of solved false positive matches:
"+36 20 123 4567" and "+36 30 123 4567" were matched incorrectly.
"+36 20 123 4567" and "+44 20 123 4567" were matched incorrectly.

The new approach is to cache the full phone number and perform the
matching between the full numbers using libphonenumber, which has
a quite resilient algorithm for this. Local numbers and
international direct dial (IDD) are still matched:

Examples of local and IDD numbers that still work:
"+36 20 123 4567" still matches "06 20 123 4567" (local)
"+36 20 123 4567" still matches "00 36 70 381 0581" (IID)

Limitations:
We currently do NOT pass a default region code to libphonenumber,
which means that some false positives can still occour in edge
cases when a country code would be required to distinguish between
the two numbers. This only happens when some numbers are supplied
in a non-international format.

Examples of remaining false positives:
"06 20 123 4567" is a false positive match of "+1 0620 123 4567"
"00 36 20 123 4567" is a false positive match of "+1 00 36 20 123 4567"

We deem that the remaining false positives are unlikely enough that
they don't matter in practice. If this becomes relevant, we will need
to make it possible to pass a region code to the matching algorithm.
  • Loading branch information
Timur Kristóf committed Feb 4, 2020
1 parent 2df678a commit cdb0005
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 12 deletions.
1 change: 1 addition & 0 deletions rpm/libcontacts-qt5.spec
Expand Up @@ -15,6 +15,7 @@ BuildRequires: pkgconfig(mlite5)
BuildRequires: pkgconfig(mlocale5)
BuildRequires: pkgconfig(mce)
BuildRequires: pkgconfig(qtcontacts-sqlite-qt5-extensions) >= 0.2.31
BuildRequires: libphonenumber-devel
BuildRequires: qt5-qttools
BuildRequires: qt5-qttools-linguist

Expand Down
50 changes: 38 additions & 12 deletions src/seasidecache.cpp
Expand Up @@ -73,6 +73,7 @@

#include <mce/dbus-names.h>
#include <mce/mode-names.h>
#include <phonenumbers/phonenumberutil.h>

QTVERSIT_USE_NAMESPACE

Expand Down Expand Up @@ -3313,22 +3314,47 @@ void SeasideCache::resolveAddress(ResolveListener *listener, const QString &firs
SeasideCache::CacheItem *SeasideCache::itemMatchingPhoneNumber(const QString &number, const QString &normalized, bool requireComplete)
{
QMultiHash<QString, CachedPhoneNumber>::const_iterator it = m_phoneNumberIds.find(number), end = m_phoneNumberIds.constEnd();
if (it != end) {
// How many matches are there for this number?
int matchCount = 1;
QMultiHash<QString, CachedPhoneNumber>::const_iterator matchingIt = it + 1;
while ((matchingIt != end) && (matchingIt.key() == number)) {
++matchCount;
++matchingIt;
if (it == end)
return 0;

QHash<QString, quint32> possibleMatches;
::i18n::phonenumbers::PhoneNumberUtil *util = ::i18n::phonenumbers::PhoneNumberUtil::GetInstance();
::std::string normalizedStdStr = normalized.toStdString();

for (QMultiHash<QString, CachedPhoneNumber>::const_iterator matchingIt = it;
matchingIt != end && matchingIt.key() == number;
++matchingIt) {

const CachedPhoneNumber &cachedPhoneNumber = matchingIt.value();

// Bypass libphonenumber if the numbers match exactly
if (matchingIt->normalizedNumber == normalized)
return itemById(cachedPhoneNumber.iid, requireComplete);

::i18n::phonenumbers::PhoneNumberUtil::MatchType matchType =
util->IsNumberMatchWithTwoStrings(normalizedStdStr, cachedPhoneNumber.normalizedNumber.toStdString());

switch (matchType) {
case ::i18n::phonenumbers::PhoneNumberUtil::EXACT_MATCH:
// This is the optimal outcome
return itemById(cachedPhoneNumber.iid, requireComplete);
case ::i18n::phonenumbers::PhoneNumberUtil::NSN_MATCH:
case ::i18n::phonenumbers::PhoneNumberUtil::SHORT_NSN_MATCH:
// Store numbers whose NSN (national significant number) might match
// Example: if +36701234567 is calling, then 1234567 is an NSN match
possibleMatches.insert(cachedPhoneNumber.normalizedNumber, cachedPhoneNumber.iid);
break;
default:
// Either couldn't parse the number or it was NO_MATCH, ignore it
break;
}
}
if (matchCount == 1)
return itemById(it->iid, requireComplete);

// Choose the best match from these contacts
int bestMatchLength = 0;
CacheItem *matchItem = 0;
for ( ; matchCount > 0; ++it, --matchCount) {
if (CacheItem *item = existingItem(it->iid)) {
for (QHash<QString, quint32>::const_iterator matchingIt = possibleMatches.begin(); matchingIt != possibleMatches.end(); ++matchingIt) {
if (CacheItem *item = existingItem(*matchingIt)) {
int matchLength = bestPhoneNumberMatchLength(item->contact, normalized);
if (matchLength > bestMatchLength) {
bestMatchLength = matchLength;
Expand All @@ -3345,9 +3371,9 @@ SeasideCache::CacheItem *SeasideCache::itemMatchingPhoneNumber(const QString &nu
}
return matchItem;
}
}

return 0;

}

int SeasideCache::contactIndex(quint32 iid, FilterType filterType)
Expand Down
1 change: 1 addition & 0 deletions src/src.pro
Expand Up @@ -22,6 +22,7 @@ packagesExist(mlite5) {
warning("mlite not available. Some functionality may not work as expected.")
}
PKGCONFIG += mlocale5 mce qtcontacts-sqlite-qt5-extensions
LIBS += -lphonenumber

DEFINES += CONTACTCACHE_BUILD

Expand Down
1 change: 1 addition & 0 deletions tests/tst_resolve/tst_resolve.pro
Expand Up @@ -4,6 +4,7 @@ TARGET = tst_resolve
QT += contacts-private dbus

PKGCONFIG += mlocale5
LIBS += -lphonenumber

# We need the moc output for ContactManagerEngine from sqlite-extensions
extensionsIncludePath = $$system(pkg-config --cflags-only-I qtcontacts-sqlite-qt5-extensions)
Expand Down

0 comments on commit cdb0005

Please sign in to comment.