Created
July 17, 2014 07:12
-
-
Save antoviaque/c43617be44c9fc5ee6df to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --cc api/search.rb | |
| index a49c2f4,a6815f3..0000000 | |
| --- a/api/search.rb | |
| +++ b/api/search.rb | |
| @@@ -1,88 -1,45 +1,103 @@@ | |
| require 'new_relic/agent/method_tracer' | |
| get "#{APIPREFIX}/search/threads" do | |
| + local_params = params # Necessary for params to be available inside blocks | |
| + sort_criteria = get_sort_criteria(local_params) | |
| - sort_key_mapper = { | |
| - "date" => :created_at, | |
| - "activity" => :last_activity_at, | |
| - "votes" => :votes_point, | |
| - "comments" => :comment_count, | |
| - } | |
| - | |
| - sort_order_mapper = { | |
| - "desc" => :desc, | |
| - "asc" => :asc, | |
| - } | |
| - | |
| - sort_key = sort_key_mapper[params["sort_key"]] | |
| - sort_order = sort_order_mapper[params["sort_order"]] | |
| - | |
| - sort_keyword_valid = (!params["sort_key"] && !params["sort_order"] || sort_key && sort_order) | |
| - | |
| - if (!params["text"] && !params["commentable_ids"]) || !sort_keyword_valid | |
| + search_text = local_params["text"] | |
| + if !search_text || !sort_criteria | |
| {}.to_json | |
| else | |
| ++<<<<<<< HEAD | |
| + page = (local_params["page"] || DEFAULT_PAGE).to_i | |
| + per_page = (local_params["per_page"] || DEFAULT_PER_PAGE).to_i | |
| + | |
| + # Because threads and comments are currently separate unrelated documents in | |
| + # Elasticsearch, we must first query for all matching documents, then | |
| + # extract the set of thread ids, and then sort the threads by the specified | |
| + # criteria and paginate. For performance reasons, we currently limit the | |
| + # number of documents considered (ordered by update recency), which means | |
| + # that matching threads can be missed if the search terms are very common. | |
| + | |
| + get_matching_thread_ids = lambda do |search_text| | |
| + self.class.trace_execution_scoped(["Custom/get_search_threads/es_search"]) do | |
| + search = Tire.search Content::ES_INDEX_NAME do | |
| + query do | |
| + match [:title, :body], search_text, :operator => "AND" | |
| + filtered do | |
| + filter :term, :commentable_id => local_params["commentable_id"] if local_params["commentable_id"] | |
| + filter :terms, :commentable_id => local_params["commentable_ids"].split(",") if local_params["commentable_ids"] | |
| + filter :term, :course_id => local_params["course_id"] if local_params["course_id"] | |
| + if local_params["group_id"] | |
| + filter :or, [ | |
| + {:not => {:exists => {:field => :group_id}}}, | |
| + {:term => {:group_id => local_params["group_id"]}} | |
| + ] | |
| + end | |
| + end | |
| + end | |
| + sort do | |
| + by "updated_at", "desc" | |
| + end | |
| + size CommentService.config["max_deep_search_comment_count"].to_i | |
| + end | |
| + thread_ids = Set.new | |
| + search.results.each do |content| | |
| + case content.type | |
| + when "comment_thread" | |
| + thread_ids.add(content.id) | |
| + when "comment" | |
| + thread_ids.add(content.comment_thread_id) | |
| + end | |
| + end | |
| + thread_ids | |
| + end | |
| + end | |
| ++======= | |
| + page = (params["page"] || DEFAULT_PAGE).to_i | |
| + per_page = (params["per_page"] || DEFAULT_PER_PAGE).to_i | |
| + # for multi commentable searching | |
| + params["commentable_ids"] = params["commentable_ids"].split(',') if params["commentable_ids"] | |
| + params["group_ids"] = params["group_ids"].split(',') if params["group_ids"] | |
| + | |
| + options = { | |
| + sort_key: sort_key, | |
| + sort_order: sort_order, | |
| + page: page, | |
| + per_page: per_page, | |
| + } | |
| ++>>>>>>> 6095f2c... Add the ability to query multiple group ids | |
| - results = CommentThread.perform_search(params, options) | |
| + # Sadly, Elasticsearch does not have a facility for computing suggestions | |
| + # with respect to a filter. It would be expensive to determine the best | |
| + # suggestion with respect to our filter parameters, so we simply re-query | |
| + # with the top suggestion. If that has no results, then we return no results | |
| + # and no correction. | |
| + thread_ids = get_matching_thread_ids.call(search_text) | |
| + corrected_text = nil | |
| + if thread_ids.empty? | |
| + suggest = Tire.suggest Content::ES_INDEX_NAME do | |
| + suggestion "" do | |
| + text search_text | |
| + phrase :_all | |
| + end | |
| + end | |
| + corrected_text = suggest.results.texts.first | |
| + thread_ids = get_matching_thread_ids.call(corrected_text) if corrected_text | |
| + corrected_text = nil if thread_ids.empty? | |
| + end | |
| - if page > results.total_pages #TODO find a better way for this | |
| - results = CommentThread.perform_search(params, options.merge(page: results.total_pages)) | |
| + results = nil | |
| + self.class.trace_execution_scoped(["Custom/get_search_threads/mongo_sort_page"]) do | |
| + results = CommentThread. | |
| + where(:id.in => thread_ids.to_a). | |
| + order_by(sort_criteria). | |
| + page(page). | |
| + per(per_page). | |
| + to_a | |
| end | |
| + total_results = thread_ids.size | |
| + num_pages = (total_results + per_page - 1) / per_page | |
| if results.length == 0 | |
| collection = [] | |
| diff --cc models/comment_thread.rb | |
| index dbbcdf7,3db513d..0000000 | |
| --- a/models/comment_thread.rb | |
| +++ b/models/comment_thread.rb | |
| @@@ -76,6 -74,121 +76,124 @@@ class CommentThread < Conten | |
| c | |
| end | |
| ++<<<<<<< HEAD | |
| ++======= | |
| + def self.perform_search(params, options={}) | |
| + | |
| + page = [1, options[:page] || 1].max | |
| + per_page = options[:per_page] || 20 | |
| + sort_key = options[:sort_key] | |
| + sort_order = options[:sort_order] | |
| + | |
| + | |
| + #GET /api/v1/search/threads?user_id=1&recursive=False&sort_key=date&│[2013-06-28 10:16:46,104][INFO ][plugins ] [Glamor] loaded [], sites [] | |
| + #text=response&sort_order=desc&course_id=HarvardX%2FHLS1xD%2FCopyright&per_page=20&api_key=PUT_YOUR_API_KE│T1GYWxzZSZzb3J0X2tleT1kYXRlJnRleHQ9cmVzcG9uc2Umc29ydF9vcmRlcj1kZXNjJmNvdXJzZV9pZA==: initialized | |
| + #Y_HERE&page=1 | |
| + | |
| + #KChugh - Unfortunately, there's no algorithmically nice way to handle pagination with | |
| + #stitching together Comments and CommentThreads, because there is no determinstic relationship | |
| + #between the ordinality of comments and threads. | |
| + #the best solution is to find all of the thread ids for matching comment hits, and union them | |
| + #with the comment thread query, however, Tire does not support ORing a query key with a term filter | |
| + | |
| + #so the 3rd best solution is to run two Tire searches (3 actually, one to query the comments, one to query the threads based on | |
| + #thread ids and the original thread search) and merge the results, uniqifying the results in the process. | |
| + | |
| + #so first, find the comment threads associated with comments that hit the query | |
| + | |
| + search = Tire::Search::Search.new 'comment_threads' | |
| + | |
| + search.query {|query| query.match [:title, :body], params["text"]} if params["text"] | |
| + search.highlight({title: { number_of_fragments: 0 } } , {body: { number_of_fragments: 0 } }, options: { tag: "<highlight>" }) | |
| + search.filter(:term, commentable_id: params["commentable_id"]) if params["commentable_id"] | |
| + search.filter(:terms, commentable_id: params["commentable_ids"]) if params["commentable_ids"] | |
| + search.filter(:term, course_id: params["course_id"]) if params["course_id"] | |
| + | |
| + group_ids = [] | |
| + group_ids.concat(params["group_ids"]) if params["group_ids"] | |
| + group_ids << params["group_id"] if params["group_id"] | |
| + | |
| + if not group_ids.empty? | |
| + search.filter :or, [ | |
| + {:not => {:exists => {:field => :group_id}}}, | |
| + {:terms => {:group_id => group_ids}} | |
| + ] | |
| + end | |
| + | |
| + search.sort {|sort| sort.by sort_key, sort_order} if sort_key && sort_order #TODO should have search option 'auto sort or sth' | |
| + | |
| + #again, b/c there is no relationship in ordinality, we cannot paginate if it's a text query | |
| + | |
| + if not params["text"] | |
| + search.size per_page | |
| + search.from per_page * (page - 1) | |
| + end | |
| + | |
| + results = search.results | |
| + | |
| + #if this is a search query, then also search the comments and harvest the matching comments | |
| + if params["text"] | |
| + | |
| + search = Tire::Search::Search.new 'comments' | |
| + search.query {|query| query.match :body, params["text"]} if params["text"] | |
| + search.filter(:term, course_id: params["course_id"]) if params["course_id"] | |
| + search.size CommentService.config["max_deep_search_comment_count"].to_i | |
| + | |
| + #unforutnately, we cannot paginate here, b/c we don't know how the ordinality is totally | |
| + #unrelated to that of threads | |
| + | |
| + c_results = comment_ids = comments = thread_ids = nil | |
| + self.class.trace_execution_scoped(['Custom/perform_search/collect_comment_search_results']) do | |
| + c_results = search.results | |
| + comment_ids = c_results.collect{|c| c.id}.uniq | |
| + end | |
| + self.class.trace_execution_scoped(['Custom/perform_search/collect_comment_thread_ids']) do | |
| + comments = Comment.where(:id.in => comment_ids) | |
| + thread_ids = comments.collect{|c| c.comment_thread_id} | |
| + end | |
| + | |
| + #thread_ids = c_results.collect{|c| c.comment_thread_id} | |
| + #as soon as we can add comment thread id to the ES index, via Tire updgrade, we'll | |
| + #use ES instead of mongo to collect the thread ids | |
| + | |
| + #use the elasticsearch index instead to avoid DB hit | |
| + | |
| + self.class.trace_execution_scoped(['Custom/perform_search/collect_unique_thread_ids']) do | |
| + original_thread_ids = results.collect{|r| r.id} | |
| + | |
| + #now add the original search thread ids | |
| + thread_ids += original_thread_ids | |
| + | |
| + thread_ids = thread_ids.uniq | |
| + end | |
| + | |
| + #now run one more search to harvest the threads and filter by group | |
| + search = Tire::Search::Search.new 'comment_threads' | |
| + search.filter(:terms, :thread_id => thread_ids) | |
| + search.filter(:terms, commentable_id: params["commentable_ids"]) if params["commentable_ids"] | |
| + search.filter(:term, course_id: params["course_id"]) if params["course_id"] | |
| + | |
| + search.size per_page | |
| + search.from per_page * (page - 1) | |
| + | |
| + if params["group_id"] | |
| + | |
| + search.filter :or, [ | |
| + {:not => {:exists => {:field => :group_id}}}, | |
| + {:term => {:group_id => params["group_id"]}} | |
| + | |
| + ] | |
| + end | |
| + | |
| + search.sort {|sort| sort.by sort_key, sort_order} if sort_key && sort_order | |
| + results = search.results | |
| + | |
| + end | |
| + | |
| + results | |
| + end | |
| + | |
| ++>>>>>>> 6095f2c... Add the ability to query multiple group ids | |
| def activity_since(from_time=nil) | |
| if from_time | |
| activities.where(:created_at => {:$gte => from_time}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment