Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/Tests/GRDBTests/FTS3PatternTests.swift
T
2025-01-28 12:28:03 +08:00

188 lines
10 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import XCTest
import GRDB
class FTS3PatternTests: GRDBTestCase {
override func setup(_ dbWriter: some DatabaseWriter) throws {
try dbWriter.write { db in
try db.create(virtualTable: "books", using: FTS3()) { t in
t.column("title")
t.column("author")
t.column("body")
}
try db.execute(sql: "INSERT INTO books (title, author, body) VALUES (?, ?, ?)", arguments: ["Moby Dick", "Herman Melville", "Call me Ishmael. Some years ago--never mind how long precisely--having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world."])
try db.execute(sql: "INSERT INTO books (title, author, body) VALUES (?, ?, ?)", arguments: ["Red Mars", "Kim Stanley Robinson", "History is not evolution! It is a false analogy! Evolution is a matter of environment and chance, acting over millions of years. But history is a matter of environment and choice, acting within lifetimes, and sometimes within years, or months, or days! History is Lamarckian!"])
try db.execute(sql: "INSERT INTO books (title, author, body) VALUES (?, ?, ?)", arguments: ["Querelle de Brest", "Jean Genet", "Lidée de mer évoque souvent lidée de mer, de marins. Mer et marins ne se présentent pas alors avec la précision dune image, le meurtre plutôt fait en nous l’émotion déferler par vagues."])
try db.execute(sql: "INSERT INTO books (title, author, body) VALUES (?, ?, ?)", arguments: ["Éden, Éden, Éden", "Pierre Guyotat", "/ Les soldats, casqués, jambes ouvertes, foulent, muscles retenus, les nouveau-nés emmaillotés dans les châles écarlates, violets : les bébés roulent hors des bras des femmes accroupies sur les tôles mitraillées des G. M. C. ;"])
}
}
func testValidFTS3Pattern() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
// Couples (raw pattern, expected count of matching rows)
let validRawPatterns: [(String, Int)] = [
// Empty query
("", 0),
("?!", 0),
// Token queries
("Moby", 1),
("écarlates", 1),
("fooéı👨👨🏿🇫🇷🇨🇮", 0),
// Prefix queries
("*", 0),
("Robin*", 1),
// Phrase queries
("\"foulent muscles\"", 1),
("\"Kim Stan* Robin*\"", 1),
// NEAR queries
("history NEAR evolution", 1),
// Logical queries
("years NOT months", 1),
("years AND months", 1),
("years OR months", 2),
// column queries
("title:brest", 1)
]
for (rawPattern, expectedCount) in validRawPatterns {
let pattern = try FTS3Pattern(rawPattern: rawPattern)
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
}
}
}
func testInvalidFTS3Pattern() {
let invalidRawPatterns = ["NOT", "(", "AND", "OR", "\""]
for rawPattern in invalidRawPatterns {
do {
_ = try FTS3Pattern(rawPattern: rawPattern)
XCTFail("Expected pattern to be invalid: \(String(reflecting: rawPattern))")
} catch is DatabaseError {
} catch {
XCTFail("Expected DatabaseError, not \(error)")
}
}
}
func testFTS3PatternWithAnyToken() throws {
let wrongInputs = ["", "*", "^", " ", "(", "()", "\"", "?!"]
for string in wrongInputs {
if let pattern = FTS3Pattern(matchingAnyTokenIn: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTFail("Unexpected raw pattern \(String(reflecting: rawPattern)) from string \(String(reflecting: string))")
}
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
// Couples (pattern, expected raw pattern, expected count of matching rows)
let cases = [
("écarlates", "écarlates", 1),
("^Moby*", "moby", 1),
(" \t\nyears \t\nmonths \t\n", "years OR months", 2),
("\"years months days\"", "years OR months OR days", 2),
("FOOÉı👨👨🏿🇫🇷🇨🇮", "fooÉı👨👨🏿🇫🇷🇨🇮", 0),
]
for (string, expectedRawPattern, expectedCount) in cases {
if let pattern = FTS3Pattern(matchingAnyTokenIn: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTAssertEqual(rawPattern, expectedRawPattern)
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
}
}
}
}
func testFTS3PatternWithAllTokens() throws {
let wrongInputs = ["", "*", "^", " ", "(", "()", "\"", "?!"]
for string in wrongInputs {
if let pattern = FTS3Pattern(matchingAllTokensIn: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTFail("Unexpected raw pattern \(String(reflecting: rawPattern)) from string \(String(reflecting: string))")
}
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
// Couples (pattern, expected raw pattern, expected count of matching rows)
let cases = [
("écarlates", "écarlates", 1),
("^Moby*", "moby", 1),
(" \t\nyears \t\nmonths \t\n", "years months", 1),
("\"years months days\"", "years months days", 1),
("FOOÉı👨👨🏿🇫🇷🇨🇮", "fooÉı👨👨🏿🇫🇷🇨🇮", 0),
]
for (string, expectedRawPattern, expectedCount) in cases {
if let pattern = FTS3Pattern(matchingAllTokensIn: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTAssertEqual(rawPattern, expectedRawPattern)
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
}
}
}
}
func testFTS3PatternWithAllPrefixes() throws {
let wrongInputs = ["", "*", "^", " ", "(", "()", "\"", "?!"]
for string in wrongInputs {
if let pattern = FTS3Pattern(matchingAllPrefixesIn: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTFail("Unexpected raw pattern \(String(reflecting: rawPattern)) from string \(String(reflecting: string))")
}
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
// Couples (pattern, expected raw pattern, expected count of matching rows)
let cases = [
("écarlate", "écarlate*", 1),
("^Mob*", "mob*", 1),
(" \t\nyear \t\nmonth \t\n", "year* month*", 1),
("\"year month day\"", "year* month* day*", 1),
("FOOÉı👨👨🏿🇫🇷", "fooÉı👨👨🏿🇫🇷*", 0),
]
for (string, expectedRawPattern, expectedCount) in cases {
if let pattern = FTS3Pattern(matchingAllPrefixesIn: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTAssertEqual(rawPattern, expectedRawPattern)
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
}
}
}
}
func testFTS3PatternWithPhrase() throws {
let wrongInputs = ["", "*", "^", " ", "(", "()", "\"", "?!"]
for string in wrongInputs {
if let pattern = FTS3Pattern(matchingPhrase: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTFail("Unexpected raw pattern \(String(reflecting: rawPattern)) from string \(String(reflecting: string))")
}
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
// Couples (pattern, expected raw pattern, expected count of matching rows)
let cases = [
("écarlates", "\"écarlates\"", 1),
("^Moby*", "\"moby\"", 1),
(" \t\nyears \t\nmonths \t\n", "\"years months\"", 0),
("\"years months days\"", "\"years months days\"", 0),
("FOOÉı👨👨🏿🇫🇷🇨🇮", "\"fooÉı👨👨🏿🇫🇷🇨🇮\"", 0),
]
for (string, expectedRawPattern, expectedCount) in cases {
if let pattern = FTS3Pattern(matchingPhrase: string) {
let rawPattern = String.fromDatabaseValue(pattern.databaseValue)!
XCTAssertEqual(rawPattern, expectedRawPattern)
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
}
}
}
}
}