132 lines
3.4 KiB
Ruby
132 lines
3.4 KiB
Ruby
require_dependency 'enum'
|
|
|
|
class SearchLog < ActiveRecord::Base
|
|
validates_presence_of :term, :ip_address
|
|
|
|
def self.search_types
|
|
@search_types ||= Enum.new(
|
|
header: 1,
|
|
full_page: 2
|
|
)
|
|
end
|
|
|
|
def self.search_result_types
|
|
@search_result_types ||= Enum.new(
|
|
topic: 1,
|
|
user: 2,
|
|
category: 3,
|
|
tag: 4
|
|
)
|
|
end
|
|
|
|
def self.log(term:, search_type:, ip_address:, user_id: nil)
|
|
|
|
search_type = search_types[search_type]
|
|
return [:error] unless search_type.present? && ip_address.present?
|
|
|
|
update_sql = <<~SQL
|
|
UPDATE search_logs
|
|
SET term = :term,
|
|
created_at = :created_at
|
|
WHERE created_at > :timeframe AND
|
|
position(term IN :term) = 1 AND
|
|
((:user_id IS NULL AND ip_address = :ip_address) OR
|
|
(user_id = :user_id))
|
|
RETURNING id
|
|
SQL
|
|
|
|
rows = exec_sql(
|
|
update_sql,
|
|
term: term,
|
|
created_at: Time.zone.now,
|
|
timeframe: 5.seconds.ago,
|
|
user_id: user_id,
|
|
ip_address: ip_address
|
|
)
|
|
|
|
if rows.cmd_tuples == 0
|
|
result = create(
|
|
term: term,
|
|
search_type: search_type,
|
|
ip_address: ip_address,
|
|
user_id: user_id
|
|
)
|
|
[:created, result.id]
|
|
else
|
|
[:updated, rows[0]['id'].to_i]
|
|
end
|
|
end
|
|
|
|
def self.term_details(term, period = :weekly, search_type = :all)
|
|
details = []
|
|
|
|
result = SearchLog.select("COUNT(*) AS count, created_at::date AS date")
|
|
.where('term LIKE ?', term)
|
|
.where('created_at > ?', start_of(period))
|
|
|
|
result = result.where('search_type = ?', search_types[search_type]) if search_type == :header || search_type == :full_page
|
|
result = result.where('search_result_id IS NOT NULL') if search_type == :click_through_only
|
|
|
|
result.group(:term)
|
|
.order("date")
|
|
.group("date")
|
|
.each do |record|
|
|
details << { x: Date.parse(record['date'].to_s), y: record['count'] }
|
|
end
|
|
|
|
return {
|
|
type: "search_log_term",
|
|
title: I18n.t("search_logs.graph_title"),
|
|
data: details
|
|
}
|
|
end
|
|
|
|
def self.trending(period = :all, search_type = :all)
|
|
result = SearchLog.select("term,
|
|
COUNT(*) AS searches,
|
|
SUM(CASE
|
|
WHEN search_result_id IS NOT NULL THEN 1
|
|
ELSE 0
|
|
END) AS click_through,
|
|
COUNT(DISTINCT ip_address) AS unique")
|
|
.where('created_at > ?', start_of(period))
|
|
|
|
result = result.where('search_type = ?', search_types[search_type]) unless search_type == :all
|
|
result = result.group(:term)
|
|
.order('COUNT(DISTINCT ip_address) DESC, COUNT(*) DESC')
|
|
.limit(100).to_a
|
|
end
|
|
|
|
def self.start_of(period)
|
|
case period
|
|
when :yearly then 1.year.ago
|
|
when :monthly then 1.month.ago
|
|
when :quarterly then 3.months.ago
|
|
when :weekly then 1.week.ago
|
|
when :daily then 1.day.ago
|
|
else 1000.years.ago
|
|
end
|
|
end
|
|
|
|
def self.clean_up
|
|
search_id = SearchLog.order(:id).offset(SiteSetting.search_query_log_max_size).limit(1).pluck(:id)
|
|
if search_id.present?
|
|
SearchLog.where('id < ?', search_id[0]).delete_all
|
|
end
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: search_logs
|
|
#
|
|
# id :integer not null, primary key
|
|
# term :string not null
|
|
# user_id :integer
|
|
# ip_address :inet not null
|
|
# search_result_id :integer
|
|
# search_type :integer not null
|
|
# created_at :datetime not null
|
|
# search_result_type :integer
|
|
#
|