From b46743fdd9c968416b055ec39e2f663836131436 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Mon, 17 May 2021 18:00:08 +0100 Subject: [PATCH 1/5] Add some tests for searching with a query body --- .../ElasticsearchClient+Requests.swift | 23 +++++++++++++ .../Models/ESSearchRequest.swift | 6 ++++ .../ElasticsearchNIOClientTests.swift | 33 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift index 745188d..43e3e46 100644 --- a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift +++ b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift @@ -176,6 +176,29 @@ extension ElasticsearchClient { } } + public func searchDocumentsCount(from indexName: String, query: Query) -> EventLoopFuture { + do { + let url = try buildURL(path: "/\(indexName)/_count") + let body = try ByteBuffer(data: self.jsonEncoder.encode(query)) + return sendRequest(url: url, method: .GET, headers: .init(), body: body) + } catch { + return self.eventLoop.makeFailedFuture(error) + } + } + + public func searchDocumentsPagination(from indexName: String, query: Query, size: Int = 10, offset: Int = 0, type: Document.Type = Document.self) -> EventLoopFuture> { + do { + let url = try buildURL(path: "/\(indexName)/_search") + let queryBody = ESComplexSearchRequest(from: offset, size: size, query: query) + let body = try ByteBuffer(data: self.jsonEncoder.encode(queryBody)) + var headers = HTTPHeaders() + headers.add(name: "content-type", value: "application/json") + return sendRequest(url: url, method: .GET, headers: headers, body: body) + } catch { + return self.eventLoop.makeFailedFuture(error) + } + } + public func deleteIndex(_ name: String) -> EventLoopFuture { do { let url = try buildURL(path: "/\(name)") diff --git a/Sources/ElasticsearchNIOClient/Models/ESSearchRequest.swift b/Sources/ElasticsearchNIOClient/Models/ESSearchRequest.swift index ce1499c..aa402b9 100644 --- a/Sources/ElasticsearchNIOClient/Models/ESSearchRequest.swift +++ b/Sources/ElasticsearchNIOClient/Models/ESSearchRequest.swift @@ -12,6 +12,12 @@ struct ESSearchRequest: Codable { } } +struct ESComplexSearchRequest: Encodable { + let from: Int + let size: Int + let query: Query +} + struct ESSearchQueryString: Codable { let queryString: ESSearchQuery diff --git a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift index e326504..a289a9d 100644 --- a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift +++ b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift @@ -328,6 +328,39 @@ class ElasticSearchIntegrationTests: XCTestCase { XCTAssertEqual(retrievedItem.source.count, 1) } + func testCountWithQueryBody() throws { + try setupItems() + + struct SearchQuery: Encodable { + let query: QueryBody + } + + struct QueryBody: Encodable { + let term: String + } + + let queryBody = QueryBody(term: "Apples") + let searchQuery = SearchQuery(query: queryBody) + let results = try client.searchDocumentsCount(from: indexName, query: searchQuery).wait() + XCTAssertEqual(results.count, 5) + } + + func testPaginationQueryWithQueryBody() throws { + for index in 1...100 { + let name = "Some \(index) Apples" + let item = SomeItem(id: UUID(), name: name) + _ = try client.createDocument(item, in: self.indexName).wait() + } + + // This is required for ES to settle and load the indexes to return the right results + Thread.sleep(forTimeInterval: 1.0) + + let results: ESGetMultipleDocumentsResponse = try client.searchDocumentsPaginated(from: indexName, searchTerm: "Apples", size: 20, offset: 10).wait() + XCTAssertEqual(results.hits.hits.count, 20) + XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 11 Apples" })) + XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" })) + } + // MARK: - Private private func setupItems() throws { for index in 1...10 { From e03add554c04740bef83f15e331398e64f80912d Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Mon, 17 May 2021 18:01:29 +0100 Subject: [PATCH 2/5] Add correct headers to search --- .../ElasticsearchNIOClient/ElasticsearchClient+Requests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift index 43e3e46..3ad6cc8 100644 --- a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift +++ b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift @@ -180,7 +180,9 @@ extension ElasticsearchClient { do { let url = try buildURL(path: "/\(indexName)/_count") let body = try ByteBuffer(data: self.jsonEncoder.encode(query)) - return sendRequest(url: url, method: .GET, headers: .init(), body: body) + var headers = HTTPHeaders() + headers.add(name: "content-type", value: "application/json") + return sendRequest(url: url, method: .GET, headers: headers, body: body) } catch { return self.eventLoop.makeFailedFuture(error) } From 2e580c17356f4a332eca319c18f8ab3eb77cef72 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 18 May 2021 09:59:40 +0100 Subject: [PATCH 3/5] Add tests for passing search body through --- .../ElasticsearchClient+Requests.swift | 2 +- .../ElasticsearchNIOClientTests.swift | 35 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift index 3ad6cc8..d0e0498 100644 --- a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift +++ b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift @@ -188,7 +188,7 @@ extension ElasticsearchClient { } } - public func searchDocumentsPagination(from indexName: String, query: Query, size: Int = 10, offset: Int = 0, type: Document.Type = Document.self) -> EventLoopFuture> { + public func searchDocumentsPaginated(from indexName: String, query: Query, size: Int = 10, offset: Int = 0, type: Document.Type = Document.self) -> EventLoopFuture> { do { let url = try buildURL(path: "/\(indexName)/_search") let queryBody = ESComplexSearchRequest(from: offset, size: size, query: query) diff --git a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift index a289a9d..bc3467e 100644 --- a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift +++ b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift @@ -336,10 +336,19 @@ class ElasticSearchIntegrationTests: XCTestCase { } struct QueryBody: Encodable { - let term: String + let queryString: QueryString + + enum CodingKeys: String, CodingKey { + case queryString = "query_string" + } } - let queryBody = QueryBody(term: "Apples") + struct QueryString: Encodable { + let query: String + } + + let queryString = QueryString(query: "Apples") + let queryBody = QueryBody(queryString: queryString) let searchQuery = SearchQuery(query: queryBody) let results = try client.searchDocumentsCount(from: indexName, query: searchQuery).wait() XCTAssertEqual(results.count, 5) @@ -355,7 +364,27 @@ class ElasticSearchIntegrationTests: XCTestCase { // This is required for ES to settle and load the indexes to return the right results Thread.sleep(forTimeInterval: 1.0) - let results: ESGetMultipleDocumentsResponse = try client.searchDocumentsPaginated(from: indexName, searchTerm: "Apples", size: 20, offset: 10).wait() + struct SearchQuery: Encodable { + let query: QueryBody + } + + struct QueryBody: Encodable { + let queryString: QueryString + + enum CodingKeys: String, CodingKey { + case queryString = "query_string" + } + } + + struct QueryString: Encodable { + let query: String + } + + let queryString = QueryString(query: "Apples") + let queryBody = QueryBody(queryString: queryString) + let searchQuery = SearchQuery(query: queryBody) + + let results: ESGetMultipleDocumentsResponse = try client.searchDocumentsPaginated(from: indexName, query: searchQuery, size: 20, offset: 10).wait() XCTAssertEqual(results.hits.hits.count, 20) XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 11 Apples" })) XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" })) From b123290bdbec3c6cdd6e1f8593d681b6b3a88911 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 18 May 2021 10:00:56 +0100 Subject: [PATCH 4/5] Add complex search tests --- .../ElasticsearchClient+Requests.swift | 4 ++-- .../ElasticsearchNIOClientTests.swift | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift index d0e0498..cdd5b81 100644 --- a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift +++ b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift @@ -188,10 +188,10 @@ extension ElasticsearchClient { } } - public func searchDocumentsPaginated(from indexName: String, query: Query, size: Int = 10, offset: Int = 0, type: Document.Type = Document.self) -> EventLoopFuture> { + public func searchDocumentsPaginated(from indexName: String, queryBody: QueryBody, size: Int = 10, offset: Int = 0, type: Document.Type = Document.self) -> EventLoopFuture> { do { let url = try buildURL(path: "/\(indexName)/_search") - let queryBody = ESComplexSearchRequest(from: offset, size: size, query: query) + let queryBody = ESComplexSearchRequest(from: offset, size: size, query: queryBody) let body = try ByteBuffer(data: self.jsonEncoder.encode(queryBody)) var headers = HTTPHeaders() headers.add(name: "content-type", value: "application/json") diff --git a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift index bc3467e..1ad1f33 100644 --- a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift +++ b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift @@ -364,10 +364,6 @@ class ElasticSearchIntegrationTests: XCTestCase { // This is required for ES to settle and load the indexes to return the right results Thread.sleep(forTimeInterval: 1.0) - struct SearchQuery: Encodable { - let query: QueryBody - } - struct QueryBody: Encodable { let queryString: QueryString @@ -382,9 +378,8 @@ class ElasticSearchIntegrationTests: XCTestCase { let queryString = QueryString(query: "Apples") let queryBody = QueryBody(queryString: queryString) - let searchQuery = SearchQuery(query: queryBody) - let results: ESGetMultipleDocumentsResponse = try client.searchDocumentsPaginated(from: indexName, query: searchQuery, size: 20, offset: 10).wait() + let results: ESGetMultipleDocumentsResponse = try client.searchDocumentsPaginated(from: indexName, queryBody: queryBody, size: 20, offset: 10).wait() XCTAssertEqual(results.hits.hits.count, 20) XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 11 Apples" })) XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" })) From 470e45d965e72582d3ea7df4345e8f5ef3740cd7 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 18 May 2021 10:04:35 +0100 Subject: [PATCH 5/5] Allow query body to be completely custom --- .../ElasticsearchClient+Requests.swift | 12 ++++++ .../ElasticsearchNIOClientTests.swift | 38 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift index cdd5b81..e2e2dba 100644 --- a/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift +++ b/Sources/ElasticsearchNIOClient/ElasticsearchClient+Requests.swift @@ -201,6 +201,18 @@ extension ElasticsearchClient { } } + public func customSearch(from indexName: String, query: Query, type: Document.Type = Document.self) -> EventLoopFuture> { + do { + let url = try buildURL(path: "/\(indexName)/_search") + let body = try ByteBuffer(data: self.jsonEncoder.encode(query)) + var headers = HTTPHeaders() + headers.add(name: "content-type", value: "application/json") + return sendRequest(url: url, method: .GET, headers: headers, body: body) + } catch { + return self.eventLoop.makeFailedFuture(error) + } + } + public func deleteIndex(_ name: String) -> EventLoopFuture { do { let url = try buildURL(path: "/\(name)") diff --git a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift index 1ad1f33..6cdbfe2 100644 --- a/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift +++ b/Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift @@ -385,6 +385,44 @@ class ElasticSearchIntegrationTests: XCTestCase { XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" })) } + func testCustomSearch() throws { + for index in 1...100 { + let name = "Some \(index) Apples" + let item = SomeItem(id: UUID(), name: name) + _ = try client.createDocument(item, in: self.indexName).wait() + } + + // This is required for ES to settle and load the indexes to return the right results + Thread.sleep(forTimeInterval: 1.0) + + struct Query: Encodable { + let query: QueryBody + let from: Int + let size: Int + } + + struct QueryBody: Encodable { + let queryString: QueryString + + enum CodingKeys: String, CodingKey { + case queryString = "query_string" + } + } + + struct QueryString: Encodable { + let query: String + } + + let queryString = QueryString(query: "Apples") + let queryBody = QueryBody(queryString: queryString) + let query = Query(query: queryBody, from: 10, size: 20) + + let results: ESGetMultipleDocumentsResponse = try client.customSearch(from: indexName, query: query).wait() + XCTAssertEqual(results.hits.hits.count, 20) + XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 11 Apples" })) + XCTAssertTrue(results.hits.hits.contains(where: { $0.source.name == "Some 29 Apples" })) + } + // MARK: - Private private func setupItems() throws { for index in 1...10 {