Graph Oriented Objects for Ruby (Goo)

Goo is a Ruby library that provides ORM-alike capabilities to interact with RDF/SPARQL backends. Goo provides a DSL for defining schemas for objects and controls how they get validated, serialized, saved, and retrieved from the triplestore. Using RDF and SPARQL for large-scale applications creates challenges in terms of scalability and technology adoption. We designed Goo with two main objectives:

  • Goo abstracts SPARQL so developers do not need to be SPARQL experts to handle large RDF graphs efficiently.

  • Goo is designed to serve BioPortal growing REST traffic. BioPortal's REST API provides access to hundreds of millions of Biomedical artifacts. Scalability and efficiency is at the core of Goo's design.


How to use

Github documentation see: http://ncbo.github.io/goo/


Examples

Example: getting a user and editing it

Using bin/ncbo_cron --console

user = LinkedData::Models::User.find("admin").first
user.bring_remaining

user.password=("my_password")

user.valid?
user.save


Example: get all mappings

LinkedData::Models::Mappings.all.include(:id, :source, :classes)

#Get all mappings with all attributes

LinkedData::Models::RestBackupMapping.all
            .include(LinkedData::Models::RestBackupMapping.attributes(:all)).each do |m|
                   
   puts m.id.to_s
end

#Get and delete all mappings

LinkedData::Models::RestBackupMapping.all.each do |m|
            puts m.id.to_s
            mapping_id = RDF::URI.new(m.id.to_s)
            puts LinkedData::Mappings.delete_rest_mapping(mapping_id)
end


Example: get a slice and edit it


# Retrieve an ontology
ont = LinkedData::Models::Ontology.find("STY").first
sub = 

ont = LinkedData::Models::Ontology.find("STY").include(LinkedData::Models::Ontology.attributes(:all)).first

# Retrieve all submissions of ont
sub = LinkedData::Models::OntologySubmission.where(ontology: ont).include(:submissionId).include(:submissionStatus).all

# Retrieve a submission of ont1 including all its attributes
sub = LinkedData::Models::OntologySubmission.where(ontology: ont).include(LinkedData::Models::OntologySubmission.attributes(:all)).first

# Retrive latest submission and bring all properties
sub = ont.latest_submission
sub.bring_remaining
sub.ontology.bring_remaining

# Retrieve a slice
slice = LinkedData::Models::Slice.find("umls").include(:acronym, :name, :ontologies).first

# To include all attributes:
slice = LinkedData::Models::Slice.find("umls")
	.include(LinkedData::Models::Slice.attributes(:all))
	.first

# Change the name
slice.name = "test"
# Change the ontologies of this slice
slice.ontologies = [ont]

# Save the changes
if slice.valid?
  slice.save
end

# Display errors
slice.errors

How it works

Model set up

Model settings

TODO see https://github.com/ncbo/goo/blob/master/lib/goo/base/resource.rb

Model attributes

# Get all attributes of a model
LinkedData::Models::OntologySubmission.attributes(:all)

# Get the settings of an attribute
LinkedData::Models::OntologySubmission.attribute_settings(:naturalLanguage)
# Returns the following JSON object
{:namespace=>:omv, :enforce=>[:list]}

# Get the value of an attribute?
# In OntologySubmission.rb
self.naturalLanguage = "en"


Generating SPARQL queries

There are 7 types of generated queries

Include/unmapped/general query

SELECT DISTINCT ?id ?attributeProperty ?attributeObject
FROM <graph_i> # for each graph from 1..n
WHERE { 
	?id a <type_uri> . 
	OPTIONAL {

		  { ?id <attribute_1_uri> ?attributeObject . BIND("var_1" as ?attributeProperty) } 
			UNION
				....
			UNION
		  { ?attributeObject <attribute_j_uri> ?id . BIND( "var_j" as ?attributeProperty)} # if attribute_j_uri is an inverse attribute 
		  UNION 
			  ....
			{ ?id <attribute_n_uri> ?attributeObject . BIND("var_n" as ?attributeProperty) } 
	}

FILTER(?id = <id_uri>) 
}

Aggregate query

TODO

BNODE query

SELECT DISTINCT ?id ?line1 ?line2 
FROM <http://goo.org/default/PersonPersistent>
 WHERE { 
   ?id a <http://goo.org/default/PersonPersistent> . 
   ?id <http://goo.org/default/contact_data> _:g64460 . 
   _:g64460 <http://goo.org/default/line1> ?line1. 
   _:g64460 <http://goo.org/default/line2> ?line2 . 
   FILTER(?id = <http://goo.org/default/person_persistent/Lewis> || ?id = <http://goo.org/default/person_persistent/John>) }

FILTER query (test/test_where#test_filter)

SELECT DISTINCT ?id ?attributeProperty ?attributeObject 
FROM <graph> 
WHERE { 
	  ?id a <type_uri> . 
    ?id <attribute_uri> ?internal_join_var_0 . #can alos be optional
    FILTER(?internal_join_var_0 > "var")
}

ORDER BY query

SELECT DISTINCT ?id ?attributeProperty ?attributeObject 
FROM <graph> 
WHERE { 
	  ?id a <type_uri> . 
    ?id <attribute_uri> ?internal_join_var_0 . #can also be optional
}
ORDER BY ?internal_join_var_0

Equivalent predicates case

see def self*.expand_equivalent_predicates(query, eq_p)


SELECT DISTINCT ?id ?attributeProperty ?attributeObject
FROM <graph_i>
WHERE { 
    ?id a <type_uri> .
    OPTIONAL { 
         {?id <equivalent_uri_1> ?attributeObject . BIND( "var_1" as ?attributeProperty)) }
				 UNION
         ...
         UNION
           {?id <equivalent_uri_2> ?attributeObject . BIND( "var_1" as ?attributeProperty)) }
     ...
		
}