From 6e8d85da26bd9a80e0dd3f13ef2f6fbb04681f62 Mon Sep 17 00:00:00 2001 From: Jason Buckner Date: Mon, 20 Jul 2026 12:41:44 -0700 Subject: [PATCH] Fix #96 - Don't escape the ES operator characters that archive.org rejects #70 backslash-escaped the full Lucene special set in non-exactMatch values, but archive.org's Elasticsearch backend returns a backend error for the escaped operators (`\-`, `\+`, `\&`, `\|`, `\"`). `-` is in nearly every etree recording identifier, so `identifier:(gd77\-05\-08...)` fails while the unescaped form works. Drop `+ - & | "` from the escape set (they don't need escaping mid-value on archive.org anyway). Keep escaping the parser characters #70 needed (`( ) { } [ ] ^ ~ : \ / !`), which ES accepts escaped. Verified live against advancedsearch.php. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Cf7er8mrabacGCMtLjd6mQ --- InternetArchiveKit/InternetArchiveQuery.swift | 10 +++++++--- .../InternetArchiveQueryTests.swift | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) 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() {