Skip to content

Commit

Permalink
Add search by multiple keyboard layouts #13
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitriy Strukov committed Aug 7, 2019
1 parent be95d0b commit 746818c
Show file tree
Hide file tree
Showing 56 changed files with 282 additions and 16 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ Thumbs.db

db/REDIS/
db/SPHINX/
db/sphinx/
config/ENV/*
config/database.yml
config/SPHINX.config
conifg/development.sphinx.conf

binlog
log/*
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ GEM
redis (~> 3.0, >= 3.0.4)
responders (2.1.0)
railties (>= 4.2.0, < 5)
riddle (2.1.0)
riddle (2.4.0)
role_block_haml (0.2.4)
haml (~> 4.0)
route_downcaser (1.1.4)
Expand Down Expand Up @@ -674,4 +674,4 @@ DEPENDENCIES
whenever

BUNDLED WITH
1.16.1
1.16.6
25 changes: 12 additions & 13 deletions app/controllers/search_controller.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# frozen_string_literal: true

class SearchController < ApplicationController
before_action :define_hub_ids

def pub_types
[ Recipe, Post, Blog, Page, Interview ]
end

def autocomplete
@squery = params[:term].to_s.strip
to_search = Riddle::Query.escape @squery

res = ThinkingSphinx.search(
to_search,
star: true,
classes: pub_types,
classes: SearchService::PUBLICATION_TYPES,
field_weights: {
title: 10,
content: 5
Expand All @@ -24,21 +22,23 @@ def autocomplete
end

def search
@squery = params[:squery].to_s.strip
to_search = Riddle::Query.escape @squery
search_service = SearchService.call(params[:squery], @hub_ids, params)

@pubs = if @hub_ids.blank?
ThinkingSphinx.search(to_search, star: true, classes: pub_types, sql: { include: :hub }).pagination(params)
else
ThinkingSphinx.search(to_search, star: true, classes: pub_types, sql: { include: :hub }, with: { hub_id: @hub_ids }).pagination(params)
end
@pubs = search_service.search_result
@multiple_keyboard_layouts = search_service.multiple_keyboard_layouts

@search_results_size = @pubs.size
@search_results_count = @pubs.count

render layout: 'ok2/layouts/application', template: 'ok2/search/index'
end

def multiple_keyboard_layouts?
@multiple_keyboard_layouts
end

helper_method :multiple_keyboard_layouts?

private

def define_hub_ids
Expand All @@ -54,5 +54,4 @@ def define_hub_ids
@hub_ids << post_hub.self_and_descendants.select(:id).map(&:id).uniq
end
end

end
74 changes: 74 additions & 0 deletions app/services/search_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

module SearchService
PUBLICATION_TYPES = [
Recipe, Post, Blog, Page, Interview
].freeze

LOWERCASE_DICTIONARY = {
'q' => 'й', 'w' => 'ц', 'e' => 'у', 'r' => 'к',
't' => 'е', 'y' => 'н', 'u' => 'г', 'i' => 'ш',
'o' => 'щ', 'p' => 'з', 'a' => 'ф', 's' => 'ы',
'd' => 'в', 'f' => 'а', 'g' => 'п', 'h' => 'р',
'j' => 'о', 'k' => 'л', 'l' => 'д', ';' => 'ж',
"'" => 'э', 'z' => 'я', 'x' => 'ч', 'c' => 'с',
'v' => 'м', 'b' => 'и', 'n' => 'т', 'm' => 'ь',
',' => 'б', '.' => 'ю'
}.freeze

UPPERCASE_DICTIONARY = {
'Q' => 'Й', 'W' => 'Ц', 'E' => 'У', 'R' => 'К',
'T' => 'Е', 'Y' => 'Н', 'U' => 'Г', 'I' => 'Ш',
'O' => 'Щ', 'P' => 'З', 'A' => 'Ф', 'S' => 'Ы',
'D' => 'В', 'F' => 'А', 'G' => 'П', 'H' => 'Р',
'J' => 'О', 'K' => 'Л', 'L' => 'Д', ':' => 'Ж',
'"' => 'Э', 'Z' => 'Я', 'X' => 'Ч', 'С' => 'С',
'V' => 'М', 'B' => 'И', 'N' => 'Т', 'M' => 'Ь',
'<' => 'Б', '>' => 'Ю'
}.freeze

def call(query, hub_ids, params)
query = query.to_s.strip
riddle_query = Riddle::Query.escape(query)

search_result = if hub_ids.blank?
ThinkingSphinx.search(riddle_query, star: true, classes: PUBLICATION_TYPES, sql: { include: :hub }).pagination(params)
else
ThinkingSphinx.search(riddle_query, star: true, classes: PUBLICATION_TYPES, sql: { include: :hub }, with: { hub_id: hub_ids }).pagination(params)
end

if search_result.blank?
OpenStruct.new(search_result: search_by_multiple_keyboard_layouts(query, params), multiple_keyboard_layouts: true)
else
OpenStruct.new(search_result: search_result, multiple_keyboard_layouts: false)
end
end

module_function :call

def convert_to_another_keyboard_layout(query)
search_query = []

query.each_char do |char|
search_query.push(dictionary[char])
end

search_query.join
end

module_function :convert_to_another_keyboard_layout

private

def search_by_multiple_keyboard_layouts(query, params)
ThinkingSphinx.search(Riddle::Query.escape(convert_to_another_keyboard_layout(query)), star: true, classes: PUBLICATION_TYPES, sql: { include: :hub }).pagination(params)
end

module_function :search_by_multiple_keyboard_layouts

def dictionary
LOWERCASE_DICTIONARY.merge(UPPERCASE_DICTIONARY)
end

module_function :dictionary
end
5 changes: 4 additions & 1 deletion app/views/ok2/search/index.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
h1.tac.fs30.fwn.i.pt20.pb15.ffg(style='color:#024e9f')
| Поиск:
'
= @squery
- if multiple_keyboard_layouts? && @pubs.present?
= SearchService.convert_to_another_keyboard_layout(params[:squery].to_s.strip)
- else
= params[:squery]

-# MAIN CONTENT
- if @pubs.blank?
Expand Down
184 changes: 184 additions & 0 deletions config/development.sphinx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@

indexer
{
}

searchd
{
listen = 127.0.0.1:9306:mysql41
log = /home/dmitriy/Projects/ok-2018/log/development.searchd.log
query_log = /home/dmitriy/Projects/ok-2018/log/development.searchd.query.log
pid_file = /home/dmitriy/Projects/ok-2018/log/development.sphinx.pid
workers = threads
binlog_path = /home/dmitriy/Projects/ok-2018/tmp/binlog/development
}

source blog_core_0
{
type = pgsql
sql_host = localhost
sql_user = dmitriy
sql_pass =
sql_db = open_cook_dev
sql_query_pre = SET TIME ZONE UTC
sql_query = SELECT "blogs"."id" * 10 + 0 AS "id", "blogs"."title" AS "title", "blogs"."intro" AS "intro", "blogs"."content" AS "content", "blogs"."id" AS "sphinx_internal_id", 'Blog' AS "sphinx_internal_class", 0 AS "sphinx_deleted", "blogs"."user_id" AS "user_id", "blogs"."hub_id" AS "hub_id", extract(epoch from "blogs"."published_at")::int AS "published_at", extract(epoch from "blogs"."created_at")::int AS "created_at", extract(epoch from "blogs"."updated_at")::int AS "updated_at" FROM "blogs" WHERE ("blogs"."id" BETWEEN $start AND $end AND state = 'published') GROUP BY "blogs"."id", "blogs"."title", "blogs"."intro", "blogs"."content", "blogs"."id", "blogs"."user_id", "blogs"."hub_id", "blogs"."published_at", "blogs"."created_at", "blogs"."updated_at"
sql_query_range = SELECT COALESCE(MIN("blogs"."id"), 1), COALESCE(MAX("blogs"."id"), 1) FROM "blogs"
sql_attr_uint = sphinx_internal_id
sql_attr_uint = sphinx_deleted
sql_attr_uint = user_id
sql_attr_uint = hub_id
sql_attr_timestamp = published_at
sql_attr_timestamp = created_at
sql_attr_timestamp = updated_at
sql_attr_string = sphinx_internal_class
}

index blog_core
{
type = plain
path = /home/dmitriy/Projects/ok-2018/db/sphinx/development/blog_core
docinfo = extern
source = blog_core_0
}

source interview_core_0
{
type = pgsql
sql_host = localhost
sql_user = dmitriy
sql_pass =
sql_db = open_cook_dev
sql_query_pre = SET TIME ZONE UTC
sql_query = SELECT "interviews"."id" * 10 + 1 AS "id", "interviews"."title" AS "title", "interviews"."intro" AS "intro", "interviews"."content" AS "content", "interviews"."id" AS "sphinx_internal_id", 'Interview' AS "sphinx_internal_class", 0 AS "sphinx_deleted", "interviews"."user_id" AS "user_id", "interviews"."hub_id" AS "hub_id", extract(epoch from "interviews"."published_at")::int AS "published_at", extract(epoch from "interviews"."created_at")::int AS "created_at", extract(epoch from "interviews"."updated_at")::int AS "updated_at" FROM "interviews" WHERE ("interviews"."id" BETWEEN $start AND $end AND state = 'published') GROUP BY "interviews"."id", "interviews"."title", "interviews"."intro", "interviews"."content", "interviews"."id", "interviews"."user_id", "interviews"."hub_id", "interviews"."published_at", "interviews"."created_at", "interviews"."updated_at"
sql_query_range = SELECT COALESCE(MIN("interviews"."id"), 1), COALESCE(MAX("interviews"."id"), 1) FROM "interviews"
sql_attr_uint = sphinx_internal_id
sql_attr_uint = sphinx_deleted
sql_attr_uint = user_id
sql_attr_uint = hub_id
sql_attr_timestamp = published_at
sql_attr_timestamp = created_at
sql_attr_timestamp = updated_at
sql_attr_string = sphinx_internal_class
}

index interview_core
{
type = plain
path = /home/dmitriy/Projects/ok-2018/db/sphinx/development/interview_core
docinfo = extern
source = interview_core_0
}

source page_core_0
{
type = pgsql
sql_host = localhost
sql_user = dmitriy
sql_pass =
sql_db = open_cook_dev
sql_query_pre = SET TIME ZONE UTC
sql_query = SELECT "pages"."id" * 10 + 2 AS "id", "pages"."title" AS "title", "pages"."intro" AS "intro", "pages"."content" AS "content", "pages"."id" AS "sphinx_internal_id", 'Page' AS "sphinx_internal_class", 0 AS "sphinx_deleted", "pages"."user_id" AS "user_id", "pages"."hub_id" AS "hub_id", extract(epoch from "pages"."published_at")::int AS "published_at", extract(epoch from "pages"."created_at")::int AS "created_at", extract(epoch from "pages"."updated_at")::int AS "updated_at" FROM "pages" WHERE ("pages"."id" BETWEEN $start AND $end AND state = 'published') GROUP BY "pages"."id", "pages"."title", "pages"."intro", "pages"."content", "pages"."id", "pages"."user_id", "pages"."hub_id", "pages"."published_at", "pages"."created_at", "pages"."updated_at"
sql_query_range = SELECT COALESCE(MIN("pages"."id"), 1), COALESCE(MAX("pages"."id"), 1) FROM "pages"
sql_attr_uint = sphinx_internal_id
sql_attr_uint = sphinx_deleted
sql_attr_uint = user_id
sql_attr_uint = hub_id
sql_attr_timestamp = published_at
sql_attr_timestamp = created_at
sql_attr_timestamp = updated_at
sql_attr_string = sphinx_internal_class
}

index page_core
{
type = plain
path = /home/dmitriy/Projects/ok-2018/db/sphinx/development/page_core
docinfo = extern
source = page_core_0
}

source post_core_0
{
type = pgsql
sql_host = localhost
sql_user = dmitriy
sql_pass =
sql_db = open_cook_dev
sql_query_pre = SET TIME ZONE UTC
sql_query = SELECT "posts"."id" * 10 + 3 AS "id", "posts"."title" AS "title", "posts"."intro" AS "intro", "posts"."content" AS "content", "posts"."id" AS "sphinx_internal_id", 'Post' AS "sphinx_internal_class", 0 AS "sphinx_deleted", "posts"."user_id" AS "user_id", "posts"."hub_id" AS "hub_id", extract(epoch from "posts"."published_at")::int AS "published_at", extract(epoch from "posts"."created_at")::int AS "created_at", extract(epoch from "posts"."updated_at")::int AS "updated_at" FROM "posts" WHERE ("posts"."id" BETWEEN $start AND $end AND state = 'published') GROUP BY "posts"."id", "posts"."title", "posts"."intro", "posts"."content", "posts"."id", "posts"."user_id", "posts"."hub_id", "posts"."published_at", "posts"."created_at", "posts"."updated_at"
sql_query_range = SELECT COALESCE(MIN("posts"."id"), 1), COALESCE(MAX("posts"."id"), 1) FROM "posts"
sql_attr_uint = sphinx_internal_id
sql_attr_uint = sphinx_deleted
sql_attr_uint = user_id
sql_attr_uint = hub_id
sql_attr_timestamp = published_at
sql_attr_timestamp = created_at
sql_attr_timestamp = updated_at
sql_attr_string = sphinx_internal_class
}

index post_core
{
type = plain
path = /home/dmitriy/Projects/ok-2018/db/sphinx/development/post_core
docinfo = extern
source = post_core_0
}

source recipe_core_0
{
type = pgsql
sql_host = localhost
sql_user = dmitriy
sql_pass =
sql_db = open_cook_dev
sql_query_pre = SET TIME ZONE UTC
sql_query = SELECT "recipes"."id" * 10 + 4 AS "id", "recipes"."title" AS "title", "recipes"."intro" AS "intro", "recipes"."content" AS "content", "recipes"."id" AS "sphinx_internal_id", 'Recipe' AS "sphinx_internal_class", 0 AS "sphinx_deleted", "recipes"."user_id" AS "user_id", "recipes"."hub_id" AS "hub_id", extract(epoch from "recipes"."published_at")::int AS "published_at", extract(epoch from "recipes"."created_at")::int AS "created_at", extract(epoch from "recipes"."updated_at")::int AS "updated_at" FROM "recipes" WHERE ("recipes"."id" BETWEEN $start AND $end AND state = 'published') GROUP BY "recipes"."id", "recipes"."title", "recipes"."intro", "recipes"."content", "recipes"."id", "recipes"."user_id", "recipes"."hub_id", "recipes"."published_at", "recipes"."created_at", "recipes"."updated_at"
sql_query_range = SELECT COALESCE(MIN("recipes"."id"), 1), COALESCE(MAX("recipes"."id"), 1) FROM "recipes"
sql_attr_uint = sphinx_internal_id
sql_attr_uint = sphinx_deleted
sql_attr_uint = user_id
sql_attr_uint = hub_id
sql_attr_timestamp = published_at
sql_attr_timestamp = created_at
sql_attr_timestamp = updated_at
sql_attr_string = sphinx_internal_class
}

index recipe_core
{
type = plain
path = /home/dmitriy/Projects/ok-2018/db/sphinx/development/recipe_core
docinfo = extern
source = recipe_core_0
}

index blog
{
type = distributed
local = blog_core
}

index interview
{
type = distributed
local = interview_core
}

index page
{
type = distributed
local = page_core
}

index post
{
type = distributed
local = post_core
}

index recipe
{
type = distributed
local = recipe_core
}
Binary file added db/sphinx/development/blog_core.spa
Binary file not shown.
Binary file added db/sphinx/development/blog_core.spd
Binary file not shown.
1 change: 1 addition & 0 deletions db/sphinx/development/blog_core.spe
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file added db/sphinx/development/blog_core.sph
Binary file not shown.
Binary file added db/sphinx/development/blog_core.spi
Binary file not shown.
Empty file.
Empty file.
Empty file.
Binary file added db/sphinx/development/blog_core.spp
Binary file not shown.
Binary file added db/sphinx/development/blog_core.sps
Binary file not shown.
Binary file added db/sphinx/development/interview_core.spa
Binary file not shown.
Binary file added db/sphinx/development/interview_core.spd
Binary file not shown.
1 change: 1 addition & 0 deletions db/sphinx/development/interview_core.spe
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file added db/sphinx/development/interview_core.sph
Binary file not shown.
Binary file added db/sphinx/development/interview_core.spi
Binary file not shown.
Empty file.
Empty file.
Empty file.
Binary file added db/sphinx/development/interview_core.spp
Binary file not shown.
Binary file added db/sphinx/development/interview_core.sps
Binary file not shown.
Binary file added db/sphinx/development/page_core.spa
Binary file not shown.
Binary file added db/sphinx/development/page_core.spd
Binary file not shown.
1 change: 1 addition & 0 deletions db/sphinx/development/page_core.spe
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file added db/sphinx/development/page_core.sph
Binary file not shown.
Binary file added db/sphinx/development/page_core.spi
Binary file not shown.
Empty file.
Empty file.
Empty file.
Binary file added db/sphinx/development/page_core.spp
Binary file not shown.
Binary file added db/sphinx/development/page_core.sps
Binary file not shown.
Binary file added db/sphinx/development/post_core.spa
Binary file not shown.
Binary file added db/sphinx/development/post_core.spd
Binary file not shown.
1 change: 1 addition & 0 deletions db/sphinx/development/post_core.spe
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file added db/sphinx/development/post_core.sph
Binary file not shown.
Binary file added db/sphinx/development/post_core.spi
Binary file not shown.
Empty file.
Empty file.
Empty file.
Binary file added db/sphinx/development/post_core.spp
Binary file not shown.
Binary file added db/sphinx/development/post_core.sps
Binary file not shown.
Binary file added db/sphinx/development/recipe_core.spa
Binary file not shown.
Binary file added db/sphinx/development/recipe_core.spd
Binary file not shown.
Binary file added db/sphinx/development/recipe_core.spe
Binary file not shown.
Binary file added db/sphinx/development/recipe_core.sph
Binary file not shown.
Binary file added db/sphinx/development/recipe_core.spi
Binary file not shown.
Empty file.
Empty file.
Empty file.
Binary file added db/sphinx/development/recipe_core.spp
Binary file not shown.
Binary file added db/sphinx/development/recipe_core.sps
Binary file not shown.

0 comments on commit 746818c

Please sign in to comment.