Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support enhanced filtering on interactions query #494

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading