Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building a Speedy Autocomplete with Postgres on...

Building a Speedy Autocomplete with Postgres on Rails

Bradley Priest

August 27, 2013
Tweet

More Decks by Bradley Priest

Other Decks in Programming

Transcript

  1. API: - Rails + Postgres + Redis Main Site: CoffeeScript

    + EmberJS ~3 other Rails mini-apps
  2. I

  3. - Every new PG Feature is my hammer - So

    many nails - I’ve made plenty of over-optimisations - Mostly hstore - e.g. Product Prices/Options
  4. pg_search V2 class Variant < AR::B include PgSearch pg_search_scope(:autocomplete, against:

    [:name, :sku, :barcode], using: {tsearch: {prefix: true}} ) ### end
  5. pg_search V2 class Variant < AR::B include PgSearch pg_search_scope(:autocomplete, against:

    [:name, :sku, :barcode, :description], using: {tsearch: {prefix: true}} ) ### end
  6. pg_search V2 class Variant < AR::B include PgSearch pg_search_scope(:autocomplete, against:

    [:name, :sku, :barcode, :description], associated_against: {product: :name}, using: {tsearch: {prefix: true}} ) ### end
  7. Build it yourself class Variant < AR::B include PgSearch def

    self.autocomplete(term) match = “%#{term}%” self.where(‘name ilike ? or sku ilike ? or description ilike ? or barcode ilike ?’, match, match, match, match) end ### end
  8. Build it yourself class Variant < AR::B include PgSearch def

    self.autocomplete(term) match = “%#{term}%” self.joins(:product) .where(‘variants.name ilike ? or sku ilike ? or description ilike ? or barcode ilike ? or products.name ilike ?’, match, match, match, match, match) end ### end
  9. Bonus - Use AREL def self.autocomplete(term) match = “%#{term}%” search_arel

    = arel_table[:name].matches(match).or( arel_table[:sku].matches(match).or( arel_table[:description].matches(match).or( arel_table[:barcode].matches(match).or( Product.arel_table[:name].matches(match) ) ) ) ) self.joins(:product).where(search_arel) end
  10. tsvector “A tsvector value is a sorted list of distinct

    lexemes, which are words that have been normalized to make different variants of the same word look alike.” - Postgres manual - http://www.postgresql.org/docs/9.2/static/datatype-textsearch.html
  11. tsvector def change add_column :variants, :search, :tsvector end class Variant

    < AR::B include PgSearch pg_search_scope(:autocomplete, against: :search, using: {tsearch: {prefix: true}} ) end
  12. coalesce() ...returns the first of its arguments that is not

    null… coalesce(null, ‘’) #=> ‘’ Make sure we don’t have any pesky nulls
  13. setweight() ...used to label the entries of a tsvector with

    a given weight, where a weight is one of the letters A, B, C, or D... setweight(to_tsvector(name), 'A')
  14. 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(‘simple’, name)
  15. to_tsvector() to_tsvector(‘simple’,’a fat cat sat on a mat’) #=> 'a':1,6

    'cat':3 'fat':2 'mat':7 'on':5 'sat':4 to_tsvector(‘english’,’a fat cat sat on a mat’) #=> 'cat':3 'fat':2 'mat':7 'sat':4"
  16. schema.rb doesn’t know about triggers application.rb - config.active_record.schema_format = :sql

    But then you have to deal with even more merge conflicts than schema.rb