Skip to content

Commit 4ef4e9a

Browse files
authored
Merge pull request #5 from brokenhandsio/complex-search
Complex Search API
2 parents 0ddebc2 + 470e45d commit 4ef4e9a

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,43 @@ extension ElasticsearchClient {
176176
}
177177
}
178178

179+
public func searchDocumentsCount<Query: Encodable>(from indexName: String, query: Query) -> EventLoopFuture<ESCountResponse> {
180+
do {
181+
let url = try buildURL(path: "/\(indexName)/_count")
182+
let body = try ByteBuffer(data: self.jsonEncoder.encode(query))
183+
var headers = HTTPHeaders()
184+
headers.add(name: "content-type", value: "application/json")
185+
return sendRequest(url: url, method: .GET, headers: headers, body: body)
186+
} catch {
187+
return self.eventLoop.makeFailedFuture(error)
188+
}
189+
}
190+
191+
public func searchDocumentsPaginated<Document: Decodable, QueryBody: Encodable>(from indexName: String, queryBody: QueryBody, size: Int = 10, offset: Int = 0, type: Document.Type = Document.self) -> EventLoopFuture<ESGetMultipleDocumentsResponse<Document>> {
192+
do {
193+
let url = try buildURL(path: "/\(indexName)/_search")
194+
let queryBody = ESComplexSearchRequest(from: offset, size: size, query: queryBody)
195+
let body = try ByteBuffer(data: self.jsonEncoder.encode(queryBody))
196+
var headers = HTTPHeaders()
197+
headers.add(name: "content-type", value: "application/json")
198+
return sendRequest(url: url, method: .GET, headers: headers, body: body)
199+
} catch {
200+
return self.eventLoop.makeFailedFuture(error)
201+
}
202+
}
203+
204+
public func customSearch<Document: Decodable, Query: Encodable>(from indexName: String, query: Query, type: Document.Type = Document.self) -> EventLoopFuture<ESGetMultipleDocumentsResponse<Document>> {
205+
do {
206+
let url = try buildURL(path: "/\(indexName)/_search")
207+
let body = try ByteBuffer(data: self.jsonEncoder.encode(query))
208+
var headers = HTTPHeaders()
209+
headers.add(name: "content-type", value: "application/json")
210+
return sendRequest(url: url, method: .GET, headers: headers, body: body)
211+
} catch {
212+
return self.eventLoop.makeFailedFuture(error)
213+
}
214+
}
215+
179216
public func deleteIndex(_ name: String) -> EventLoopFuture<ESDeleteIndexResponse> {
180217
do {
181218
let url = try buildURL(path: "/\(name)")

Sources/ElasticsearchNIOClient/Models/ESSearchRequest.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ struct ESSearchRequest: Codable {
1212
}
1313
}
1414

15+
struct ESComplexSearchRequest<Query: Encodable>: Encodable {
16+
let from: Int
17+
let size: Int
18+
let query: Query
19+
}
20+
1521
struct ESSearchQueryString: Codable {
1622
let queryString: ESSearchQuery
1723

Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,101 @@ class ElasticSearchIntegrationTests: XCTestCase {
328328
XCTAssertEqual(retrievedItem.source.count, 1)
329329
}
330330

331+
func testCountWithQueryBody() throws {
332+
try setupItems()
333+
334+
struct SearchQuery: Encodable {
335+
let query: QueryBody
336+
}
337+
338+
struct QueryBody: Encodable {
339+
let queryString: QueryString
340+
341+
enum CodingKeys: String, CodingKey {
342+
case queryString = "query_string"
343+
}
344+
}
345+
346+
struct QueryString: Encodable {
347+
let query: String
348+
}
349+
350+
let queryString = QueryString(query: "Apples")
351+
let queryBody = QueryBody(queryString: queryString)
352+
let searchQuery = SearchQuery(query: queryBody)
353+
let results = try client.searchDocumentsCount(from: indexName, query: searchQuery).wait()
354+
XCTAssertEqual(results.count, 5)
355+
}
356+
357+
func testPaginationQueryWithQueryBody() throws {
358+
for index in 1...100 {
359+
let name = "Some \(index) Apples"
360+
let item = SomeItem(id: UUID(), name: name)
361+
_ = try client.createDocument(item, in: self.indexName).wait()
362+
}
363+
364+
// This is required for ES to settle and load the indexes to return the right results
365+
Thread.sleep(forTimeInterval: 1.0)
366+
367+
struct QueryBody: Encodable {
368+
let queryString: QueryString
369+
370+
enum CodingKeys: String, CodingKey {
371+
case queryString = "query_string"
372+
}
373+
}
374+
375+
struct QueryString: Encodable {
376+
let query: String
377+
}
378+
379+
let queryString = QueryString(query: "Apples")
380+
let queryBody = QueryBody(queryString: queryString)
381+
382+
let results: ESGetMultipleDocumentsResponse<SomeItem> = try client.searchDocumentsPaginated(from: indexName, queryBody: queryBody, size: 20, offset: 10).wait()
383+
XCTAssertEqual(results.hits.hits.count, 20)
384+
XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 11 Apples" }))
385+
XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" }))
386+
}
387+
388+
func testCustomSearch() throws {
389+
for index in 1...100 {
390+
let name = "Some \(index) Apples"
391+
let item = SomeItem(id: UUID(), name: name)
392+
_ = try client.createDocument(item, in: self.indexName).wait()
393+
}
394+
395+
// This is required for ES to settle and load the indexes to return the right results
396+
Thread.sleep(forTimeInterval: 1.0)
397+
398+
struct Query: Encodable {
399+
let query: QueryBody
400+
let from: Int
401+
let size: Int
402+
}
403+
404+
struct QueryBody: Encodable {
405+
let queryString: QueryString
406+
407+
enum CodingKeys: String, CodingKey {
408+
case queryString = "query_string"
409+
}
410+
}
411+
412+
struct QueryString: Encodable {
413+
let query: String
414+
}
415+
416+
let queryString = QueryString(query: "Apples")
417+
let queryBody = QueryBody(queryString: queryString)
418+
let query = Query(query: queryBody, from: 10, size: 20)
419+
420+
let results: ESGetMultipleDocumentsResponse<SomeItem> = try client.customSearch(from: indexName, query: query).wait()
421+
XCTAssertEqual(results.hits.hits.count, 20)
422+
XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 11 Apples" }))
423+
XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" }))
424+
}
425+
331426
// MARK: - Private
332427
private func setupItems() throws {
333428
for index in 1...10 {

0 commit comments

Comments
 (0)