How to use bool to limit Elasticsearch query results? - ruby-on-rails

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}
}]
}
}
}

Related

Searching postgresql arrays with searchkick

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,
},
},
},
} })

ElasticSearch: A query that allows nil parameters

So i have the below module in an ElasticSearch concern for my Model in rails.
This is working, but how do I make each of the bool query(must, must_not, filter) accept nil or empty parameters?
Say if I pass an empty query_string it would get all the documents.
Then when I pass an empty size parameter it will return all sizes.
module ClassMethods
def home_page_search(query_string, size, start_date, end_date)
search({
query: {
bool: {
must: [
{
multi_match: {
query: query_string,
fields: [:brand, :name, :notes, :size_notes]
}
}
],
must_not: [
range: {
unavailable_dates: { gte: start_date, lte: end_date }
}
],
filter: [
{ term: { size: size } }
]
}
}
})
end
end
I solved a similar problem by constructing the query string on more of an as-needed basis, so I only included a clause if there was a search term for it. The query I sent to Elasticsearch only included the terms that were actually set by the user. For example:
if size.present?
query[:query][:bool][:filter] = { term: { size: size } }
end
(assuming the correct representation of the query, etc.)

Fetching results in random order with a :where clause using searchkick and rails

This is probably a simple thing, but I can't seem to get my head around it:
I want elasticsearch to return:
#random_books   = Book.search("*", where: { status: :published }, body: { query: { function_score: { random_score: { seed: seed }}}}, page: params[:page], per_page: 12)
The results returned do not honor the where: {status: :published} clause. How do I syntax up this query?
Edits: Found another question asking basically the same thing; without a working solution/answer of course.
Please, replace your where clause like this:
where: { 'status IN(?)', ['published'] }
Okay, here's the solution:
seed = Time.zone.now.to_i
#random_books = Book
.search("*",
body: {
query: {
function_score: {
query: {
match: { status: :published }
},
random_score: {
seed: seed
}
}
}
},
page: params[:random],
per_page: 12)
Searchkick will ignore the options (like where: clause) if we pass body to elasticsearch. So we match the query inside of function_score as described here.
Easy-peasy.

Add Filter To ElasticSearch Query

I'm trying to create a query where the user can search for ES documents where the brand field is equal to some string. Here is the query I currently have that works but has no filtering. I'm using elasticsearch-rails with Ruby on Rails.
#products = Product.search(
query:{
function_score:{
query: {
multi_match: {
fields: ['brand^5', '_all'],
query: "#{query}",
fuzziness: "AUTO"
}
},
field_value_factor:{
field: "popularity",
modifier: "log1p"
}
}
}).page(page).per(25)
I've statically assigned a brand name to the filter for testing purposes. In this case the user should be seeing results for their search keyword where the brand name is "NordicTrack".
query:{
function_score:{
query: {
multi_match: {
fields: ['brand^5', '_all'],
query: "#{query}",
fuzziness: "AUTO"
}
},
filter: {
term: {"brand":"NordicTrack"}
},
field_value_factor:{
field: "popularity",
modifier: "log1p"
}
}
}
).page(page).per(25)
This query gives me the following error:
[400] {"error":{"root_cause":[{"type":"parsing_exception","reason":"no [query] registered for [filter]","line":1,"col":139}],"type":"parsing_exception","reason":"no [query] registered for [filter]","line":1,"col":139},"status":400}
I'm not sure why this isn't working. Any help would be appreciated!
I'm not sure about how Elasticsearch-rails with Ruby on Rails parses Query. But I tried below query in Kibana :
GET test/testt/_search
{
"query": {
"filter": {
"term": {
"brand": "NordicTrack"
}
}
}
}
which is somewhat similar to your part of the query which is giving you the error and I too got that same error.
But when I wrap the term query with bool query then it returns desired results. Query :
GET test/_search
{
"query": {
"bool": {
"filter": {
"term": {
"brand": "NordicTrack"
}
}
}
}
}
Give it a Shot.
Try using a filtered query like this:
query: {
function_score: {
query: {
filtered: {
query: {
multi_match: {
fields: ['brand^5', '_all'],
query: "#{query}",
fuzziness: "AUTO"
}
},
filter: {
term: {
brand: "NordicTrack"
}
}
}
},
field_value_factor:{
field: "popularity",
modifier: "log1p"
}
}
}

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
}
}
}
}

Resources