From 26d978baaccbe45360e0f97fae415c36099f80b1 Mon Sep 17 00:00:00 2001 From: Ryan Carver Date: Mon, 13 Jan 2025 14:23:52 -0800 Subject: [PATCH 1/2] add Aggs types --- .../Components.swift | 83 +++++++++++++++---- .../ComponentTests.swift | 54 ++++++++++++ 2 files changed, 121 insertions(+), 16 deletions(-) diff --git a/Sources/ElasticsearchQueryBuilder/Components.swift b/Sources/ElasticsearchQueryBuilder/Components.swift index 1ff87b4..8b1fbca 100644 --- a/Sources/ElasticsearchQueryBuilder/Components.swift +++ b/Sources/ElasticsearchQueryBuilder/Components.swift @@ -60,22 +60,6 @@ extension esb { } } - /// Adds `query` block to the query syntax. - public struct Query: DictComponent { - var component: Component - public init(@QueryDictBuilder component: () -> Component) { - self.component = component() - } - public func makeDict() -> QueryDict { - let dict = self.component.makeDict() - if dict.isEmpty { - return [:] - } else { - return [ "query" : .dict(self.component.makeDict()) ] - } - } - } - /// Adds a `key` with any type of value to the query syntax. public struct Key: DictComponent { var key: String @@ -108,6 +92,73 @@ extension esb { } } + /// Adds a block to the syntax. + public struct Dict: DictComponent { + var key: String + var component: Component + public init(_ key: String, @QueryDictBuilder component: () -> Component) { + self.key = key + self.component = component() + } + public func makeDict() -> QueryDict { + let dict = self.component.makeDict() + if dict.isEmpty { + return [:] + } else { + return [ key : .dict(self.component.makeDict()) ] + } + } + } + + /// Adds a `query` block to the syntax. + public struct Query: DictComponent { + var component: Component + public init(@QueryDictBuilder component: () -> Component) { + self.component = component() + } + public func makeDict() -> QueryDict { + let dict = self.component.makeDict() + if dict.isEmpty { + return [:] + } else { + return [ "query" : .dict(self.component.makeDict()) ] + } + } + } + + /// Adds an `aggs` block to the syntax. + public struct Aggs: DictComponent { + var component: Component + public init(@QueryDictBuilder component: () -> Component) { + self.component = component() + } + public func makeDict() -> QueryDict { + let dict = self.component.makeDict() + if dict.isEmpty { + return [:] + } else { + return [ "aggs" : .dict(self.component.makeDict()) ] + } + } + } + + /// Defines and named aggregate within `Aggs` + public struct Agg: DictComponent { + var name: String + var term: QueryDict + public init(_ name: String, field: String) { + self.name = name + self.term = [ "field" : .string(field) ] + } + public init(_ name: String, term: QueryDict) { + self.name = name + self.term = term + } + public func makeDict() -> QueryDict { + return [ self.name : [ "terms" : .dict(self.term) ] ] + } + } + /// Adds `minimum_should_match` to the query syntax. public struct MinimumShouldMatch: DictComponent { var count: Int diff --git a/Tests/ElasticsearchQueryBuilderTests/ComponentTests.swift b/Tests/ElasticsearchQueryBuilderTests/ComponentTests.swift index c67b212..cd6f10a 100644 --- a/Tests/ElasticsearchQueryBuilderTests/ComponentTests.swift +++ b/Tests/ElasticsearchQueryBuilderTests/ComponentTests.swift @@ -43,6 +43,35 @@ final class KeyTests: XCTestCase { } } +final class DictTests: XCTestCase { + func testBuildDict() throws { + @ElasticsearchQueryBuilder func build() -> some esb.QueryDSL { + esb.Dict("query") { + esb.Key("match_bool_prefix") { + [ + "message": "quick brown f" + ] + } + } + } + expectNoDifference(build().makeQuery(), [ + "query": [ + "match_bool_prefix": [ + "message": "quick brown f" + ] + ] + ]) + } + func testBuildDictEmpty() throws { + @ElasticsearchQueryBuilder func build() -> some esb.QueryDSL { + esb.Dict("query") { + esb.Key("match", .dict([:])) + } + } + expectNoDifference(build().makeQuery(), [:]) + } +} + final class ComposableBuilderTests: XCTestCase { func testBuildDict() throws { @QueryDictBuilder func makeKey() -> some DictComponent { @@ -153,6 +182,31 @@ final class QueryTests: XCTestCase { } } +final class AggsTests: XCTestCase { + func testBuild() throws { + @ElasticsearchQueryBuilder func build() -> some esb.QueryDSL { + esb.Aggs { + esb.Agg("name", field: "name") + } + } + expectNoDifference(build().makeQuery(), [ + "aggs": [ + "name": [ + "terms": [ "field": "name" ] + ] + ] + ]) + } + func testBuildEmpty() throws { + @ElasticsearchQueryBuilder func build() -> some esb.QueryDSL { + esb.Aggs { + esb.Key("name", .dict([:])) + } + } + expectNoDifference(build().makeQuery(), [:]) + } +} + final class BoolTests: XCTestCase { func testBuild() throws { @ElasticsearchQueryBuilder func build(_ enabled: Bool) -> some esb.QueryDSL { From 920ae3e959f469af760458488da0bdcc2bc52ef9 Mon Sep 17 00:00:00 2001 From: Ryan Carver Date: Mon, 13 Jan 2025 14:25:51 -0800 Subject: [PATCH 2/2] update expectNoDifference --- Package.resolved | 4 +- .../BuilderTests.swift | 38 +++++++++---------- .../EncodingTests.swift | 20 ++++++---- .../QueryValueTests.swift | 12 +++--- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/Package.resolved b/Package.resolved index dcf3367..f94e0c7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "27d767d643fa2cf083d0a73d74fa84cacb53e85c", - "version" : "1.4.1" + "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", + "version" : "1.4.3" } } ], diff --git a/Tests/ElasticsearchQueryBuilderTests/BuilderTests.swift b/Tests/ElasticsearchQueryBuilderTests/BuilderTests.swift index e4a1c37..11237b0 100644 --- a/Tests/ElasticsearchQueryBuilderTests/BuilderTests.swift +++ b/Tests/ElasticsearchQueryBuilderTests/BuilderTests.swift @@ -16,7 +16,7 @@ final class ElasticsearchQueryBuilderTests: XCTestCase { esb.Pagination(from: 10) } let query = build(tags: nil) - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "query": [ "match": [ "title": "Hello World" @@ -32,11 +32,11 @@ final class ElasticsearchQueryBuilderTests: XCTestCase { } } let queryTrue = build(bool: true) - XCTAssertNoDifference(queryTrue.makeQuery(), [ + expectNoDifference(queryTrue.makeQuery(), [ "from": 10 ]) let queryFalse = build(bool: false) - XCTAssertNoDifference(queryFalse.makeQuery(), [:]) + expectNoDifference(queryFalse.makeQuery(), [:]) } } @@ -46,7 +46,7 @@ final class DictQueryBuilderTests: XCTestCase { esb.Pagination(from: 10) } let query = build(tags: nil) - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "from": 10 ]) } @@ -56,7 +56,7 @@ final class DictQueryBuilderTests: XCTestCase { esb.Pagination(size: 20) } let query = build(tags: nil) - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "from": 10, "size": 20 ]) @@ -70,13 +70,13 @@ final class DictQueryBuilderTests: XCTestCase { } } let queryTrue = build(bool: true) - XCTAssertNoDifference(queryTrue.makeQuery(), [ + expectNoDifference(queryTrue.makeQuery(), [ "query": [ "from": 10 ] ]) let queryFalse = build(bool: false) - XCTAssertNoDifference(queryFalse.makeQuery(), [:]) + expectNoDifference(queryFalse.makeQuery(), [:]) } func testBuildEither() throws { @ElasticsearchQueryBuilder func build(bool: Bool) -> some esb.QueryDSL { @@ -89,13 +89,13 @@ final class DictQueryBuilderTests: XCTestCase { } } let queryTrue = build(bool: true) - XCTAssertNoDifference(queryTrue.makeQuery(), [ + expectNoDifference(queryTrue.makeQuery(), [ "query": [ "from": 10 ] ]) let queryFalse = build(bool: false) - XCTAssertNoDifference(queryFalse.makeQuery(), [ + expectNoDifference(queryFalse.makeQuery(), [ "query": [ "from": 20, ] @@ -115,7 +115,7 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let query = build() - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "should": [ [ "match": [ "title": "Hello World" ] ] ] @@ -137,7 +137,7 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let query = build() - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "should": [ [ "match": [ "title": "Hello World" ] ], [ "match": [ "content": "Elasticsearch" ] ], @@ -156,7 +156,7 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let query = build() - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "should": [ [ "match": [ "title": "Hello World" ] ], [ "from": 10 ] @@ -181,13 +181,13 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let queryFalse = build(title: nil) - XCTAssertNoDifference(queryFalse.makeQuery(), [ + expectNoDifference(queryFalse.makeQuery(), [ "should": [ [ "match": [ "content": "Elasticsearch" ] ], ] ]) let queryTrue = build(title: "Hello World") - XCTAssertNoDifference(queryTrue.makeQuery(), [ + expectNoDifference(queryTrue.makeQuery(), [ "should": [ [ "match": [ "title": "Hello World" ] ], [ "match": [ "content": "Elasticsearch" ] ], @@ -214,9 +214,9 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let queryFalse = build(title: nil) - XCTAssertNoDifference(queryFalse.makeQuery(), [:]) + expectNoDifference(queryFalse.makeQuery(), [:]) let queryTrue = build(title: "Hello World") - XCTAssertNoDifference(queryTrue.makeQuery(), [ + expectNoDifference(queryTrue.makeQuery(), [ "should": [ [ "match": [ "title": "Hello World" ] ], [ "match": [ "content": "Hello World" ] ], @@ -234,13 +234,13 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let queryTrue = build(true) - XCTAssertNoDifference(queryTrue.makeQuery(), [ + expectNoDifference(queryTrue.makeQuery(), [ "should": [ [ "from": 10 ] ] ]) let queryFalse = build(false) - XCTAssertNoDifference(queryFalse.makeQuery(), [ + expectNoDifference(queryFalse.makeQuery(), [ "should": [ [ "from": 20 ] ] @@ -259,7 +259,7 @@ final class ArrayQueryBuilderTests: XCTestCase { } } let query = build() - XCTAssertNoDifference(query.makeQuery(), [ + expectNoDifference(query.makeQuery(), [ "should": [ [ "match": [ "title": "Hello" ] ], [ "match": [ "title": "World" ] ], diff --git a/Tests/ElasticsearchQueryBuilderTests/EncodingTests.swift b/Tests/ElasticsearchQueryBuilderTests/EncodingTests.swift index b42b294..dd16fc7 100644 --- a/Tests/ElasticsearchQueryBuilderTests/EncodingTests.swift +++ b/Tests/ElasticsearchQueryBuilderTests/EncodingTests.swift @@ -8,27 +8,31 @@ final class EncodingTests: XCTestCase { func assertFormattedDate( _ value: QueryValue, _ want: String, - file: StaticString = #file, - line: UInt = #line + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column ) throws { let encoder = JSONEncoder() let got = try encoder.encode(value) - let gotString = try XCTUnwrap(String(data: got, encoding: .utf8), file: file, line: line) - XCTAssertNoDifference(gotString, want, file: file, line: line) + let gotString = try XCTUnwrap(String(data: got, encoding: .utf8), file: filePath, line: line) + expectNoDifference(gotString, want, fileID: fileID, filePath: filePath, line: line, column: column) } func assertFormattedDate( _ value: Date, dateEncodingStrategy: JSONEncoder.DateEncodingStrategy, _ want: String, - file: StaticString = #file, - line: UInt = #line + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column ) throws { let encoder = JSONEncoder() encoder.dateEncodingStrategy = dateEncodingStrategy let got = try encoder.encode(value) - let gotString = try XCTUnwrap(String(data: got, encoding: .utf8), file: file, line: line) - XCTAssertNoDifference(gotString, want, file: file, line: line) + let gotString = try XCTUnwrap(String(data: got, encoding: .utf8), file: filePath, line: line) + expectNoDifference(gotString, want, fileID: fileID, filePath: filePath, line: line, column: column) } let date = Date(timeIntervalSince1970: 1001.12345) diff --git a/Tests/ElasticsearchQueryBuilderTests/QueryValueTests.swift b/Tests/ElasticsearchQueryBuilderTests/QueryValueTests.swift index 4884e02..73f5a05 100644 --- a/Tests/ElasticsearchQueryBuilderTests/QueryValueTests.swift +++ b/Tests/ElasticsearchQueryBuilderTests/QueryValueTests.swift @@ -6,26 +6,26 @@ import XCTest final class QueryValueTests: XCTestCase { func test_array() { - XCTAssertNoDifference( + expectNoDifference( QueryValue.array([Float(0.1), 0.2]), QueryValue.array([.float(0.1), .float(0.2)]) ) - XCTAssertNoDifference( + expectNoDifference( QueryValue.array([Double(0.1), 0.2]), QueryValue.array([.float(0.1), .float(0.2)]) ) - XCTAssertNoDifference( + expectNoDifference( QueryValue.array([1, 2]), QueryValue.array([.int(1), .int(2)]) ) - XCTAssertNoDifference( + expectNoDifference( QueryValue.array(["a", "b"]), QueryValue.array([.string("a"), .string("b")]) ) struct Custom: CustomStringConvertible { var description: String } - XCTAssertNoDifference( + expectNoDifference( QueryValue.array(describing: [Custom(description: "a"), Custom(description: "b")]), QueryValue.array([.string("a"), .string("b")]) ) @@ -35,7 +35,7 @@ final class QueryValueTests: XCTestCase { struct Custom: CustomStringConvertible { var description: String } - XCTAssertNoDifference( + expectNoDifference( QueryValue.string(describing: Custom(description: "a")), QueryValue.string("a") )