I have a following model:
class Article < ActiveRecord::Base
define_index do
indexes content
indexes :name, sortable: true
has type
end
end
and special type of an article is:
class About < Article
end
and the same for Contact
I would like to have searchable articles index action without articles with type of "About" or "Contact"
class ArticlesController < ApplicationController
load_and_authorize_resource
def index
#articles = Article.search(params[:search],
:with_all => {:type => nil},
:page => params[:page],
:per_page => 10)
end
end
But the #articles instance variable contains everytime also "About" & "Contact" articles.
This is very strange (seems like will_paginate messing everything up):
#articles = Article.search(
:without => {:type => %w(About Contact)}).include?(About.first) # false
#articles = Article.search(
:without => {:type => %w(About Contact)},
:page => 1,
:per_page => 1000).include?(About.first) # true
=============================================================================
Finally I did:
class Article < ActiveRecord::Base
define_index do
indexes content
indexes :name, sortable: true
has "CRC32(type)", :as => :article_type, :type => :integer
end
end
and:
class ArticlesController < ApplicationController
load_and_authorize_resource
def index
#articles = Article.search(params[:search],
:without => {:article_type => ["About".to_crc32, "Contact".to_crc32]},
:page => params[:page],
:per_page => 10)
end
end
and it works. Thanks guys!
From the Thinking Sphinx FAQ:
Filtering on String Attributes
While you can have string columns as
attributes in Sphinx, they aren’t stored as strings. Instead, Sphinx
figures out the alphabetical order, and gives each string an integer
value to make them useful for sorting. However, this means it’s close
to impossible to filter on these attributes.
So, to get around this, there’s two options: firstly, use integer
attributes instead, if you possibly can. This works for small result
sets (for example: gender). Otherwise, you might want to consider
manually converting the string to a CRC integer value:
has "CRC32(category)", :as => :category, :type => :integer
This way, you can filter on it like so:
Article.search 'pancakes', :with => { :category => 'Ruby'.to_crc32 }
Of course, this isn’t amazingly clean, but it will work quite well.
You should also take note that CRC32 encoding can have collisions, so
it’s not the perfect solution.
Exclude About & Contact type from conditions like -
#articles = Article.search(params[:search],
:without => {:type => %w(About Contact)},
:page => params[:page],
:per_page => 10)
OR
I am not very sure on below but you can give it try..
define_index do
indexes content
indexes :name, sortable: true
indexes(:type), :as => :article_type
end
#articles = Article.search(params[:search],
:without => {:article_type => %w(About Contact)},
:page => params[:page],
:per_page => 10)
Also, make sure inside your database records you have type attribute got inserted properly..
Couldn't you use "with" instead of "with_all"? For example:
#articles = Article.search(params[:search],
:with => {:type => nil},
:page => params[:page],
:per_page => 10)
Related
I have the code like following
Listing.search(
Riddle::Query.escape(params[:search]),
:include => params[:include],
:page => page,
:per_page => per_page,
:star => true,
:with => with,
:with_all => with_all,
:order => params[:sort]
)
params[:include] contains the value like [:listing_images, :author, :category, :origin_loc]
I don’t know what was wrong here.
I have a page that when visted brings up the most recently active users. Above the users are some filtering options such as filtering by one or a combination of:
location
gender
sexual preference
age range
country
I'm using a form_tag helper for my form. My issue is passing each of these parameters to my controller:
class BrowsersController < ApplicationController
def index
#default_image = "/assets/default_avatar.jpg"
#users = Profile.search params[:search], :page => params[:page], :per_page => 26
end
end
If I was searching with one field with the param "Search" I would be fine but I have multiple fields, select menu's on my form. How am I suppose to pass that info to my controller in order to filter the search results?
I'm sure I'm not the first to use search filtering in ruby on rails
<%= form_tag browsers_path, :method => 'get' do %>
<p>
Location: <%= text_field_tag :location %><br />
Gender: <%= select_tag :gender,
options_for_select([["Select", nil],
["Male", 1],
["Female", 2]]) %>
<br />
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<br />
Kind regards
update
#users = Profile.search 'HERE IS WHERE THE POWER LIES', :page => params[:page], :per_page => 20, :conditions_all => { :gender => params[:gender], :location => params[:location]}
I use :conditions_all to get my field params and in rails server logs I can see that they are being picked up.. now I just need to some how get them all seen by thinking sphinx
Update 2
i have has gender in the define_index block and indexes location because it seems i need at least 1 field.
this working to return genders:
#users = Profile.search params[:location], :page => params[:page], :per_page => 40, :with => { :gender => [params[:gender]]}
I've tried to check for both location and gender and it seems to work but I'll double check in console because it's returning 1 female in united kingdom out of 1000 and that could be wrong but I'll double check in console and edit this update appropriately.
I'm not quite sure where you got :conditions_all from - if you're dealing with fields, then :conditions is what you're after:
#users = Profile.search 'HERE IS WHERE THE POWER LIES',
:page => params[:page],
:per_page => 20,
:conditions => {:gender => params[:gender], :location => params[:location]}
But, it sounds like you've got gender as an attribute instead of a field - and so, you want to filter on it instead:
#users = Profile.search 'HERE IS WHERE THE POWER LIES',
:page => params[:page],
:per_page => 20,
:conditions => {:location => params[:location]},
:with => {:gender => params[:gender]}
As I said here, I will try to explain a bit of Thinking Sphinx and my suggested approach to solve your problem.
Let's say you have the following Profile model:
# == Schema Information
#
# Table name: access_number_campaigns
#
# id :integer
# latitude :float
# longitude :float
# created_at :datetime
# updated_at :datetime
class Profile < ActiveRecord::Base
GENDER = {1 => "Male", 2 => "Female"}
belongs_to :country
has_and_belongs_to_many :sexual_preferences
has_and_belongs_to_many :age_ranges
end
And you may have those models:
class SexualPreference < ActiveRecord::Base
has_and_belongs_to_many :profiles
end
class AgeRange < ActiveRecord::Base
has_and_belongs_to_many :profiles
end
class Country < ActiveRecord::Base
has_many :profiles
end
Then you may define your ThinkingSphinx index in the following schema:
# within model Profile
define_index
has "RADIANS(latitude)", :as => :latitude, :type => :float
has "RADIANS(longitude)", :as => :longitude, :type => :float
has sexual_preferences(:id), :as => :sexual_preference_ids
has age_ranges(:id), :as => :age_range_ids
has country_id, :type => :integer
has gender, :type => :integer
end
And you can create a class to build the following ThinkingSphinx query to handle all needed associations and attributes:
Profile.search "your keywords", :page => 1, :per_page => 10, :with => {"#geodist" => 0.0..NUMBER_OF_METERS, :with_all=>{:sexual_preference_ids=>["1", "2", "3"], :age_range_ids=>["1", "2", "3"], :country_id => ["123"], :gender => ["1"]} }
You can see above a Thinking Sphinx search query with :with and :with_all hashes included. There is also an important Sphinx's #geodist function called. I hope you have readable example and the best reference for Thinking Sphinx gem you can find here:
Thinking Sphinx reference
Section about indexing
Section about searchin
Section about geodist
I hope you enjoy reading my example and very clear Thinking Sphinx reference.
I'm building a site for users to post events they wish to sell tickets for.
I'm writing a query where the conditions are the follow:
active equals true
sales_stop is < Time.now
The problem I am having is coming up with a condition which tests whether or not a record's sales_stop time is less than Time.now.
Below is what I have as of now:
#events = Event.paginate :page => params[:page],
:conditions => {:active => true},
:order => "created_at DESC"
In turn, I've been toying around with the sales_stop condition with no luck.
I've been trying something like this:
#events = Event.paginate :page => params[:page],
:conditions => {:active => true, :sales_stop < Time.now},
:order => "created_at DESC"
This of course doesn't work.
Does anyone know how I can set this query up so that I only retrieve records where the sales_stop attribute is less than Time.now?
Thank you.
Use the alternate syntax for :conditions, which uses a bind-style:
#events = Event.paginate :page => params[:page],
:conditions => ['active = ? AND sales_stop < ?', true, Time.now],
:order => "created_at DESC"
This should work:
#events = Event.paginate :page => params[:page],
:conditions => ['active=? AND sales_stop < ?', true, Time.now],
:order => "created_at DESC"
Just a different syntax.
How do you effectively search among many fields in a model?
# user.rb model
def self.search(search, page)
paginate :per_page => 20, :page => page,
:conditions =>
['name like ? OR notes like ? OR code like ? OR city like ? OR state like ?,
"%#{search}%","%#{search}%","%#{search}%","%#{search}%","%#{search}%"
], :order => 'name'
This code is horrible for any more than a few fields, and it doesn't return a result if, for instance word #1 comes from :name and word #2 comes from :code. Is there a more elegant way?
I think that do work
def self.search(search, page)
fields = [:name, :notes, :code, :city, :state]
paginate :per_page => 20, :page => page,
:conditions => [fields.map{|f| "#{f} like ?"}.join(' OR '),
*fields.map{|f| "%#{search}%"}], :order => 'name'
You can use searchlogic
def self.search(search, page)
search_cond = resource.search(name_or_notes_or_code_or_city_or_state_like => search.to_s)
search_cond.all
end
Hope you got the idea
def self.search(search, page)
fields = %w(name notes code city state)
paginate :per_page => 20, :page => page,
:conditions => [fields.map{|f| "#{f} like :phrase"}.join(' OR '), {:phrase => search}],
:order => 'name'
I installed Sphinx and Thinking Sphinx for ruby on rails 2.3.2.
When I search without conditions search works ok. Now, what I'd like to do is filter by tags, so, as I'm using the acts_as_taggable_on plugin, my Announcement model looks like this:
class Announcement < ActiveRecord::Base
acts_as_taggable_on :tags,:category
define_index do
indexes title, :as => :title, :sortable => true
indexes description, :as => :description, :sortable => true
indexes tags.name, :as => :tags
indexes category.name, :as => :category
has category(:id), :as => :category_ids
has tags(:id), :as => :tag_ids
end
For some reason, when I run the following command, it will bring just one announcement, that has nothing to do with what I expect. I've got many announcements, so I expected a lot of results instead.
Announcement.search params[:announcement][:search].to_s, :with => {:tag_ids => 1}, :page => params[:page], :per_page => 10
I guess something is wrong, and it's not searching correctly.
Can anyone give my a clue of what's going on?
Thanks,
Brian
Thinking Sphinx relies on associations in model. In common situations you only have to put index definition below your associations.
With acts_as_taggable_on plug-in you don't have tag-related associations in model file and when you write
indexes tags.name, :as => :tags
TS interprets it like:
CAST(`announcements`.`name` AS CHAR) AS `tags`
(look at sql_query in development.sphinx.conf, in my case).
I suppose that you have attribute name in model Announcement and don't run into error when rebuild index.
But we expect:
CAST(GROUP_CONCAT(DISTINCT IFNULL(`tags`.`name`, '0') SEPARATOR ' ') AS CHAR) AS `tags`
and:
LEFT OUTER JOIN `taggings` ON (`announcements`.`id` = `taggings`.`taggable_id`)
LEFT OUTER JOIN `tags` ON (`tags`.`id` = `taggings`.`tag_id`) AND taggings.taggable_type = 'Announcement'
To get things working just add tag-related associations in your model before you rebuild index:
class Announcement < ActiveRecord::Base
acts_as_taggable_on :tags,:category
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging",
:conditions => "taggings.taggable_type = 'Announcement'"
#for context-dependent tags:
has_many :category_tags, :through => :taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag",
:conditions => "taggings.context = 'categories'"
In define_index method:
indexes category_tags(:name), :as => :tags
has category_tags(:id), :as => :tag_ids, :facet => true
In controller:
#announcement_facets = Announcement.facets params[:search], :with => {:tag_ids => [...]}
#announcements = #announcement_facets.for.paginate( :page => params[:page], :per_page => 10 )
I found that simply defining the index thus:
Class Thing < ActiveRecord::Base
acts_as_taggable
define_index do
..other indexing...
indexes taggings.tag.name, :as => :tags
end
end
worked fine.
One possibility is that you need to declare the type for tag_ids as :multi because TS can get confused (I just discovered this here http://groups.google.com/group/thinking-sphinx/browse_thread/thread/9bd4572398f35712/14d4c1503f5959a9?lnk=gst&q=yanowitz#14d4c1503f5959a9).
But why not use the tag names to search? E.g.,
Announcement.search params[:announcement][:search].to_s, :conditions => {:tags => "my_tag"}, :page => params[:page], :per_page => 10
Or, if you need to search for multiple tags:
Announcement.search( "#{params[:announcement][:search].to_s} (#tags my_tag | #tags your_tag)", :page => params[:page], :per_page => 10 )
(as aside, you may want to sanitize/remove sphinx-control-characters from the user-provided query before using it).
For debugging, I would go into console and strip down your query as much as possible (eliminate pagination arguments, even the query (just do ""), etc.).