Поиск RoR, основанный на множестве ассоциаций, таких как теги - PullRequest
1 голос
/ 12 августа 2011

Я создал базу данных RoR с несколькими разными моделями. Каждая «запись» имеет много тегов и также помещается в категорию с помощью «cat_id». Я построил все модели, но мне нужна помощь с фильтрацией. Прямо сейчас у меня есть возможность выбрать категорию в виде дерева с узлами, и она показывает все записи в этой категории и дочерние категории. Я также могу выполнить простой текстовый поиск с помощью функции поиска (оператор LIKE в SQL). Я хочу добавить дополнительный фильтр тегов, чтобы иметь возможность выбрать категорию и тег и показать результаты.

Вот что у меня так далеко:

МОДЕЛЬ ЗАПИСИ

class AuctionRecord < ActiveRecord::Base

  #comments
  has_many :comments, :dependent => :destroy

  #tags
  has_many :auction_records_tags
  has_many :tags, :through => :auction_records_tags 

МОДЕЛЬ TAG

class Tag < ActiveRecord::Base
  attr_accessible :name
  has_many :auction_records_tags
  has_many :auction_records, :through => :auction_records_tags
end

AUCTIONS_RECORDS_TAGS MODEL

class AuctionRecordsTag < ActiveRecord::Base
  attr_accessible :auction_record_id, :tag_id

  belongs_to :tag
  belongs_to :auction_record

end

Как вы можете видеть, записи и теги имеют отношение многие ко многим, поэтому можно выбрать много тегов и получить множество записей аукциона.

КОНТРОЛЛЕР ЗАПИСИ

@auction_records = AuctionRecord.filter(session[:search], @cat_sql).order(sort_column + " " + sort_direction).paginate(:per_page => 20, :page => session[:page] )

ФУНКЦИЯ ФИЛЬТРА

 #this is the main search engine
        def self.filter(search, cat_sql)   

        search_sql = "" 

        if search != ""  
          search_sql = "title LIKE '%#{search}%' OR itemnumber LIKE '%#{search}%' OR buyer LIKE '%#{search}%' OR seller LIKE '%#{search}%'"
          end

        if cat_sql != "" && search == ""
          search_sql = cat_sql
        end    

        if cat_sql != "" && search != ""
          search_sql = search_sql + " AND " + cat_sql
        end

        if search_sql !=""      
          where(search_sql)
        else 
          scoped
        end

      end

Я сгенерирую оператор sql категории вручную для циклического прохождения всех узлов и дочерних узлов. Как видите, я делаю почти все "вручную" (не лучшим образом). Тот факт, что некоторые фильтры также не могут быть определены, был проблематичным (например, выбор категории, но не поисковый термин). Я думаю, что все три фильтра могут быть выполнены в одной строке кода, но я не уверен, как это делается. Возможно, что-то вроде tags.auction_records? Если мне нужно опубликовать схему таблиц, я тоже могу это сделать, но на данный момент все категоризация и тегирование выполняются с помощью целочисленных / идентичных отношений.

Заранее спасибо за любую помощь / комментарии

С уважением

Добавлена ​​информация # 1

NODE MODEL (используется для категоризации / категорий)

class Node < ActiveRecord::Base
  has_many :nodes
  belongs_to :node
end

1035 * SCHEMA *

 create_table "auction_records", :force => true do |t|
    t.string   "title"    
    t.string   "categorization"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

 create_table "auction_records_tags", :force => true do |t|
    t.integer  "auction_record_id"
    t.integer  "tag_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "nodes", :force => true do |t|
    t.integer  "node_id"
    t.string   "text"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "tags", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

Для "auction_records" поле категоризации содержит целое число node_id, указывающее, какому узлу оно принадлежит в представлении дерева. Если вы щелкнете по одному узлу (категории), появится много аукционов. Auction_records_tags также создает связь между auction_records и тегами. Я нашел что-то, что работает только для одного tag_id, но мне придется работать над чем-то для нескольких тегов.

Сейчас у меня есть этот код, и он возвращает ошибку "Ассоциация с именем" узлы "не найдена"

       search_term = session[:search]
        term = "%#{search_term}%"
        @auction_records = AuctionRecord.scoped
        @auction_records = @auction_records.where('title LIKE ?', term).where('description LIKE ?',term)  if search_term
        @auction_records = @auction_records.joins(:nodes).where('node.node_id IN ?', session[:cat_id])  if session[:cat_id]
            @auction_records = @auction_records.joins(:tags).where('tags.id = ?', session[:tag_ids])  if session[:tag_ids]
        @auction_records = @auction_records.order(sort_column + " " + sort_direction).paginate(:per_page => 20, :page => session[:page] )

1 Ответ

1 голос
/ 12 августа 2011

Помните, что в Rails 3 вы можете выполнять цепочечные запросы, которые менее подвержены ошибкам, чем конкатенация, также лучше использовать подстановочные знаки для предотвращения внедрения SQL. Вы можете изменить код так, чтобы он выглядел примерно так:

def self.filter(search_term, categories, tags)
  term = "%#{search_term}%"
  auction_records = AuctionRecord.scoped
  auction_records = auction_records.where('title LIKE ?', term).where('other_field LIKE ?',term)  if search_term
  auction_records = auction_records.joins(:categories).where('categories.name IN ?', categories)  if categories
  auction_records = auction_records.joins(:tags).where('tags.name IN ?' tags)  if tags
end
...