|
|
|
|
@ -472,32 +472,42 @@ class SearchViewModel: ObservableObject, Identifiable { |
|
|
|
|
static func pastePredicate( |
|
|
|
|
pasteField: String, mostRecentDate: Date?, filterOption: PlayerFilterOption |
|
|
|
|
) -> NSPredicate? { |
|
|
|
|
print("🔍 pastePredicate called with: \(pasteField)") |
|
|
|
|
print("📅 mostRecentDate: \(String(describing: mostRecentDate))") |
|
|
|
|
print("🔍 filterOption: \(filterOption)") |
|
|
|
|
|
|
|
|
|
var andPredicates = [NSPredicate]() |
|
|
|
|
var orPredicates = [NSPredicate]() |
|
|
|
|
|
|
|
|
|
// Check for license numbers |
|
|
|
|
let matches = pasteField.licencesFound() |
|
|
|
|
print("🎫 Licenses found: \(matches)") |
|
|
|
|
let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) } |
|
|
|
|
orPredicates = licensesPredicates |
|
|
|
|
|
|
|
|
|
if matches.count == 2 { |
|
|
|
|
print("✅ Returning early with 2 license predicates") |
|
|
|
|
return NSCompoundPredicate(orPredicateWithSubpredicates: orPredicates) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Add gender filter if specified |
|
|
|
|
if filterOption == .female { |
|
|
|
|
print("👩 Adding female filter") |
|
|
|
|
andPredicates.append(NSPredicate(format: "male == NO")) |
|
|
|
|
} else if filterOption == .male { |
|
|
|
|
print("👨 Adding male filter") |
|
|
|
|
andPredicates.append(NSPredicate(format: "male == YES")) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Add date filter if specified |
|
|
|
|
if let mostRecentDate { |
|
|
|
|
print("📆 Adding date filter for: \(mostRecentDate)") |
|
|
|
|
andPredicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check for slashes (representing alternatives) |
|
|
|
|
if let slashPredicate = getSpecialSlashPredicate(inputString: pasteField) { |
|
|
|
|
print("🔀 Found slash predicate") |
|
|
|
|
orPredicates.append(slashPredicate) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -506,66 +516,80 @@ class SearchViewModel: ObservableObject, Identifiable { |
|
|
|
|
pasteField |
|
|
|
|
.replacingOccurrences(of: "[\\(\\)\\[\\]\\{\\}]", with: "", options: .regularExpression) |
|
|
|
|
.replacingOccurrences(of: "/", with: " ") // Replace slashes with spaces |
|
|
|
|
.replacingOccurrences(of: "[\\.,;:!?]", with: " ", options: .regularExpression) // Replace common punctuation with spaces |
|
|
|
|
.trimmingCharacters(in: .whitespacesAndNewlines) |
|
|
|
|
|
|
|
|
|
print("🧹 Cleaned text: \"\(text)\"") |
|
|
|
|
|
|
|
|
|
// Remove digits |
|
|
|
|
let digitPattern = /\b\w*\d\w*\b/ |
|
|
|
|
text = text.replacing(digitPattern, with: "").trimmingCharacters( |
|
|
|
|
in: .whitespacesAndNewlines) |
|
|
|
|
print("🔢 After digit removal: \"\(text)\"") |
|
|
|
|
|
|
|
|
|
// Split text by whitespace to get potential name components |
|
|
|
|
let textComponents = text.components(separatedBy: .whitespacesAndNewlines) |
|
|
|
|
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } |
|
|
|
|
.filter { |
|
|
|
|
!$0.isEmpty && $0.count > 1 && !["de", "la", "le", "du"].contains($0.lowercased()) |
|
|
|
|
!$0.isEmpty && $0.count > 1 && !["de", "la", "le", "du", "et", "si"].contains($0.lowercased()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
print("📚 Text components: \(textComponents)") |
|
|
|
|
|
|
|
|
|
if textComponents.count < 50 { |
|
|
|
|
print("✓ Text components count is reasonable: \(textComponents.count)") |
|
|
|
|
|
|
|
|
|
// Handle exact fullname match |
|
|
|
|
let fullName = textComponents.joined(separator: " ") |
|
|
|
|
if !fullName.isEmpty { |
|
|
|
|
print("📝 Adding predicate for full name: \"\(fullName)\"") |
|
|
|
|
orPredicates.append( |
|
|
|
|
NSPredicate(format: "canonicalFullName CONTAINS[cd] %@", fullName)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Handle hyphenated last names |
|
|
|
|
let hyphenatedComponents = textComponents.filter { $0.contains("-") } |
|
|
|
|
print("🔗 Hyphenated components: \(hyphenatedComponents)") |
|
|
|
|
for component in hyphenatedComponents { |
|
|
|
|
orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", component)) |
|
|
|
|
print("➕ Added hyphenated last name predicate: \"\(component)\"") |
|
|
|
|
|
|
|
|
|
// Also search for each part of the hyphenated name |
|
|
|
|
let parts = component.components(separatedBy: "-") |
|
|
|
|
for part in parts { |
|
|
|
|
if part.count > 1 { |
|
|
|
|
orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", part)) |
|
|
|
|
print("➕ Added hyphenated part predicate: \"\(part)\"") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try different combinations for first/last name |
|
|
|
|
if textComponents.count > 1 { |
|
|
|
|
print("🔄 Creating combinations with \(textComponents.count) components") |
|
|
|
|
// Try each pair of components as first+last and last+first |
|
|
|
|
for i in 0..<textComponents.count { |
|
|
|
|
for j in 0..<textComponents.count where i != j { |
|
|
|
|
orPredicates.append( |
|
|
|
|
NSPredicate( |
|
|
|
|
format: |
|
|
|
|
"(firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@) OR (firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@)", |
|
|
|
|
textComponents[i], textComponents[j], textComponents[j], |
|
|
|
|
textComponents[i] |
|
|
|
|
)) |
|
|
|
|
|
|
|
|
|
// Also try beginswith for more precise matches |
|
|
|
|
orPredicates.append( |
|
|
|
|
NSPredicate( |
|
|
|
|
format: |
|
|
|
|
"(firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@) OR (firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@)", |
|
|
|
|
textComponents[i], textComponents[j], textComponents[j], |
|
|
|
|
textComponents[i] |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
for i in 0..<textComponents.count-1 { |
|
|
|
|
let j = i + 1 |
|
|
|
|
print("👥 Trying adjacent pair: \"\(textComponents[i])\" and \"\(textComponents[j])\"") |
|
|
|
|
// orPredicates.append( |
|
|
|
|
// NSPredicate( |
|
|
|
|
// format: |
|
|
|
|
// "(firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@) OR (firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@)", |
|
|
|
|
// textComponents[i], textComponents[j], textComponents[j], |
|
|
|
|
// textComponents[i] |
|
|
|
|
// )) |
|
|
|
|
|
|
|
|
|
// Also try beginswith for more precise matches |
|
|
|
|
orPredicates.append( |
|
|
|
|
NSPredicate( |
|
|
|
|
format: |
|
|
|
|
"(firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@) OR (firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@)", |
|
|
|
|
textComponents[i], textComponents[j], textComponents[j], |
|
|
|
|
textComponents[i] |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
} else if textComponents.count == 1 { |
|
|
|
|
print("👤 Single component search: \"\(textComponents[0])\"") |
|
|
|
|
// If only one component, search in both first and last name |
|
|
|
|
orPredicates.append( |
|
|
|
|
NSPredicate( |
|
|
|
|
@ -574,14 +598,19 @@ class SearchViewModel: ObservableObject, Identifiable { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Add pattern match for canonical full name |
|
|
|
|
let pattern = textComponents.joined(separator: ".*") |
|
|
|
|
orPredicates.append(NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern)) |
|
|
|
|
// let pattern = textComponents.joined(separator: ".*") |
|
|
|
|
// print("🔍 Adding pattern match: \"\(pattern)\"") |
|
|
|
|
// orPredicates.append(NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern)) |
|
|
|
|
} else { |
|
|
|
|
print("⚠️ Too many text components: \(textComponents.count) - skipping name combinations") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Construct final predicate |
|
|
|
|
var predicate = NSCompoundPredicate(andPredicateWithSubpredicates: andPredicates) |
|
|
|
|
print("📊 AND predicates count: \(andPredicates.count)") |
|
|
|
|
|
|
|
|
|
if !orPredicates.isEmpty { |
|
|
|
|
print("📊 OR predicates count: \(orPredicates.count)") |
|
|
|
|
let orCompoundPredicate = NSCompoundPredicate( |
|
|
|
|
orPredicateWithSubpredicates: orPredicates) |
|
|
|
|
predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ |
|
|
|
|
@ -589,6 +618,8 @@ class SearchViewModel: ObservableObject, Identifiable { |
|
|
|
|
]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
print("🏁 Final predicate created") |
|
|
|
|
print(predicate) |
|
|
|
|
return predicate |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|