Ruby on Rails search with array of values to find in multiple fields (datatables) - ruby-on-rails

I implemented datatables like proposed on the http://railscasts.com/episodes/340-datatables and it works just fine. Unfortunately it doesnt concern the point of searching with spaces in the input field. If I implement it with the search function processing it with javascript it works just fine.
So what I want to do is search on the database on many fields where the parameters of the sql are splitted by space (dynamic size of parameters)
eg. "name1 street city" -> this means returned objects must contain all three "name1", "street", "city" in one of the objects fields.
Here's an example:
Person :name, :address, :city, :country
Person("Peter Mayer", "Some Street 111", "New York", "United States")
if a user searches with "Peter York" it should find the object
if a user searches with "Peter Los Angeles" it should not find anything
if a user searches with "111 Mayer States York" it should find the object
okey, i could write many different sql's respecting the amount of params but that istn't so nice
Is there an easy way to solve that?
Filtering after searching with just the first param isn't an option since pagination wouldnt work anymore

You could do the following:
Join all the attributes that must be found in one string
search_attributes = [person.name1, person.address, person.city, person.country].join(' ')
for your example search_attributes would be equal to "Peter Mayer Some Street 111 New York United States"
Then you iterate over every string in the query and make sure it is found in search_attributes using .include?, and return a record only if all strings in the sent query are found in search_attributes

I did it like this now:
searchData = params[:sSearch].split(" ")
searchString = "1 = 1"
searchParams = {}
i = 0
searchData.each do |searchParam|
searchString += " AND (persons.name like :search"+i.to_s+" or persons.address like
:search"+i.to_s+" or persons.city like :search"+i.to_s+" or persons.country like
:search"+i.to_s+"+")"
searchParams["search#{i}".to_sym] = "%"+searchParam+"%"
i += 1
end
Person.where(searchString, searchParams)

Related

Elasticsearch match field=value (not contains)

I have problem while searching through elasticsearch.
I have index product with fields title and gender
When I make query with default_field: title I need to get results only with gender=male (not female or others)
query: dress AND gender:male
Results contain both genders: male and female and male,female
It seems to me that gender:* search all which contains male, but not full match of value. How to do the query right?
I use it through Ruby on Rails
Product.search({
query: {
query_string: {
query: query,
default_field: "title"
}
},
size: per_page,
sort: [ _score: { order: relevance } ]
})
Is gender a keyword data type? I suspect that you left/set the default mapping to the gender field (i.e., text + keyword subfield). In this case, try the following query: dress AND gender.keyword:male
According with this I just need to put value in double quotes..
query = '(dress) AND (gender:"male")'
do not forget to escape them if needed "gender:\"male\""

Rails ActiveRecord Query a field based on word contains but not partial word

I know we have LIKE option to search partial word, exact word match and also word contains (but having some problem, let me explain that below)
User.where("name like ?", "%john%")
# above code will search the name column and it will return records even if it
matches the character partially i.e "joh" also return a record.
User.where("name like ?", "john")
# above code will return a record if 'john' word is in the name column.
It will match the exact word.
User.where("name like ?", "% john %")
# this will return a record if name column contains 'john' text i.e "Sample john victor".
But if a record has "John victor" then it won't find this record because '% john %'
can only match middle of the words except first and last word in the name column
since we having space in both front and end of the word in our search i.e "% john %"
Can someone help me to find a record based on word contains in a string of one column in a Table?
You don't specify which database you're using and it's possible different databases allow different forms of matching. E.g. in postgres you could use SIMILAR TO
For a straight SQL LIKE I think you'll need to do several options ORed together.
E.g.
User.where("(name = ?) or (name like ?) or (name like ?) or (name like ?))",
'john'
'% john %',
'john %',
'% john')
That'll get values that are 'john', that inclued 'john', that start with 'john' and that end with 'john' respectively.
That might be pretty slow depending on the data you're querying but I think it'll achieve what you want.

How to have search functionality on association models as well

By using a railscast video i create a simple search that works on same model. But now i have a model that shows associated model data as well and i would like to search on them as well.
Right now i managed to make it semi work, but i assume i have conflict if i add the field "name" into the joins as i have two models that have a column named "name"
def self.search(search)
if search
key = "'%#{search}%'"
columns = %w{ city station venue area country plate_number }
joins(:services).joins(:washer).joins(:location).where(columns.map {|c| "#{c} ILIKE #{key}" }.join(' OR '))
else
where(nil)
end
end
What do i need to change to be sure i can search across all columns?
I think when you have ambiguous field name after join then you can mention table_name.field_name so it remove the ambiguity and works. something like.
joins(:services).joins(:washer).where("services.name = ? or washer.name = ?", "test", "test")

Searching on multiple fields with Indextank

I'm indexing books and perform text searches on different fields of the book :
Title
Author
Book summary
i tried to create an index by concatenating the book name, the author name and the book summary but some of my searches don't return the expected results and i dont understand why.
What is the right way to index the books so that i search on all these fields at the same time ?
--
Here is the code sample :
book_text_index = "#{book.name} #{book.author} #{book.summary}"
idx.document("book_502").add({ :text => book_text_index,
:book_id => "#{book.id}",
:name => "#{book.name}",
:author => "#{book.author}",
:summary => "#{book.summary}"
})
And here is an example of the results i get for the book "L'art de la guerre" by "Sun Tzu".
If i search the author name ("tzu") it returns the book:
idx.search("tzu", :function => 1, :fetch => 'text' )['results']
=> [{"text"=>"L'art de la guerre Sun Tzu Youboox libres de droits
Traduit pour la première fois...", "docid"=>"book_502",
"query_relevance_score"=>-2967.0}]
But if i search a part of the book title ("guerre") i dont get the book in the results.
idx.search("guerre", :function => 1, :fetch => 'book_id'
)['results'].map { |result| result["docid"]}
=> ["book_1962", "book_1963", "book_1951", "book_1832", "book_1812",
"book_1787", "book_1775", "book_1778", "book_1730", "book_1740"]
You can see that the book_502 is not in the results.
In reply to your question, "What is the right way to index the books so that i search on all these fields at the same time ?" - concatenating the fields into a single 'text' field is the simplest way to achieve this. One possible downside to this method is that for relevance (the order of the results), this gives equal weight to the book title, author, and summary.
In this particular case (Book title, author, and summary), I would guess that the book title and author are more "important" for matching than the description. In other words, if the user's query matches a book title, it is a better result than if it only matched the summary. If this is the case, here is how you can get more relevant results for your users (it's a little more work, but often worth it).
First, you index into 3 separate fields:
name - contains the book title
author - contains the author
text - contains the book summary, and possibly other keywords you want to
match
Then at search time, in order to search across all fields, you will use an OR query. However, to give more weight to the title and author than the summary, your queries will look like this (example user search for "guerre"):
name:(guerre)^6 OR author:(guerre)^5 OR text:(guerre)
Another example, if the user searches for "sun tzu":
name:(sun tzu)^6 OR author:(sun tzu)^5 OR text:(sun tzu)
The parenthesis are necessary to keep proper field grouping. So your query template will be something like this (note, my Ruby is rusty):
searchify_query = "name:(#{user_query})^6 OR author:(#{user_query})^5 OR text:(#{user_query})"
Hope this helps!

Ruby on Rails: How to store form data

I have a state field that stores the value as 2 characters. For example, Alabama is saved as AL, Alaska is saved as AK, Arizona is saved as AZ, etc. In the show.html.erb, how do I display the long name for the state such as Alabama instead of just showing AL? Is this possible or should I just store the long name in the database such as Alabama, Alaska, Arizona, etc?
Write a method that would output long name of a stateand call it in show.html.erb
some_model.rb:
SomeModel < ActiveRecord::Base
STATE_CODES = {
"AL": "Alabama", "AK": "Alaska",
# add remaining 50
}
def state_human_name
STATE_CODES[self.state]
end
show.html.erb:
<%= record.state_human_name %>
EDIT: It does not help to store full names of states in your database -- you'll need short forms at least somewhere and therefore would need to add mapping between short and long forms anyway.
Is there a reason for using the 2 letter codes (e.g. a legacy database)? If not I would stick to the usual ActiveRecord idiom and have a separate "states" table linked by id. If you need the 2 letter code for display purposes, printing address labels or whatever then add add a 'state_code' attribute to the states table but don't use it as a primary key.
I put this in a comment, but I've decided it's sufficiently different that it warrants an answer.
When you're deciding where to keep your state map, consider whether you'll ever need to ship things to Canada, or further afield. If so, it's worth the effort to set up a states table, linked to a countries table.
And anyway, if your data rarely changes, it's less issue-prone to put it in the database, because code changes far more often. More frequent changes = more opportunities to mess it up. Plus, it's then trivial to sort as you like.
class State < ActiveRecord::Base
def self.get_states
##states || State.find(:all,
:include => :country,
:order => 'countries.name, long_name')
end
end
Tilendor, I notice that if I use STATE_CODES.invert, the drop-down menu selection would get out of order. For example, the first five lines of my option list is shown below:
New Hampshire
Ohio
Colorado
Minnesota
Alabama
...
In my STATES_CODES hash, I have the following listed in the order below:
"AL" => "Alabama",
"AK" => "Alaska",
"AZ" => "Arizona",
"AR" => "Arkansas",
"CA" => "California",
...
Is there a way to have the options listed in the form in the same order as the STATES_CODES? Maybe sort them alphabetically?

Resources