Skip to content

Commit

Permalink
feat: adds facets
Browse files Browse the repository at this point in the history
  • Loading branch information
micheleriva committed Jul 24, 2024
1 parent 0438312 commit 280fa6b
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 30 deletions.
145 changes: 116 additions & 29 deletions Sources/oramacloud-client/builder-search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,164 @@ struct ClientSearchParams: Codable {
case desc
}

struct FacetLimitOrder: Codable {
let limit: Int?
let order: Order?
let offset: Int?
struct SortByDirective: Codable {
let property: String
var order: Order = .asc
}

enum FacetsString: Codable {
case JSObject(FacetLimitOrder)
}
enum Facet: Codable {
case string(limit: Int? = 10, order: Order? = .asc, offset: Int? = 0)
case number(ranges: [NumberRange])
case boolean(isTrue: Bool? = true, isFalse: Bool? = false)

struct FacetsNumberRange: Codable {
let from: Int
let to: Int
}
struct NumberRange: Codable {
let from: Int
let to: Int
}

struct FacetsNumber: Codable {
let ranges: [FacetsNumberRange]
}
enum Order: String, Codable {
case asc, desc
}

enum CodingKeys: String, CodingKey {
case limit, order, offset, ranges, `true`, `false`
}

struct FacetsBoolean: Codable {
let isTrue: Bool?
let isFalse: Bool?
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .string(limit, order, offset):
try container.encodeIfPresent(limit, forKey: .limit)
try container.encodeIfPresent(order, forKey: .order)
try container.encodeIfPresent(offset, forKey: .offset)
case let .number(ranges):
try container.encode(ranges, forKey: .ranges)
case let .boolean(isTrue, isFalse):
try container.encodeIfPresent(isTrue, forKey: .true)
try container.encodeIfPresent(isFalse, forKey: .false)
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if container.contains(.ranges) {
let ranges = try container.decode([NumberRange].self, forKey: .ranges)
self = .number(ranges: ranges)
} else if container.contains(.true) || container.contains(.false) {
let isTrue = try container.decodeIfPresent(Bool.self, forKey: .true)
let isFalse = try container.decodeIfPresent(Bool.self, forKey: .false)
self = .boolean(isTrue: isTrue, isFalse: isFalse)
} else {
let limit = try container.decodeIfPresent(Int.self, forKey: .limit)
let order = try container.decodeIfPresent(Order.self, forKey: .order)
let offset = try container.decodeIfPresent(Int.self, forKey: .offset)
self = .string(limit: limit, order: order, offset: offset)
}
}
}

struct Facets: Codable {
let string: JSObject<FacetsString>?
let number: JSObject<FacetsNumber>?
let boolean: JSObject<FacetsBoolean>?
private enum CodingKeys: String, CodingKey {
case term, mode, properties, limit, offset, returning, facets, sortBy
}

struct SortByDirective: Codable {
let property: String
var order: Order = .asc
private struct DynamicCodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}

var intValue: Int?
init?(intValue _: Int) {
return nil
}
}

let term: String
var mode: SearchMode = .fulltext
let properties: [String]?
let limit: Int?
let offset: Int?
let returning: [String]?
let facets: Facets?
let facets: [String: Facet]?
let sortBy: [SortByDirective]?

private init(term: String, mode: SearchMode, limit: Int?, offset: Int?, returning: [String]?, facets: Facets?, sortBy: [SortByDirective]?) {
private init(term: String, mode: SearchMode, properties: [String]?, limit: Int?, offset: Int?, returning: [String]?, facets: [String: Facet]?, sortBy: [SortByDirective]?) {
self.term = term
self.mode = mode
self.properties = properties
self.limit = limit
self.offset = offset
self.returning = returning
self.facets = facets
self.sortBy = sortBy
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(term, forKey: .term)
try container.encode(mode, forKey: .mode)
try container.encodeIfPresent(properties, forKey: .properties)
try container.encodeIfPresent(limit, forKey: .limit)
try container.encodeIfPresent(offset, forKey: .offset)
try container.encodeIfPresent(returning, forKey: .returning)
try container.encodeIfPresent(sortBy, forKey: .sortBy)

if let facets = facets {
var facetsContainer = container.nestedContainer(keyedBy: DynamicCodingKeys.self, forKey: .facets)
for (key, value) in facets {
try facetsContainer.encode(value, forKey: DynamicCodingKeys(stringValue: key)!)
}
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
term = try container.decode(String.self, forKey: .term)
mode = try container.decodeIfPresent(SearchMode.self, forKey: .mode) ?? .fulltext
properties = try container.decodeIfPresent([String].self, forKey: .properties)
limit = try container.decodeIfPresent(Int.self, forKey: .limit)
offset = try container.decodeIfPresent(Int.self, forKey: .offset)
returning = try container.decodeIfPresent([String].self, forKey: .returning)
sortBy = try container.decodeIfPresent([SortByDirective].self, forKey: .sortBy)

if container.contains(.facets) {
let facetsContainer = try container.nestedContainer(keyedBy: DynamicCodingKeys.self, forKey: .facets)
var facetsDict = [String: Facet]()
for key in facetsContainer.allKeys {
facetsDict[key.stringValue] = try facetsContainer.decode(Facet.self, forKey: key)
}
facets = facetsDict
} else {
facets = nil
}
}

static func builder(term: String, mode: SearchMode) -> Builder {
return Builder(term: term, mode: mode)
}

class Builder {
private var term: String
private var mode: SearchMode
private var properties: [String]?
private var limit: Int
private var offset: Int
private var returning: [String]?
private var facets: Facets?
private var facets: [String: Facet]?
private var sortBy: [SortByDirective]?

fileprivate init(term: String, mode: SearchMode) {
init(term: String, mode: SearchMode) {
self.term = term
self.mode = mode
limit = 10
offset = 0
returning = nil
properties = nil
}

func properties(_ properties: [String]) -> Self {
self.properties = properties
return self
}

func limit(_ limit: Int) -> Self {
Expand All @@ -93,7 +179,7 @@ struct ClientSearchParams: Codable {
return self
}

func facets(_ facets: Facets) -> Self {
func facets(_ facets: [String: Facet]) -> Self {
self.facets = facets
return self
}
Expand All @@ -107,6 +193,7 @@ struct ClientSearchParams: Codable {
return ClientSearchParams(
term: term,
mode: mode,
properties: properties,
limit: limit,
offset: offset,
returning: returning,
Expand Down
2 changes: 1 addition & 1 deletion Sources/oramacloud-client/client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class OramaClient {
return try decoder.decode(SearchResults<T>.self, from: data)
}

private func encodeSearchQuery(query: ClientSearchParams) throws -> Data {
func encodeSearchQuery(query: ClientSearchParams) throws -> Data {
let encoder = JSONEncoder()
let jsonData = try encoder.encode(query)

Expand Down

0 comments on commit 280fa6b

Please sign in to comment.