Я решил это сам. Решение, которое я использовал, состояло в том, чтобы скомпилировать необходимые области в виде строк, объединить их, а затем оценить их внутри блока поиска.
Для этого требовалась отдельная библиотека построителя запросов, которая опрашивает индексы solr, чтобы убедиться, что область не создана для несуществующего поля индекса.
Код очень специфичен для моего проекта и слишком длинный, чтобы публиковать его полностью, но я так и делаю:
1. Разделить условия поиска
это дает мне массив терминов или терминов плюс поля:
['field:term', 'non field terms']
2. Это передается построителю запросов.
Построитель преобразует массив в области на основе доступных индексов. Этот метод является примером, который берет класс модели, поле и значение и возвращает область, если поле проиндексировано.
def convert_text_query_to_search_scope(model_clazz, field, value)
if field_is_indexed?(model_clazz, field)
escaped_value = value.gsub(/'/, "\\\\'")
"keywords('#{escaped_value}', :fields => [:#{field}])"
else
""
end
end
3. Объедините все возможности
Созданные области объединяются join("\n")
, то есть eval
ed.
Этот подход позволяет пользователю выбирать модели, которые он хочет найти, и, при желании, выполнять поиск по конкретному полю. Затем система будет выполнять поиск моделей только с указанными полями (или общими полями), игнорируя остальные.
Метод проверки индексации поля:
# based on http://blog.locomotivellc.com/post/6321969631/sunspot-introspection
def field_is_indexed?(model_clazz, field)
# first part returns an array of all indexed fields - text and other types - plus ':class'
Sunspot::Setup.for(model_clazz).all_field_factories.map(&:name).include?(field.to_sym)
end
А если кому-то это нужно, проверка на сорбируемость:
def field_is_sortable?(classes_to_check, field)
if field.present?
classes_to_check.each do |table_clazz|
return false if ! Sunspot::Setup.for(table_clazz).field_factories.map(&:name).include?(field.to_sym)
end
return true
end
false
end