aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrhenium <re4k@re4k.info>2013-05-31 22:39:18 +0900
committerrhenium <re4k@re4k.info>2013-05-31 22:39:18 +0900
commita0e5995cd5b8bea5f58b88b092a2f928682e8f20 (patch)
tree6842199ff226293940c3c10e5927904a4a54e338
parent72fb7942e6ea02b7182a3442175332009d1dac14 (diff)
downloadaclog-a0e5995cd5b8bea5f58b88b092a2f928682e8f20.tar.gz
rewrite search
-rw-r--r--app/controllers/errors_controller.rb6
-rw-r--r--app/controllers/search_controller.rb135
-rw-r--r--app/views/layouts/_base.html.haml2
-rw-r--r--app/views/search/_search.html.haml4
-rw-r--r--app/views/search/search.html.haml7
-rw-r--r--app/views/tweets/_tweets.html.haml2
-rw-r--r--config/routes.rb2
7 files changed, 63 insertions, 95 deletions
diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb
index 08d7f82..86f32d9 100644
--- a/app/controllers/errors_controller.rb
+++ b/app/controllers/errors_controller.rb
@@ -6,7 +6,6 @@ class ErrorsController < ApplicationController
def render_error
@exception = env["action_dispatch.exception"]
- #@status = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code
@title = "?"
case @exception
@@ -46,10 +45,7 @@ class ErrorsController < ApplicationController
@user = @exception.user
end
- respond_to do |format|
- format.html { render status: @status }
- format.json { render status: @status }
- end
+ render status: @status
end
private
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 56aa9a0..7d94393 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,97 +1,66 @@
-# -*- coding: utf-8 -*-
-require "shellwords"
-
class SearchController < ApplicationController
def search
- @show_search = true
+ @caption = "search"
+ @tweets = Tweet.where(parse_query(params[:query])).order_by_id.reacted.list(params, force_page: true)
+ end
- # TODO: OR とか () とか対応したいよね
- unless params[:query]
- @tweets = Tweet.none
- render "tweets/_tweets"
- return
- end
- words = Shellwords.shellwords(params[:query])
- dateformat = "(20[0-9]{2})([-_\/]?)([0-9]{2})\\2([0-9]{2})" # $1: year, $3: month, $4: day
+ private
+ def parse_query(input)
+ str = input.dup
+ strings = []
+ str.gsub!(/"((?:\\"|[^"])*?)"/) {|m| strings << $1; "##{strings.size - 1}" }
+ groups = []
+ while str.sub!(/\(([^()]*?)\)/) {|m| groups << $1; "$#{groups.size - 1}" }; end
- result = words.inject(Tweet.order_by_id) do |tweets, word|
- case word
- when /^-?[a-z]+:.+$/
- # 特殊
- key, value = word.split(":", 2)
- case key.downcase
- when /^-?user$/
- user = User.where(screen_name: value).first
- if key[0] == "-"
- tweets.where("user_id != ?", user ? user.id : -1)
+ conv = -> s do
+ s.scan(/\S+(?: OR \S+)*/).map {|co|
+ co.split(" OR ").map {|token|
+ if /^\$(\d+)$/ =~ token
+ conv.call(groups[$1.to_i])
else
- tweets.where(user_id: user ? user.id : -1)
+ parse_condition(token, strings)
end
- when /^-?fav/
- search_unless_zero(tweets, "favorites_count", key[0], value)
- when /^-?re?t/
- search_unless_zero(tweets, "retweets_count", key[0], value)
- when /^-?reactions?$/
- search_unless_zero(tweets, "favorites_count + retweets_count", key[0], value)
- when "order"
- case value
- when "old", /^asc/
- tweets.order("id ASC")
- when "reaction"
- tweets.order_by_reactions
- when /^fav/
- tweets.order_by_favorites
- when /^re?t/
- tweets.order_by_retweets
- else
- tweets
- end
- when /^-?text$/
- sourcetext = word.split(":", 2).last.gsub("%", "\\%").gsub("*", "%")
- sourcetext = "%#{sourcetext}%".gsub(/%+/, "%")
- op = key[0] == "-" ? " NOT LIKE " : " LIKE "
- tweets.where("text #{op} ?", sourcetext)
- when /^-?source$/
- sourcetext = word.split(":", 2).last.gsub("%", "\\%").gsub("*", "%")
- op = key[0] == "-" ? " NOT LIKE " : " LIKE "
- tweets.where("source #{op} ? OR source #{op} ?", "<url:%:#{sourcetext.gsub(":", "%3A")}>", "<url:%:#{CGI.escape(sourcetext)}>")
- else
- # unknown command
- tweets
- end
- when /^-?#{dateformat}\.\.#{dateformat}$/
-
- since = Time.utc($1.to_i, $3.to_i, $4.to_i) - 9 * 60 * 60
- to = Time.utc($5.to_i, $7.to_i, $8.to_i + 1) - 9 * 60 * 60
-
- if word[0] == "-"
- tweets.where("id < ? OR id >= ?", first_id_of_time(since), first_id_of_time(to))
- else
- tweets.where(id: first_id_of_time(since)...first_id_of_time(to))
- end
- else
- # TODO: ツイート検索
- tweets.none
- end
+ }.inject(&:or)
+ }.inject(&:and)
end
-
- @tweets = result.list(params, force_page: true)
- render "tweets/_tweets"
+ conv.call(str)
end
- private
- def first_id_of_time(time)
- (time.to_i * 1000 - 1288834974657) << 22
- end
-
- def search_unless_zero(tweets, column, flag, value)
- num = Integer(value) rescue 0
- n = flag == "-"
+ def parse_condition(token, strings)
+ tweets = Tweet.arel_table
+ escape_text = -> str do
+ str.gsub(/#(\d+)/) { strings[$1.to_i] }
+ .gsub("%", "\\%")
+ .gsub("*", "%")
+ .gsub("_", "\\_")
+ .gsub("?", "_")
+ end
- unless num == 0
- tweets.where("#{column} #{n ? "<" : ">="} ?", num)
+ positive = token[0] != "-"
+ case token
+ when /^-?(?:user|from):([A-Za-z0-9_]{1,20})$/
+ u = User.find_by(screen_name: $1)
+ uid = u && u.id || 0
+ tweets[:user_id].__send__(positive ? :eq :not_eq, uid)
+ when /^-?date:(\d{4}(-?)\d{2}\2\d{2})(?:\.\.|-)(\d{4}\2\d{2}\2\d{2})$/ # $1: begin, $2: end
+ tweets[:id].__send__(positive ? :in : :not_in, date_to_id($1)...date_to_id($3, 1))
+ when /^-?favs?:(\d+)$/
+ tweets[:favorites_count].__send__(positive ? :gteq : :lt, $1.to_i)
+ when /^-?rts?:(\d+)$/
+ tweets[:retweets_count].__send__(positive ? :gteq : :lt, $1.to_i)
+ when /^-?(?:sum|reactions?):(\d+)$/
+ (tweets[:favorites_count] + tweets[:retweets_count]).__send__(positive ? :gteq : :lt, $1.to_i)
+ when /^(?:source|via):(.+)$/
+ source_text = "<url:%:#{escape_text.call($1).gsub(":", "%3A")}>"
+ tweets[:source].__send__(positive ? :matches : :does_not_match, source_text)
else
- tweets
+ search_text = escape_text.call(positive ? token : token[1..-1])
+ tweets[:text].__send__(positive ? :matches : :does_not_match, "%#{search_text}%")
end
end
+
+ def date_to_id(str, offset = 0)
+ time = (Date.parse(str) + offset).to_datetime
+ (time.to_i * 1000 - 1288834974657) << 22
+ end
end
diff --git a/app/views/layouts/_base.html.haml b/app/views/layouts/_base.html.haml
index 7494f89..ea416f9 100644
--- a/app/views/layouts/_base.html.haml
+++ b/app/views/layouts/_base.html.haml
@@ -14,6 +14,8 @@
%ul.nav.pull-right
%li
= link_to "about", about_path
+ %li
+ = link_to "search", search_path
- if logged_in?
%li= link_to "settings", settings_path
%li= link_to "logout", logout_path
diff --git a/app/views/search/_search.html.haml b/app/views/search/_search.html.haml
deleted file mode 100644
index 8c2b4e8..0000000
--- a/app/views/search/_search.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-.search
- = form_tag({}, {method: :get}) do
- = text_field_tag :query, params[:query]
- = submit_tag "search", name: nil
diff --git a/app/views/search/search.html.haml b/app/views/search/search.html.haml
new file mode 100644
index 0000000..6faf859
--- /dev/null
+++ b/app/views/search/search.html.haml
@@ -0,0 +1,7 @@
+.search
+ = form_tag({}, method: "get", class: "form-inline") do
+ = field_set_tag do
+ = text_field_tag :query, params[:query]
+ = submit_tag "search", class: "btn"
+= render partial: "tweets/tweets"
+
diff --git a/app/views/tweets/_tweets.html.haml b/app/views/tweets/_tweets.html.haml
index 9e9bc0e..d0030b2 100644
--- a/app/views/tweets/_tweets.html.haml
+++ b/app/views/tweets/_tweets.html.haml
@@ -1,5 +1,3 @@
-- if params[:action] == "search"
- = render partial: "search/search"
.tweets
= render partial: "tweets/tweet", collection: @tweets.includes(:user), as: :tweet
.loading
diff --git a/config/routes.rb b/config/routes.rb
index 333b96c..f92e144 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -9,7 +9,7 @@ Aclog::Application.routes.draw do
# HTML only pages
scope format: "html" do
- get "/search" => "search#search", as: "search"
+ get "/search" => "search#search", as: "search"
# Internals / SessionsController
get "/i/import/:id" => "i#import", as: "import"