Skip to content

Commit d25b70e

Browse files
authored
Merge pull request #3 from rcarver/term
Add Term, TermsOR and TermsAND
2 parents e1317d2 + 6d8424c commit d25b70e

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

Sources/ElasticsearchQueryBuilder/Components.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,82 @@ extension esb {
199199
}
200200
}
201201

202+
/// Adds `term` block to the query syntax.
203+
///
204+
/// Excludes the component if value is nil.
205+
public struct Term: DictComponent {
206+
let field: String
207+
let value: String?
208+
public init(_ field: String, _ value: String?) {
209+
self.field = field
210+
self.value = value
211+
}
212+
public init<V: RawRepresentable>(_ field: String, _ value: V?) where V.RawValue == String {
213+
self.field = field
214+
self.value = value?.rawValue
215+
}
216+
public init<V: CustomStringConvertible>(_ field: String, describing value: V?) {
217+
self.field = field
218+
self.value = value?.description
219+
}
220+
public func makeDict() -> QueryDict {
221+
guard let value = self.value else { return [:] }
222+
return [ "term" : [ self.field : .string(value) ] ]
223+
}
224+
}
225+
226+
/// Adds multiple `term` block to the query syntax.
227+
///
228+
/// Excludes the component if values is empty.
229+
public struct TermsAND: ArrayComponent {
230+
let field: String
231+
let values: [String]
232+
public init(_ field: String, _ values: [String]) {
233+
self.field = field
234+
self.values = values
235+
}
236+
public init<V>(_ field: String, _ values: [V]) where V: RawRepresentable, V.RawValue == String {
237+
self.field = field
238+
self.values = values.map(\.rawValue)
239+
}
240+
public init<V>(_ field: String, describing values: [V]) where V: CustomStringConvertible {
241+
self.field = field
242+
self.values = values.map(\.description)
243+
}
244+
public func makeArray() -> [QueryDict] {
245+
return self.values.map {
246+
[ "term" : [ self.field : .string($0) ] ]
247+
}
248+
}
249+
}
250+
251+
/// Adds a `terms` block to the query syntax.
252+
///
253+
/// Excludes the component if values is empty.
254+
public struct TermsOR: DictComponent {
255+
let field: String
256+
let values: [String]
257+
public init(_ field: String, _ values: [String]) {
258+
self.field = field
259+
self.values = values
260+
}
261+
public init<V>(_ field: String, _ values: [V]) where V: RawRepresentable, V.RawValue == String {
262+
self.field = field
263+
self.values = values.map(\.rawValue)
264+
}
265+
public init<V>(_ field: String, describing values: [V]) where V: CustomStringConvertible {
266+
self.field = field
267+
self.values = values.map(\.description)
268+
}
269+
public func makeDict() -> QueryDict {
270+
if self.values.isEmpty {
271+
return [:]
272+
} else {
273+
return [ "terms" : [ self.field : .array(self.values) ] ]
274+
}
275+
}
276+
}
277+
202278
/// Adds `knn` block to the query syntax.
203279
public struct kNearestNeighbor<Component: ArrayComponent>: DictComponent {
204280
let field: String

Tests/ElasticsearchQueryBuilderTests/ComponentTests.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,64 @@ final class BoolTests: XCTestCase {
256256
}
257257
}
258258

259+
final class TermTests: XCTestCase {
260+
func testBuild() throws {
261+
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {
262+
esb.Term("name", "joe")
263+
}
264+
XCTAssertNoDifference(build().makeQuery(), [
265+
"term": [ "name": "joe" ]
266+
])
267+
}
268+
func testBuildEmpty() throws {
269+
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {
270+
esb.Term("name", nil)
271+
}
272+
XCTAssertNoDifference(build().makeQuery(), [:])
273+
}
274+
}
275+
276+
final class TermsORTests: XCTestCase {
277+
func testBuild() throws {
278+
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {
279+
esb.TermsOR("name", ["joe", "mary"])
280+
}
281+
XCTAssertNoDifference(build().makeQuery(), [
282+
"terms": [ "name": ["joe", "mary"] ]
283+
])
284+
}
285+
func testBuildEmpty() throws {
286+
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {
287+
esb.TermsOR("name", [])
288+
}
289+
XCTAssertNoDifference(build().makeQuery(), [:])
290+
}
291+
}
292+
293+
final class TermsANDTests: XCTestCase {
294+
func testBuild() throws {
295+
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {
296+
esb.Filter {
297+
esb.TermsAND("name", ["joe", "mary"])
298+
}
299+
}
300+
XCTAssertNoDifference(build().makeQuery(), [
301+
"filter": [
302+
[ "term": [ "name": "joe" ] ],
303+
[ "term": [ "name": "mary" ] ],
304+
]
305+
])
306+
}
307+
func testBuildEmpty() throws {
308+
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {
309+
esb.Filter {
310+
esb.TermsAND("name", [])
311+
}
312+
}
313+
XCTAssertNoDifference(build().makeQuery(), [:])
314+
}
315+
}
316+
259317
final class KNearestNeighborTests: XCTestCase {
260318
func testBuildBasic() throws {
261319
@ElasticsearchQueryBuilder func build() -> some esb.QueryDSL {

0 commit comments

Comments
 (0)