diff --git a/InternetArchiveKit/InternetArchiveQuery.swift b/InternetArchiveKit/InternetArchiveQuery.swift index 3546290..6949f6f 100644 --- a/InternetArchiveKit/InternetArchiveQuery.swift +++ b/InternetArchiveKit/InternetArchiveQuery.swift @@ -127,10 +127,14 @@ extension InternetArchive { return "\(booleanOperator.rawValue)\(fieldKey)\(finalValue)" } - /// Lucene syntax characters that get backslash-escaped in values. `*` and - /// `?` are deliberately absent so wildcard searches keep working. + /// Lucene syntax characters that get backslash-escaped in non-exactMatch + /// values. The query operators `+ - & | "` and the wildcards `* ?` are + /// deliberately absent: archive.org's Elasticsearch backend rejects the + /// escaped forms of the operators (`\-`, `\+`, `\&`, `\|`, `\"` come back + /// as a backend error) and they don't need escaping mid-value there, while + /// `* ?` stay raw so wildcard searches keep working. private static let luceneSpecialCharacters: Set = [ - "+", "-", "&", "|", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", ":", "\\", "/", + "(", ")", "{", "}", "[", "]", "^", "~", ":", "\\", "/", "!", ] static func escapingLuceneSyntax(in value: String) -> String { diff --git a/InternetArchiveKitTests/InternetArchiveQueryTests.swift b/InternetArchiveKitTests/InternetArchiveQueryTests.swift index 36a53e1..74ec70f 100644 --- a/InternetArchiveKitTests/InternetArchiveQueryTests.swift +++ b/InternetArchiveKitTests/InternetArchiveQueryTests.swift @@ -63,12 +63,21 @@ class InternetArchiveQueryTests: XCTestCase { XCTAssertEqual(clause.asURLString, "venue:(foo fest \\[bar stage\\])") } - func testQueryClauseEscapesEveryLuceneSpecialCharacter() { + func testQueryClauseEscapesParserSpecialCharacters() { let clause: InternetArchive.QueryClause = InternetArchive.QueryClause( - field: "foo", value: "a+b-c&d|e!f(g)h{i}j[k]l^m\"n~o:p\\q/r") + field: "foo", value: "a!b(c)d{e}f[g]h^i~j:k\\l/m") XCTAssertEqual( clause.asURLString, - "foo:(a\\+b\\-c\\&d\\|e\\!f\\(g\\)h\\{i\\}j\\[k\\]l\\^m\\\"n\\~o\\:p\\\\q\\/r)") + "foo:(a\\!b\\(c\\)d\\{e\\}f\\[g\\]h\\^i\\~j\\:k\\\\l\\/m)") + } + + /// archive.org's Elasticsearch backend errors on the escaped forms of these + /// operator characters (`\-`, `\+`, `\&`, `\|`, `\"`), so they pass through + /// unescaped — verified live against advancedsearch.php. + func testQueryClauseLeavesElasticsearchOperatorsRaw() { + let clause: InternetArchive.QueryClause = InternetArchive.QueryClause( + field: "foo", value: "a+b-c&d|e\"f") + XCTAssertEqual(clause.asURLString, "foo:(a+b-c&d|e\"f)") } func testQueryClauseKeepsWildcards() {