Commit a7f6755d by Hoang Phuc Do

Merge branch 'feature/search' into 'master'

create func search & auto index data to solr server

See merge request !15
parents 4f4b0e89 ec198c36
Pipeline #1094 failed with stages
in 0 seconds
...@@ -27,3 +27,8 @@ ...@@ -27,3 +27,8 @@
# Ignore master key for decrypting credentials and more. # Ignore master key for decrypting credentials and more.
/config/master.key /config/master.key
/lib/csv /lib/csv
.vscode/settings.json
config/settings.local.yml
config/settings/*.local.yml
config/environments/*.local.yml
...@@ -35,6 +35,9 @@ gem 'devise' ...@@ -35,6 +35,9 @@ gem 'devise'
gem 'carrierwave' gem 'carrierwave'
gem 'rails-i18n' gem 'rails-i18n'
gem 'i18n-js' gem 'i18n-js'
gem 'rsolr'
gem 'config'
# Use ActiveStorage variant # Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8' # gem 'mini_magick', '~> 4.8'
...@@ -47,8 +50,10 @@ gem 'bootsnap', '>= 1.1.0', require: false ...@@ -47,8 +50,10 @@ gem 'bootsnap', '>= 1.1.0', require: false
group :development, :test do group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console # Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'rubocop'
gem 'dotenv-rails' gem 'dotenv-rails'
gem 'pry' gem 'pry'
gem 'pry-nav'
end end
group :development do group :development do
......
...@@ -51,9 +51,10 @@ GEM ...@@ -51,9 +51,10 @@ GEM
archive-zip (0.12.0) archive-zip (0.12.0)
io-like (~> 0.3.0) io-like (~> 0.3.0)
arel (9.0.0) arel (9.0.0)
ast (2.4.1)
bcrypt (3.1.15) bcrypt (3.1.15)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.4.7) bootsnap (1.4.8)
msgpack (~> 1.0) msgpack (~> 1.0)
breadcrumbs_on_rails (4.0.0) breadcrumbs_on_rails (4.0.0)
rails (>= 5.0) rails (>= 5.0)
...@@ -87,8 +88,12 @@ GEM ...@@ -87,8 +88,12 @@ GEM
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.12.2) coffee-script-source (1.12.2)
concurrent-ruby (1.1.6) concurrent-ruby (1.1.7)
config (2.2.1)
deep_merge (~> 1.2, >= 1.2.1)
dry-validation (~> 1.0, >= 1.0.0)
crass (1.0.6) crass (1.0.6)
deep_merge (1.2.1)
devise (4.7.2) devise (4.7.2)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
...@@ -105,8 +110,48 @@ GEM ...@@ -105,8 +110,48 @@ GEM
activemodel-serializers-xml (>= 1.0) activemodel-serializers-xml (>= 1.0)
activesupport (>= 5.0) activesupport (>= 5.0)
request_store (>= 1.0) request_store (>= 1.0)
dry-configurable (0.11.6)
concurrent-ruby (~> 1.0)
dry-core (~> 0.4, >= 0.4.7)
dry-equalizer (~> 0.2)
dry-container (0.7.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (0.4.9)
concurrent-ruby (~> 1.0)
dry-equalizer (0.3.0)
dry-inflector (0.2.0)
dry-initializer (3.0.3)
dry-logic (1.0.7)
concurrent-ruby (~> 1.0)
dry-core (~> 0.2)
dry-equalizer (~> 0.2)
dry-schema (1.5.3)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.8, >= 0.8.3)
dry-core (~> 0.4)
dry-equalizer (~> 0.2)
dry-initializer (~> 3.0)
dry-logic (~> 1.0)
dry-types (~> 1.4)
dry-types (1.4.0)
concurrent-ruby (~> 1.0)
dry-container (~> 0.3)
dry-core (~> 0.4, >= 0.4.4)
dry-equalizer (~> 0.3)
dry-inflector (~> 0.1, >= 0.1.2)
dry-logic (~> 1.0, >= 1.0.2)
dry-validation (1.5.4)
concurrent-ruby (~> 1.0)
dry-container (~> 0.7, >= 0.7.1)
dry-core (~> 0.4)
dry-equalizer (~> 0.2)
dry-initializer (~> 3.0)
dry-schema (~> 1.5)
erubi (1.9.0) erubi (1.9.0)
execjs (2.7.0) execjs (2.7.0)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
ffi (1.13.1) ffi (1.13.1)
globalid (0.4.2) globalid (0.4.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
...@@ -136,28 +181,34 @@ GEM ...@@ -136,28 +181,34 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7) rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2) ruby_dep (~> 1.2)
loofah (2.6.0) loofah (2.7.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.1) mail (2.7.1)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
marcel (0.3.3) marcel (0.3.3)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
method_source (1.0.0) method_source (0.9.2)
mimemagic (0.3.5) mimemagic (0.3.5)
mini_magick (4.10.1) mini_magick (4.10.1)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
minitest (5.14.1) minitest (5.14.2)
msgpack (1.3.3) msgpack (1.3.3)
multipart-post (2.1.1)
mysql2 (0.5.3) mysql2 (0.5.3)
nio4r (2.5.2) nio4r (2.5.2)
nokogiri (1.10.10) nokogiri (1.10.10)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
pry (0.13.1) parallel (1.19.2)
coderay (~> 1.1) parser (2.7.1.4)
method_source (~> 1.0) ast (~> 2.4.1)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-nav (0.3.0)
pry (>= 0.9.10, < 0.13.0)
public_suffix (4.0.5) public_suffix (4.0.5)
puma (3.12.6) puma (3.12.6)
rack (2.2.3) rack (2.2.3)
...@@ -190,6 +241,7 @@ GEM ...@@ -190,6 +241,7 @@ GEM
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0) thor (>= 0.19.0, < 2.0)
rainbow (3.0.0)
rake (13.0.1) rake (13.0.1)
rb-fsevent (0.10.4) rb-fsevent (0.10.4)
rb-inotify (0.10.1) rb-inotify (0.10.1)
...@@ -200,6 +252,22 @@ GEM ...@@ -200,6 +252,22 @@ GEM
responders (3.0.1) responders (3.0.1)
actionpack (>= 5.0) actionpack (>= 5.0)
railties (>= 5.0) railties (>= 5.0)
rexml (3.2.4)
rsolr (2.3.0)
builder (>= 2.1.2)
faraday (>= 0.9.0)
rubocop (0.89.1)
parallel (~> 1.10)
parser (>= 2.7.1.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.7)
rexml
rubocop-ast (>= 0.3.0, < 1.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.3.0)
parser (>= 2.7.1.4)
ruby-progressbar (1.10.1)
ruby-vips (2.0.17) ruby-vips (2.0.17)
ffi (~> 1.9) ffi (~> 1.9)
ruby_dep (1.5.0) ruby_dep (1.5.0)
...@@ -218,7 +286,7 @@ GEM ...@@ -218,7 +286,7 @@ GEM
selenium-webdriver (3.142.7) selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0) childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2) rubyzip (>= 1.2.2)
spring (2.1.0) spring (2.1.1)
spring-watcher-listen (2.0.1) spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0) listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0) spring (>= 1.2, < 3.0)
...@@ -239,8 +307,9 @@ GEM ...@@ -239,8 +307,9 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (4.2.0) uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
warden (1.2.8) unicode-display_width (1.7.0)
rack (>= 2.0.6) warden (1.2.9)
rack (>= 2.0.9)
web-console (3.7.0) web-console (3.7.0)
actionview (>= 5.0) actionview (>= 5.0)
activemodel (>= 5.0) activemodel (>= 5.0)
...@@ -265,6 +334,7 @@ DEPENDENCIES ...@@ -265,6 +334,7 @@ DEPENDENCIES
carrierwave carrierwave
chromedriver-helper chromedriver-helper
coffee-rails (~> 4.2) coffee-rails (~> 4.2)
config
devise devise
dotenv-rails dotenv-rails
draper draper
...@@ -275,9 +345,12 @@ DEPENDENCIES ...@@ -275,9 +345,12 @@ DEPENDENCIES
mysql2 (>= 0.4.4, < 0.6.0) mysql2 (>= 0.4.4, < 0.6.0)
nokogiri nokogiri
pry pry
pry-nav
puma (~> 3.11) puma (~> 3.11)
rails (~> 5.2.4, >= 5.2.4.3) rails (~> 5.2.4, >= 5.2.4.3)
rails-i18n rails-i18n
rsolr
rubocop
sass-rails (~> 5.0) sass-rails (~> 5.0)
selenium-webdriver selenium-webdriver
spring spring
......
...@@ -406,18 +406,29 @@ $main-color: #23303D; ...@@ -406,18 +406,29 @@ $main-color: #23303D;
transform: translateY(-50%); transform: translateY(-50%);
} }
} }
.search_industries select{
@mixin custom_select {
padding: 12px 10px 12px 5px; padding: 12px 10px 12px 5px;
width: 100%; width: 100%;
border-radius: 4px !important; border-radius: 4px !important;
border: none;
box-shadow: none; box-shadow: none;
background: transparent; background: transparent;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
text-indent: 0.01px; text-indent: 0.01px;
text-overflow: ellipsis; text-overflow: ellipsis;
padding-left: 16px;
color: #666;
font-size: 15px;
}
.search_industries select{
@include custom_select()
}
.search_location select {
@include custom_select()
} }
.search_location { .search_location {
...@@ -557,10 +568,12 @@ $main-color: #23303D; ...@@ -557,10 +568,12 @@ $main-color: #23303D;
background-color: #fff; background-color: #fff;
color: #666; color: #666;
margin-bottom: 10px; margin-bottom: 10px;
border: none;
font-size: 13px;
// padding: 0px 4px;
} }
.search_industries_mobile { .search_industries_mobile {
position: relative; position: relative;
@include custom_selected_mobile();
&::before { &::before {
position: absolute; position: absolute;
content: ''; content: '';
...@@ -572,7 +585,13 @@ $main-color: #23303D; ...@@ -572,7 +585,13 @@ $main-color: #23303D;
transform: translateY(-50%); transform: translateY(-50%);
} }
} }
.search_location_mobile { .search_industries_mobile select{
padding: 0px 4px;
@include custom_selected_mobile()
}
.search_location_mobile select{
padding: 0px 4px;
@include custom_selected_mobile() @include custom_selected_mobile()
} }
...@@ -1362,12 +1381,12 @@ $main-color: #23303D; ...@@ -1362,12 +1381,12 @@ $main-color: #23303D;
.btn_search_submit { .btn_search_submit {
height: 30px; height: 30px;
} }
.search_industries_mobile { // .search_industries_mobile {
padding-right: 4px; // padding-right: 4px;
} // }
.search_location_mobile { // .search_location_mobile {
padding-right: 4px; // padding-right: 4px;
} // }
} }
@media only screen and (max-width: 576px) { @media only screen and (max-width: 576px) {
.lol { .lol {
......
# frozen_string_literal: true
class IndexedJob
attr_accessor :job
def initialize(job)
@job = job
end
def do
index_strategy = Sync::Entities::Job.new(job)
Sync::Indexer.new.do(index_strategy)
end
end
# frozen_string_literal: true
module InitSearch
def searches
Search.new search_params
end
private
def search_params
return {} if params[:search].blank?
params.require(:search).permit(:keyword, :industry_id, :city_id)
end
end
...@@ -5,6 +5,6 @@ class HistoriesController < ApplicationController ...@@ -5,6 +5,6 @@ class HistoriesController < ApplicationController
def index def index
histories = current_user.histories.order(created_at: :desc) histories = current_user.histories.order(created_at: :desc)
@jobs = histories.map(&:job) @jobs = histories.map(&:job).compact
end end
end end
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
# Home page # Home page
class HomeController < ApplicationController class HomeController < ApplicationController
include InitSearch
def index def index
@industries = Industry.order(name: :asc).all @industries = Industry.order(name: :asc)
@job_count = Job.count @job_count = Job.count
@cities = City.all_cities @cities = City.all_cities
@lasted_jobs = Job.order(created_at: :desc).limit(Job::NUMBER_LASTED_JOB) @lasted_jobs = Job.order(created_at: :desc).limit(Job::NUMBER_LASTED_JOB)
@top_cities = City.top_cities(9) @top_cities = City.top_cities(9)
@top_industries = Industry.top_industries(9) @top_industries = Industry.top_industries(9)
@search = searches
end end
end end
...@@ -28,6 +28,7 @@ class JobController < ApplicationController ...@@ -28,6 +28,7 @@ class JobController < ApplicationController
end end
def render_result(obj) def render_result(obj)
@search = Search.new
@keyword = obj.name @keyword = obj.name
@data = obj.jobs.page(params[:page]) @data = obj.jobs.page(params[:page])
return render 'error/page_not_found' if @data.blank? return render 'error/page_not_found' if @data.blank?
......
# frozen_string_literal: true
class PasswordsController < Devise::PasswordsController
def new
super
end
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
helpers.render_errors(resource)
respond_with(resource) { |format| format.html { redirect_to new_password_path(resource) } }
end
end
end
class SearchController < ApplicationController
before_action :load_data_dropdown, only: :search
include InitSearch
def search
return render 'error/internal_server_error' if params[:search].blank?
@keyword = params[:search][:keyword]
@industry = Industry.find_by(id: params[:search][:industry_id])
@city = City.find_by(id: params[:search][:city_id])
@results = Searches::Query.new.search(params)
return render 'error/page_not_found' if params[:page].to_i > @results.total_pages
@search = searches
render :result
end
def load_data_dropdown
@industries = Industry.order(name: :asc)
@cities = City.select(:id, :name)
end
end
# frozen_string_literal: true
class Search
include ActiveModel::Model
attr_accessor :keyword, :industry_id, :city_id
selected_industry = @industry.blank? ? '*' : @industry.id
selected_city = @city.blank? ? '*' : @city.id
end
# frozen_string_literal: true
module ApplyJobHelper
end
module RegistrationsHelper
end
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Description/Explanation of Person class # Description/Explanation of Person class
class Job < ApplicationRecord class Job < ApplicationRecord
after_update :indexed_job
NUMBER_LASTED_JOB = 5 NUMBER_LASTED_JOB = 5
belongs_to :company belongs_to :company
...@@ -16,4 +18,8 @@ class Job < ApplicationRecord ...@@ -16,4 +18,8 @@ class Job < ApplicationRecord
has_many :histories has_many :histories
has_many :favorites has_many :favorites
def indexed_job
IndexedJob.new(self).do
end
end end
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Process search pc--> <!-- Process search pc-->
<div class="box-search"> <div class="box-search">
<div class="search_scaffold"> <div class="search_scaffold">
...@@ -23,29 +22,28 @@ ...@@ -23,29 +22,28 @@
<h4 class="show_total_job"><%= t('pages.banner.slogan_3', job_count: @job_count) %></h4> <h4 class="show_total_job"><%= t('pages.banner.slogan_3', job_count: @job_count) %></h4>
<% end %> <% end %>
</div> </div>
<div class="filter_box"> <%= form_with url: search_path, method: :get, local: true, skip_enforcing_utf8: true do |f| %>
<div class="search_input"> <div class="filter_box">
<input type="search" name="" id="" class="custom_input_search" placeholder="<%= t('pages.banner.search_placeholder') %>"> <div class="search_input">
</div> <%= f.search_field :keyword, value: @keyword, class: 'custom_input_search', placeholder: t('pages.banner.search_placeholder') %>
<div class="search_industries">
<div class="icon_list">
<i class="fa fa-list-ul" aria-hidden="true"></i>
</div> </div>
<%= select_tag 'industry', options_from_collection_for_select(@industries, 'id', 'name'), prompt: t('pages.banner.all_industries'), class: 'show_list_industries' %> <div class="search_industries">
</div> <div class="icon_list">
<div class="search_location"> <i class="fa fa-list-ul" aria-hidden="true"></i>
<div class="icon_list"> </div>
<i class="fas fa-map-marker-alt"></i> <%= select_tag 'industry_id', options_from_collection_for_select(@industries, 'id', 'name', selected_industry), prompt: t('pages.banner.all_industries'), class: 'show_list_industries' %>
</div>
<div class="search_location">
<div class="icon_list">
<i class="fas fa-map-marker-alt"></i>
</div>
<%= select_tag 'city_id', options_from_collection_for_select(@cities, 'id', 'name', selected_city), prompt: t('pages.banner.all_locations'), class: 'show_list_cities' %>
</div>
<div class="search_submit">
<%= f.submit t('pages.banner.btn_text_search'), name: nil, class: 'btn_search_submit'%>
</div> </div>
<%= select_tag 'city', options_from_collection_for_select(@cities, 'id', 'name'), prompt: t('pages.banner.all_locations'), class: 'show_list_cities' %>
</div>
<div class="search_submit">
<button type="submit" class="btn_search_submit">
<i class="fas fa-search"></i>
<%= t('pages.banner.btn_text_search') %>
</button>
</div> </div>
</div> <% end %>
</div> </div>
</div> </div>
<!-- Process search tablet--> <!-- Process search tablet-->
...@@ -54,29 +52,28 @@ ...@@ -54,29 +52,28 @@
<div class = "total_job"> <div class = "total_job">
<h4 class="show_total_job"><%= t('pages.banner.slogan_3', job_count: @job_count) %> </h4> <h4 class="show_total_job"><%= t('pages.banner.slogan_3', job_count: @job_count) %> </h4>
</div> </div>
<div class="filter_box filter_box_tablet"> <%= form_with url: search_path, method: :get, local: true, skip_enforcing_utf8: true do |f| %>
<div class="search_input"> <div class="filter_box filter_box_tablet">
<input type="search" name="" id="" class="custom_input_search" placeholder="<%= t('pages.banner.search_placeholder') %>"> <div class="search_input">
</div> <%= f.search_field :keyword, value: @keyword, class: 'custom_input_search', placeholder: t('pages.banner.search_placeholder') %>
<div class="search_industries"> </div>
<div class="search_industries">
<div class="icon_list"> <div class="icon_list">
<i class="fa fa-list-ul" aria-hidden="true"></i> <i class="fa fa-list-ul" aria-hidden="true"></i>
</div> </div>
<%= select_tag 'industry', options_from_collection_for_select(@industries, 'id', 'name'), prompt: t('pages.banner.all_industries'), class: 'show_list_industries' %> <%= select_tag 'industry_id', options_from_collection_for_select(@industries, 'id', 'name', selected_industry), prompt: t('pages.banner.all_industries'), class: 'show_list_industries' %>
</div> </div>
<div class="search_location"> <div class="search_location">
<div class="icon_list"> <div class="icon_list">
<i class="fas fa-map-marker-alt"></i> <i class="fas fa-map-marker-alt"></i>
</div>
<%= select_tag 'city_id', options_from_collection_for_select(@cities, 'id', 'name', selected_city), prompt: t('pages.banner.all_locations'), class: 'show_list_cities' %>
</div>
<div class="search_submit">
<%= f.submit t('pages.banner.btn_text_search'), name: nil, class: 'btn_search_submit'%>
</div> </div>
<%= select_tag 'city', options_from_collection_for_select(@cities, 'id', 'name'), prompt: t('pages.banner.all_locations'), class: 'show_list_cities' %>
</div>
<div class="search_submit">
<button type="submit" class="btn_search_submit">
<i class="fas fa-search"></i>
<%= t('pages.banner.btn_text_search') %>
</button>
</div> </div>
</div> <% end %>
</div> </div>
</div> </div>
<div class="box-show-total-jobs-mobile"> <div class="box-show-total-jobs-mobile">
...@@ -84,34 +81,33 @@ ...@@ -84,34 +81,33 @@
</div> </div>
<!-- Process search mobile--> <!-- Process search mobile-->
<div class="box-search-mobile"> <div class="box-search-mobile">
<div class="fillter-search-mobile"> <%= form_with url: search_path, method: :get, local: true, skip_enforcing_utf8: true do |f| %>
<div class="container"> <div class="fillter-search-mobile">
<div class="row no-gutters"> <div class="container">
<div class="col-md-12"> <div class="row no-gutters">
<input type="search" name="" id="" class="custom_input_mobile_search" placeholder="<%= t('pages.banner.search_placeholder') %>"> <div class="col-md-12">
</div> <%= f.search_field :keyword, value: @keyword, class: 'custom_input_mobile_search', placeholder: t('pages.banner.search_placeholder') %>
<div class="col-sm-6 col-xs-12">
<div class="search_industries_mobile">
<%= select_tag 'industry', options_from_collection_for_select(@industries, 'id', 'name'), prompt: t('pages.banner.all_industries'), class: 'show_list_industries_mobile' %>
</div> </div>
</div> <div class="col-sm-6 col-xs-12">
<div class="col-sm-6 col-xs-12"> <div class="search_industries_mobile">
<div class="search_location_mobile"> <%= select_tag 'industry_id', options_from_collection_for_select(@industries, 'id', 'name', selected_industry), prompt: t('pages.banner.all_industries'), class: 'show_list_industries_mobile' %>
<%= select_tag 'city', options_from_collection_for_select(@cities, 'id', 'name'), prompt: t('pages.banner.all_locations'), class: 'show_list_cities_mobile' %> </div>
</div>
<div class="col-sm-6 col-xs-12">
<div class="search_location_mobile">
<%= select_tag 'city_id', options_from_collection_for_select(@cities, 'id', 'name', selected_city), prompt: t('pages.banner.all_locations'), class: 'show_list_cities_mobile' %>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="container">
<div class="container"> <div class="row no-gutters">
<div class="row no-gutters"> <div class="col-sm-12">
<div class="col-sm-12"> <%= f.submit t('pages.banner.btn_text_search'), name: nil, class: 'btn_search_submit'%>
<button type="submit" class="btn_search_submit"> </div>
<i class="fas fa-search"></i>
<%= t('pages.banner.btn_text_search') %>
</button>
</div> </div>
</div> </div>
</div> </div>
</div> <% end %>
</div> </div>
<% provide(:title, "#{t('title.home')}") %> <% provide(:title, "#{t('title.home')}") %>
<%= render 'home/banner_and_search' %> <%= render 'search/banner_and_search' %>
<div class="padding_index"></div> <div class="padding_index"></div>
<% unless @job_count.nil? %> <% unless @job_count.blank? %>
<div class="box_text_five_jobs"> <div class="box_text_five_jobs">
<span><%= t('pages.index.lasted_job') %></span> <span><%= t('pages.index.lasted_job') %></span>
</div> </div>
......
<% provide(:title ,"#{t('pages.detail.br_title')}") %> <% provide(:title ,"#{t('pages.detail.br_title')}") %>
<%= render 'home/banner_and_search' %> <%= render 'search/banner_and_search' %>
<div class="padding_index"></div> <div class="padding_index"></div>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<%= t('pages.result.result_find', job: @data.total_count, key: @keyword).html_safe %> <%= t('pages.result.result_find', job: @data.total_count, key: @keyword).html_safe %>
</div> </div>
<% else %> <% else %>
<%= t('pages.result.no_result_find', key: @keyword).html_safe %> <%= t('pages.result.no_result_find', key: @keyword).html_safe %>
<% end %> <% end %>
</span> </span>
</div> </div>
......
<div class = "box-banner">
<%= image_tag 'banner-search-box-home.png', class: "banner-top" %>
<div class="row">
<div class="col-xs-12">
<div class = "slogan">
<h2 class = "slogan_text">
<%= t('pages.banner.slogan') %>
</h2>
<h5 class = "slogan_text_h5">
<%= t('pages.banner.slogan') %>
</h5>
</div>
</div>
</div>
<!-- Process search pc-->
<div class="box-search">
<div class="search_scaffold">
<div class = "total_job">
<h4 class="show_total_job">
<%= @job_count.blank? ? t('pages.banner.slogan_2') : t('pages.banner.slogan_3', job_count: @job_count) %>
</h4>
</div>
<%= form_for @search, enforce_utf8: false, method: :get do |f| %>
<div class="filter_box">
<div class="search_input">
<%= f.search_field :keyword, value: @keyword, class: 'custom_input_search', placeholder: t('pages.banner.search_placeholder') %>
</div>
<div class="search_industries">
<div class="icon_list">
<i class="fa fa-list-ul" aria-hidden="true"></i>
</div>
<% selected = @industry.blank? ? '*' : @industry.id %>
<%= f.select 'industry_id', options_from_collection_for_select(@industries, 'id', 'name', selected), prompt: t('pages.banner.all_industries'), class: 'show_list_industries'%>
</div>
<div class="search_location">
<div class="icon_list">
<i class="fas fa-map-marker-alt"></i>
</div>
<% selected = @city.blank? ? '*' : @city.id %>
<%= f.select 'city_id', options_from_collection_for_select(@cities, 'id', 'name', selected), prompt: t('pages.banner.all_locations'), class: 'show_list_cities' %>
</div>
<div class="search_submit">
<%= f.submit t('pages.banner.btn_text_search'), name: nil, class: 'btn_search_submit'%>
</div>
</div>
<% end %>
</div>
</div>
<!-- Process search tablet-->
<div class="box-search-tablet">
<div class="search_scaffold-tablet">
<div class = "total_job">
<h4 class="show_total_job"><%= t('pages.banner.slogan_3', job_count: @job_count) %> </h4>
</div>
<%= form_for @search, enforce_utf8: false, method: :get do |f| %>
<div class="filter_box filter_box_tablet">
<div class="search_input">
<%= f.search_field :keyword, value: @keyword, class: 'custom_input_search', placeholder: t('pages.banner.search_placeholder') %>
</div>
<div class="search_industries">
<div class="icon_list">
<i class="fa fa-list-ul" aria-hidden="true"></i>
</div>
<% selected = @industry.blank? ? '*' : @industry.id %>
<%= f.select 'industry_id', options_from_collection_for_select(@industries, 'id', 'name', selected), prompt: t('pages.banner.all_industries'), class: 'show_list_industries' %>
</div>
<div class="search_location">
<div class="icon_list">
<i class="fas fa-map-marker-alt"></i>
</div>
<% selected = @city.blank? ? '*' : @city.id %>
<%= f.select 'city_id', options_from_collection_for_select(@cities, 'id', 'name', selected), prompt: t('pages.banner.all_locations'), class: 'show_list_cities' %>
</div>
<div class="search_submit">
<%= f.submit t('pages.banner.btn_text_search'), name: nil, class: 'btn_search_submit'%>
</div>
</div>
<% end %>
</div>
</div>
<div class="box-show-total-jobs-mobile">
<span><%= t('pages.banner.slogan_3', job_count: @job_count) %> </span>
</div>
<!-- Process search mobile-->
<div class="box-search-mobile">
<%= form_for @search, enforce_utf8: false, method: :get do |f| %>
<div class="fillter-search-mobile">
<div class="container">
<div class="row no-gutters">
<div class="col-md-12">
<%= f.search_field :keyword, value: @keyword, class: 'custom_input_mobile_search', placeholder: t('pages.banner.search_placeholder') %>
</div>
<div class="col-sm-6 col-xs-12">
<div class="search_industries_mobile">
<% selected = @industry.blank? ? '*' : @industry.id %>
<%= f.select 'industry_id', options_from_collection_for_select(@industries, 'id', 'name', selected), prompt: t('pages.banner.all_industries'), class: 'show_list_industries_mobile' %>
</div>
</div>
<div class="col-sm-6 col-xs-12">
<div class="search_location_mobile">
<% selected = @city.blank? ? '*' : @city.id %>
<%= f.select 'city_id', options_from_collection_for_select(@cities, 'id', 'name', selected), prompt: t('pages.banner.all_locations'), class: 'show_list_cities_mobile' %>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row no-gutters">
<div class="col-sm-12">
<%= f.submit t('pages.banner.btn_text_search'), name: nil, class: 'btn_search_submit'%>
</div>
</div>
</div>
</div>
<% end %>
</div>
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="box_jobs">
<div class="col-sm-12 d-block d-sm-none">
<div class="link_favorite_top block_link_favorite md">
<%= render 'btn_func_favorite', data: data %>
</div>
</div>
<div class="row">
<div class="col-sm-10 col-md-9 col-lg-10">
<div class="box_info">
<div class="lol">
<%= link_to data['name'], detail_job_path(id: data['id']), class: 'job_name' %>
</div>
<div class="cop">
<h5 class="box_info_copany_name"><i class="far fa-building"></i> <%= data['company_name'] %></h5>
</div>
<div class="loc">
<h5 class="box_info_location"><i class="fas fa-map-marker-alt"></i>
<%= data['locations'].join(' | ') %>
</h5>
</div>
<h5 class="box_info_salary"><i class="fas fa-dollar-sign"></i>&nbsp;<%= "#{t('pages.index.salary')}: #{data['salary']}" %></h5>
<div class="coc">
<h5 class="box_info_des"><%= strip_tags(data['description']) %></h5>
</div>
</div>
</div>
<div class="col-sm-2 col-md-3 d-none d-sm-block col-lg-2">
<div class="link_favorite_top block_link_favorite">
<%= render 'btn_func_favorite', data: data %>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<% if user_signed_in? %>
<% favorite_id = current_user.favorites.find_by(job_id: data['id']) %>
<% if favorite_id.present? %>
<%= render 'job/link_destroy', favorite_id: favorite_id %>
<% else %>
<%= render 'job/link_create', job_id: data['id'] %>
<% end %>
<% else %>
<span class="link_favorite span_click_favorite"><i class="far fa-heart"></i></span>
<% end %>
<div class="col-lg-12">
<div class="breadcrumb_total_search_pagination_jobs">
<div class="pagination_jobs">
<div id="paginator">
<%= paginate results %>
</div>
</div>
</div>
</div>
\ No newline at end of file
<% provide(:title ,"#{t('pages.detail.br_title')}") %>
<%= render 'search/banner_and_search' %>
<div class="padding_index"></div>
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="breadcrumb_total_search">
<span class="breadcrumb-item active">
<% if @results.total_count == Job.count %>
<div style="word-wrap: break-word;">
<%= "#{t('pages.result.all')} #{@results.total_count}" %>
</div>
<% elsif @results.total_count.positive? %>
<%= t('pages.result.result_find', job: @results.total_count, key: @keyword).html_safe %>
<% else %>
<%= t('pages.result.no_result_find', key: @keyword).html_safe %>
<% end %>
<b><%= "#{@industry.try(:name)} #{@city.try(:name)}" %></b>
</span>
</div>
</div>
<%= render 'paginate', results: @results %>
</div>
</div>
<%= render 'job/modal_login' %>
<%= render partial: 'block_info_job', collection: @results, as: :data %>
<div class="container">
<div class="row">
<%= render 'paginate', results: @results %>
</div>
</div>
<%= render 'shared/scroll_top' %>
...@@ -11,14 +11,12 @@ module Venjob ...@@ -11,14 +11,12 @@ module Venjob
# Initialize configuration defaults for originally generated Rails version. # Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2 config.load_defaults 5.2
config.exceptions_app = self.routes config.exceptions_app = self.routes
config.autoload_paths << Rails.root.join('lib/service') config.autoload_paths += Dir[Rails.root.join('lib', '{service,src}')]
config.autoload_paths << Rails.root.join('lib/src') config.eager_load_paths += Dir[Rails.root.join('lib', '{business,solr,user}')]
config.eager_load_paths << Rails.root.join('lib/business')
# i18n # i18n
config.i18n.available_locales = [:en, :vi] config.i18n.available_locales = [:en, :vi]
config.i18n.default_locale = :vi config.i18n.default_locale = :vi
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')] config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
# Settings in config/environments/* take precedence over those specified here. # Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers # Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading # -- all .rb files in that directory are automatically loaded after loading
......
...@@ -14,19 +14,19 @@ default: &default ...@@ -14,19 +14,19 @@ default: &default
encoding: utf8mb4 encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root username: root
password: '1' password: 'Trunghung5055@'
socket: /var/run/mysqld/mysqld.sock socket: /var/run/mysqld/mysqld.sock
development: development:
<<: *default <<: *default
database: venjob_development database: hungnt_development
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake". # re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production. # Do not set this db to the same as development or production.
test: test:
<<: *default <<: *default
database: venjob_test database: hungnt_test
# As with config/secrets.yml, you never want to store sensitive information, # As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is # like your database password, in your source code. If your source code is
......
...@@ -22,7 +22,7 @@ Rails.application.configure do ...@@ -22,7 +22,7 @@ Rails.application.configure do
config.eager_load = false config.eager_load = false
# Show full error reports. # Show full error reports.
config.consider_all_requests_local = false config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled. # Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching. # Run rails dev:cache to toggle caching.
......
Config.setup do |config|
# Name of the constant exposing loaded settings
config.const_name = 'Settings'
# Ability to remove elements of the array set in earlier loaded settings file. For example value: '--'.
#
# config.knockout_prefix = nil
# Overwrite an existing value when merging a `nil` value.
# When set to `false`, the existing value is retained after merge.
#
# config.merge_nil_values = true
# Overwrite arrays found in previously loaded settings file. When set to `false`, arrays will be merged.
#
# config.overwrite_arrays = true
# Load environment variables from the `ENV` object and override any settings defined in files.
#
# config.use_env = false
# Define ENV variable prefix deciding which variables to load into config.
#
# Reading variables from ENV is case-sensitive. If you define lowercase value below, ensure your ENV variables are
# prefixed in the same way.
#
# When not set it defaults to `config.const_name`.
#
config.env_prefix = 'SETTINGS'
# What string to use as level separator for settings loaded from ENV variables. Default value of '.' works well
# with Heroku, but you might want to change it for example for '__' to easy override settings from command line, where
# using dots in variable names might not be allowed (eg. Bash).
#
# config.env_separator = '.'
# Ability to process variables names:
# * nil - no change
# * :downcase - convert to lower case
#
# config.env_converter = :downcase
# Parse numeric values as integers instead of strings.
#
# config.env_parse_values = true
# Validate presence and type of specific config values. Check https://github.com/dry-rb/dry-validation for details.
#
# config.schema do
# required(:name).filled
# required(:age).maybe(:int?)
# required(:email).filled(format?: EMAIL_REGEX)
# end
end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Kaminari.configure do |config| Kaminari.configure do |config|
config.default_per_page = 20 config.default_per_page = 20
# config.max_per_page = nil # config.max_per_page = nil
config.window = 3 config.window = 2
# config.outer_window = 0 # config.outer_window = 0
# config.left = 0 # config.left = 0
# config.right = 0 # config.right = 0
......
...@@ -33,15 +33,16 @@ en: ...@@ -33,15 +33,16 @@ en:
slogan: 'VENJOB HUMAN RESOURCE PLATFORM' slogan: 'VENJOB HUMAN RESOURCE PLATFORM'
slogan_2: 'Instantly discover the best jobs on VenJob' slogan_2: 'Instantly discover the best jobs on VenJob'
slogan_3: 'Instantly discover %{job_count} the best jobs on VenJob' slogan_3: 'Instantly discover %{job_count} the best jobs on VenJob'
search_placeholder: 'Input title, companies, locations...' search_placeholder: 'Input title, companies,...'
btn_text_search: 'Find Job' btn_text_search: 'Find Job'
all_industries: 'All industries' all_industries: 'All industries'
all_locations: 'All locations' all_locations: 'All locations'
confirm: confirm:
modal_login: 'PLEASE SIGN IN TO EXECUTE THIS FUNCTION' modal_login: 'PLEASE SIGN IN TO EXECUTE THIS FUNCTION'
result: result:
result_find: 'There are <b>%{job}</b> results matching keyword <b>%{key}</b>' result_find: 'There are &nbsp;<b>%{job}</b>&nbsp; results matching keyword &nbsp;<b>%{key}</b>&nbsp;'
no_result_find: 'No results match the keyword <b>%{key}</b>' no_result_find: 'No results match the keyword &nbsp;<b>%{key}</b>&nbsp;'
all: 'All Job'
detail: detail:
br_title: 'Result' br_title: 'Result'
btr_text_submit_cv: 'Apply Now' btr_text_submit_cv: 'Apply Now'
...@@ -50,6 +51,7 @@ en: ...@@ -50,6 +51,7 @@ en:
expiration_date: ' Expiration date' expiration_date: ' Expiration date'
experience: ' Experience' experience: ' Experience'
level: ' Level' level: ' Level'
industry: ' Industry:'
cities: cities:
ask: 'Do you want to find a job at Viet Nam or oversea?' ask: 'Do you want to find a job at Viet Nam or oversea?'
domestic: 'Viet Nam' domestic: 'Viet Nam'
...@@ -147,3 +149,7 @@ en: ...@@ -147,3 +149,7 @@ en:
user_id: '' user_id: ''
favorite: favorite:
user_id: '' user_id: ''
views:
pagination:
last: 'Last'
first: 'First'
...@@ -33,15 +33,16 @@ vi: ...@@ -33,15 +33,16 @@ vi:
slogan: 'NỀN TẢNG TUYỂN DỤNG NHÂN SỰ VENJOB' slogan: 'NỀN TẢNG TUYỂN DỤNG NHÂN SỰ VENJOB'
slogan_2: 'Khám phá ngay những việc làm tốt nhất trên VenJob' slogan_2: 'Khám phá ngay những việc làm tốt nhất trên VenJob'
slogan_3: "Khám phá ngay %{job_count} việc làm tốt nhất trên VenJob" slogan_3: "Khám phá ngay %{job_count} việc làm tốt nhất trên VenJob"
search_placeholder: 'Nhập tiêu đề, công ty, địa điểm...' search_placeholder: 'Nhập tiêu đề, công ty,...'
btn_text_search: 'Tìm việc' btn_text_search: 'Tìm việc'
all_industries: 'Tất cả nghành nghề' all_industries: 'Tất cả nghành nghề'
all_locations: 'Tất cả đỉa điểm' all_locations: 'Tất cả đỉa điểm'
confirm: confirm:
modal_login: 'VUI LÒNG ĐĂNG NHẬP ĐỂ THỰC HIỆN CHỨC NĂNG NÀY' modal_login: 'VUI LÒNG ĐĂNG NHẬP ĐỂ THỰC HIỆN CHỨC NĂNG NÀY'
result: result:
result_find: ' <b>%{job}</b> kết quả phù hợp với từ khóa <b>%{key}</b>' result_find: ' &nbsp;<b>%{job}</b>&nbsp; kết quả phù hợp với từ khóa &nbsp;<b>%{key}</b>&nbsp;'
no_result_find: 'Không kết quả phù hợp với từ khóa <b>%{key}</b>' no_result_find: 'Không kết quả phù hợp với từ khóa &nbsp;<b>%{key}</b>&nbsp;'
all: 'Tất cả công việc hiện tại'
detail: detail:
br_title: 'Kết quả tìm kiếm' br_title: 'Kết quả tìm kiếm'
btr_text_submit_cv: 'Nộp CV ngay' btr_text_submit_cv: 'Nộp CV ngay'
...@@ -50,6 +51,7 @@ vi: ...@@ -50,6 +51,7 @@ vi:
expiration_date: ' Ngày hết hạn' expiration_date: ' Ngày hết hạn'
experience: ' Kinh nghiệm' experience: ' Kinh nghiệm'
level: ' Cấp bậc' level: ' Cấp bậc'
industry: ' Ngành nghề:'
cities: cities:
ask: 'Bạn muốn tìm việc trong hay ngoài nước?' ask: 'Bạn muốn tìm việc trong hay ngoài nước?'
domestic: 'Việt Nam' domestic: 'Việt Nam'
...@@ -147,4 +149,8 @@ vi: ...@@ -147,4 +149,8 @@ vi:
user_id: '' user_id: ''
favorite: favorite:
user_id: '' user_id: ''
views:
pagination:
last: 'Cuối'
first: 'Đầu'
\ No newline at end of file
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
Rails.application.routes.draw do Rails.application.routes.draw do
get 'setting', to: 'users#set_lang', as: :set_lang get 'setting', to: 'users#set_lang', as: :set_lang
scope '(:locale)', locale: /en|vi/ do scope '(:locale)', locale: /en|vi/ do
devise_for :users, controllers: { registrations: 'registrations' } devise_for :users, controllers: { registrations: 'registrations', passwords: 'passwords'}
root 'home#index' root 'home#index'
# Search
get 'search', to: 'search#search', as: :searches
get 'register/:code', to: 'users#confirm_sign_up', as: :confirm_sign_up get 'register/:code', to: 'users#confirm_sign_up', as: :confirm_sign_up
get 'industries', to: 'industry#index', as: :industry_index get 'industries', to: 'industry#index', as: :industry_index
get 'cities', to: 'city#index', as: :city_index get 'cities', to: 'city#index', as: :city_index
......
number_result_search_in_page: 10
url_server_solr: 'http://localhost:8983/solr/venjob'
url_server_solr: 'http://.../solr/venjob'
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
batchSize="-1"
url="jdbc:mysql://127.0.0.1:3306/hungnt_development"
user="root"
password="Trunghung5055@"/>
<document>
<entity name="jobs"
pk="id"
query="SELECT jobs.*, companies.name AS company_name FROM jobs, companies WHERE jobs.company_id = companies.id">
<field column="id" name="id"/>
<field column="name" name="name"/>
<field column="company_name" name="company_name"/>
<field column="salary" name="salary"/>
<field column="description" name="description"/>
<!-- QUERY INDUSTRIES -->
<entity name="industries"
query="SELECT industries.id AS industry_id, industries.name AS industry_name FROM industries, industry_jobs, jobs WHERE jobs.id = industry_jobs.job_id AND industry_jobs.industry_id = industries.id AND jobs.id='${jobs.id}'">
<field column="industry_name" name="industries" />
<field column="industry_id" name="industry_id" />
</entity>
<!-- QUERY CITIES -->
<entity name="cities"
query="SELECT cities.id AS location_id, cities.name AS location_name FROM cities, city_jobs, jobs WHERE jobs.id = city_jobs.job_id AND city_jobs.city_id = cities.id AND jobs.id='${jobs.id}'">
<field column="location_name" name="locations" />
<field column="location_id" name="location_id" />
</entity>
</entity>
</document>
</dataConfig>
...@@ -43,13 +43,17 @@ class ImportData ...@@ -43,13 +43,17 @@ class ImportData
industry_name = val['category'].gsub(',', '/').gsub('/', ' / ') industry_name = val['category'].gsub(',', '/').gsub('/', ' / ')
industry = Industry.find_or_create_by(name: industry_name.strip) industry = Industry.find_or_create_by(name: industry_name.strip)
Job.find_or_create_by(name: val['name'], company_id: company_id) do |job| record = Job.find_or_initialize_by(name: val['name'], company_id: company_id) do |job|
job.level = val['level'] job.level = val['level']
job.salary = val['salary'] job.salary = val['salary']
job.description = desc job.description = desc
job.city_ids.blank? ? job.cities << city : job.cities == city job.city_ids.blank? ? job.cities << city : job.cities == city
job.industry_ids.blank? ? job.industries << industry : job.industries == industry job.industry_ids.blank? ? job.industries << industry : job.industries == industry
end end
if record.new_record?
record.save
IndexedJob.new(job).do
end
rescue StandardError => e rescue StandardError => e
logger.error "Import_jobs: #{e}" logger.error "Import_jobs: #{e}"
end end
......
module Searches
class Query
def search(params)
solr = connect_solr
# Escape input & Convert space to asterisks
data = sub_space_params!(params[:search][:keyword], params[:search][:industry_id], params[:search][:city_id])
query = if [params[:search][:keyword], params[:search][:industry_id], params[:search][:city_id]].all?(&:blank?)
'*:*'
else
"#{data[:keyword]} AND (industry_id: #{data[:industry_id]}) AND (location_id: #{data[:location_id]})"
end
response = solr.paginate(params[:page], Settings.number_result_search_in_page, 'select', params: { q: query })
Kaminari.paginate_array(response['response']['docs'], total_count: response['response']['numFound']).page(params[:page]).per(Settings.number_result_search_in_page)
end
def sub_space_params!(keyword, industry_id, location_id)
arr_params = {}
industry_id = '' if Industry.find_by_id(industry_id).blank?
location_id = '' if City.find_by_id(location_id).blank?
arr_params[:keyword] = RSolr.solr_escape(keyword.downcase || '')
arr_params[:industry_id] = RSolr.solr_escape(industry_id)
arr_params[:location_id] = RSolr.solr_escape(location_id)
arr_params.each_value { |val| val.sub!('', '*') if val.blank? }
end
private
def connect_solr
SolrSetting::SolrServer.connection
end
end
end
# frozen_string_literal: true
module SolrSetting
class SolrServer
def self.connection
RSolr.connect url: Settings.url_server_solr
end
end
end
# frozen_string_literal: true
module Sync::Entities
class Base
def initialize(entity)
@entity = entity
end
def to_h
raise 'Not implemented error'
end
end
end
# frozen_string_literal: true
module Sync::Entities
class Job < Sync::Entities::Base
def to_h
data = {}
data[:id] = job.id
data[:name] = job.name
data[:company_name] = job.company.name
data[:salary] = job.salary
data[:description] = job.description
data[:industries] = job.industries.pluck(:name)
data[:industry_id] = job.industry_jobs.map(&:industry_id)
data[:locations] = job.cities.pluck(:name)
data[:location_id] = job.city_jobs.map(&:city_id)
data
end
def job
@entity
end
end
end
# frozen_string_literal: true
module Sync
class Indexer
def logger
@logger ||= Logger.new(Rails.root.join('log', 'indexed_data_to_solr.log'))
end
def do(data)
solr = SolrSetting::SolrServer.connection
solr.add data.to_h, add_attributes: { commitWithin: 10 }
rescue StandardError => e
logger.error "Indexed data to Solr have error: #{e}"
end
end
end
...@@ -56,6 +56,8 @@ class CrawlerJob < Crawler ...@@ -56,6 +56,8 @@ class CrawlerJob < Crawler
description: data[:description]) description: data[:description])
create_industry_relation(data[:industry_name], job) create_industry_relation(data[:industry_name], job)
create_city_relation(data[:city_name], job) create_city_relation(data[:city_name], job)
# Index data to solr
IndexedJob.new(job).do
rescue StandardError => e rescue StandardError => e
logger.error "Crawler data jobs has error: #{e}" logger.error "Crawler data jobs has error: #{e}"
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment