Commit 220a5f3e316e83661fb6b781e15d8ee8b2dc7a03
1 parent
a82bdd13
Exists in
fix_sign_up_form
Elasticsearch: Refactored controller and api
Signed-off-by: Macartur Sousa <macartur.sc@gmail.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>
Showing
15 changed files
with
268 additions
and
25 deletions
Show diff stats
plugins/elasticsearch/controllers/elasticsearch_plugin_controller.rb
| @@ -12,8 +12,27 @@ class ElasticsearchPluginController < ApplicationController | @@ -12,8 +12,27 @@ class ElasticsearchPluginController < ApplicationController | ||
| 12 | def search | 12 | def search |
| 13 | define_searchable_types | 13 | define_searchable_types |
| 14 | define_search_fields_types | 14 | define_search_fields_types |
| 15 | + define_results | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def define_results | ||
| 19 | + @query = params[:query] | ||
| 20 | + @results = process_results | ||
| 21 | + end | ||
| 15 | 22 | ||
| 16 | - process_results | 23 | + def define_searchable_types |
| 24 | + @searchable_types = ElasticsearchHelper::searchable_types | ||
| 25 | + @selected_type = (params[:selected_type]|| :all ).to_sym | ||
| 17 | end | 26 | end |
| 18 | 27 | ||
| 28 | + def define_search_fields_types | ||
| 29 | + @search_filter_types = ElasticsearchHelper::search_filters | ||
| 30 | + @selected_filter_field = (params[:selected_filter_field] || ElasticsearchHelper::search_filters.keys.first).to_sym | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + private | ||
| 34 | + | ||
| 35 | +# def permit_params | ||
| 36 | +# params.require(:per_page, :query) | ||
| 37 | +# end | ||
| 19 | end | 38 | end |
| @@ -0,0 +1,91 @@ | @@ -0,0 +1,91 @@ | ||
| 1 | +module ElasticsearchHelper | ||
| 2 | + | ||
| 3 | + def self.searchable_types | ||
| 4 | + { | ||
| 5 | + :all => { label: _("All Results")}, | ||
| 6 | + :community => { label: _("Communities")}, | ||
| 7 | + :event => { label: _("Events")}, | ||
| 8 | + :person => { label: _("People")} | ||
| 9 | + } | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + def self.search_filters | ||
| 13 | + { | ||
| 14 | + :lexical => { label: _("Alphabetical Order")}, | ||
| 15 | + :recent => { label: _("More Recent Order")}, | ||
| 16 | + :access => { label: _("More accessed")} | ||
| 17 | + } | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + def fields_from_model | ||
| 21 | + klass::SEARCHABLE_FIELDS.map do |key, value| | ||
| 22 | + if value[:weight] | ||
| 23 | + "#{key}^#{value[:weight]}" | ||
| 24 | + else | ||
| 25 | + "#{key}" | ||
| 26 | + end | ||
| 27 | + end | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + def get_query text, klass=nil | ||
| 31 | + query = {} | ||
| 32 | + unless text.blank? | ||
| 33 | + text = text.downcase | ||
| 34 | + query = { | ||
| 35 | + query: { | ||
| 36 | + match_all: { | ||
| 37 | + } | ||
| 38 | + }, | ||
| 39 | + filter: { | ||
| 40 | + regexp: { | ||
| 41 | + name: { | ||
| 42 | + value: ".*" + text + ".*" } | ||
| 43 | + } | ||
| 44 | + }, | ||
| 45 | + suggest: { | ||
| 46 | + autocomplete: { | ||
| 47 | + text: text, | ||
| 48 | + term: { | ||
| 49 | + field: "name", | ||
| 50 | + suggest_mode: "always" | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + } | ||
| 56 | + end | ||
| 57 | + query | ||
| 58 | + end | ||
| 59 | + | ||
| 60 | + def process_results | ||
| 61 | + selected_type = (params[:selected_type]|| :all).to_sym | ||
| 62 | + if selected_type == :all | ||
| 63 | + search_from_all_models | ||
| 64 | + else | ||
| 65 | + search_from_model selected_type | ||
| 66 | + end | ||
| 67 | + end | ||
| 68 | + | ||
| 69 | + def search_from_all_models | ||
| 70 | + models = [] | ||
| 71 | + query = get_query params[:query] | ||
| 72 | + | ||
| 73 | + ElasticsearchHelper::searchable_types.keys.each {| model | models.append( model.to_s.classify.constantize) if model != :all } | ||
| 74 | + Elasticsearch::Model.search(query, models, size: default_per_page(params[:per_page])).page(params[:page]).records | ||
| 75 | + end | ||
| 76 | + | ||
| 77 | + def search_from_model model | ||
| 78 | + begin | ||
| 79 | + klass = model.to_s.classify.constantize | ||
| 80 | + query = get_query params[:query], klass | ||
| 81 | + klass.search(query, size: default_per_page(params[:per_page])).page(params[:page]).records | ||
| 82 | + rescue | ||
| 83 | + [] | ||
| 84 | + end | ||
| 85 | + end | ||
| 86 | + | ||
| 87 | + def default_per_page per_page | ||
| 88 | + per_page ||= 10 | ||
| 89 | + end | ||
| 90 | + | ||
| 91 | +end |
plugins/elasticsearch/lib/elasticsearch_plugin/api.rb
| 1 | require_relative '../../helpers/elasticsearch_helper' | 1 | require_relative '../../helpers/elasticsearch_helper' |
| 2 | - | 2 | +require_relative 'entities' |
| 3 | 3 | ||
| 4 | class ElasticsearchPlugin::API < Grape::API | 4 | class ElasticsearchPlugin::API < Grape::API |
| 5 | include Api::Helpers | 5 | include Api::Helpers |
| 6 | + helpers ElasticsearchHelper | ||
| 6 | 7 | ||
| 7 | resource :search do | 8 | resource :search do |
| 8 | get do | 9 | get do |
| 9 | - present target, :with => Api::Entities::Person | 10 | + target = process_results |
| 11 | + present target, | ||
| 12 | + :with => Elasticsearch::Entities::Result, | ||
| 13 | + :types => ElasticsearchHelper.searchable_types.except(:all).keys.map { |key| key.to_s.classify } | ||
| 10 | end | 14 | end |
| 11 | 15 | ||
| 12 | get 'types' do | 16 | get 'types' do |
| 13 | - types = {types: ElasticsearchHelper::SEARCHABLE_TYPES.stringify_keys.keys} | 17 | + types = {types: ElasticsearchHelper::searchable_types.stringify_keys.keys} |
| 14 | present types, with: Grape::Presenters::Presenter | 18 | present types, with: Grape::Presenters::Presenter |
| 15 | end | 19 | end |
| 16 | 20 |
plugins/elasticsearch/lib/elasticsearch_plugin/entities.rb
0 → 100644
| @@ -0,0 +1,17 @@ | @@ -0,0 +1,17 @@ | ||
| 1 | +module Elasticsearch | ||
| 2 | + module Entities | ||
| 3 | + class Result < Api::Entity | ||
| 4 | + root "results","result" | ||
| 5 | + | ||
| 6 | + expose :type do |object, options| | ||
| 7 | + options[:types].detect { |type| type.to_s.upcase if object.is_a? (type.to_s.classify.constantize) } | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + expose :name | ||
| 11 | + | ||
| 12 | + expose :author, if: lambda { |object,options| object.respond_to? 'author'} do |object, options| | ||
| 13 | + object.author.present? ? object.author.name : "" | ||
| 14 | + end | ||
| 15 | + end | ||
| 16 | + end | ||
| 17 | +end |
plugins/elasticsearch/lib/ext/text_article.rb
| @@ -4,8 +4,8 @@ require_relative '../elasticsearch_indexed_model' | @@ -4,8 +4,8 @@ require_relative '../elasticsearch_indexed_model' | ||
| 4 | class TextArticle | 4 | class TextArticle |
| 5 | def self.control_fields | 5 | def self.control_fields |
| 6 | { | 6 | { |
| 7 | - :advertise => nil, | ||
| 8 | - :published => nil, | 7 | + :advertise => {}, |
| 8 | + :published => {}, | ||
| 9 | } | 9 | } |
| 10 | end | 10 | end |
| 11 | include ElasticsearchIndexedModel | 11 | include ElasticsearchIndexedModel |
plugins/elasticsearch/lib/ext/uploaded_file.rb
| @@ -4,8 +4,8 @@ require_relative '../elasticsearch_indexed_model' | @@ -4,8 +4,8 @@ require_relative '../elasticsearch_indexed_model' | ||
| 4 | class UploadedFile | 4 | class UploadedFile |
| 5 | def self.control_fields | 5 | def self.control_fields |
| 6 | { | 6 | { |
| 7 | - :advertise => nil, | ||
| 8 | - :published => nil, | 7 | + :advertise => {}, |
| 8 | + :published => {}, | ||
| 9 | } | 9 | } |
| 10 | end | 10 | end |
| 11 | include ElasticsearchIndexedModel | 11 | include ElasticsearchIndexedModel |
plugins/elasticsearch/test/test_helper.rb
| 1 | require 'test_helper' | 1 | require 'test_helper' |
| 2 | +require_relative '../../../test/api/test_helper.rb' | ||
| 2 | 3 | ||
| 3 | -class ElasticsearchTestHelper < ActionController::TestCase | 4 | +module ElasticsearchTestHelper |
| 4 | 5 | ||
| 5 | def setup | 6 | def setup |
| 6 | setup_environment | 7 | setup_environment |
| 8 | + create_instances | ||
| 7 | import_instancies | 9 | import_instancies |
| 8 | end | 10 | end |
| 9 | 11 | ||
| 12 | + def create_instances | ||
| 13 | + end | ||
| 14 | + | ||
| 10 | def teardown | 15 | def teardown |
| 11 | indexed_models.each {|model| | 16 | indexed_models.each {|model| |
| 12 | model.__elasticsearch__.client.indices.delete index: model.index_name | 17 | model.__elasticsearch__.client.indices.delete index: model.index_name |
| @@ -16,9 +21,10 @@ class ElasticsearchTestHelper < ActionController::TestCase | @@ -16,9 +21,10 @@ class ElasticsearchTestHelper < ActionController::TestCase | ||
| 16 | def import_instancies | 21 | def import_instancies |
| 17 | indexed_models.each {|model| | 22 | indexed_models.each {|model| |
| 18 | model.__elasticsearch__.create_index! | 23 | model.__elasticsearch__.create_index! |
| 24 | + sleep 2 | ||
| 19 | model.import | 25 | model.import |
| 26 | + sleep 1 | ||
| 20 | } | 27 | } |
| 21 | - sleep 2 | ||
| 22 | end | 28 | end |
| 23 | 29 | ||
| 24 | def setup_environment | 30 | def setup_environment |
| @@ -31,7 +37,7 @@ class ElasticsearchTestHelper < ActionController::TestCase | @@ -31,7 +37,7 @@ class ElasticsearchTestHelper < ActionController::TestCase | ||
| 31 | end | 37 | end |
| 32 | 38 | ||
| 33 | def indexed_fields model | 39 | def indexed_fields model |
| 34 | - model.mappings.to_hash[model.name.downcase.to_sym][:properties] | 40 | + model.mappings.to_hash[model.name.underscore.to_sym][:properties] |
| 35 | end | 41 | end |
| 36 | 42 | ||
| 37 | end | 43 | end |
plugins/elasticsearch/test/unit/api/elasticsearch_plugin_api_test.rb
0 → 100644
| @@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
| 1 | +require "#{File.dirname(__FILE__)}/../../test_helper" | ||
| 2 | + | ||
| 3 | +class ElasticsearchPluginApiTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 6 | + | ||
| 7 | + def indexed_models | ||
| 8 | + [Community, Person] | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def create_instances | ||
| 12 | + 7.times.each {|index| create_user "person_#{index}"} | ||
| 13 | + 4.times.each {|index| fast_create Community, name: "community_#{index}", created_at: Date.new } | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | + should 'show all types avaliable in /search/types endpoint' do | ||
| 17 | + get "/api/v1/search/types" | ||
| 18 | + json = JSON.parse(last_response.body) | ||
| 19 | + assert_equal 200, last_response.status | ||
| 20 | + assert_equal ElasticsearchHelper::searchable_types.stringify_keys.keys, json["types"] | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + should 'respond with endpoint /search with more than 10 results' do | ||
| 24 | + get "/api/v1/search" | ||
| 25 | + json = JSON.parse(last_response.body) | ||
| 26 | + assert_equal 200, last_response.status | ||
| 27 | + assert_equal 10, json["results"].count | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + should 'respond with query in downcase' do | ||
| 31 | + get "/api/v1/search?query=person_" | ||
| 32 | + json = JSON.parse(last_response.body) | ||
| 33 | + assert_equal 200, last_response.status | ||
| 34 | + assert_equal 7, json["results"].count | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + should 'respond with query in upcase' do | ||
| 38 | + get "/api/v1/search?query=PERSON_" | ||
| 39 | + json = JSON.parse(last_response.body) | ||
| 40 | + assert_equal 200, last_response.status | ||
| 41 | + assert_equal 7, json["results"].count | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + should 'respond with selected_type' do | ||
| 45 | + get "/api/v1/search?selected_type=community" | ||
| 46 | + json = JSON.parse(last_response.body) | ||
| 47 | + assert_equal 200, last_response.status | ||
| 48 | + assert_equal 4, json["results"].count | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | +end |
plugins/elasticsearch/test/unit/controllers/elasticsearch_plugin_controller_test.rb
| 1 | require "#{File.dirname(__FILE__)}/../../test_helper" | 1 | require "#{File.dirname(__FILE__)}/../../test_helper" |
| 2 | 2 | ||
| 3 | -class ElasticsearchPluginControllerTest < ElasticsearchTestHelper | 3 | +class ElasticsearchPluginControllerTest < ActionController::TestCase |
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 4 | 6 | ||
| 5 | def indexed_models | 7 | def indexed_models |
| 6 | [Community, Person] | 8 | [Community, Person] |
| 7 | end | 9 | end |
| 8 | 10 | ||
| 9 | - def setup | ||
| 10 | - create_instances | ||
| 11 | - super | ||
| 12 | - end | ||
| 13 | - | ||
| 14 | - def teardown | ||
| 15 | - super | ||
| 16 | - end | ||
| 17 | - | ||
| 18 | def create_instances | 11 | def create_instances |
| 19 | create_people | 12 | create_people |
| 20 | create_communities | 13 | create_communities |
plugins/elasticsearch/test/unit/elasticsearch_test.rb
| 1 | require "#{File.dirname(__FILE__)}/../test_helper" | 1 | require "#{File.dirname(__FILE__)}/../test_helper" |
| 2 | 2 | ||
| 3 | -class ElasticsearchTest < ElasticsearchTestHelper | 3 | +class ElasticsearchTest < ActionController::TestCase |
| 4 | 4 | ||
| 5 | should 'be return yellow for health status' do | 5 | should 'be return yellow for health status' do |
| 6 | cluster = Elasticsearch::Model.client.cluster | 6 | cluster = Elasticsearch::Model.client.cluster |
plugins/elasticsearch/test/unit/models/community_test.rb
| 1 | require "#{File.dirname(__FILE__)}/../../test_helper" | 1 | require "#{File.dirname(__FILE__)}/../../test_helper" |
| 2 | 2 | ||
| 3 | -class CommunityTest < ElasticsearchTestHelper | 3 | +class CommunityTest < ActionController::TestCase |
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 4 | 6 | ||
| 5 | def indexed_models | 7 | def indexed_models |
| 6 | [Community] | 8 | [Community] |
plugins/elasticsearch/test/unit/models/event_test.rb
| 1 | require "#{File.dirname(__FILE__)}/../../test_helper" | 1 | require "#{File.dirname(__FILE__)}/../../test_helper" |
| 2 | 2 | ||
| 3 | -class EventTest < ElasticsearchTestHelper | 3 | +class EventTest < ActionController::TestCase |
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 4 | 6 | ||
| 5 | def indexed_models | 7 | def indexed_models |
| 6 | [Event] | 8 | [Event] |
plugins/elasticsearch/test/unit/models/person_test.rb
| 1 | require "#{File.dirname(__FILE__)}/../../test_helper" | 1 | require "#{File.dirname(__FILE__)}/../../test_helper" |
| 2 | 2 | ||
| 3 | -class PersonTest < ElasticsearchTestHelper | 3 | +class PersonTest < ActionController::TestCase |
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 4 | 6 | ||
| 5 | def indexed_models | 7 | def indexed_models |
| 6 | [Person] | 8 | [Person] |
plugins/elasticsearch/test/unit/models/text_article_test.rb
0 → 100644
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +require "#{File.dirname(__FILE__)}/../../test_helper" | ||
| 2 | + | ||
| 3 | +class TextArticleTest < ActionController::TestCase | ||
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 6 | + | ||
| 7 | + def indexed_models | ||
| 8 | + [TextArticle] | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def setup | ||
| 12 | + super | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + should 'index searchable fields for TextArticle model' do | ||
| 16 | + TextArticle::SEARCHABLE_FIELDS.each do |key, value| | ||
| 17 | + assert_includes indexed_fields(TextArticle), key | ||
| 18 | + end | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + should 'index control fields for TextArticle model' do | ||
| 22 | + TextArticle::control_fields.each do |key, value| | ||
| 23 | + assert_includes indexed_fields(TextArticle), key | ||
| 24 | + assert_includes indexed_fields(TextArticle)[key][:type], value[:type] || 'string' | ||
| 25 | + end | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | +end |
plugins/elasticsearch/test/unit/models/uploaded_file_test.rb
0 → 100644
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +require "#{File.dirname(__FILE__)}/../../test_helper" | ||
| 2 | + | ||
| 3 | +class UploadedFileTest < ActionController::TestCase | ||
| 4 | + | ||
| 5 | + include ElasticsearchTestHelper | ||
| 6 | + | ||
| 7 | + def indexed_models | ||
| 8 | + [UploadedFile] | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def setup | ||
| 12 | + super | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + should 'index searchable fields for UploadedFile model' do | ||
| 16 | + UploadedFile::SEARCHABLE_FIELDS.each do |key, value| | ||
| 17 | + assert_includes indexed_fields(UploadedFile), key | ||
| 18 | + end | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + should 'index control fields for UploadedFile model' do | ||
| 22 | + UploadedFile::control_fields.each do |key, value| | ||
| 23 | + assert_includes indexed_fields(UploadedFile), key | ||
| 24 | + assert_includes indexed_fields(UploadedFile)[key][:type], value[:type].presence || 'string' | ||
| 25 | + end | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | +end |