Im пытается реплицировать стиль списка поиска crunchbase, используя ruby on rails. У меня есть набор фильтров, который выглядит примерно так:
[
{
"id":"0",
"className":"Company",
"field":"name",
"operator":"starts with",
"val":"a"
},
{
"id":"1",
"className":"Company",
"field":"hq_city",
"operator":"equals",
"val":"Karachi"
},
{
"id":"2",
"className":"Category",
"field":"name",
"operator":"does not include",
"val":"ECommerce"
}
]
Я отправляю эту строку json в свой рубиновый контроллер, где я реализовал эту логику:
filters = params[:q]
table_names = {}
filters.each do |filter|
filter = filters[filter]
className = filter["className"]
fieldName = filter["field"]
operator = filter["operator"]
val = filter["val"]
if table_names[className].blank?
table_names[className] = []
end
table_names[className].push({
fieldName: fieldName,
operator: operator,
val: val
})
end
table_names.each do |k, v|
i = 0
where_string = ''
val_hash = {}
v.each do |field|
if i > 0
where_string += ' AND '
end
where_string += "#{field[:fieldName]} = :#{field[:fieldName]}"
val_hash[field[:fieldName].to_sym] = field[:val]
i += 1
end
className = k.constantize
puts className.where(where_string, val_hash)
end
Что я делаю, я петлю над массивом json и создаю хэш с ключами как имена таблиц, а значения - это массив с именем столбца, оператором и значением для применения этого оператора. Поэтому после создания тэга table_names
у меня было бы что-то подобное:
{
'Company':[
{
fieldName:'name',
operator:'starts with',
val:'a'
},
{
fieldName:'hq_city',
operator:'equals',
val:'karachi'
}
],
'Category':[
{
fieldName:'name',
operator:'does not include',
val:'ECommerce'
}
]
}
Теперь я перебираю хэш таблицы table_names и создаю запрос where с использованием синтаксиса Model.where("column_name = :column_name", {column_name: 'abcd'})
.
Итак, я бы создал два запроса:
SELECT "companies".* FROM "companies" WHERE (name = 'a' AND hq_city = 'b')
SELECT "categories".* FROM "categories" WHERE (name = 'c')
Теперь у меня есть две проблемы:
1. Операторы:
У меня есть много операторов, которые можно применить к столбцу типа "начинается с", "заканчивается", "равно", "не равно", "включает", "не включает", "больше", 'меньше, чем'. Я предполагаю, что лучшим способом было бы сделать случай переключения на операторе и использовать соответствующий символ при построении строки where. Например, если оператор "начинает с", я бы сделал что-то вроде where_string += "#{field[:fieldName]} like %:#{field[:fieldName]}"
, а также для других.
Итак, этот подход правильный, и этот тип подстановочного синтаксиса разрешен в этом типе .where
?
2. Более 1 таблицы
Как вы видели, мой подход создает 2 запроса для более чем 2 таблиц. Мне не нужны 2 запроса, мне нужно, чтобы имя категории было в том же запросе, где категория принадлежит компании.
Теперь, что я хочу сделать, мне нужно создать такой запрос:
Company.joins(:categories).where("name = :name and hq_city = :hq_city and categories.name = :categories[name]", {name: 'a', hq_city: 'Karachi', categories: {name: 'ECommerce'}})
Но это не так. Поиск может стать очень сложным. Например:
Компания имеет много FundingRound. FundingRound может иметь много инвестиций и инвестиций, которые могут иметь много IndividualInvestor. Поэтому я могу выбрать создать фильтр, например:
{
"id":"0",
"className":"IndividualInvestor",
"field":"first_name",
"operator":"starts with",
"val":"za"
}
Мой подход создаст такой запрос:
SELECT "individual_investors".* FROM "individual_investors" WHERE (first_name like %za%)
Этот запрос неверен. Я хочу обратиться к отдельным инвесторам с просьбами об инвестициях в раунд финансирования компании. Который много соединяет таблицы.
Подход, который я использовал, применим к одной модели и не может решить проблему, о которой я говорил выше.
Как я могу решить эту проблему?