Skip to content

Commit

Permalink
feat: support enhanced filtering on interactions query (#494)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsstevenson authored Apr 26, 2024
1 parent 35f779b commit 185f899
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 2 deletions.
62 changes: 62 additions & 0 deletions server/app/graphql/resolvers/interactions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'search_object'
require 'search_object/plugin/graphql'

class Resolvers::Interactions < GraphQL::Schema::Resolver
include SearchObject.module(:graphql)

type Types::InteractionType.connection_type, null: true

scope { Interaction.all }

option(:drug_names, type: [String], description: 'Filters interactions to only include those involving any of the specified drug names. This field accepts a list of drug names, which are case-insensitive, and returns interactions where the associated drug\'s name matches any name in the provided list.') do |scope, value|
if drug_names.nil? || drug_names.length == 0
scope
else
names = value.map { |v| v.upcase }
scope.joins(:drug).where(drugs: {name: names})
end
end

option(:gene_names, type: [String], description: 'Filters interactions to only include those involving any of the specified gene names. This field accepts a list of gene names, which are case-insensitive, and returns interactions where the gene\'s name matches any name in the provided list.') do |scope, value|
if gene_names.nil? || gene_names.length == 0
scope
else
names = value.map { |v| v.upcase }
scope.joins(:gene).where(genes: {name: names})
end
end

option(:drug_concept_ids, type: [String], description: 'Filters interactions to only include those involving any of the specified drug concept IDs. This field accepts a list of drug concept IDs, which are case-insensitive, and returns interactions where the associated drug\'s concept ID matches any ID in the provided list.') do |scope, value|
if drug_concept_ids.nil? || drug_concept_ids.length == 0
scope
else
concept_ids = value.map { |v| v.upcase }
scope.joins(:drug).where(drugs: {concept_id: concept_ids})
end
end

option(:gene_concept_ids, type: [String], description: 'Filters interactions to only include those involving any of the specified gene concept IDs. This field accepts a list of gene concept IDs, which are case-insensitive, and returns interactions where the associated gene\'s concept ID matches any ID in the provided list.') do |scope, value|
if gene_concept_ids.nil? || gene_concept_ids.length == 0
scope
else
concept_ids = value.map { |v| v.upcase }
scope.joins(:gene).where(genes: {concept_id: concept_ids})
end
end

option(:approved, type: Boolean, description: 'Filters interactions to include only those involving drugs with the specified approval status. Setting this to `true` returns interactions associated with approved drugs, while `false` returns interactions with drugs that are not known to be approved') do |scope, value|
scope.joins(:drug).where(drugs: {approved: value})
end

option(:immunotherapy, type: Boolean, description: 'Filters interactions based on whether the involved drugs are classified as immunotherapies. Set this to `true` to retrieve interactions involving immunotherapy drugs, or `false` to retrieve interactions involving non-immunotherapeutics') do |scope, value|
scope.joins(:drug).where(drugs: {immunotherapy: value})
end

option(:anti_neoplastic, type: Boolean, description: 'Filters interactions based on whether the involved drugs are classified as antineoplastics. Use `true` to include interactions involving antineoplastic drugs, or `false` to include those involving non-antineoplastic drugs') do |scope, value|
scope.joins(:drug).where(drugs: {anti_neoplastic: value})
end

option(:sources, type: [String], description: 'Filters interactions to include only those provided by the specified sources. This filter accepts a list of source names and returns interactions that have at least one matching source in the list') do |scope, value|
scope.joins(:sources).where(sources: {source_db_name: value})
end
end
3 changes: 2 additions & 1 deletion server/app/graphql/types/query_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class QueryType < Types::BaseObject
field :sources, resolver: Resolvers::Sources
field :categories, resolver: Resolvers::Categories
field :interaction_claim_types, resolver: Resolvers::InteractionClaimTypes
field :interactions, resolver: Resolvers::Interactions

field :service_info, Types::MetaType, null: false

Expand Down Expand Up @@ -317,7 +318,7 @@ def interaction_claim(id:)
end

field :interaction, Types::InteractionType, null: true do
description "An interaction"
description "An interaction between a drug and a gene"
argument :id, ID, required: true
end

Expand Down
2 changes: 1 addition & 1 deletion server/spec/factories/interaction.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FactoryBot.define do
factory :interaction do
sequence(:id) { |i| "DRUG #{i}" } # should always be uppercase
sequence(:id) { |i| "INTERACTION #{i}" } # should always be uppercase
gene
drug
score { rand }
Expand Down
51 changes: 51 additions & 0 deletions server/spec/queries/interactions_queries_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'rails_helper'

RSpec.describe 'Interactions queries', type: :graphql do
before(:example) do
@drug1 = create(:drug)
@drug2 = create(:drug)
@drug3 = create(:drug)

@gene1 = create(:gene)
@gene2 = create(:gene)

@int1 = create(:interaction, drug: @drug1, gene: @gene1)
@int2 = create(:interaction, drug: @drug1, gene: @gene2)
@int3 = create(:interaction, drug: @drug2, gene: @gene1)
@int4 = create(:interaction, drug: @drug3, gene: @gene1)
end

let :interactions_name_query do
<<-GRAPHQL
query interactions($drugNames: [String!], $geneNames: [String!]) {
interactions(drugNames: $drugNames, geneNames: $geneNames) {
edges {
node {
id
}
}
}
}
GRAPHQL
end

it 'should execute basic interactions searches by name correctly' do
result = execute_graphql(interactions_name_query, variables: { drugNames: [@drug1.name] })
expect(result["data"]["interactions"]["edges"].length).to eq 2

result = execute_graphql(interactions_name_query, variables: { drugNames: [@drug1.name], geneNames: [@gene1.name]})
expect(result["data"]["interactions"]["edges"].length).to eq 1

result = execute_graphql(interactions_name_query, variables: { drugNames: [@drug1.name] , geneNames: []})
expect(result["data"]["interactions"]["edges"].length).to eq 2

result = execute_graphql(interactions_name_query, variables: { drugNames: [], geneNames: [@gene1.name] })
expect(result["data"]["interactions"]["edges"].length).to eq 3

result = execute_graphql(interactions_name_query, variables: { drugNames: [], geneNames: [@gene2.name] })
expect(result["data"]["interactions"]["edges"].length).to eq 1

result = execute_graphql(interactions_name_query, variables: { drugNames: [@drug1.name, @drug2.name, @drug3.name], geneNames: [@gene1.name, @gene2.name] })
expect(result["data"]["interactions"]["edges"].length).to eq 4
end
end

0 comments on commit 185f899

Please sign in to comment.