I have a function which returns a list of Items using elastic search and falls back on realm cache. I'm wondering how can I use Combine
to achieve the same.
I am trying to do something like this where I have a publisher for each store but I am getting stuck on the sorting them by score.
func search(for text: String) -> AnyPublisher<[Item], Error> {
return store.search(with: text)
// Invalid syntax *
.map { searchResults in
let sorted = cacheStore.search(with: text)
.map { items in
items
.map { item in (item, searchResults.first { $0.id == item.id }?.score ?? 0) }
.sorted { $0.1 > $1.1 } // by score
.map { $0.0 } // to item
}
return sorted.eraseToAnyPublisher()
}
// *
.catch { _ in cacheStore.search(with: text) }
.eraseToAnyPublisher()
}
This is the original function.
func search(for text: String, completion: @escaping (Result<[Item], Error>) -> Void) {
store.search(with: text) {
// Search network via elastic search or fall back to cache search
// searchResults is of type [(id: Int, score: Double)] where id is item.id
guard let searchResult = $0.value, $0.isSuccess else {
return self.cacheStore.search(with: text, completion: completion)
}
self.cacheStore.fetch(ids: searchResult.map { $0.id }) {
guard let items = $0.value, $0.isSuccess else {
return self.cacheStore.search(with: text, completion: completion)
}
let scoredItems = items
.map { item in (item, searchResult.first { $0.id == item.id }?.score ?? 0) }
.sorted { $0.1 > $1.1 } // by score
.map { $0.0 } // to item
completion(.success(scoredItems))
}
}
}
I figured out the solution by doing something like this:
let cachedPublisher = cacheStore.search(with: text)
let createPublisher: (Item) -> AnyPublisher<Item, Error> = {
return Just($0).eraseToAnyPublisher()
}
return store.search(with: request)
.flatMap { Item -> AnyPublisher<[Item], Error> in
let ids = searchResults.map { $0.id }
let results = self.cacheStore.fetch(ids: ids, filterActive: true)
.flatMap { items -> AnyPublisher<[Item], Error> in
let sorted = items
.map { item in (item, searchResults.first { $0.id == item.id }?.score ?? 0) }
.sorted { $0.1 > $1.1 } // by score
.map{ $0.0 } // to item
return Publishers.mergeMappedRetainingOrder(sorted, mapTransform: createPublisher) // Helper function that calls Publishers.MergeMany
}
return results.eraseToAnyPublisher()
}
.catch { _ in cachedPublisher }
.eraseToAnyPublisher()