Skip to content

Commit

Permalink
Merge pull request #76 from pksprojects/feature/_field_caps_support
Browse files Browse the repository at this point in the history
add support for _field_caps requests
  • Loading branch information
prafsoni committed Jan 12, 2020
2 parents 2fbb9fb + 2651fe8 commit fb2a536
Show file tree
Hide file tree
Showing 6 changed files with 681 additions and 0 deletions.
21 changes: 21 additions & 0 deletions Sources/ElasticSwift/ElasticSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,16 @@ extension ElasticClient {
public func explain(_ explainRequest: ExplainRequest, completionHandler: @escaping (_ result: Result<ExplainResponse, Error>) -> Void) {
return execute(request: explainRequest, options: .default, completionHandler: completionHandler)
}

/// Asynchronously executes a request using the Field Capabilities API.
///
/// [Field Capabilities API on elastic.co](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-field-caps.html)
/// - Parameters:
/// - fieldCapabilitiesRequest: the request.
/// - completionHandler: callback to be invoked upon request completion.
public func fieldCaps(_ fieldCapabilitiesRequest: FieldCapabilitiesRequest, completionHandler: @escaping (_ result: Result<FieldCapabilitiesResponse, Error>) -> Void) {
return execute(request: fieldCapabilitiesRequest, options: .default, completionHandler: completionHandler)
}
}

extension ElasticClient {
Expand Down Expand Up @@ -383,6 +393,17 @@ extension ElasticClient {
public func explain(_ explainRequest: ExplainRequest, with options: RequestOptions, completionHandler: @escaping (_ result: Result<ExplainResponse, Error>) -> Void) {
return execute(request: explainRequest, options: options, completionHandler: completionHandler)
}

/// Asynchronously executes a request using the Field Capabilities API.
///
/// [Field Capabilities API on elastic.co](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-field-caps.html)
/// - Parameters:
/// - fieldCapabilitiesRequest: the request.
/// - options: the request options (e.g. headers), use `RequestOptions.default` if nothing to be customized.
/// - completionHandler: callback to be invoked upon request completion.
public func fieldCaps(_ fieldCapabilitiesRequest: FieldCapabilitiesRequest, with options: RequestOptions, completionHandler: @escaping (_ result: Result<FieldCapabilitiesResponse, Error>) -> Void) {
return execute(request: fieldCapabilitiesRequest, options: options, completionHandler: completionHandler)
}
}

/// Extention declaring various flavors of elasticsearch client
Expand Down
192 changes: 192 additions & 0 deletions Sources/ElasticSwift/Requests/FieldCaps.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
//
// FieldCaps.swift
// ElasticSwift
//
//
// Created by Prafull Kumar Soni on 1/12/20.
//

import ElasticSwiftCore
import ElasticSwiftQueryDSL
import Foundation
import NIOHTTP1

// MARK: - FieldCaps Request Builder

public class FieldCapabilitiesRequestBuilder: RequestBuilder {
public typealias RequestType = FieldCapabilitiesRequest

private var _indices: [String]?
private var _fields: [String]?

private var _ignoreUnavailable: Bool?
private var _allowNoIndices: Bool?
private var _expandWildcards: ExpandWildcards?

public init() {}

@discardableResult
public func set(indices: String...) -> Self {
_indices = indices
return self
}

@discardableResult
public func set(indices: [String]) -> Self {
_indices = indices
return self
}

@discardableResult
public func set(fields: String...) -> Self {
_fields = fields
return self
}

@discardableResult
public func set(fields: [String]) -> Self {
_fields = fields
return self
}

@discardableResult
public func add(index: String) -> Self {
if _indices != nil {
_indices?.append(index)
} else {
_indices = [index]
}
return self
}

@discardableResult
public func add(field: String) -> Self {
if _fields != nil {
_fields?.append(field)
} else {
_fields = [field]
}
return self
}

@discardableResult
public func set(ignoreUnavailable: Bool) -> Self {
_ignoreUnavailable = ignoreUnavailable
return self
}

@discardableResult
public func set(allowNoIndices: Bool) -> Self {
_allowNoIndices = allowNoIndices
return self
}

@discardableResult
public func set(expandWildcards: ExpandWildcards) -> Self {
_expandWildcards = expandWildcards
return self
}

public var indices: [String]? {
return _indices
}

public var fields: [String]? {
return _fields
}

public var ignoreUnavailable: Bool? {
return _ignoreUnavailable
}

public var allowNoIndices: Bool? {
return _allowNoIndices
}

public var expandWildcards: ExpandWildcards? {
return _expandWildcards
}

public func build() throws -> FieldCapabilitiesRequest {
return try FieldCapabilitiesRequest(withBuilder: self)
}
}

// MARK: - FieldCaps Request

public struct FieldCapabilitiesRequest: Request {
/// A list of index names; use `_all` or empty array to perform the operation on all indices
public let indices: [String]?
/// A list of field names
public let fields: [String]
/// Whether specified concrete indices should be ignored when unavailable (missing or closed)
public var ignoreUnavailable: Bool?
/// Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
public var allowNoIndices: Bool?
/// Whether to expand wildcard expression to concrete indices that are open, closed or both.
public var expandWildcards: ExpandWildcards?

public init(indices: [String]? = nil, fields: [String]) {
self.indices = indices
self.fields = fields
}

public init(indices: [String]? = nil, fields: String...) {
self.init(indices: indices, fields: fields)
}

internal init(withBuilder builder: FieldCapabilitiesRequestBuilder) throws {
guard builder.fields != nil else {
throw RequestBuilderError.missingRequiredField("fields")
}

guard !builder.fields!.isEmpty else {
throw RequestBuilderError.atlestOneElementRequired("fields")
}

self.init(indices: builder.indices, fields: builder.fields!)

ignoreUnavailable = builder.ignoreUnavailable
allowNoIndices = builder.allowNoIndices
expandWildcards = builder.expandWildcards
}

public var headers: HTTPHeaders {
return HTTPHeaders()
}

public var queryParams: [URLQueryItem] {
var params = [URLQueryItem]()
if let ignoreUnavailable = self.ignoreUnavailable {
params.append(.init(name: .ignoreUnavailable, value: ignoreUnavailable))
}
if let allowNoIndices = self.allowNoIndices {
params.append(.init(name: .allowNoIndices, value: allowNoIndices))
}
if let expandWildcards = self.expandWildcards {
params.append(.init(name: .expandWildcards, value: expandWildcards.rawValue))
}

params.append(.init(name: .fields, value: fields))

return params
}

public var method: HTTPMethod {
return .GET
}

public var endPoint: String {
var _endPoint = "_field_caps"
if let indices = self.indices, !indices.isEmpty {
_endPoint = indices.joined(separator: ",") + "/" + _endPoint
}
return _endPoint
}

public func makeBody(_: Serializer) -> Result<Data, MakeBodyError> {
return .failure(.noBodyForRequest)
}
}

extension FieldCapabilitiesRequest: Equatable {}
89 changes: 89 additions & 0 deletions Sources/ElasticSwift/Responses/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -797,3 +797,92 @@ public struct ExplainResponse: Codable {
}

extension ExplainResponse: Equatable {}

// MARK: - Field Capabilities Response

/// Response for FieldCapabilitiesIndexRequest requests.
public struct FieldCapabilitiesResponse {
public let fields: [String: [String: FieldCapabilities]]
}

extension FieldCapabilitiesResponse: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let fieldsResponse = try container.decode([String: [String: FieldCaps]].self, forKey: .fields)
var fields = [String: [String: FieldCapabilities]]()
for field in fieldsResponse.keys {
for type in fieldsResponse[field]!.keys {
let fieldCaps = fieldsResponse[field]![type]!
if fields.keys.contains(field) {
fields[field]![type] = fieldCaps.toFieldCapabilities(field, type: type)
} else {
fields[field] = [String: FieldCapabilities]()
fields[field]![type] = fieldCaps.toFieldCapabilities(field, type: type)
}
}
}
self.fields = fields
}

public func encode(to encoder: Encoder) throws {
var fields = [String: [String: FieldCaps]]()
for field in self.fields.keys {
for type in self.fields[field]!.keys {
let fieldCaps = self.fields[field]![type]!
if fields.keys.contains(field) {
fields[field]![type] = FieldCaps.fromFieldCapabilities(fieldCaps)
} else {
fields[field] = [String: FieldCaps]()
fields[field]![type] = FieldCaps.fromFieldCapabilities(fieldCaps)
}
}
}
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fields, forKey: .fields)
}

internal struct FieldCaps: Codable, Equatable {
public let type: String?
public let searchable: Bool
public let aggregatable: Bool
public let indices: [String]?
public let nonSearchableIndices: [String]?
public let nonAggregatableIndicies: [String]?

func toFieldCapabilities(_ name: String, type: String) -> FieldCapabilities {
return FieldCapabilities(name: name, type: type, isSearchable: searchable, isAggregatable: aggregatable, indices: indices, nonSearchableIndices: nonSearchableIndices, nonAggregatableIndicies: nonAggregatableIndicies)
}

static func fromFieldCapabilities(_ caps: FieldCapabilities) -> FieldCaps {
return FieldCaps(type: caps.type, searchable: caps.isSearchable, aggregatable: caps.isAggregatable, indices: caps.indices, nonSearchableIndices: caps.nonSearchableIndices, nonAggregatableIndicies: caps.nonAggregatableIndicies)
}

enum CodingKeys: String, CodingKey {
case type
case searchable
case aggregatable
case indices
case nonSearchableIndices = "non_searchable_indices"
case nonAggregatableIndicies = "non_aggregatable_indices"
}
}

enum CodingKeys: String, CodingKey {
case fields
}
}

extension FieldCapabilitiesResponse: Equatable {}

/// Describes the capabilities of a field optionally merged across multiple indices.
public struct FieldCapabilities: Codable {
public let name: String
public let type: String
public let isSearchable: Bool
public let isAggregatable: Bool
public let indices: [String]?
public let nonSearchableIndices: [String]?
public let nonAggregatableIndicies: [String]?
}

extension FieldCapabilities: Equatable {}
Loading

0 comments on commit fb2a536

Please sign in to comment.