Skip to content

Tasks Parity #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: 406-index-settings
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
002d261
Add support for search ranking score
Sherlouk Sep 17, 2023
117b3ad
Update code samples with ranking score example
Sherlouk Sep 17, 2023
d0a6a20
Patch Tests & Rename 'result' to 'document'
Sherlouk Sep 21, 2023
4913acb
Merge #417
meili-bors[bot] Sep 27, 2023
4af1ac8
Merge branch 'main' into 398-ranking-score
curquiza Sep 27, 2023
e3ba51a
Merge #413
meili-bors[bot] Sep 27, 2023
742008b
Add internal typo-tolerance settings APIs
Sherlouk Sep 16, 2023
f7495d7
Add public typo-tolerance settings APIs
Sherlouk Sep 16, 2023
df2c34e
Add unit tests for typo-tolerance APIs
Sherlouk Sep 16, 2023
c494b5f
Add integration tests for typo-tolerance APIs
Sherlouk Sep 17, 2023
9a7db09
Add typo-tolerance to existing settings APIs
Sherlouk Sep 17, 2023
62ae002
Run SwiftLint (and fix formatting issues)
Sherlouk Sep 17, 2023
330acbc
Patch Unit Tests
Sherlouk Sep 21, 2023
8c6620f
Refactor after Rebase
Sherlouk Sep 27, 2023
33d66ff
Merge #411
meili-bors[bot] Sep 27, 2023
e611698
Add async overload for search API
Sherlouk Sep 16, 2023
6f0291d
Separate Async Code
Sherlouk Sep 28, 2023
338faad
Update README to use async example
Sherlouk Sep 28, 2023
1d68308
Merge #410
meili-bors[bot] Sep 28, 2023
8fe2855
Improve Type Safety around Tasks
Sherlouk Sep 23, 2023
1c8756e
Add cancel and delete tasks public APIs
Sherlouk Sep 23, 2023
cc8bb7d
Safely Throw Errors in Tests
Sherlouk Sep 23, 2023
2dd2fce
Add Integration Tests for New APIs
Sherlouk Sep 23, 2023
9dce221
Use Types instead of Strings for Task Queries
Sherlouk Sep 23, 2023
aeb6f44
Update Tests after Rebase
Sherlouk Sep 28, 2023
41d7976
Improve Resilience of Delete Test
Sherlouk Sep 28, 2023
f10885c
Update Code Samples
Sherlouk Sep 28, 2023
4bcee63
Apply fix for unit test
Sherlouk Sep 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ async_guide_filter_by_ids_1: |-
}
}
async_guide_filter_by_statuses_1: |-
client.getTasks(params: TasksQuery(statuses: ["failed", "canceled"])) { result in
client.getTasks(params: TasksQuery(statuses: [.failed, .canceled])) { result in
switch result {
case .success(let taskResult):
print(taskResult)
Expand All @@ -142,7 +142,7 @@ async_guide_filter_by_statuses_1: |-
}
}
async_guide_filter_by_types_1: |-
client.getTasks(params: TasksQuery(types: ["dumpCreation", "indexSwap"])) { result in
client.getTasks(params: TasksQuery(types: [.dumpCreation, .indexSwap])) { result in
switch result {
case .success(let taskResult):
print(taskResult)
Expand Down Expand Up @@ -234,6 +234,16 @@ search_parameter_guide_attributes_to_search_on_1: |-
print(error)
}
}
search_parameter_guide_show_ranking_score_1: |-
let searchParameters = SearchParameters(query: "dragon", showRankingScore: true)
client.index("movies").search(searchParameters) { (result: Result<Searchable<Movie>, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult.rankingScore)
case .failure(let error):
print(error)
}
}
authorization_header_1: |-
client = try MeiliSearch(host: "http://localhost:7700", apiKey: "masterKey")
client.getKeys { result in
Expand Down Expand Up @@ -407,15 +417,6 @@ search_post_1: |-
print(error)
}
}
get_task_by_index_1: |-
client.index("movies").getTask(taskUid: 1) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
get_task_1: |-
client.getTask(taskUid: 1) { (result) in
switch result {
Expand All @@ -426,14 +427,32 @@ get_task_1: |-
}
}
get_all_tasks_1: |-
client.getTasks() { (result) in
client.getTasks { (result) in
switch result {
case .success(let tasks):
print(tasks)
case .failure(let error):
print(error)
}
}
delete_tasks_1: |-
client.deleteTasks(filter: DeleteTasksQuery(uids: [1, 2])) { (result) in
switch result {
case .success(let taskInfo):
print(taskInfo)
case .failure(let error):
print(error)
}
}
cancel_tasks_1: |-
client.cancelTasks(filter: CancelTasksQuery(uids: [1, 2])) { (result) in
switch result {
case .success(let taskInfo):
print(taskInfo)
case .failure(let error):
print(error)
}
}
get_one_key_1: |-
client.getKey(keyOrUid: "6062abda-a5aa-4414-ac91-ecd7944c0f8d") { result in
switch result {
Expand Down
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ To do a simple search using the client, you can create a Swift script like this:
) { result in
switch result {
case .success(let task):
print(task) // => Task(uid: 0, status: "enqueued", ...)
print(task) // => Task(uid: 0, status: Task.Status.enqueued, ...)
case .failure(let error):
print(error.localizedDescription)
}
Expand All @@ -149,24 +149,13 @@ With the `uid` of the task, you can check the status (`enqueued`, `canceled`, `p
#### Basic Search <!-- omit in toc -->

```swift

let semaphore = DispatchSemaphore(value: 0)

// Typealias that represents the result from Meilisearch.
typealias MeiliResult = Result<SearchResult<Movie>, Swift.Error>

// Call the function search and wait for the closure result.
client.index("movies").search(SearchParameters( query: "philoudelphia" )) { (result: MeiliResult) in
switch result {
case .success(let searchResult):
dump(searchResult)
case .failure(let error):
print(error.localizedDescription)
}
semaphore.signal()
do {
// Call the search function and wait for the result.
let result: SearchResult<Movie> = try await client.index("movies").search(SearchParameters(query: "philoudelphia"))
dump(result)
} catch {
print(error.localizedDescription)
}
semaphore.wait()

```

Output:
Expand All @@ -191,6 +180,8 @@ Output:

Since Meilisearch is typo-tolerant, the movie `philadelphia` is a valid search response to `philoudelphia`.

> Note: All package APIs support closure-based results for backwards compatibility. Newer async/await variants are being added under [issue 332](https://github.com/meilisearch/meilisearch-swift/issues/332).

## 🤖 Compatibility with Meilisearch

This package guarantees compatibility with [version v1.x of Meilisearch](https://github.com/meilisearch/meilisearch/releases/latest), but some features may not be present. Please check the [issues](https://github.com/meilisearch/meilisearch-swift/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3Aenhancement) for more info.
Expand Down
19 changes: 19 additions & 0 deletions Sources/MeiliSearch/Async/Indexes+async.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Indexes {
/**
Search in the index.

- Parameter searchParameters: Options on search.
- Throws: Error if a failure occurred.
- Returns: On completion if the request was successful a `Searchable<T>` instance is returned containing the values.
*/
public func search<T: Codable & Equatable>(_ searchParameters: SearchParameters) async throws -> Searchable<T> {
try await withCheckedThrowingContinuation { continuation in
self.search.search(self.uid, searchParameters) { result in
continuation.resume(with: result)
}
}
}
}
28 changes: 28 additions & 0 deletions Sources/MeiliSearch/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,34 @@ public struct MeiliSearch {
self.tasks.getTasks(params: params, completion)
}

/**
Cancel any number of enqueued or processing tasks, stopping them from continuing to run

- parameter filter: The filter in which chooses which tasks will be canceled
- parameter completion: The completion closure is used to notify when the server
completes the query request, it returns a `Result` object that contains `TaskInfo`
value. If the request was successful or `Error` if a failure occurred.
*/
public func cancelTasks(
filter: CancelTasksQuery,
completion: @escaping (Result<TaskInfo, Swift.Error>) -> Void) {
self.tasks.cancelTasks(filter, completion)
}

/**
Delete a finished (succeeded, failed, or canceled) task

- parameter filter: The filter in which chooses which tasks will be deleted
- parameter completion: The completion closure is used to notify when the server
completes the query request, it returns a `Result` object that contains `TaskInfo`
value. If the request was successful or `Error` if a failure occurred.
*/
public func deleteTasks(
filter: DeleteTasksQuery,
completion: @escaping (Result<TaskInfo, Swift.Error>) -> Void) {
self.tasks.deleteTasks(filter, completion)
}

// MARK: Keys

/**
Expand Down
4 changes: 4 additions & 0 deletions Sources/MeiliSearch/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ import Foundation
public extension MeiliSearch {
// MARK: Error
struct MSErrorResponse: Decodable, Encodable, Equatable {
/// A human-readable description of the error
public let message: String
/// The error code (https://www.meilisearch.com/docs/reference/errors/error_codes)
public let code: String
/// The error type (https://www.meilisearch.com/docs/reference/errors/overview#errors)
public let type: String
/// A link to the relevant section of the documentation
public let link: String?
}

Expand Down
42 changes: 41 additions & 1 deletion Sources/MeiliSearch/Indexes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct Indexes {
private let documents: Documents

// Search methods
private let search: Search
internal let search: Search

// Settings methods
private let settings: Settings
Expand Down Expand Up @@ -1028,6 +1028,46 @@ public struct Indexes {
self.settings.resetPaginationSettings(self.uid, completion)
}

// MARK: Typo Tolerance

/**
Get the typo tolerance settings.

- parameter completion: The completion closure is used to notify when the server
completes the query request, it returns a `Result` object that contains a `TypoToleranceResult`
value if the request was successful, or `Error` if a failure occurred.
*/
public func getTypoTolerance(
_ completion: @escaping (Result<TypoToleranceResult, Swift.Error>) -> Void) {
self.settings.getTypoTolerance(self.uid, completion)
}

/**
Update the typo tolerance settings.

- parameter typoTolerance: An object containing the settings for the `Index`.
- parameter completion: The completion closure is used to notify when the server
completes the query request, it returns a `Result` object that contains `TaskInfo`
value if the request was successful, or `Error` if a failure occurred.
*/
public func updateTypoTolerance(
_ typoTolerance: TypoTolerance,
_ completion: @escaping (Result<TaskInfo, Swift.Error>) -> Void) {
self.settings.updateTypoTolerance(self.uid, typoTolerance, completion)
}

/**
Reset the typo tolerance settings.

- parameter completion: The completion closure is used to notify when the server
completes the query request, it returns a `Result` object that contains `TaskInfo`
value if the request was successful, or `Error` if a failure occurred.
*/
public func resetTypoTolerance(
_ completion: @escaping (Result<TaskInfo, Swift.Error>) -> Void) {
self.settings.resetTypoTolerance(self.uid, completion)
}

// MARK: Stats

/**
Expand Down
2 changes: 1 addition & 1 deletion Sources/MeiliSearch/Keys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct Keys {
return
}
do {
let decoder = JSONDecoder()
let decoder = Constants.customJSONDecoder
let key: Key = try decoder.decode(Key.self, from: data)
completion(.success(key))
} catch {
Expand Down
8 changes: 7 additions & 1 deletion Sources/MeiliSearch/Model/SearchParameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ public struct SearchParameters: Codable, Equatable {
/// Whether to return the raw matches or not.
public let showMatchesPosition: Bool?

/// Whether to return the search ranking score or not.
public let showRankingScore: Bool?

// MARK: Initializers

public init(
Expand All @@ -81,7 +84,8 @@ public struct SearchParameters: Codable, Equatable {
filter: String? = nil,
sort: [String]? = nil,
facets: [String]? = nil,
showMatchesPosition: Bool? = nil) {
showMatchesPosition: Bool? = nil,
showRankingScore: Bool? = nil) {
self.query = query
self.offset = offset
self.limit = limit
Expand All @@ -99,6 +103,7 @@ public struct SearchParameters: Codable, Equatable {
self.sort = sort
self.facets = facets
self.showMatchesPosition = showMatchesPosition
self.showRankingScore = showRankingScore
}

// MARK: Query Initializers
Expand Down Expand Up @@ -133,5 +138,6 @@ public struct SearchParameters: Codable, Equatable {
case showMatchesPosition
case hitsPerPage
case page
case showRankingScore
}
}
33 changes: 32 additions & 1 deletion Sources/MeiliSearch/Model/SearchResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class Searchable<T>: Equatable, Codable where T: Codable, T: Equatable {
// MARK: Properties

/// Possible hints from the search query.
public var hits: [T] = []
public var hits: [SearchHit<T>] = []

/// Distribution of the given facets.
public var facetDistribution: [String: [String: Int]]?
Expand All @@ -34,6 +34,37 @@ public class Searchable<T>: Equatable, Codable where T: Codable, T: Equatable {
}
}

@dynamicMemberLookup
public struct SearchHit<T>: Equatable, Codable where T: Codable, T: Equatable {
public let document: T
public internal(set) var rankingScore: Double?

/// Dynamic member lookup is used to allow easy access to instance members of the hit result, maintaining a level of backwards compatibility.
public subscript<V>(dynamicMember keyPath: KeyPath<T, V>) -> V {
document[keyPath: keyPath]
}

// MARK: Codable

enum CodingKeys: String, CodingKey {
case rankingScore = "_rankingScore"
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.document = try T(from: decoder)
self.rankingScore = try container.decodeIfPresent(Double.self, forKey: .rankingScore)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(document)

var containerTwo = encoder.container(keyedBy: CodingKeys.self)
try containerTwo.encodeIfPresent(rankingScore, forKey: .rankingScore)
}
}

/**
`SearchResult` instances represent the result of a search.
Requires that the value `T` conforms to the `Codable` and `Equatable` protocols.
Expand Down
9 changes: 7 additions & 2 deletions Sources/MeiliSearch/Model/Setting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
#endif

/**
Settings object provided byb the user
Settings object provided by the user
*/
public struct Setting: Codable, Equatable {
// MARK: Properties
Expand Down Expand Up @@ -33,6 +33,9 @@ public struct Setting: Codable, Equatable {
/// List of attributes used for sorting
public let sortableAttributes: [String]?

/// Settings for typo tolerance
public let typoTolerance: TypoTolerance?

/// List of tokens that will be considered as word separators by Meilisearch.
public let separatorTokens: [String]?

Expand All @@ -59,7 +62,8 @@ public struct Setting: Codable, Equatable {
separatorTokens: [String]? = nil,
nonSeparatorTokens: [String]? = nil,
dictionary: [String]? = nil,
pagination: Pagination? = nil
pagination: Pagination? = nil,
typoTolerance: TypoTolerance? = nil
) {
self.rankingRules = rankingRules
self.searchableAttributes = searchableAttributes
Expand All @@ -73,5 +77,6 @@ public struct Setting: Codable, Equatable {
self.separatorTokens = separatorTokens
self.dictionary = dictionary
self.pagination = pagination
self.typoTolerance = typoTolerance
}
}
3 changes: 3 additions & 0 deletions Sources/MeiliSearch/Model/SettingResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ public struct SettingResult: Codable, Equatable {

/// Pagination settings for the current index
public let pagination: Pagination

/// Settings for typo tolerance
public let typoTolerance: TypoToleranceResult
}
Loading