Skip to content

Commit dac441a

Browse files
authored
Merge pull request #1226 from ctti-clinicaltrials/feat-search-results
Studies by Search Term Results
2 parents 2950022 + 535fad5 commit dac441a

File tree

7 files changed

+156
-1
lines changed

7 files changed

+156
-1
lines changed

app/models/search_term.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class SearchTerm < ActiveRecord::Base
2+
self.table_name = "ctgov.search_terms"
3+
4+
before_save :normalize_term
5+
has_many :search_term_results
6+
validates :term, presence: true, uniqueness: true
7+
attribute :group, :string, default: 'other'
8+
9+
private
10+
11+
def normalize_term
12+
self.term = term.downcase
13+
self.group = group.downcase
14+
end
15+
end

app/models/search_term_result.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class SearchTermResult < ActiveRecord::Base
2+
self.table_name = "ctgov.search_term_results"
3+
4+
belongs_to :search_term
5+
belongs_to :study, foreign_key: :nct_id, primary_key: :nct_id
6+
7+
validates :nct_id, presence: true
8+
validates :search_term_id, presence: true
9+
validates :nct_id, uniqueness: { scope: :search_term_id }
10+
end

app/models/study_relationship.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ def self.blacklist
4949
ctgov_metadata_snapshots
5050
ctgov_mapping_snapshots
5151
ctgov_schema_snapshots
52+
search_terms
53+
search_term_results
5254
)
5355
end
5456

app/services/ct_gov/api_client/v2.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ def nct_id_path
1414
["protocolSection", "identificationModule", "nctId"]
1515
end
1616

17-
def fetch_studies(range: nil, nct_ids: nil, page_size: nil)
17+
# TODO: Make this method private eventually
18+
def fetch_studies(range: nil, search: nil, nct_ids: nil, page_size: nil, nct_ids_only: false)
1819
page_token = nil
1920
total_count = 0
2021
total_fetched = 0
2122

2223
params = { pageSize: page_size, countTotal: true }
2324
params["query.term"] = range
2425
params["filter.ids"] = nct_ids
26+
params["query.cond"] = search
27+
params["fields"] = "NCTId" if nct_ids_only
2528

2629
loop do
2730
params[:pageToken] = page_token
@@ -64,6 +67,12 @@ def get_studies_by_nct_ids(list:, page_size: 50)
6467
end
6568
end
6669

70+
def search_studies(query:, page_size: nil)
71+
fetch_studies(search: query, page_size: page_size, nct_ids_only: true) do |studies|
72+
yield studies
73+
end
74+
end
75+
6776
private
6877

6978
def build_date_range_query(start_date:, end_date: nil)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
module CTGov
2+
class SearchResultsService
3+
def initialize(api_client = CTGov::ApiClient::V2.new)
4+
unless api_client.is_a?(CTGov::ApiClient::Base)
5+
raise ArgumentError, "Invalid API client. Must inherit from CTGov::ApiClient::Base"
6+
end
7+
@api_client = api_client
8+
end
9+
10+
def fetch_studies_for(search_term, group: nil, page_size: 1000)
11+
puts "Fetching search results for: #{search_term}"
12+
13+
attributes = { term: search_term.downcase }
14+
attributes[:group] = group if group.present?
15+
search_term_record = SearchTerm.find_by(term: attributes[:term]) || SearchTerm.create!(attributes)
16+
17+
@api_client.search_studies(query: search_term, page_size: page_size) do |studies|
18+
persist(studies, search_term_record)
19+
end
20+
end
21+
22+
def refresh_search_results_for(group)
23+
raise ArgumentError, "Group parameter is required" if group.blank?
24+
25+
terms = SearchTerm.where(group: group.downcase)
26+
Rails.logger.info("Refreshing #{terms.count} terms for group: #{group}")
27+
28+
terms.each do |term|
29+
fetch_studies_for(term.term, group: term.group)
30+
end
31+
32+
end
33+
34+
private
35+
36+
def persist(studies, search_term)
37+
silence_active_record do
38+
nct_ids = studies.map { |study| study.dig(*@api_client.nct_id_path) }.compact
39+
valid_nct_ids = Study.where(nct_id: nct_ids).pluck(:nct_id)
40+
41+
study_results = valid_nct_ids.map do |nct_id|
42+
{
43+
nct_id: nct_id,
44+
search_term_id: search_term.id,
45+
created_at: Time.current,
46+
updated_at: Time.current
47+
}
48+
end
49+
50+
# current logic: remove all existing search results for the term and insert fresh results
51+
ActiveRecord::Base.transaction do
52+
SearchTermResult.where(search_term_id: search_term.id).delete_all
53+
return if study_results.empty?
54+
SearchTermResult.insert_all(study_results)
55+
end
56+
57+
Rails.logger.info("Imported #{study_results.size} search results for term: #{search_term.term}")
58+
end
59+
rescue => e
60+
Rails.logger.error("Error persisting search results: #{e.message}")
61+
raise
62+
end
63+
end
64+
65+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class CreateSearchResultsTables < ActiveRecord::Migration[6.0]
2+
def change
3+
create_table "ctgov.search_terms" do |t|
4+
t.string :term, null: false
5+
t.string :group, null: true
6+
t.timestamps null: false
7+
end
8+
9+
add_index "ctgov.search_terms", :term, unique: true,
10+
name: "index_ctgov_search_terms_on_term"
11+
12+
create_table "ctgov.search_term_results" do |t|
13+
t.string :nct_id, null: false
14+
t.references :search_term, foreign_key: { to_table: "ctgov.search_terms" }, null: false
15+
t.timestamps null: false
16+
end
17+
18+
add_index "ctgov.search_term_results", [:nct_id, :search_term_id],
19+
unique: true,
20+
name: "index_ctgov_search_term_results_on_nct_id_and_search_term_id"
21+
end
22+
end

lib/tasks/search_results.rake

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# require_relative 'application_record'
2+
require_relative '../../app/models/search_term_result'
3+
4+
namespace :search_results do
5+
desc 'Fetch and persist studies for a search term'
6+
task :fetch, [:term, :group] => :environment do |t, args|
7+
fail "Term is required. Group is optional" unless args[:term]
8+
9+
service = CTGov::SearchResultsService.new
10+
begin
11+
service.fetch_studies_for(args[:term], group: args[:group])
12+
puts "✅ Successfully fetched studies for term: #{args[:term]}"
13+
rescue => e
14+
puts "❌ Error fetching studies: #{e.message}"
15+
raise e
16+
end
17+
end
18+
19+
desc 'Refresh search results for a group'
20+
task :refresh, [:group] => :environment do |t, args|
21+
fail "Group is required" unless args[:group]
22+
23+
service = CTGov::SearchResultsService.new
24+
begin
25+
service.refresh_search_results_for(args[:group])
26+
puts "✅ Successfully refreshed search results for group: #{args[:group]}"
27+
rescue => e
28+
puts "❌ Error refreshing search results: #{e.message}"
29+
raise e
30+
end
31+
end
32+
end

0 commit comments

Comments
 (0)