Searching postgresql arrays with searchkick - ruby-on-rails

I have a search functionality that I would like to search by various fields. I am already using multi_search to look for different records from different models.
#entries_result = Post.search(params[:q].presence || '*',
fields: ['title^10', 'content', 'rating', 'parent_id', 'tags'],
...
where: { parent_id: #parent.id },
limit: 3)
#categories_result = Category.search(params[:q].presence || '*', ...)
#tags_result = Post.search(params[:q].presence || '*',
fields: ['tags'],
match: :word_start,
highlight: true,
misspellings: { below: 3 },
where: { parent_id: #parent.id, tags: params[:q] })
Searchkick.multi_search([#categories_result, #entries_result, #tags_result])
#tags_result.map(:&tags)
As you can see I am searching through Post model twice, as I need
Posts with specific name(shown on top of the search results)
Tags which are an Array in psql, that I will be showing on the side of the search results
# tags :text default([]), is an Array, indexed
I can't figure out how to find these tags that are a result of searching by params[:q]. I know that Elasticsearch can search these arrays in documents, but I couldn't find any info on how to do this with searchkick gem.
For example when params[:q] is dog I'd like to search for Posts that start with the keyword dog and also tags that start with keyword dog.
Rails 7, gems opensearch (2.0.0), searchkick (5.0.3)

I ended up building an advanced query as it's also possible to just use elasticsearch/opensearch queries in searchkick, and below code works fine for me.
#tags_result = Post.search(body:
{ query: {
bool: {
should: [
prefix: {
tags: params[:q].presence || '*',
},
],
filter: {
term: {
parent_id: #parent.id,
},
},
},
} })

Related

Rails - How to add more fields for filter in searchkick?

In rails, I am using searchkick gem for search. When I am adding more fields for where clause then returning zero results.
Actually search is working for below method(User.rb),
searchkick word_start: [:name]
def initialize(name, limit = User::SUGGESTION_LIMIT, page = nil)
#name = name
#limit = limit
#page = page
#per_page = limit.to_i
end
query = {
match: :word_start,
fields: [{ emails: "exact" }, "name^5"],
misspellings: { prefix_length: 2 },
load: false
}
User.search(name, query).records
When I add condition like where: {active: false, inactive: true, deleted_at: nil} it returns no data.
query = {
match: :word_start,
where: {active: false, inactive: true, deleted_at: nil},
fields: [{ emails: "exact"}, "name^5"],
misspellings: { prefix_length: 2 },
load: false
}
Is there any mistake in above where condition? Please help me to solve this issue. I am using this gem for the first time.
Actually, the fields used in the where clause are indexed differently.
You have to define those fields as filterable:
searchkick word_start: [:name], filterable: [:active, :inactive, :deleted_at]
This should work.
BTW: You don't need both "active" and "inactive" attributes. If the "active" attribute is false, you know it is inactive.

Elasticsearch different behaviour on test server

My elasticsearch is currently giving different results on different environments even though I'm doing the same search.
It works fine in development on my localhost, however it doesn't work on my test server (doesn't give expected records, yes I do have the database seeded).
Far as I understand what this should do is check whether it finds a hit on one of the three matches, and if it does return all the hits.
I'm running Windows 10, just using rails s.
The server is running Ubuntu 16, using nginx and unicorn.
Here's my mapping: (note: I'm not completely sure whether the analyzer does anything but it shouldn't matter)
settings index: { number_of_shards: 1 } do
mappings dynamic: 'true' do
indexes :reportdate, type: 'date'
indexes :client do
indexes :id
indexes :name, analyzer: 'dutch'
end
indexes :animal do
indexes :id
indexes :species, analyzer: 'dutch'
indexes :other_species, analyzer: 'dutch'
indexes :chip_code
end
indexes :locations do
indexes :id
indexes :street, analyzer: 'dutch'
indexes :city, analyzer: 'dutch'
indexes :postalcode
end
end
end
Here's my search:
__elasticsearch__.search({
sort: [
{ reportdate: { order: "desc" }},
"_score"
],
query: {
bool: {
should: [
{ multi_match: {
query: query,
type: "phrase_prefix",
fields: [ "other_species", "name"]
}},
{ prefix: {
chip_code: query
}},
{ match_phrase: {
"_all": {
query: query,
fuzziness: "AUTO"
}
}}
]
}
}
})
EDIT #1: Note: I'm fairly new to ruby on rails, started about 2 weeks ago, doing maintenance work on an old project and they also requested a search function.
Turns out that the problem was that I was using foreign tables (well, kinda) and nested mapping (probably this).
Here's the updated code that works on both production and locally:
__elasticsearch__.search({
sort: [
{ reportdate: { order: "desc" }},
"_score"
],
query: {
bool: {
should: [
{ multi_match: {
query: query,
type: "phrase_prefix",
fields: [ "animal.other_species", "client.name"]
}},
{ prefix: {
"animal.chip_code": query
}},
{ match_phrase: {
"_all": {
query: query,
fuzziness: "AUTO"
}
}}
]
}
}
})
Not sure why it doesn't need the animal and client parents preappended to work locally whilst it does need them on my testing server. However this works on both this way.

How to use bool to limit Elasticsearch query results?

In my Rails app I have 2 models: User(id) & Document(id,user_id, document_title,document)
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['document_title^10', 'document']
}
},
}
end
I'm using the above search query which works great for return results across the entire table. The problem is, the results are not limited to the current_user. I'm trying to update the search method to only return results for the current_user. Per the docs, I'm doing:
def self.search(query, user_id)
__elasticsearch__.search(
{
bool: {
filter: ["user_id", user_id]
},
query: {
multi_match: {
query: query,
fields: ['document_title^10', 'document']
}
},
}
end
However, that is erroring with:
[400] {"error":{"root_cause":[{"type":"search_parse_exception","reason":"failed to parse search source. unknown search element [bool]","line":1,"col":2}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"documents","node":"52GAD0HbT4OlekjesTZY_A","reason":{"type":"search_parse_exception","reason":"failed to parse search source. unknown search element [bool]","line":1,"col":2}}]},"status":400}
I'm not sure what docs you are looking at but that query isn't right: the multi match query should be in the must clause of the bool query.
{
query: {
bool: {
must: [{
multi_match: {...}
}],
filter: [{
term: {user_id: user_id}
}]
}
}
}

Find model with part of title using ElasticSearch / Rails

There is the following Post model:
class Post < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
def self.search query
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title']
}
},
filter: {
and: [
{ term: { deleted: false } },
{ term: { enabled: true } }
]
}
}
)
end
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :title, analyzer: 'english'
end
end
end
Post.import
I have one Post with 'Amsterdam' title. When I execute Post.search('Amsterdam') I will get one record, all is good. But if I execute Post.search('Amster') I will get no records. What do I wrong? How can I fix it? Thanks!
OS - OS X, ElasticSearch I installed using Homebrew
You will have to use nGram tokenizer, in order to create a partial text search. A very good example of how to do this can be found here. That said, I would be very careful with nGram, as it can often turn up unrelated results.
This is because the substring "mon" is contained within all of the strings: "monkey", "money", and "monday". All of which are unrelated.
Alternatively (What I would do.)
You could try making it a fuzzy search. However, the max distance with fuzzy search is only two, which still doesn't return anything in your example. However, it tends to return relevant results.
The example I found: How to use Fuzzy Search
# Perform a fuzzy search!
POST /fuzzy_products/product/_search
{
"query": {
"match": {
"name": {
"query": "Vacuummm",
"fuzziness": 2,
"prefix_length": 1
}
}
}
}

Sorting search results with mongoid-elasticsearch gem

I am trying to implement sorting into my search. I am searching with use of the gem mongoid-elasticsearch. This is my model configuration:
class ActivityLog
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Elasticsearch
elasticsearch!(
{
wrapper: :load,
sort: [
{ created_at: "desc" }
]
}
)
field :type, type: String
end
This configuration does not raise any errors, but it does not either seem like it has any effect, because search results are listed randomly.
I think I am implementing the configuration in accordance to the documentation:
Check mongoid-elasticsearch documentation here
Check elasticsearch documentation here
My search query btw is:
ActivityLog.es.search(params[:query], page: params[:page]).results.paginate(per_page: 5, page: params[:page])
You should remove Array wrapper:
elasticsearch!(
{
wrapper: :load,
sort: { created_at: "desc" }
}
)
The same way you would do it in Elasticsearch itself
ActivityLog.es.search({
body: {
query: {
query_string: {
query: params[:search]
}
},
sort: [
{created_at: {order: "asc"}},
]
})

Resources