I am using elasticsearch to make my search fast, but unfortunately i don't know the reason why it is returning an empty array of result. Looking forward a help from you guys.
class Post < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
has_many :attachments, as: :attachable
accepts_nested_attributes_for :attachments, allow_destroy: true
mapping do
indexes :work_type
indexes :title
indexes :location
indexes :industry_type
indexes :key_words
indexes :type
end
after_save { update_index }
def self.search(params)
tire.search(load: true) do
query do
boolean do
must { string params[:query] } if params[:query].present?
must { match :type, params[:type] } if params[:type].present? && params[:type] != "all"
must { match :title, params[:title] } if params[:title].present?
must { match :industry_type, params[:industry_type] } if params[:industry_type].present?
must { match :key_words, params[:key_words] } if params[:key_words].present?
end
end
sort { by :title, 'desc' }
end
end
params = {"utf8"=>"✓",
"type"=>"Job",
"query"=>"",
"location"=>"",
"post"=>{"work_type"=>""},
"action"=>"index",
"controller"=>"posts"}
Related
I get this error on my posts index page :
This the model :
class Post < ApplicationRecord
include Filterable
belongs_to :region
belongs_to :category
belongs_to :topic
validates :title, presence: true, length: { maximum: 500 }
validates :content, presence: true
validates :published_at, presence: true
translates :title, :content, :slug, touch: true, fallbacks_for_empty_translations: true
has_attached_file :image, styles: { thumb: "100x70#", featured: "1560x868#", small: "760x868#", big: ">1600x1600" }
validates_attachment :image, content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] }
validates_attachment_presence :image
scope :published, -> (published) { where(published: (['true', true].include? published)).order(featured: :desc, published_at: :desc) }
scope :published_until_now, -> { where("published_at < ?", Time.now).merge(Post.published(true)) }
scope :topic, -> (topic_id) {
joins(:topic).where('topic_id = ?', topic_id) }
scope :category, -> (post_category) {
joins(:category).where('category_id = ?', post_category) }
scope :match, -> (search_term) {
with_translations(I18n.locale).where('content like ? or title like ?', "%#{search_term}%", "%#{search_term}%") }
self.per_page = 10
after_save :unfeature_older_posts, if: Proc.new { |post| post.featured? }
extend FriendlyId
friendly_id :title, use: :globalize
def unfeature_older_posts
featured_posts = Post.where(featured: true).where.not(id: id).order(published_at: :desc)
if featured_posts.size == 1
featured_posts.last.update(featured: false)
end
end
end
This the controller :
class PostsController < ApplicationController
before_action :get_pages_tree, :get_privacy_policy, only: [:index, :show]
def index
#filters = params.slice(:topic, :category)
#posts = Post.published_until_now
.filter(#filters)
.paginate(:page => params[:page], per_page: 11)
end
def show
#post = Post.friendly.find(params[:id])
end
end
and filter is defined here :
module Filterable
extend ActiveSupport::Concern
module ClassMethods
def filter(filtering_params)
results = self.where(nil)
filtering_params.each do |key, value|
results = results.public_send(key, value) if value.present?
end
results
end
end
end
I'm not sure where to go from here. I recently upgraded to Ruby on Rails 5 and Ruby 2.7.0, I don't know if it's related.
Try replacing module ClassMethods with class_methods do.
If it works, then please keep in mind:
filter method comes from Ruby. It's defined in Array. As you can see in the doc, filter method on Array takes no argument. That's the direct cause of the error you see.
In Rails, when methods on Array are called on ActiveRecord object (in your case, Post.published_until_now) and when methods cannot be found on a model, it automatically converts itself into an Array. So, it calls filter method on Array. Generally, you don't want to define methods such as filter which is confusing.
I want to use ElasticSearch to search with multiple parameters (name, sex, age at a time).
what I've done so far is included elastic search in my model and added a as_indexed_json method for indexing and included relationship.
require 'elasticsearch/model'
class User < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
belongs_to :product
belongs_to :item
validates :product_id, :item_id, :weight, presence: true
validates :product_id, uniqueness: {scope: [:item_id] }
def as_indexed_json(options = {})
self.as_json({
only: [:id],
include: {
product: { only: [:name, :price] },
item: { only: :name },
}
})
end
def self.search(query)
# i'm sure this method is wrong I just don't know how to call them from their respective id's
__elasticsearch__.search(
query: {
filtered: {
filter: {
bool: {
must: [
{
match: {
"product.name" => query
}
}
],
must: [
{
match: {
"item.name" => query
}
}
]
}
}
}
}
)
end
end
User.import force: true
And In controller
def index
#category = Category.find(params[:category_id])
if params[:search].present? and params[:product_name].present?
#users = User.search(params[:product_name]).records
end
if params[:search].present? and params[:product_price].present?
#users = User.search(params[:product_price]).records
end
if params[:search].present? and params[:item].present?
if #users.present?
#users.search(item: params[:item], product: params[:product_name]).records
else
#users = User.search(params[:item]).records
end
end
end
There are basically 3 inputs for searching with product name , product price and item name, This is what i'm trying to do like if in search field only product name is present then
#users = User.search(params[:product_name]).records
this will give me records but If user inputs another filter say product price or item name in another search bar then it's not working. any ideas or where I'm doing wrong :/ stucked from last 3 days
I have a index view in my rails application that allows filtering via search params. When a group op articles are returned its is wropped in an articles colllection like {"articles":[{"article":{"id":341,"updated":"2015-08-18T13:05:08.427Z","title":". But if only a single object is found the articles level is missing, {"article":{"id":398,"updated":"2015-08-07T11:37:26.200Z","title":. How can I fix it so that a single object behaves like multiple?
_articles.list.json.jbuilder
require 'uri'
require 'publish_on'
json.cache! ['v1', articles] do
json.articles articles do |article|
json.cache! ['v1', article] do
json.article do
json.id article.id
json.updated as_ns_date(article.updated_at)
json.title article.label
json.numberOfViews article.view_mappings.count
json.numberOfFavorites article.favorite_mappings.count
json.imageURLs article.images if article.images.any?
json.youtubeURL article.youtube unless article.youtube.blank?
json.tags article.categories.map(&:label)
json.isFeatured article.featured
json.isPublished article.is_published
json.published as_ns_date(article.publish_on)
end
end
end
end
index.json.jbuilder
json.partial! 'articles/articles_list', articles: #articles
articles_controller.rb
def index
#articles = SearchArticlesCommand.new(params).execute
render :index
end
search_articles_command.rb
class SearchArticlesCommand
def initialize(params = {})
#since = params[:since_date]
#keys = params[:search_query]
#category = params[:category]
end
def execute
Article.unscoped do
query = if #since.present?
Article.article.since_date(#since)
else
Article.published_article
end
query = query.search_by_keywords(#keys) if #keys.present?
query = query.search_by_category(#category) if #category.present?
query.select(:id, :updated_at, :label, :is_published, :featured, :slug, :created_at).order(created_at: :desc)
end
end
end
article.rb
class Article < Comfy::Cms::Page
include PgSearch
include ActionView::Helpers::SanitizeHelper
HOSTNAME = ENV['HOSTNAME'] || Socket.gethostname
has_many :view_mappings, dependent: :destroy
has_many :favorite_mappings, dependent: :destroy
pg_search_scope :search_by_keywords, against: [:content_cache, :label], using: { tsearch: { any_word: true, prefix: true } }
pg_search_scope :search_by_category, associated_against: {
categories: [:label]
}
scope :since_date, -> (date) { where('created_at > ? OR updated_at > ? ', date, date) if date.present? }
scope :folder, -> { where.not(layout_id: ENV['ARTICLE_LAYOUT_ID']) }
scope :published_article, -> { published.article }
scope :article, -> { where(layout_id: ENV['ARTICLE_LAYOUT_ID']) }
It is what i suspected. If you want the same behavior your query should return the same type of object when it finds one or many articles. The problem is that either you are returning an ActiveRecordRelation or a Article object depending on your params.
#articles = Article.all # => ActiveRecordRelation, an array per se
#articles = Article.find(1) # => Article object
When it comes to jbuilder to construct the JSON it checks if it is an array of objects and then wrap the json with a { keyword => array }. WHen it is a single object, it defaults to a single object {article: {}}.
The solution is simple, you can tweak your SearchArticlesCommand to always return an ActiveRecordRelation, even if it finds only one object.
Basically I got 3 models(Book,Chapter,Author), and I want to include some of the books and author attributes when indexing chapter.
here is my Chapter.rb
class Chapter < ActiveRecord::Base
belongs_to :book, :counter_cache => true
include Elasticsearch::Model
index_name [Rails.env, model_name.collection.gsub(/\//, '-')].join('_')
mappings do
indexes :id, type: :integer
indexes :title, type: :string
indexes :description, type: :string
indexes :content, type: :string
indexes :updated_at, type: :date # Date example
indexes :book_title
indexes :book_type
indexes :author_name
indexes :book_id
end
def book_title
book.title
end
def book_type
book.book_type
end
def author_name
" #{book.author.firstname} #{book.author.lastname} "
end
def to_indexed_json
to_json methods: [:book_title, :book_type, :author_name]
end
end
http://localhost:9200/development_chapters/_mapping?pretty shows correct mapping
{
"development_chapters" : {
"mappings" : {
"chapter" : {
"properties" : {
"author_name" : {
"type" : "string"
},
"book_title" : {
"type" : "string"
},....
}
}
}
}
}
Then why do I not get author_name, book_title etc... in the search results
<Elasticsearch::Model::Response::Result:0x00000105e393a0 #result=#<Hashie::Mash _id="415" _index="development_chapters" _score=1.0 _source=#<Hashie::Mash book_id=153 content="[\"Explicabo accusantium odit .\"]" created_at="2015-04-22T18:43:58.586Z" description="You can't generate the application without quantifying the cross-platform SDD bandwidth!" id=415 title="Future Communications Orchestrator" updated_at="2015-04-22T18:43:58.586Z"> _type="chapter">>
You are defining wrong serialization method. Elasticsearch::Model searches for method as_indexed_json and you are defining to_indexed_json. In elasticesearch-model gem you can find examples https://github.com/elastic/elasticsearch-rails/blob/master/elasticsearch-model/examples/activerecord_associations.rb#L82
It should look something like this:
def as_indexed_json(options = {})
as_json methods: [:book_title, :book_type, :author_name]
end
I am trying build a search function in rails based on elasticsearch+tire enabling search for Persons with filtering for associated Objects and their Values. A Person has_many Objects, and an Object has_many Values.
I have managed to get the filtering on the object name (params[:object]) to work, but not for object+value. How should I construct the range filter for the values and the mapping so that the value is dependent on the object?
Person controller
mapping do
indexes :objects do
indexes :_id
indexes :object_values do
indexes :value
end
end
indexes :name, type: 'string', analyzer: 'snowball'
end
def self.search(params)
tire.search do
query do
boolean do
must { string params[:query]} if params[:query].present?
end
end
filter :term, {"objects._id" => params[:object]} if params[:object].present?
filter :range, “objects.object_values.value” => {from: params[:value] } if params[:value].present?
end
end
def to_indexed_json
{
:name => name,
:objects => objects.map { |o| {
:_type => 'object',
:_id => o.id,
:object_values => o.object_values.map {|ov| {
:_type => 'object_value',
:_id => ov.id,
:value => ov.value } },
} }
}.to_json
end
Use gt or gte rather than from to specify the lower bounds of your range
filter :range, “objects.object_values.value” => {from: params[:value] }