Skip to content
Open
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
245 changes: 172 additions & 73 deletions config/sparql_queries.yml
Original file line number Diff line number Diff line change
@@ -1,109 +1,208 @@
---
# SPARQL Example Queries
# Format:
# query_name:
# title: "Human readable title"
# description: "Optional description"
# query: |
# SPARQL query here
# SPARQL Example Queries for SEEK
# Uses JERM ontology: http://jermontology.org/ontology/JERMOntology#
# Prefixes: jerm = http://jermontology.org/ontology/JERMOntology#
# dcterms = http://purl.org/dc/terms/
# foaf = http://xmlns.com/foaf/0.1/

basic_exploration:
title: "List all classes"
description: "Find all RDF classes used in the dataset"
investigations:
title: "List all investigations"
description: "Find all investigations with their titles"
query: |
SELECT DISTINCT ?class
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?investigation ?title
WHERE {
[] a ?class
?investigation a jerm:Investigation ;
dcterms:title ?title .
}
LIMIT 20

basic_triples:
title: "Sample triples"
description: "Show basic subject-predicate-object patterns"
studies:
title: "List all studies"
description: "Find all studies and which investigation they belong to"
query: |
SELECT ?subject ?predicate ?object
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?study ?title ?investigation
WHERE {
?subject ?predicate ?object .
?study a jerm:Study ;
dcterms:title ?title ;
jerm:isPartOf ?investigation .
}
LIMIT 10

properties:
title: "List all properties"
description: "Find all predicates/properties used"
assays:
title: "List all assays"
description: "Find all assays with their type and parent study"
query: |
SELECT DISTINCT ?property
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?assay ?title ?type ?study
WHERE {
[] ?property []
?assay a ?class ;
dcterms:title ?title ;
jerm:isPartOf ?study .
FILTER(?class IN (jerm:Experimental_assay, jerm:Modelling_analysis))
OPTIONAL { ?assay jerm:hasType ?type }
}
LIMIT 20

count_by_type:
title: "Count entities by type"
description: "Count how many entities exist for each class"
data_files:
title: "List all data files"
description: "Find all data files with their titles and associated assays"
query: |
SELECT ?type (COUNT(?entity) as ?count)
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?datafile ?title ?assay
WHERE {
?entity a ?type
?datafile a jerm:Data ;
dcterms:title ?title .
OPTIONAL { ?datafile jerm:isPartOf ?assay }
}
GROUP BY ?type
ORDER BY DESC(?count)

describe_example:
title: "Describe a resource"
description: "Get all information about a specific resource (replace with actual URI)"
models:
title: "List all models"
description: "Find all models with their titles and associated assays"
query: |
DESCRIBE <http://example.org/resource>
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?model ?title ?assay
WHERE {
?model a jerm:Model ;
dcterms:title ?title .
OPTIONAL { ?model jerm:isPartOf ?assay }
}

organisms:
title: "Find organisms"
description: "Look for organism-related data"
sops:
title: "List all SOPs"
description: "Find all standard operating procedures"
query: |
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?sop ?title ?assay
WHERE {
?sop a jerm:SOP ;
dcterms:title ?title .
OPTIONAL { ?sop jerm:isPartOf ?assay }
}

publications:
title: "List all publications"
description: "Find all publications and their associated assays"
query: |
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?publication ?title ?assay
WHERE {
?publication a jerm:Publication ;
dcterms:title ?title .
OPTIONAL { ?publication jerm:isPartOf ?assay }
}

people:
title: "List all people"
description: "Find all people with their names and associated projects"
query: |
SELECT DISTINCT ?organism ?label
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?person ?name ?project
WHERE {
?organism a ?type .
FILTER(CONTAINS(LCASE(STR(?type)), "organism") ||
CONTAINS(LCASE(STR(?type)), "species") ||
CONTAINS(LCASE(STR(?type)), "taxon"))
OPTIONAL { ?organism rdfs:label ?label }
?person a jerm:Person ;
foaf:name ?name .
OPTIONAL { ?person jerm:isAssociatedWith ?project }
}
LIMIT 10

projects:
title: "Find projects"
description: "Look for project-related data"
title: "List all projects"
description: "Find all projects with their people and investigations"
query: |
SELECT DISTINCT ?project ?title
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?project ?title ?person
WHERE {
?project a ?type .
FILTER(CONTAINS(LCASE(STR(?type)), "project"))
OPTIONAL { ?project dc:title ?title }
OPTIONAL { ?project dcterms:title ?title }
OPTIONAL { ?project rdfs:label ?title }
?project a jerm:Project ;
dcterms:title ?title .
OPTIONAL { ?project jerm:isAssociatedWith ?person }
}
LIMIT 10

studies:
title: "Find studies"
description: "Look for study-related data"
organisms:
title: "List all organisms"
description: "Find all organisms with their NCBI taxonomy IDs"
query: |
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?organism ?title ?ncbi
WHERE {
?organism a jerm:Organism ;
dcterms:title ?title .
OPTIONAL { ?organism jerm:NCBI_ID ?ncbi }
}

samples:
title: "List all samples"
description: "Find all samples and which assay they belong to"
query: |
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?sample ?title ?assay
WHERE {
?sample a jerm:Sample ;
dcterms:title ?title .
OPTIONAL { ?sample jerm:hasPart ?assay }
}

strains:
title: "List all strains"
description: "Find all strains with their provider IDs and organism links"
query: |
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?strain ?title ?providerID
WHERE {
?strain a jerm:Strain ;
dcterms:title ?title .
OPTIONAL { ?strain jerm:externalSupplierID ?providerID }
}

isa_graph:
title: "Full ISA graph"
description: "Show the complete Investigation > Study > Assay hierarchy"
query: |
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?investigationTitle ?studyTitle ?assayTitle
WHERE {
?investigation a jerm:Investigation ;
dcterms:title ?investigationTitle ;
jerm:hasPart ?study .
?study dcterms:title ?studyTitle ;
jerm:hasPart ?assay .
?assay dcterms:title ?assayTitle .
}

assets_by_person:
title: "Assets created by a person"
description: "Find all assets that a specific person has created"
query: |
SELECT DISTINCT ?study ?title
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?personName ?asset ?assetTitle
WHERE {
?study a ?type .
FILTER(CONTAINS(LCASE(STR(?type)), "study"))
OPTIONAL { ?study dc:title ?title }
OPTIONAL { ?study dcterms:title ?title }
OPTIONAL { ?study rdfs:label ?title }
?person a jerm:Person ;
foaf:name ?personName ;
jerm:isCreatorOf ?asset .
?asset dcterms:title ?assetTitle .
}
LIMIT 10
ORDER BY ?personName

graph_exploration:
title: "List named graphs"
description: "Show all named graphs in the dataset"
count_by_type:
title: "Count entities by type"
description: "Count how many entities exist for each JERM class"
query: |
SELECT DISTINCT ?graph
PREFIX jerm: <http://jermontology.org/ontology/JERMOntology#>
SELECT ?type (COUNT(?entity) as ?count)
WHERE {
GRAPH ?graph {
?s ?p ?o
}
?entity a ?type .
FILTER(STRSTARTS(STR(?type), STR(jerm:)))
}
LIMIT 10
GROUP BY ?type
ORDER BY DESC(?count)
66 changes: 66 additions & 0 deletions test/integration/sparql_queries_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require 'test_helper'

class SparqlQueriesTest < ActionDispatch::IntegrationTest
QUERIES = YAML.load_file(Rails.root.join('config', 'sparql_queries.yml')).freeze

def setup
@repository = Seek::Rdf::RdfRepository.instance
skip('these tests need a configured triple store') unless @repository.configured?

@person = FactoryBot.create(:person)
@project = @person.projects.first
@programme = @project.programme

User.current_user = @person.user

@investigation = FactoryBot.create(:investigation, projects: [@project], contributor: @person,
policy: FactoryBot.create(:public_policy))
@study = FactoryBot.create(:study, investigation: @investigation, contributor: @person,
policy: FactoryBot.create(:public_policy))
@assay = FactoryBot.create(:experimental_assay, study: @study, contributor: @person,
policy: FactoryBot.create(:public_policy))
@data_file = FactoryBot.create(:data_file, projects: [@project], contributor: @person,
policy: FactoryBot.create(:public_policy))
@model = FactoryBot.create(:model, projects: [@project], contributor: @person,
policy: FactoryBot.create(:public_policy))
@sop = FactoryBot.create(:sop, projects: [@project], contributor: @person,
policy: FactoryBot.create(:public_policy))
@publication = FactoryBot.create(:publication, projects: [@project], contributor: @person,
policy: FactoryBot.create(:public_policy))
@sample_type = FactoryBot.create(:simple_sample_type, projects: [@project], contributor: @person)
@sample = FactoryBot.create(:sample, sample_type: @sample_type, projects: [@project],
contributor: @person, policy: FactoryBot.create(:public_policy))
@organism = FactoryBot.create(:organism)
@strain = FactoryBot.create(:strain, organism: @organism, projects: [@project],
policy: FactoryBot.create(:public_policy))

rdf_items.each { |item| @repository.send_rdf(item) if item.rdf_supported? }
end

def teardown
rdf_items.each { |item| @repository.remove_rdf(item) if item&.rdf_supported? }
end

def rdf_items
[@investigation, @study, @assay, @data_file, @model, @sop,
@publication, @sample, @organism, @strain, @person, @project, @programme].compact
end

# Dynamically generate a test for each query in sparql_queries.yml.
# Each query must execute without error AND return at least one result,
# ensuring the example seed data is properly reflected in the triple store.
QUERIES.each do |key, config|
define_method("test_query_#{key}_returns_results") do
results = run_query(config['query'])
assert results.any?, "Query '#{key}' (#{config['title']}) returned no results — check seed data and RDF propagation"
end
end

private

def run_query(sparql)
client = SPARQL::Client.new(@repository.get_configuration.uri,
default_graph: @repository.get_configuration.private_graph)
client.query(sparql)
end
end
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def open_fixture_file(path)
# ignore sparql requests, for some of the RDF integration tests
# fixme: in the future it would be good to make the sparql data consistent enough to work with VCR
config.ignore_request do |request|
request.uri =~ /sparql-auth/
request.uri =~ /sparql-auth/ || request.uri =~ /localhost:8890/
end

# Disable VCR recording when running in CI to detect missing cassettes and avoid making live requests.
Expand Down
Loading