parent
f10cbf2250
commit
0a286bf5b0
@ -0,0 +1,10 @@
|
|||||||
|
class SearchsController < ApplicationController
|
||||||
|
def index
|
||||||
|
@results = SearchService.call(search_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def search_params
|
||||||
|
params.permit(:keyword, :type, :page, :per_page)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,37 @@
|
|||||||
|
module Searchable::Course
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
searchkick language: 'chinese', callbacks: :async
|
||||||
|
|
||||||
|
scope :search_import, -> { includes(:teacher_users, teacher: { user_extension: :school } ) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_title
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_data
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
author_name: teacher&.real_name
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_searchable_json
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
author_name: teacher.real_name,
|
||||||
|
author_school_name: teacher.school_name,
|
||||||
|
visits_count: visits,
|
||||||
|
members_count: members_count,
|
||||||
|
is_public: is_public == 1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def searchable_includes
|
||||||
|
{ teacher: { user_extension: :school } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,2 @@
|
|||||||
|
module Searchable::Dependents
|
||||||
|
end
|
@ -0,0 +1,16 @@
|
|||||||
|
module Searchable::Dependents::ChallengeTag
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create_commit :check_searchable_dependents
|
||||||
|
after_update_commit :check_searchable_dependents
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_searchable_dependents
|
||||||
|
if new_record? || name_previously_changed?
|
||||||
|
challenge.shixun.reindex(:searchable_challenge_data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,15 @@
|
|||||||
|
module Searchable::Dependents::Stage
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_update_commit :check_searchable_dependents
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_searchable_dependents
|
||||||
|
if name_previously_changed? || description_previously_changed?
|
||||||
|
subject.reindex(:searchable_stages_data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,22 @@
|
|||||||
|
module Searchable::Dependents::User
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_update_commit :check_searchable_dependents
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_searchable_dependents
|
||||||
|
if firstname_previously_changed? || lastname_previously_changed? || user_extension.school_id_previously_changed?
|
||||||
|
# reindex shixun
|
||||||
|
created_shixuns.each{ |shixun| shixun.reindex(:searchable_user_data) }
|
||||||
|
|
||||||
|
# reindex course
|
||||||
|
manage_courses.each(&:reindex)
|
||||||
|
|
||||||
|
# reindex subject
|
||||||
|
created_subjects.each { |subject| subject.reindex(:searchable_user_data) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,46 @@
|
|||||||
|
module Searchable::Memo
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
searchkick language: 'chinese', callbacks: :async
|
||||||
|
|
||||||
|
scope :search_import, -> { includes(:descendants) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_title
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_index?
|
||||||
|
hidden.zero? && root_id.blank? && parent_id.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_data
|
||||||
|
{
|
||||||
|
name: subject,
|
||||||
|
content: Util.extract_content(content)[0..Searchable::MAXIMUM_LENGTH],
|
||||||
|
}.merge!(searchable_descendants_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_descendants_data
|
||||||
|
{
|
||||||
|
descendants_contents: Util.map_or_pluck(descendants, :content)
|
||||||
|
.map { |content| Util.extract_content(content)[0..Searchable::MAXIMUM_LENGTH] }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_searchable_json
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
author_name: author.full_name,
|
||||||
|
visits_count: viewed_count,
|
||||||
|
all_replies_count: all_replies_count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def searchable_includes
|
||||||
|
[:author]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,58 @@
|
|||||||
|
module Searchable::Shixun
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
searchkick language: 'chinese'#, callbacks: :async
|
||||||
|
|
||||||
|
scope :search_import, -> { includes(:shixun_info, :challenges, :challenge_tags, :users, user: { user_extension: :school }) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_title
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_data
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
description: Util.extract_content(description)[0..Searchable::MAXIMUM_LENGTH]
|
||||||
|
}.merge!(searchable_user_data)
|
||||||
|
.merge!(searchable_challenge_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_user_data
|
||||||
|
{
|
||||||
|
author_name: user.real_name,
|
||||||
|
author_school_name: user.school_name,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_challenge_data
|
||||||
|
challenge_names = Util.map_or_pluck(challenges, :subject)
|
||||||
|
.each_with_index.map { |subject, index| "第#{index + 1}关 #{subject}" }
|
||||||
|
|
||||||
|
{
|
||||||
|
challenge_names: challenge_names,
|
||||||
|
challenge_tag_names: Util.map_or_pluck(challenge_tags, :name).uniq.join(' ')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_index?
|
||||||
|
status == 2 # published
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_searchable_json
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
author_name: user.real_name,
|
||||||
|
author_school_name: user.school_name,
|
||||||
|
visits_count: visits,
|
||||||
|
challenges_count: challenges_count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def searchable_includes
|
||||||
|
{ user: { user_extension: :school } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,61 @@
|
|||||||
|
module Searchable::Subject
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
searchkick language: 'chinese', callbacks: :async
|
||||||
|
|
||||||
|
scope :search_import, -> { includes(:users, :stages, user: { user_extension: :school }) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_title
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_index?
|
||||||
|
!hidden? && status == 2 # published
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_data
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
description: Util.extract_content(description)[0..Searchable::MAXIMUM_LENGTH]
|
||||||
|
}.merge!(searchable_user_data)
|
||||||
|
.merge!(searchable_stages_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_user_data
|
||||||
|
{
|
||||||
|
author_name: user.real_name,
|
||||||
|
author_school_name: user.school_name,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def searchable_stages_data
|
||||||
|
subject_stages =
|
||||||
|
stages.map do |stage|
|
||||||
|
{
|
||||||
|
name: stage.name,
|
||||||
|
description: Util.extract_content(stage.description)[0..Searchable::MAXIMUM_LENGTH]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
{ subject_stages: subject_stages}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_searchable_json
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
author_name: user.real_name,
|
||||||
|
author_school_name: user.school_name,
|
||||||
|
visits_count: visits,
|
||||||
|
stage_count: stages_count,
|
||||||
|
stage_shixuns_count: stage_shixuns_count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def searchable_includes
|
||||||
|
{ user: { user_extension: :school } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,41 @@
|
|||||||
|
module ElasticsearchAble
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def default_options
|
||||||
|
{
|
||||||
|
debug: Rails.env.development?,
|
||||||
|
highlight: highlight_options,
|
||||||
|
body_options: body_options,
|
||||||
|
page: page,
|
||||||
|
per_page: per_page
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def keyword
|
||||||
|
params[:keyword].to_s.strip.presence || '*'
|
||||||
|
end
|
||||||
|
|
||||||
|
def highlight_options
|
||||||
|
{
|
||||||
|
fragment_size: EduSetting.get('es_highlight_fragment_size') || 30,
|
||||||
|
tag: '<span>'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def body_options
|
||||||
|
{
|
||||||
|
min_score: EduSetting.get('es_min_score') || 10
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def per_page
|
||||||
|
per_page = params[:per_page].to_s.strip.presence || params[:limit].to_s.strip.presence
|
||||||
|
per_page.to_i <= 0 ? 20 : per_page.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def page
|
||||||
|
params[:page].to_i <= 0 ? 1 : params[:page].to_i
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,39 @@
|
|||||||
|
class SearchService < ApplicationService
|
||||||
|
include ElasticsearchAble
|
||||||
|
|
||||||
|
attr_reader :params
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
Searchkick.search(keyword, search_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def search_options
|
||||||
|
{
|
||||||
|
index_name: index_names,
|
||||||
|
model_includes: model_includes
|
||||||
|
}.merge(default_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_names
|
||||||
|
@_index_names ||=
|
||||||
|
case params[:type].to_s.strip
|
||||||
|
when 'shixun' then [Shixun]
|
||||||
|
when 'course' then [Course]
|
||||||
|
when 'subject' then [Subject]
|
||||||
|
when 'memo' then [Memo]
|
||||||
|
else [Shixun, Course, Subject, Memo]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def model_includes
|
||||||
|
index_names.each_with_object({}) do |klass, obj|
|
||||||
|
obj[klass] = klass.searchable_includes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,96 @@
|
|||||||
|
class SearchShixunService < ApplicationService
|
||||||
|
include ElasticsearchAble
|
||||||
|
|
||||||
|
attr_reader :user, :params
|
||||||
|
|
||||||
|
def initialize(user, params)
|
||||||
|
@user = user
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
Shixun.search(keyword,
|
||||||
|
fields: search_fields,
|
||||||
|
where: where_clauses,
|
||||||
|
order: order_clauses,
|
||||||
|
includes: includes_clauses,
|
||||||
|
page: page,
|
||||||
|
per_page: per_page)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def tag_filter_shixun_ids
|
||||||
|
return [] if params[:tag_level].to_i == 0 || params[:tag_id].blank?
|
||||||
|
|
||||||
|
case params[:tag_level].to_i
|
||||||
|
when 1 then
|
||||||
|
Repertoire.find(params[:tag_id]).tag_repertoires.joins(:shixun_tag_repertoires)
|
||||||
|
.pluck('shixun_tag_repertoires.shixun_id')
|
||||||
|
when 2 then
|
||||||
|
SubRepertoire.find(params[:tag_id]).tag_repertoires.joins(:shixun_tag_repertoires)
|
||||||
|
.pluck('shixun_tag_repertoires.shixun_id')
|
||||||
|
when 3 then
|
||||||
|
TagRepertoire.find(params[:tag_id]).shixun_tag_repertoires.pluck(:shixun_id)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_filter_shixun_ids
|
||||||
|
return [] if params[:order_by] != 'mine'
|
||||||
|
|
||||||
|
user.shixun_members.pluck(:shixun_id) + user.myshixuns.pluck(:shixun_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def keyword
|
||||||
|
params[:keyword].to_s.strip.presence || '*'
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_fields
|
||||||
|
%w(name^10 author_name challenge_names description challenge_tag_names)
|
||||||
|
end
|
||||||
|
|
||||||
|
def where_clauses
|
||||||
|
hash = {}
|
||||||
|
|
||||||
|
ids = user_filter_shixun_ids + tag_filter_shixun_ids
|
||||||
|
hash[:id] = ids if ids.present?
|
||||||
|
|
||||||
|
if params[:order_by] == 'mine'
|
||||||
|
hash[:status] = { not: -1 }
|
||||||
|
else
|
||||||
|
hash.merge!(hidden: false, status: 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless params[:status].to_i.zero?
|
||||||
|
params[:status] = [0, 1] if params[:status].to_i == 1
|
||||||
|
hash[:status] = params[:status]
|
||||||
|
end
|
||||||
|
|
||||||
|
hash[:trainee] = params[:diff].to_i unless params[:diff].to_i.zero?
|
||||||
|
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def includes_clauses
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def order_clauses
|
||||||
|
hash = { _score: :desc }
|
||||||
|
publish_order = { type: 'number', order: :desc, script: 'doc["status"].value=="2" ? 1 : 0' }
|
||||||
|
|
||||||
|
sort = params[:sort].to_s.strip == 'asc' ? 'asc' : 'desc'
|
||||||
|
clauses =
|
||||||
|
case params[:order_by].presence
|
||||||
|
when 'new' then { _script: publish_order, created_at: sort }
|
||||||
|
when 'hot' then { _script: publish_order, myshixuns_count: sort }
|
||||||
|
when 'mine' then { created_at: sort }
|
||||||
|
else { _script: publish_order, publish_time: sort }
|
||||||
|
end
|
||||||
|
hash.merge!(clauses)
|
||||||
|
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,10 @@
|
|||||||
|
json.count @results.total_count
|
||||||
|
json.results do
|
||||||
|
json.array! @results.with_highlights(multiple: true) do |obj, highlights|
|
||||||
|
json.merge! obj.to_searchable_json
|
||||||
|
json.type obj.class.name.downcase
|
||||||
|
|
||||||
|
json.title highlights.delete(:name)&.join('...') || obj.searchable_title
|
||||||
|
json.description highlights.values[0,5].each { |arr| arr.is_a?(Array) ? arr.join('...') : arr }.join('<br/>')
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in new issue