From 26d978baaccbe45360e0f97fae415c36099f80b1 Mon Sep 17 00:00:00 2001 From: Ryan Carver Date: Mon, 13 Jan 2025 14:23:52 -0800 Subject: [PATCH] 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 {