autocomplete: remove old autocomplete endpoints.
Remove /tag/autocomplete.json and /saved_searches/labels.json.
This commit is contained in:
@@ -6,12 +6,6 @@ class SavedSearchesController < ApplicationController
|
|||||||
respond_with(@saved_searches)
|
respond_with(@saved_searches)
|
||||||
end
|
end
|
||||||
|
|
||||||
def labels
|
|
||||||
authorize SavedSearch
|
|
||||||
@labels = SavedSearch.search_labels(CurrentUser.id, params[:search]).take(params[:limit].to_i || 10)
|
|
||||||
respond_with(@labels)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@saved_search = authorize SavedSearch.new(user: CurrentUser.user, **permitted_attributes(SavedSearch))
|
@saved_search = authorize SavedSearch.new(user: CurrentUser.user, **permitted_attributes(SavedSearch))
|
||||||
@saved_search.save
|
@saved_search.save
|
||||||
|
|||||||
@@ -12,18 +12,6 @@ class TagsController < ApplicationController
|
|||||||
respond_with(@tags)
|
respond_with(@tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
def autocomplete
|
|
||||||
if CurrentUser.is_builder?
|
|
||||||
# limit rollout
|
|
||||||
@tags = TagAutocomplete.search(params[:search][:name_matches])
|
|
||||||
else
|
|
||||||
@tags = Tag.names_matches_with_aliases(params[:search][:name_matches], params.fetch(:limit, 10).to_i)
|
|
||||||
end
|
|
||||||
|
|
||||||
# XXX
|
|
||||||
respond_with(@tags.map(&:attributes))
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@tag = authorize Tag.find(params[:id])
|
@tag = authorize Tag.find(params[:id])
|
||||||
respond_with(@tag)
|
respond_with(@tag)
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
module TagAutocomplete
|
|
||||||
module_function
|
|
||||||
|
|
||||||
PREFIX_BOUNDARIES = "(_/:;-"
|
|
||||||
LIMIT = 10
|
|
||||||
|
|
||||||
Result = Struct.new(:name, :post_count, :category, :antecedent_name, :source) do
|
|
||||||
include ActiveModel::Serializers::JSON
|
|
||||||
include ActiveModel::Serializers::Xml
|
|
||||||
|
|
||||||
def attributes
|
|
||||||
(members + [:weight]).map { |x| [x.to_s, send(x)] }.to_h
|
|
||||||
end
|
|
||||||
|
|
||||||
def weight
|
|
||||||
case source
|
|
||||||
when :exact then 1.0
|
|
||||||
when :prefix then 0.8
|
|
||||||
when :alias then 0.2
|
|
||||||
when :correct then 0.1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def search(query)
|
|
||||||
query = Tag.normalize_name(query)
|
|
||||||
|
|
||||||
count_sort(
|
|
||||||
search_exact(query, 8) +
|
|
||||||
search_prefix(query, 4) +
|
|
||||||
search_correct(query, 2) +
|
|
||||||
search_aliases(query, 3)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def count_sort(words)
|
|
||||||
words.uniq(&:name).sort_by do |x|
|
|
||||||
x.post_count * x.weight
|
|
||||||
end.reverse.slice(0, LIMIT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_exact(query, n = 4)
|
|
||||||
Tag
|
|
||||||
.where_like(:name, query + "*")
|
|
||||||
.where("post_count > 0")
|
|
||||||
.order("post_count desc")
|
|
||||||
.limit(n)
|
|
||||||
.pluck(:name, :post_count, :category)
|
|
||||||
.map {|row| Result.new(*row, nil, :exact)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_correct(query, n = 2)
|
|
||||||
if query.size <= 3
|
|
||||||
return []
|
|
||||||
end
|
|
||||||
|
|
||||||
Tag
|
|
||||||
.where("name % ?", query)
|
|
||||||
.where("abs(length(name) - ?) <= 3", query.size)
|
|
||||||
.where_like(:name, query[0] + "*")
|
|
||||||
.where("post_count > 0")
|
|
||||||
.order(Arel.sql("similarity(name, #{Tag.connection.quote(query)}) DESC"))
|
|
||||||
.limit(n)
|
|
||||||
.pluck(:name, :post_count, :category)
|
|
||||||
.map {|row| Result.new(*row, nil, :correct)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_prefix(query, n = 3)
|
|
||||||
if query.size >= 5
|
|
||||||
return []
|
|
||||||
end
|
|
||||||
|
|
||||||
if query.size <= 1
|
|
||||||
return []
|
|
||||||
end
|
|
||||||
|
|
||||||
if query =~ /[-_()]/
|
|
||||||
return []
|
|
||||||
end
|
|
||||||
|
|
||||||
if query.size >= 3
|
|
||||||
min_post_count = 0
|
|
||||||
else
|
|
||||||
min_post_count = 5_000
|
|
||||||
n += 2
|
|
||||||
end
|
|
||||||
|
|
||||||
regexp = "([a-z0-9])[a-z0-9']*($|[^a-z0-9']+)"
|
|
||||||
Tag
|
|
||||||
.where('regexp_replace(name, ?, ?, ?) like ?', regexp, '\1', 'g', query.to_escaped_for_sql_like + '%')
|
|
||||||
.where("post_count > ?", min_post_count)
|
|
||||||
.where("post_count > 0")
|
|
||||||
.order("post_count desc")
|
|
||||||
.limit(n)
|
|
||||||
.pluck(:name, :post_count, :category)
|
|
||||||
.map {|row| Result.new(*row, nil, :prefix)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_aliases(query, n = 10)
|
|
||||||
wildcard_name = query + "*"
|
|
||||||
TagAlias
|
|
||||||
.select("tags.name, tags.post_count, tags.category, tag_aliases.antecedent_name")
|
|
||||||
.joins("INNER JOIN tags ON tags.name = tag_aliases.consequent_name")
|
|
||||||
.where_like(:antecedent_name, wildcard_name)
|
|
||||||
.active
|
|
||||||
.where_not_like("tags.name", wildcard_name)
|
|
||||||
.where("tags.post_count > 0")
|
|
||||||
.order("tags.post_count desc")
|
|
||||||
.limit(n)
|
|
||||||
.pluck(:name, :post_count, :category, :antecedent_name)
|
|
||||||
.map {|row| Result.new(*row, :alias)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -71,19 +71,6 @@ class SavedSearch < ApplicationRecord
|
|||||||
all_labels.select { |ss| ss.label.ilike?(label) }.map(&:label)
|
all_labels.select { |ss| ss.label.ilike?(label) }.map(&:label)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_labels(user_id, params)
|
|
||||||
labels = labels_for(user_id)
|
|
||||||
|
|
||||||
if params[:label].present?
|
|
||||||
query = Regexp.escape(params[:label]).gsub("\\*", ".*")
|
|
||||||
query = ".*#{query}.*" unless query.include?("*")
|
|
||||||
query = /\A#{query}\z/
|
|
||||||
labels = labels.grep(query)
|
|
||||||
end
|
|
||||||
|
|
||||||
labels
|
|
||||||
end
|
|
||||||
|
|
||||||
def labels_for(user_id)
|
def labels_for(user_id)
|
||||||
SavedSearch
|
SavedSearch
|
||||||
.where(user_id: user_id)
|
.where(user_id: user_id)
|
||||||
|
|||||||
@@ -215,21 +215,13 @@ Rails.application.routes.draw do
|
|||||||
resource :related_tag, :only => [:show, :update]
|
resource :related_tag, :only => [:show, :update]
|
||||||
resources :recommended_posts, only: [:index]
|
resources :recommended_posts, only: [:index]
|
||||||
resources :robots, only: [:index]
|
resources :robots, only: [:index]
|
||||||
resources :saved_searches, :except => [:show] do
|
resources :saved_searches, :except => [:show]
|
||||||
collection do
|
|
||||||
get :labels
|
|
||||||
end
|
|
||||||
end
|
|
||||||
resource :session, only: [:new, :create, :destroy] do
|
resource :session, only: [:new, :create, :destroy] do
|
||||||
get :sign_out, on: :collection
|
get :sign_out, on: :collection
|
||||||
end
|
end
|
||||||
resource :source, :only => [:show]
|
resource :source, :only => [:show]
|
||||||
resource :status, only: [:show], controller: "status"
|
resource :status, only: [:show], controller: "status"
|
||||||
resources :tags do
|
resources :tags
|
||||||
collection do
|
|
||||||
get :autocomplete
|
|
||||||
end
|
|
||||||
end
|
|
||||||
resources :tag_aliases, only: [:show, :index, :destroy]
|
resources :tag_aliases, only: [:show, :index, :destroy]
|
||||||
resources :tag_implications, only: [:show, :index, :destroy]
|
resources :tag_implications, only: [:show, :index, :destroy]
|
||||||
resources :uploads do
|
resources :uploads do
|
||||||
|
|||||||
@@ -16,13 +16,6 @@ class SavedSearchesControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "labels action" do
|
|
||||||
should "render" do
|
|
||||||
get_auth labels_saved_searches_path, @user, as: :json
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "create action" do
|
context "create action" do
|
||||||
should "render" do
|
should "render" do
|
||||||
post_auth saved_searches_path, @user, params: { saved_search: { query: "bkub", label_string: "artist" }}
|
post_auth saved_searches_path, @user, params: { saved_search: { query: "bkub", label_string: "artist" }}
|
||||||
|
|||||||
@@ -76,20 +76,6 @@ class TagsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "autocomplete action" do
|
|
||||||
should "render" do
|
|
||||||
get autocomplete_tags_path, params: { search: { name_matches: "t" }, format: :json }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
should "respect the only param" do
|
|
||||||
get autocomplete_tags_path, params: { search: { name_matches: "t", only: "name" }, format: :json }
|
|
||||||
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "touhou", response.parsed_body.first["name"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "show action" do
|
context "show action" do
|
||||||
should "render" do
|
should "render" do
|
||||||
get tag_path(@tag)
|
get tag_path(@tag)
|
||||||
|
|||||||
@@ -44,19 +44,6 @@ class SavedSearchTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".search_labels" do
|
|
||||||
setup do
|
|
||||||
FactoryBot.create(:tag_alias, antecedent_name: "bbb", consequent_name: "ccc", creator: @user)
|
|
||||||
FactoryBot.create(:saved_search, user: @user, label_string: "blah", query: "aaa")
|
|
||||||
FactoryBot.create(:saved_search, user: @user, label_string: "blahbling", query: "CCC BBB AAA")
|
|
||||||
FactoryBot.create(:saved_search, user: @user, label_string: "qux", query: " aaa bbb ccc ")
|
|
||||||
end
|
|
||||||
|
|
||||||
should "fetch the queries used by a user for a label" do
|
|
||||||
assert_equal(%w(blah blahbling), SavedSearch.search_labels(@user.id, label: "blah"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context ".post_ids_for" do
|
context ".post_ids_for" do
|
||||||
context "with a label" do
|
context "with a label" do
|
||||||
setup do
|
setup do
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class TagAutocompleteTest < ActiveSupport::TestCase
|
|
||||||
subject { TagAutocomplete }
|
|
||||||
|
|
||||||
context "#search" do
|
|
||||||
should "be case insensitive" do
|
|
||||||
create(:tag, name: "abcdef", post_count: 1)
|
|
||||||
assert_equal(["abcdef"], subject.search("A").map(&:name))
|
|
||||||
end
|
|
||||||
|
|
||||||
should "not return duplicates" do
|
|
||||||
create(:tag, name: "red_eyes", post_count: 5001)
|
|
||||||
assert_equal(%w[red_eyes], subject.search("re").map(&:name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "#search_exact" do
|
|
||||||
setup do
|
|
||||||
@tags = [
|
|
||||||
create(:tag, name: "abcdef", post_count: 1),
|
|
||||||
create(:tag, name: "abczzz", post_count: 2),
|
|
||||||
create(:tag, name: "abcyyy", post_count: 0),
|
|
||||||
create(:tag, name: "bbbbbb")
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "find the tags" do
|
|
||||||
expected = [
|
|
||||||
@tags[1],
|
|
||||||
@tags[0]
|
|
||||||
].map(&:name)
|
|
||||||
assert_equal(expected, subject.search_exact("abc", 3).map(&:name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "#search_correct" do
|
|
||||||
setup do
|
|
||||||
CurrentUser.stubs(:id).returns(1)
|
|
||||||
|
|
||||||
@tags = [
|
|
||||||
create(:tag, name: "abcde", post_count: 1),
|
|
||||||
create(:tag, name: "abcdz", post_count: 2),
|
|
||||||
|
|
||||||
# one char mismatch
|
|
||||||
create(:tag, name: "abcez", post_count: 2),
|
|
||||||
|
|
||||||
# too long
|
|
||||||
create(:tag, name: "abcdefghijk", post_count: 2),
|
|
||||||
|
|
||||||
# wrong prefix
|
|
||||||
create(:tag, name: "bbcdef", post_count: 2),
|
|
||||||
|
|
||||||
# zero post count
|
|
||||||
create(:tag, name: "abcdy", post_count: 0),
|
|
||||||
|
|
||||||
# completely different
|
|
||||||
create(:tag, name: "bbbbb")
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "find the tags" do
|
|
||||||
expected = [
|
|
||||||
@tags[0],
|
|
||||||
@tags[1],
|
|
||||||
@tags[2]
|
|
||||||
].map(&:name)
|
|
||||||
assert_equal(expected, subject.search_correct("abcd", 3).map(&:name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "#search_prefix" do
|
|
||||||
setup do
|
|
||||||
@tags = [
|
|
||||||
create(:tag, name: "abcdef", post_count: 1),
|
|
||||||
create(:tag, name: "alpha_beta_cat", post_count: 2),
|
|
||||||
create(:tag, name: "alpha_beta_dat", post_count: 0),
|
|
||||||
create(:tag, name: "alpha_beta_(cane)", post_count: 2),
|
|
||||||
create(:tag, name: "alpha_beta/cane", post_count: 2)
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "find the tags" do
|
|
||||||
expected = [
|
|
||||||
@tags[1],
|
|
||||||
@tags[3],
|
|
||||||
@tags[4]
|
|
||||||
].map(&:name)
|
|
||||||
assert_equal(expected, subject.search_prefix("abc", 3).map(&:name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "#search_aliases" do
|
|
||||||
setup do
|
|
||||||
@user = create(:user)
|
|
||||||
@tags = [
|
|
||||||
create(:tag, name: "/abc", post_count: 0),
|
|
||||||
create(:tag, name: "abcdef", post_count: 1),
|
|
||||||
create(:tag, name: "zzzzzz", post_count: 1)
|
|
||||||
]
|
|
||||||
as(@user) do
|
|
||||||
@aliases = [
|
|
||||||
create(:tag_alias, antecedent_name: "/abc", consequent_name: "abcdef", status: "active")
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
should "find the tags" do
|
|
||||||
results = subject.search_aliases("/abc", 3)
|
|
||||||
assert_equal(1, results.size)
|
|
||||||
assert_equal("abcdef", results[0].name)
|
|
||||||
assert_equal("/abc", results[0].antecedent_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user