diff --git a/Gemfile b/Gemfile index 3027c16cd..a67fcfaea 100644 --- a/Gemfile +++ b/Gemfile @@ -30,6 +30,13 @@ gem 'rails_kindeditor',path:'lib/rails_kindeditor' #gem "rmagick", ">= 2.0.0" gem 'binding_of_caller' gem 'chinese_pinyin' +# gem 'sunspot_rails', '~> 1.3.3' +# gem 'sunspot_solr' +# gem 'sunspot' +# gem 'progress_bar' +gem 'kaminari' +gem 'elasticsearch-model' +gem 'elasticsearch-rails' group :development do gem 'grape-swagger' diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 776e6b7d5..8188ce12e 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1856,7 +1856,7 @@ class UsersController < ApplicationController # 根据资源关键字进行搜索 def resource_search search = params[:search].to_s.strip.downcase - if(params[:type].nil? || params[:type] == "1") #全部 + if(params[:type].nil? || params[:type].blank? || params[:type] == "1") #全部 if User.current.id.to_i == params[:id].to_i user_course_ids = User.current.courses.map { |c| c.id} #我的资源库的话,那么应该是我上传的所有资源 加上 我加入的课程的所有资源 取交集并查询 @attachments = Attachment.where("((author_id = #{params[:id]} and container_type in('Project','Principal','Course','Issue','Document','Message','News','StudentWorkScore','HomewCommon')) "+ diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index 784066378..4b75a3297 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -151,36 +151,101 @@ class WelcomeController < ApplicationController end def search - search_condition = params[:q] - search_type = params[:search_type].to_sym unless search_condition.blank? - search_by = params[:search_by] + @name = params[:q] + @search_type = params[:search_type] + case params[:search_type] + when 'all' + @alls = Elasticsearch::Model.search({ + query: { + multi_match: { + query: @name, + type:"most_fields", + fields: ['login^5', 'firstname','lastname','name^5','description','filename^5'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {}, + name:{}, + description:{}, + filename:{} + } + } + },[User,Course,Attachment,Project] ).page(params[:page] || 1).per(20).results + when 'user' + @users = User.search(@name).page(params[:page] || 1).per(20) + when 'project' + @projects = Project.search(@name).page(params[:page] || 1).per(20).results + when 'course' + @courses = Course.search(@name).page(params[:page] || 1).per(20).results + when 'attachment' + @attachments = Attachment.search(@name).page(params[:page] || 1).per(20).results + else + @alls = Elasticsearch::Model.search({ + query: { + multi_match: { + query: @name, + type:"most_fields", + fields: ['login^5', 'firstname','lastname','name^5','description','filename^5'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {}, + name:{}, + description:{}, + filename:{} + } + } + },[User,Course,Attachment,Project] ).page(params[:page] || 1).per(20).results - if search_type.nil? && params[:contests_search] && params[:name] != "" - search_type = :contests - search_condition = params[:name] end + @users_count = User.search(@name).results.total + + @course_count = Course.search(@name).results.total + @attach_count = Attachment.search(@name).results.total + @project_count = Project.search(@name).results.total + @total_count = Elasticsearch::Model.search({ + query: { + multi_match: { + query: @name, + type:"most_fields", + fields: ['login^5', 'firstname','lastname','name^5','description','filename^5'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {}, + name:{}, + description:{}, + filename:{} + } + } + },[User,Course,Attachment,Project] ).results.total + # search_type = params[:search_type].to_sym unless search_condition.blank? + # search_by = params[:search_by] + # + # if search_type.nil? && params[:contests_search] && params[:name] != "" + # search_type = :contests + # search_condition = params[:name] + # end + respond_to do |format| - format.html{ - case search_type - when :projects - redirect_to projects_search_url(:name => search_condition, - :project_type => Project::ProjectType_project) - when :courses - redirect_to courses_search_url(:name => search_condition) - when :contests - redirect_to contests_url(:name => search_condition) - when :users - redirect_to users_search_url(:name => search_condition,:search_by => search_by) - when :users_teacher - redirect_to users_search_url(:name => search_condition, :search_by => search_by, :role => :teacher) - when :users_student - redirect_to users_search_url(:name => search_condition, :search_by => search_by, :role => :student) - else - #redirect_to home_path, :alert => l(:label_sumbit_empty) - (redirect_to signin_path, :notice => l(:label_sumbit_empty);return) #if params[:name].blank? - end - } + format.js + format.html{ render :layout=>'users_base'} end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f4e0f88ff..15e29f39c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1825,6 +1825,23 @@ module ApplicationHelper s end + def get_user_identity identity + s = "" + case identity + when 0 + s = '教师' + when 1 + s = '学生' + when 2 + s = '组织' + when 3 + s= '开发者' + else + s = '学生' + end + s + end + def get_memo @new_memo = Memo.new @public_forum = Forum.find(1) rescue ActiveRecord::RecordNotFound diff --git a/app/models/attachment.rb b/app/models/attachment.rb index f7fb9b1aa..a98111b4f 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -17,7 +17,7 @@ require "digest/md5" require "fileutils" - +require 'elasticsearch/model' class Attachment < ActiveRecord::Base belongs_to :container, :polymorphic => true belongs_to :project, foreign_key: 'container_id', conditions: "attachments.container_type = 'Project'" @@ -38,6 +38,17 @@ class Attachment < ActiveRecord::Base validates :description, length: {maximum: 254} validate :validate_max_file_size + #elasticsearch + include Elasticsearch::Model + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 1 } do + mappings dynamic: 'false' do + indexes :filename, analyzer: 'smartcn',index_options: 'offsets' + end + end + acts_as_taggable acts_as_event :title => :filename, @@ -74,9 +85,9 @@ class Attachment < ActiveRecord::Base @@thumbnails_storage_path = File.join(Rails.root, "tmp", "thumbnails") before_save :files_to_final_location,:act_as_course_activity - after_create :office_conver, :be_user_score,:act_as_forge_activity - after_update :office_conver, :be_user_score - after_destroy :delete_from_disk,:down_user_score + after_create :office_conver, :be_user_score,:act_as_forge_activity,:create_attachment_ealasticsearch_index + after_update :office_conver, :be_user_score,:update_attachment_ealasticsearch_index + after_destroy :delete_from_disk,:down_user_score,:delete_attachment_ealasticsearch_index # add by nwb # 获取所有可公开的资源文件列表 @@ -92,7 +103,25 @@ class Attachment < ActiveRecord::Base " LEFT JOIN #{News.table_name} ON #{Attachment.table_name}.container_type='News' AND (#{News.table_name}.project_id in "+self.public_project_id + " OR #{News.table_name}.course_id in " + self.public_course_id + ")" + " LEFT JOIN #{HomeworkAttach.table_name} ON #{Attachment.table_name}.container_type='HomeworkAttach' AND #{HomeworkAttach.table_name}.bid_id in "+self.public_bid_id) } - + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + fields: ['filename'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + filename: {} + } + } + } + ) + end # add by nwb # 公开的项目id列表 def self.public_project_id @@ -560,4 +589,40 @@ class Attachment < ActiveRecord::Base self.course_acts << CourseActivity.new(:user_id => self.author_id,:course_id => self.container_id) end end + + def create_attachment_ealasticsearch_index + if self.is_public == 1 && ( (self.container_type == 'Project' && Project.find(self.container_id).is_public == 1) || + ( self.container_type == 'Course' && Course.find(self.container_id).is_public == 1) || + self.container_type == 'Principal') + self.__elasticsearch__.index_document + end + end + def update_attachment_ealasticsearch_index + if self.is_public == 1 && ( (self.container_type == 'Project' && Project.find(self.container_id).is_public == 1) || + ( self.container_type == 'Course' && Course.find(self.container_id).is_public == 1) || + self.container_type == 'Principal') + self.__elasticsearch__.update_document + end + end + def delete_attachment_ealasticsearch_index + if self.is_public == 1 && ( (self.container_type == 'Project' && Project.find(self.container_id).is_public == 1) || + ( self.container_type == 'Course' && Course.find(self.container_id).is_public == 1) || + self.container_type == 'Principal') + self.__elasticsearch__.delete_document + end + end end + +# Delete the previous articles index in Elasticsearch +# Attachment.__elasticsearch__.client.indices.delete index: Attachment.index_name rescue nil +# +# # Create the new index with the new mapping +# Attachment.__elasticsearch__.client.indices.create \ +# index: Attachment.index_name, +# body: { settings: Attachment.settings.to_hash, mappings: Attachment.mappings.to_hash } + +# Index all article records from the DB to Elasticsearch +#暂时只做公开课程/项目里的公开资源 和其他的公开资源 +Attachment.where('is_public = 1 and ((container_type in ("Principal")) ' + + 'or (container_type = "Course" and container_id in( SELECT `courses`.id FROM `courses` WHERE (courses.status <> 9 AND courses.is_public = 1)) )'+ + 'or (container_type = "Project" and container_id in(SELECT `projects`.id FROM `projects` WHERE (projects.status <> 9 AND projects.is_public = 1) ))' +')').import diff --git a/app/models/course.rb b/app/models/course.rb index 7288c3b3b..1991c00cb 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -1,10 +1,23 @@ - +require 'elasticsearch/model' class Course < ActiveRecord::Base include Redmine::SafeAttributes STATUS_ACTIVE = 1 STATUS_CLOSED = 5 STATUS_ARCHIVED = 9 + + #elasticsearch + include Elasticsearch::Model + + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 1 } do + mappings dynamic: 'false' do + indexes :name, analyzer: 'smartcn',index_options: 'offsets' + indexes :description, analyzer: 'smartcn',index_options: 'offsets' + end + end attr_accessible :code, :extra, :name, :state, :tea_id, :time , :location, :state, :term, :password,:is_public,:description,:class_period, :open_student, :enterprise_name #belongs_to :project, :class_name => 'Course', :foreign_key => :extra, primary_key: :identifier @@ -51,9 +64,9 @@ class Course < ActiveRecord::Base validates_length_of :description, :maximum => 10000 before_save :self_validate # 公开课程变成私有课程,所有资源都变成私有 - after_update :update_files_public - after_create :create_board_sync, :act_as_course_activity, :act_as_course_message - before_destroy :delete_all_members + after_update :update_files_public,:update_course_ealasticsearch_index + after_create :create_board_sync, :act_as_course_activity, :act_as_course_message,:create_course_ealasticsearch_index + before_destroy :delete_all_members,:delete_course_ealasticsearch_index safe_attributes 'extra', 'time', @@ -96,6 +109,27 @@ class Course < ActiveRecord::Base end } + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + fields: ['name', 'description'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + name: {}, + description: {} + } + } + } + ) + end + def visible?(user=User.current) user.allowed_to?(:view_course, self) end @@ -339,6 +373,38 @@ class Course < ActiveRecord::Base #def name # read_attribute('name') || Project.find_by_identifier(self.extra).try(:name) #end + + # after_commit on: [:create] do + # __elasticsearch__.index_document + # end + # + # after_commit on: [:update] do + # __elasticsearch__.update_document + # end + # + # after_commit on: [:destroy] do + # __elasticsearch__.delete_document + # end + def create_course_ealasticsearch_index + self.__elasticsearch__.index_document + end + def update_course_ealasticsearch_index + self.__elasticsearch__.update_document + end + def delete_course_ealasticsearch_index + self.__elasticsearch__.delete_document + end end +# Delete the previous articles index in Elasticsearch +# Course.__elasticsearch__.client.indices.delete index: Course.index_name rescue nil +# +# # Create the new index with the new mapping +# Course.__elasticsearch__.client.indices.create \ +# index: Course.index_name, +# body: { settings: Course.settings.to_hash, mappings: Course.mappings.to_hash } + +# Index all article records from the DB to Elasticsearch +Course.import + diff --git a/app/models/project.rb b/app/models/project.rb index 0b0420920..53e909776 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - +require 'elasticsearch/model' class Project < ActiveRecord::Base include Redmine::SafeAttributes ProjectType_project = 0 @@ -30,6 +30,18 @@ class Project < ActiveRecord::Base # Specific overidden Activities + #elasticsearch + include Elasticsearch::Model + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 1 } do + mappings dynamic: 'false' do + indexes :name, analyzer: 'smartcn',index_options: 'offsets' + indexes :description, analyzer: 'smartcn',index_options: 'offsets' + end + end + has_many :student_works has_many :time_entry_activities has_many :members, :include => [:principal, :roles], :conditions => "#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}" @@ -138,8 +150,9 @@ class Project < ActiveRecord::Base #ActiveModel::Dirty 这里有一个changed方法。对任何对象都可以用 after_save :update_inherited_members, :if => Proc.new {|project| project.inherit_members_changed?} # 创建project之后默认创建一个board,之后的board去掉了board的概念 - after_create :create_board_sync,:acts_as_forge_activities - before_destroy :delete_all_members + after_create :create_board_sync,:acts_as_forge_activities,:create_project_ealasticsearch_index + before_destroy :delete_all_members,:delete_project_ealasticsearch_index + after_update :update_project_ealasticsearch_index def remove_references_before_destroy return if self.id.nil? Watcher.delete_all ['watchable_id = ?', id] @@ -172,7 +185,27 @@ class Project < ActiveRecord::Base } scope :project_entities, -> { where(project_type: ProjectType_project) } scope :course_entities, -> { where(project_type: ProjectType_course) } - + + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + fields: ['name','description'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + name: {}, + description: {} + } + } + } + ) + end def new_course self.where('project_type = ?', 1) end @@ -1176,5 +1209,18 @@ class Project < ActiveRecord::Base end + def create_project_ealasticsearch_index + self.__elasticsearch__.index_document + end + def update_project_ealasticsearch_index + self.__elasticsearch__.update_document + end + def delete_project_ealasticsearch_index + self.__elasticsearch__.delete_document + end + + end +Project.import + diff --git a/app/models/user.rb b/app/models/user.rb index 85b9e2591..3818e5d47 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require "digest/sha1" - +require 'elasticsearch/model' class User < Principal TEACHER = 0 STUDENT = 1 @@ -25,6 +25,19 @@ class User < Principal include Redmine::SafeAttributes seems_rateable_rater + #elasticsearch + include Elasticsearch::Model + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 1 } do + mappings dynamic: 'false' do + indexes :login, analyzer: 'smartcn',index_options: 'offsets' + indexes :firstname, analyzer: 'smartcn',index_options: 'offsets' + indexes :lastname, analyzer: 'smartcn',index_options: 'offsets' + end + end + # Different ways of displaying/sorting users USER_FORMATS = { :firstname_lastname => { @@ -64,6 +77,7 @@ class User < Principal }, } + #每日一报、一事一报、不报 MAIL_NOTIFICATION_OPTIONS = [ #['week', :label_user_mail_option_week], @@ -154,6 +168,7 @@ class User < Principal ##### has_many :shares ,:dependent => :destroy + # add by zjc has_one :level, :class_name => 'UserLevels', :dependent => :destroy has_many :memos , :foreign_key => 'author_id' @@ -214,12 +229,12 @@ class User < Principal # validates_email_realness_of :mail before_create :set_mail_notification before_save :update_hashed_password - before_destroy :remove_references_before_destroy + before_destroy :remove_references_before_destroy,:delete_user_ealasticsearch_index # added by fq - after_create :act_as_activity, :add_onclick_time, :act_as_principal_activity + after_create :act_as_activity, :add_onclick_time, :act_as_principal_activity,:create_user_ealasticsearch_index # end # 更新邮箱用户或用户名的同事,同步更新邀请信息 - after_update :update_invite_list + after_update :update_invite_list,:upadte_user_ealasticsearch_index include Trustie::Gitlab::ManageUser @@ -250,7 +265,27 @@ class User < Principal end end } - + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + fields: ['login', 'firstname','lastname'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {} + } + } + } + ) + end # ====================================================================== @@ -1145,4 +1180,32 @@ class AnonymousUser < User def destroy false end + + def create_user_ealasticsearch_index + if self.id != 2 && self.id != 4 + self.__elasticsearch__.index_document + end + end + def update_user_ealasticsearch_index + if self.id != 2 && self.id != 4 + self.__elasticsearch__.update_document + end + end + def delete_user_ealasticsearch_index + if self.id != 2 && self.id != 4 + self.__elasticsearch__.delete_document + end + end end + +# Delete the previous articles index in Elasticsearch +# User.__elasticsearch__.client.indices.delete index: User.index_name rescue nil +# +# # Create the new index with the new mapping +# User.__elasticsearch__.client.indices.create \ +# index: User.index_name, +# body: { settings: User.settings.to_hash, mappings: User.mappings.to_hash } + +# Index all article records from the DB to Elasticsearch +# 匿名用户 角色 和 管理员角色不能被索引 +User.where('id not in (2,4)').import diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb index b4d5ee4ea..9a2a02ab9 100644 --- a/app/views/kaminari/_paginator.html.erb +++ b/app/views/kaminari/_paginator.html.erb @@ -8,7 +8,6 @@ -%> <%= paginator.render do -%>