Tech
Creating a full text search with Rails 4 and Postgres.
Super fast text searching without a search engine
Tomislav Simnett
4 min read
What are we actually trying to do?
- When certain fields in our database are updated (or created), we want to create a list of words used in the content of those fields.
- We want to ignore words like 'and', 'a', 'it' etc.
- We want to make records containing words in certain fields, rank more highly/less high in search results than records with the same words contained in different fields
- We need to associate a particular record with our list of words used in its fields
- So that when a user searches for a particular word or words, we can return the relevant records
Deciding on the models/fields we wanted the search to return
- Ship
- Operator
- Title
- Description
- Booking info
Deciding on which fields are more important in search results.
- Ship 1
- Ship 2
- Ship 3
- Ship 1 - in its title field
- Ship 2 - in its description field
- Ship 3 - in its booking info field
- Title (weight = A)
- Description (weight = B)
- Booking info (weight = C)
Using the PostgreSQL 'to_tsvector()' function
"to_tsvector() parses a textual document into tokens, reduces the tokens to lexemes, and returns a tsvector which lists the lexemes together with their positions in the document."
to_tsvector('english', 'some text to process')
to_tsvector('some text to process')
to_tsvector(coalesce('some text to process', ''))
setweight(to_tsvector(coalesce('some text to process', '')), 'A')
Storing the results of the to_tsvector() function
class AddTsvToShips < ActiveRecord::Migration
def change
add_column :ships, :tsv, :tsvector
end
end
class AddShipTsvTrigger < ActiveRecord::Migration def up execute <
Ship: title: 'Initforthe' description: 'A big red ship' booking_info: 'Book now for a discount'
"'initforthe':1A 'big':3B 'red':4B 'ship':5B 'book':6C 'discount':10C"
When a user searches
def self.search(term)
if term.blank?
none
else
query = sanitize_search(term)
return none if query.blank? # empty result set
where("ships.tsv @@ #{query}").order("ts_rank_cd(ships.tsv, #{query}) DESC")
end
end
Further information
- Check out the PostgreSQL docs
- There's also a Railscast
- An alternative approach would be using something like Elastic Search
How much capacity is your business leaving behind?
Use the calculator to estimate what slow processes, manual work and disconnected systems could really be costing you.
More posts.
View all
Business
Four interruptions, two lost hours and the process your people shouldn’t be driving
The answer isn’t to tell people to focus harder. It’s to design a better system, one that drives the process, preserves context, moves routine work automatically and only interrupts people when their judgement is genuinely needed.
Tomislav Simnett
10 min read
Business
The approval takes five minutes. The waiting costs far more.
Tomislav Simnett
9 min read
Business
You probably don’t need a CRM; you need the work to flow properly.
When sales, operations and finance all rely on different systems, spreadsheets and good people end up holding the process together by hand.
Tomislav Simnett
6 min read