Because this approach smells bad and I have to do two queries to the database.
ids = Model.search(where: where_params)
.results.response['hits']['hits'].map{ |x| x['_id'].to_i }
Is there proper way for doing this?
By the way, I need items ids for "converting" Searchkick::Results class to ActiveRecord::Relation to use scopes and other class methods on it. Is there turnkey solution for these needs?
...
def self.filter(params)
where_params = Search.attrs(params: params)
if where_params.any?
to_ar_relation(search(where: where_params))
else
where(nil)
end
end
private
# converts searchkick results to ActiveRecord relation to use scopes on it
def self.to_ar_relation(results)
where(id: results.response['hits']['hits'].map{ |x| x['_id'].to_i })
end
...
I ran into the same problem with having to do this in order to get a named scope to work. Here's the way I'm getting the ids using load: false to prevent the initial SQL query / model load:
ids = Model.search(where: where_params, load: false).results.map(&:id)
Edit: Unfortunately .where(id: ids) and .find(ids) both do not preserve ordering (at least in Postgres), so I ended up having to use this gem as well. Feels pretty gross but works for now.
@abevoelker, thanks, dude!
but approach that used by this gem (order_as_specified) very very slow :-(
Perhaps #490 helps deal with this? You should be able to do:
Model.search(where: where_params).records
Which gives back an ActiveRecord::Relation, similar to what you're doing.
Had a chance to verify @Mavvie's suggestion tonight - seems to work great, thank you!
Unfortunately I spoke too soon, records does not in fact preserve ordering. Guess I must've gotten lucky the first time I tried it when I only had a handful of records to test.
search = Product.search("*", { price: {order: "desc"} })
# => [14, 22, 24, 26, 8, 12, 30, 20, 7, 16, 18, 28, 11]
search.records.map(&:id)
# Product Load (0.4ms) SELECT "products".* FROM "products" WHERE "products"."id" IN (14, 22, 24, 26, 8, 12, 30, 20, 7, 16, 18, 28, 11)
# => [7, 18, 11, 8, 14, 24, 20, 26, 16, 12, 22, 28, 30]
edit: just to make clear this is a Postgres thing and the types of "fixes" at the query level are complicated http://stackoverflow.com/a/35456954/215168
edit2: (of course if you just meant that the .where(id: ids) part could be replaced with .records you're totally right, but on Postgres hacks like the order_as_specified gem are still required with it)
Right. Forgot to mention that. There should be a way to preserve ordering inside searchkick, just converting it to an AR relation. I'll look closer.
Wow that would be awesome if possible. But even if not, .records is still cleaner than what I was doing before so thank you for that! :+1:
Adding ordering to the relation is possible, but messy. I can't see a better way to do it than order_as_specified. Here's how they do it
order_by = params[:values].map do |value|
"#{table}.#{attribute}='#{value}' DESC"
end.join(", ")
Which was the most compatible way of doing it I saw mentioned in the stackoverflow answers. I don't want to add another dependency (esp. for an experimental feature), and fortunately it's simple enough there's no need to. I'll fork to add ordering and we can try it out/discuss it.
Most helpful comment
Perhaps #490 helps deal with this? You should be able to do:
Which gives back an
ActiveRecord::Relation, similar to what you're doing.