Match searches for national number format

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent d423d3487f
commit f57a5dbc77

@ -210,18 +210,16 @@ class ConversationSearcherTest: XCTestCase {
XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
} }
// TODO func testSearchContactByNumberWithoutCountryCode() {
func pending_testSearchContactByNumber() { var threads: [ThreadViewModel] = []
var resultSet: SearchResultSet = .empty
// Phone Number formatting should be forgiving // Phone Number formatting should be forgiving
resultSet = getResultSet(searchText: "234.56") threads = searchConversations(searchText: "234.56")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
resultSet = getResultSet(searchText: "234 56") threads = searchConversations(searchText: "234 56")
XCTAssertEqual(1, resultSet.conversations.count) XCTAssertEqual(3, threads.count)
XCTAssertEqual(aliceThread, resultSet.conversations.first?.thread) XCTAssertEqual([bookClubThread, snackClubThread, aliceThread], threads)
} }
func testSearchConversationByContactByName() { func testSearchConversationByContactByName() {

@ -35,10 +35,9 @@
- (NSURL *)toSystemDialerURL; - (NSURL *)toSystemDialerURL;
- (NSString *)toE164; - (NSString *)toE164;
- (NSString *)localizedDescriptionForUser;
- (NSNumber *)getCountryCode; - (NSNumber *)getCountryCode;
@property (nonatomic, readonly, nullable) NSString *nationalNumber;
- (BOOL)isValid; - (BOOL)isValid;
- (BOOL)resolvesInternationallyTo:(PhoneNumber *)otherPhoneNumber;
- (NSComparisonResult)compare:(PhoneNumber *)other; - (NSComparisonResult)compare:(PhoneNumber *)other;

@ -372,25 +372,23 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
return self.phoneNumber.countryCode; return self.phoneNumber.countryCode;
} }
- (BOOL)isValid { - (nullable NSString *)nationalNumber
return [[PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil isValidNumber:self.phoneNumber]; {
} NSError *error;
NSString *nationalNumber = [[PhoneNumberUtil sharedThreadLocal] format:self.phoneNumber
- (NSString *)localizedDescriptionForUser { numberFormat:NBEPhoneNumberFormatNATIONAL
NBPhoneNumberUtil *phoneUtil = [PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil; error:&error];
if (error) {
NSError *formatError = nil; DDLogVerbose(@"%@ error parsing number into national format: %@", self.logTag, error);
NSString *pretty = return nil;
[phoneUtil format:self.phoneNumber numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:&formatError];
if (formatError != nil) {
return self.e164;
} }
return pretty;
return nationalNumber;
} }
- (BOOL)resolvesInternationallyTo:(PhoneNumber *)otherPhoneNumber { - (BOOL)isValid
return [self.toE164 isEqualToString:otherPhoneNumber.toE164]; {
return [[PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil isValidNumber:self.phoneNumber];
} }
- (NSString *)description { - (NSString *)description {

@ -27,11 +27,11 @@ public class FullTextSearchFinder: NSObject {
return return
} }
let normalized = FullTextSearchFinder.normalize(text: searchText) let normalized = FullTextSearchFinder.normalize(queryText: searchText)
// We want a forgiving query for phone numbers // We want to match by prefix for "search as you type" functionality.
// TODO a stricter "whole word" query for body text? // SQLite does not support suffix or contains matches.
let prefixQuery = "*\(normalized)*" let prefixQuery = "\(normalized)*"
let maxSearchResults = 500 let maxSearchResults = 500
var searchResultCount = 0 var searchResultCount = 0
@ -57,10 +57,10 @@ public class FullTextSearchFinder: NSObject {
return TextSecureKitEnv.shared().contactsManager return TextSecureKitEnv.shared().contactsManager
} }
private class func normalize(text: String) -> String { private class func normalize(indexingText: String) -> String {
var normalized: String = text.trimmingCharacters(in: .whitespacesAndNewlines) var normalized: String = indexingText.trimmingCharacters(in: .whitespacesAndNewlines)
// Remove any phone number formatting from the search terms // Remove any formatting from the search terms
let nonformattingScalars = normalized.unicodeScalars.lazy.filter { let nonformattingScalars = normalized.unicodeScalars.lazy.filter {
!CharacterSet.punctuationCharacters.contains($0) !CharacterSet.punctuationCharacters.contains($0)
} }
@ -70,6 +70,27 @@ public class FullTextSearchFinder: NSObject {
return normalized return normalized
} }
private class func normalize(queryText: String) -> String {
var normalized: String = queryText.trimmingCharacters(in: .whitespacesAndNewlines)
// Remove any formatting from the search terms
let nonformattingScalars = normalized.unicodeScalars.lazy.filter {
!CharacterSet.punctuationCharacters.contains($0)
}
let normalizedChars = String(String.UnicodeScalarView(nonformattingScalars))
let digitsOnlyScalars = normalized.unicodeScalars.lazy.filter {
CharacterSet.decimalDigits.contains($0)
}
let normalizedDigits = String(String.UnicodeScalarView(digitsOnlyScalars))
if normalizedDigits.count > 0 {
return "\(normalizedChars) OR \(normalizedDigits)"
} else {
return "\(normalizedChars)"
}
}
private static let groupThreadIndexer: SearchIndexer<TSGroupThread> = SearchIndexer { (groupThread: TSGroupThread) in private static let groupThreadIndexer: SearchIndexer<TSGroupThread> = SearchIndexer { (groupThread: TSGroupThread) in
let groupName = groupThread.groupModel.groupName ?? "" let groupName = groupThread.groupModel.groupName ?? ""
@ -79,27 +100,43 @@ public class FullTextSearchFinder: NSObject {
let searchableContent = "\(groupName) \(memberStrings)" let searchableContent = "\(groupName) \(memberStrings)"
return normalize(text: searchableContent) return normalize(indexingText: searchableContent)
} }
private static let contactThreadIndexer: SearchIndexer<TSContactThread> = SearchIndexer { (contactThread: TSContactThread) in private static let contactThreadIndexer: SearchIndexer<TSContactThread> = SearchIndexer { (contactThread: TSContactThread) in
let recipientId = contactThread.contactIdentifier() let recipientId = contactThread.contactIdentifier()
let searchableContent = recipientIndexer.index(recipientId) let searchableContent = recipientIndexer.index(recipientId)
return normalize(text: searchableContent) return normalize(indexingText: searchableContent)
} }
private static let recipientIndexer: SearchIndexer<String> = SearchIndexer { (recipientId: String) in private static let recipientIndexer: SearchIndexer<String> = SearchIndexer { (recipientId: String) in
let displayName = contactsManager.displayName(forPhoneIdentifier: recipientId) let displayName = contactsManager.displayName(forPhoneIdentifier: recipientId)
let searchableContent = "\(recipientId) \(displayName)"
return normalize(text: searchableContent) let nationalNumber: String = { (recipientId: String) -> String in
guard let phoneNumber = PhoneNumber(fromE164: recipientId) else {
assertionFailure("unexpected unparseable recipientId: \(recipientId)")
return ""
}
guard let digitScalars = phoneNumber.nationalNumber?.unicodeScalars.filter({ CharacterSet.decimalDigits.contains($0) }) else {
assertionFailure("unexpected unparseable recipientId: \(recipientId)")
return ""
}
return String(String.UnicodeScalarView(digitScalars))
}(recipientId)
let searchableContent = "\(recipientId) \(nationalNumber) \(displayName)"
return normalize(indexingText: searchableContent)
} }
private static let messageIndexer: SearchIndexer<TSMessage> = SearchIndexer { (message: TSMessage) in private static let messageIndexer: SearchIndexer<TSMessage> = SearchIndexer { (message: TSMessage) in
let searchableContent = message.body ?? "" let searchableContent = message.body ?? ""
return normalize(text: searchableContent) return normalize(indexingText: searchableContent)
} }
private class func indexContent(object: Any) -> String? { private class func indexContent(object: Any) -> String? {

Loading…
Cancel
Save