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 | 12 | def search |
| 13 | 13 | define_searchable_types |
| 14 | 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 | 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 | 38 | end | ... | ... |
| ... | ... | @@ -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 | 1 | require_relative '../../helpers/elasticsearch_helper' |
| 2 | - | |
| 2 | +require_relative 'entities' | |
| 3 | 3 | |
| 4 | 4 | class ElasticsearchPlugin::API < Grape::API |
| 5 | 5 | include Api::Helpers |
| 6 | + helpers ElasticsearchHelper | |
| 6 | 7 | |
| 7 | 8 | resource :search do |
| 8 | 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 | 14 | end |
| 11 | 15 | |
| 12 | 16 | get 'types' do |
| 13 | - types = {types: ElasticsearchHelper::SEARCHABLE_TYPES.stringify_keys.keys} | |
| 17 | + types = {types: ElasticsearchHelper::searchable_types.stringify_keys.keys} | |
| 14 | 18 | present types, with: Grape::Presenters::Presenter |
| 15 | 19 | end |
| 16 | 20 | ... | ... |
plugins/elasticsearch/lib/elasticsearch_plugin/entities.rb
0 → 100644
| ... | ... | @@ -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
plugins/elasticsearch/lib/ext/uploaded_file.rb
plugins/elasticsearch/test/test_helper.rb
| 1 | 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 | 6 | def setup |
| 6 | 7 | setup_environment |
| 8 | + create_instances | |
| 7 | 9 | import_instancies |
| 8 | 10 | end |
| 9 | 11 | |
| 12 | + def create_instances | |
| 13 | + end | |
| 14 | + | |
| 10 | 15 | def teardown |
| 11 | 16 | indexed_models.each {|model| |
| 12 | 17 | model.__elasticsearch__.client.indices.delete index: model.index_name |
| ... | ... | @@ -16,9 +21,10 @@ class ElasticsearchTestHelper < ActionController::TestCase |
| 16 | 21 | def import_instancies |
| 17 | 22 | indexed_models.each {|model| |
| 18 | 23 | model.__elasticsearch__.create_index! |
| 24 | + sleep 2 | |
| 19 | 25 | model.import |
| 26 | + sleep 1 | |
| 20 | 27 | } |
| 21 | - sleep 2 | |
| 22 | 28 | end |
| 23 | 29 | |
| 24 | 30 | def setup_environment |
| ... | ... | @@ -31,7 +37,7 @@ class ElasticsearchTestHelper < ActionController::TestCase |
| 31 | 37 | end |
| 32 | 38 | |
| 33 | 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 | 41 | end |
| 36 | 42 | |
| 37 | 43 | end | ... | ... |
plugins/elasticsearch/test/unit/api/elasticsearch_plugin_api_test.rb
0 → 100644
| ... | ... | @@ -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 | 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 | 7 | def indexed_models |
| 6 | 8 | [Community, Person] |
| 7 | 9 | end |
| 8 | 10 | |
| 9 | - def setup | |
| 10 | - create_instances | |
| 11 | - super | |
| 12 | - end | |
| 13 | - | |
| 14 | - def teardown | |
| 15 | - super | |
| 16 | - end | |
| 17 | - | |
| 18 | 11 | def create_instances |
| 19 | 12 | create_people |
| 20 | 13 | create_communities | ... | ... |
plugins/elasticsearch/test/unit/elasticsearch_test.rb
plugins/elasticsearch/test/unit/models/community_test.rb
plugins/elasticsearch/test/unit/models/event_test.rb
plugins/elasticsearch/test/unit/models/person_test.rb
plugins/elasticsearch/test/unit/models/text_article_test.rb
0 → 100644
| ... | ... | @@ -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 @@ |
| 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 | ... | ... |