diff --git a/.erdconfig b/.erdconfig new file mode 100644 index 0000000000000000000000000000000000000000..6d077b17393fb5120fe8611219660aa374bb03c6 --- /dev/null +++ b/.erdconfig @@ -0,0 +1,19 @@ +attributes: + - content + - foreign_key + - inheritance +disconnected: true +filename: erd +filetype: pdf +indirect: true +inheritance: true +markup: true +notation: bachman +orientation: horizontal +polymorphism: false +sort: true +warn: true +title: PortalMEC +exclude: null +only: null +prepend_primary: false diff --git a/app/controllers/v1/search_controller.rb b/app/controllers/v1/search_controller.rb index 2a1aabec64446e46cff50a1ce33c079d53976372..c581c0866d8f36a120e74c290218cd404a4e6e3a 100644 --- a/app/controllers/v1/search_controller.rb +++ b/app/controllers/v1/search_controller.rb @@ -1,36 +1,27 @@ class V1::SearchController < ApplicationController - before_action :set_search # GET v1/search # GET v1/search.json def index - begin - render json: ActiveModel::ArraySerializer.new(search_service.fetch), status: :ok - rescue => e - if e.message == "Invalid search" - render json: @search.errors, status: :bad_request - else - render nothing: true, status: :internal_server_error - puts e.message - puts e.backtrace - end + render json: ActiveModel::ArraySerializer.new(SearchService.search(@search, current_user)), status: :ok + rescue => e + if e.message == 'Invalid search' + render json: @search.errors, status: :bad_request + else + render nothing: true, status: :internal_server_error end end # GET v1/search/autocomplete # GET v1/search/autocomplete.json def autocomplete - begin - render json: ActiveModel::ArraySerializer.new(search_service.autocomplete), status: :ok - rescue => e - if e.message == "Invalid search" - render json: @search.errors, status: :bad_request - else - render nothing: true, status: :internal_server_error - puts e.message - puts e.backtrace - end + render json: ActiveModel::ArraySerializer.new(SearchService.autocomplete(@search, current_user)), status: :ok + rescue => e + if e.message == 'Invalid search' + render json: @search.errors, status: :bad_request + else + render nothing: true, status: :internal_server_error end end @@ -39,9 +30,4 @@ class V1::SearchController < ApplicationController def set_search @search = Search.new(params) end - - def search_service - @search_service ||= SearchService.new(@search, current_user) - end - -end \ No newline at end of file +end diff --git a/app/models/search.rb b/app/models/search.rb index 0c08b232047816be0d88781c4867f8e154634068..5f9dcf1fae8b7f66ac5edf293154ee82ff82b1a4 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -4,22 +4,22 @@ class Search validates_presence_of :query, :results_per_page, :order, :search_class validates_numericality_of :results_per_page, greater_than: 0 - validates :search_class, inclusion: {in: %w(LearningObject Collection User)} + validates :search_class, inclusion: { in: %w(LearningObject Collection User) } - def initialize(params={}) - super(defaults.merge(params.select { |key,value| self.respond_to? key })) + def initialize(params = {}) + super(defaults.merge(params.select { |key, _value| respond_to? key })) end def learning_object? - @search_class == "LearningObject" + search_class == 'LearningObject' end def collection? - @search_class == "Collection" + search_class == 'Collection' end def user? - @search_class == "User" + search_class == 'User' end private @@ -32,5 +32,4 @@ class Search results_per_page: 10 } end - -end \ No newline at end of file +end diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 7a472ab7ee7c429ff6afee2f4bab7b2a438efc37..f5fa43b5a613e930ec42e349931e0210e91281f1 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -1,129 +1,16 @@ -class SearchService - - attr_accessor :search - - def initialize(search, user) - @search = search - @user = user - end - - def fetch - validate - return search_learning_object if @search.learning_object? - return search_collection if @search.collection? - return search_user if @search.user? - end - - def autocomplete - validate - - params_hash = {} - get_thumbnail = nil - get_type = nil - if @search.learning_object? - params_hash = { where: {state: validate_object}, - fields: ['name^10', 'description', 'author'] } - get_thumbnail = Proc.new { |obj| obj.default_thumbnail } - get_type = Proc.new { |obj| obj.object_type.try(:name) } - - elsif @search.collection? - params_hash = { where: {privacy: "public"}, - fields: ['name^10', 'description', 'owner'] } - get_thumbnail = Proc.new { |obj| "/assets/icons/collection" } - get_type = Proc.new { |obj| "Collection"} - - else #User - params_hash = { fields: ['name'] } - get_thumbnail = Proc.new { |obj| obj.avatar.url(:thumb) } - get_type = Proc.new { |obj| "User" } - end - - autocomplete_search(Object.const_get(@search.search_class), @search.query, params_hash, get_type, get_thumbnail) +module SearchService + def self.search(search, user) + model = instance(search, user) + model.search.results end - private - - def validate - raise "Invalid search" unless @search.valid? - end - - def search_learning_object - LearningObject.search(@search.query, where: lo_where_hash, order: lo_order_hash(@search.order), page: @search.page, per_page: @search.results_per_page).results - end - - def search_collection - Collection.search(@search.query, where: {privacy: "public"}, order: col_order_hash(@search.order), page: @search.page, per_page: @search.results_per_page).results + def self.autocomplete(search, user) + model = instance(search, user) + model.autocomplete end - def search_user - User.search(@search.query, order: user_order_hash(@search.order), page: @search.page, per_page: @search.results_per_page).results + def self.instance(search, user) + raise 'Invalid search' unless search.valid? + "SearchService::#{search.search_class}".constantize.new(search, user) end - - def autocomplete_search(search_class, query, params_hash={}, get_type, get_thumbnail) - response = [] - search_params = { limit: 10, misspellings: { below: 5 } } - objs = search_class.search(query, search_params.merge(params_hash)) - objs.each do |obj| - hash = {} - hash["id"] = obj.id - hash["name"] = obj.name - hash["type"] = get_type.call(obj) - hash["thumbnail"] = get_thumbnail.call(obj) - response << hash - end - response - end - - def lo_where_hash - hash = {} - hash[:tags] = @search.tags unless @search.tags.blank? - hash[:object_type] = @search.types unless @search.types.blank? - hash[:source] = @search.sources unless @search.sources.blank? - hash[:state] = validate_object - hash.blank? ? nil : hash - end - - def lo_order_hash(order) - case order - when 'author' - { author: {order: :asc, unmapped_type: :string} } - when 'publicationasc' - { published_at: {order: :asc, unmapped_type: :timestamp} } - when 'publicationdesc' - { published_at: {order: :desc, unmapped_type: :timestamp} } - when 'title' - { name: {order: :asc, unmapped_type: :string} } - else - { score: {order: :desc, unmapped_type: :integer} } - end - end - - def col_order_hash(order) - case order - when 'author' - { owner: {order: :asc, unmapped_type: :string} } - when 'publicationasc' - { created_at: {order: :asc, unmapped_type: :timestamp} } - when 'publicationdesc' - { created_at: {order: :desc, unmapped_type: :timestamp} } - when 'title' - { name: {order: :asc, unmapped_type: :string} } - else - { score: {order: :desc, unmapped_type: :integer} } - end - end - - def user_order_hash(order) - if order == 'title' - { name: {order: :asc, unmapped_type: :string} } - else - { score: {order: :desc, unmapped_type: :integer} } - end - end - - def validate_object - return 'published' unless !@user.nil? && @user.is_admin? - return ['published', 'suspended', 'draft'] - end - -end \ No newline at end of file +end diff --git a/app/services/search_service/collection.rb b/app/services/search_service/collection.rb new file mode 100644 index 0000000000000000000000000000000000000000..b5c17c3a36d1a4117b8939ae7347dec075c18c20 --- /dev/null +++ b/app/services/search_service/collection.rb @@ -0,0 +1,29 @@ +module SearchService + class Collection < Model + def search + ::Collection.search(@search.query, where: { privacy: 'public' }, order: order_hash, page: @search.page, per_page: @search.results_per_page) + end + + def autocomplete + params = { where: { privacy: 'public' }, + fields: ['name^10', 'description', 'owner'] } + result = ::Collection.search(@search.query, autocomplete_params.merge(params)) + + thumbnail = proc { |_obj| '/assets/icons/collection' } + type = proc { |_obj| 'Collection' } + autocomplete_render(result, type, thumbnail) + end + + private + + def order_hash + case @search.order + when 'author' then { owner: { order: :asc, unmapped_type: :string } } + when 'publicationasc' then { created_at: { order: :asc, unmapped_type: :timestamp } } + when 'publicationdesc' then { created_at: { order: :desc, unmapped_type: :timestamp } } + when 'title' then { name: { order: :asc, unmapped_type: :string } } + else { score: { order: :desc, unmapped_type: :integer } } + end + end + end +end diff --git a/app/services/search_service/learning_object.rb b/app/services/search_service/learning_object.rb new file mode 100644 index 0000000000000000000000000000000000000000..c53a300febdfb77160bf5eca942d6c0ccd4b7d58 --- /dev/null +++ b/app/services/search_service/learning_object.rb @@ -0,0 +1,43 @@ +module SearchService + class LearningObject < Model + def search + ::LearningObject.search(@search.query, where: where_hash, order: order_hash, page: @search.page, per_page: @search.results_per_page) + end + + def autocomplete + params = { where: { state: validate_object }, + fields: ['name^10', 'description', 'author'] } + result = ::LearningObject.search(@search.query, autocomplete_params.merge(params)) + + thumbnail = proc { |obj| obj.default_thumbnail } + type = proc { |obj| obj.object_type.try(:name) } + autocomplete_render(result, type, thumbnail) + end + + private + + def where_hash + hash = {} + hash[:tags] = @search.tags unless @search.tags.blank? + hash[:object_type] = @search.types unless @search.types.blank? + hash[:source] = @search.sources unless @search.sources.blank? + hash[:state] = validate_object + hash.blank? ? nil : hash + end + + def order_hash + case @search.order + when 'author' then { author: { order: :asc, unmapped_type: :string } } + when 'publicationasc' then { published_at: { order: :asc, unmapped_type: :timestamp } } + when 'publicationdesc' then { published_at: { order: :desc, unmapped_type: :timestamp } } + when 'title' then { name: { order: :asc, unmapped_type: :string } } + else { score: { order: :desc, unmapped_type: :integer } } + end + end + + def validate_object + return 'published' unless !@user.nil? && @user.is_admin? + %w(published suspended draft) + end + end +end diff --git a/app/services/search_service/model.rb b/app/services/search_service/model.rb new file mode 100644 index 0000000000000000000000000000000000000000..e1a0bbb6bf43da38383cbee5c0cbe577db8b0b7f --- /dev/null +++ b/app/services/search_service/model.rb @@ -0,0 +1,35 @@ +module SearchService + class Model + def initialize(search, user) + raise 'Invalid search' unless search.valid? + + @search = search + @user = user + end + + def search + raise 'Must implement!' + end + + def autocomplete + raise 'Must implement!' + end + + private + + def autocomplete_params + { limit: 10, misspellings: { below: 5 } } + end + + def autocomplete_render(result, type, thumbnail) + result.map do |obj| + { + id: obj.id, + name: obj.name, + type: type.call(obj), + thumbnail: thumbnail.call(obj) + } + end + end + end +end diff --git a/app/services/search_service/user.rb b/app/services/search_service/user.rb new file mode 100644 index 0000000000000000000000000000000000000000..c7ffef549acbdcc0fd80dfebaee0d5f14a2dff72 --- /dev/null +++ b/app/services/search_service/user.rb @@ -0,0 +1,22 @@ +module SearchService + class User < Model + def search + ::User.search(@search.query, order: order_hash, page: @search.page, per_page: @search.results_per_page) + end + + def autocomplete + result = ::User.search(@search.query, autocomplete_params.merge(fields: ['name'])) + + thumbnail = proc { |obj| obj.avatar.url(:thumb) } + type = proc { |_obj| 'User' } + autocomplete_render(result, type, thumbnail) + end + + private + + def order_hash + return { name: { order: :asc, unmapped_type: :string } } if @search.order == 'title' + { score: { order: :desc, unmapped_type: :integer } } + end + end +end diff --git a/test/services/search_service_test.rb b/test/services/search_service_test.rb index 3733c0418a7ca9acda375eff3cdd99e07d1af071..cd1407ba2b2dfae9ee9f448dec307e3ae412bc85 100644 --- a/test/services/search_service_test.rb +++ b/test/services/search_service_test.rb @@ -1,69 +1,66 @@ require 'test_helper' class SearchServiceTest < ActiveSupport::TestCase - test 'fetch learning object with all search params' do reindex LearningObject - service = SearchService.new(Search.new(los_complete_search), users(:john)) - assert_equal [learning_objects(:search)], service.fetch + search = SearchService.search(Search.new(los_complete_search), users(:john)) + assert_equal [learning_objects(:search)], search end test 'fetch all public collections' do reindex Collection - service = SearchService.new(Search.new(collections_search), users(:john)) - assert_equal [collections(:ufpr)], service.fetch + search = SearchService.search(Search.new(collections_search), users(:john)) + assert_equal [collections(:ufpr)], search end test 'fetch users named john' do reindex User - service = SearchService.new(Search.new(users_john_search), users(:john)) - assert_equal [users(:john), users(:one)], service.fetch + search = SearchService.search(Search.new(users_john_search), users(:john)) + assert_equal [users(:john), users(:one)], search end test 'autocomplete users named john' do reindex User - service = SearchService.new(Search.new(users_john), users(:john)) - assert_equal users_autocomplete_hashes([users(:john),users(:one)]), service.autocomplete + search = SearchService.autocomplete(Search.new(users_john), users(:john)) + assert_equal users_autocomplete_hashes([users(:john), users(:one)]), search end private def los_complete_search - default_params.merge({ - :query => 'teste', - :search_class => 'LearningObject', - :tags => ['Matemática'], - :types => ['Imagem'], - :sources => ['UFPR institution'] - }) + default_params.merge( + query: 'teste', + search_class: 'LearningObject', + tags: ['Matemática'], + types: ['Imagem'], + sources: ['UFPR institution'] + ) end def collections_search - default_params.merge({ - :search_class => 'Collection' - }) + default_params.merge(search_class: 'Collection') end def users_john_search default_params.merge(users_john) end - + def users_john { - :query => 'John', - :search_class => 'User' + query: 'John', + search_class: 'User' } end def default_params { - :page => 1, - :results_per_page => 10, - :order => 'score' + page: 1, + results_per_page: 10, + order: 'score' } end @@ -72,17 +69,14 @@ class SearchServiceTest < ActiveSupport::TestCase klass.searchkick_index.refresh end - def users_autocomplete_hashes(users=[]) - hashes = [] - users.each do |user| - hash = {} - hash["id"] = user.id - hash["name"] = user.name - hash["type"] = "User" - hash["thumbnail"] = user.avatar.url(:thumb) - hashes << hash + def users_autocomplete_hashes(users = []) + users.map do |user| + { + id: user.id, + name: user.name, + type: 'User', + thumbnail: user.avatar.url(:thumb) + } end - hashes end - -end \ No newline at end of file +end