agraph 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,137 @@
1
+
2
+ = Ruby client for the AllegroGraph RDF graph database
3
+
4
+ The agraph gem provides a client for the RESTful interface of AllegroGraph RDF graph database version 4.x. This
5
+ document should give you overview, how the client can be used. To get familiar with the database server, this
6
+ documentation[http://franz.com/agraph/support/documentation/current/agraph-introduction.html] is strongly recommended.
7
+
8
+ The features that are exposed by this client are...
9
+ * simple repository management
10
+ * add / remove statements (aka triples) to / from the store
11
+ * searching for statements by subject, predicate or object (or any combination of them)
12
+ * searching for statements by geo-spatial queries
13
+ * transactions
14
+ * performing SparQL and Prolog queries
15
+ * mapping of data type
16
+
17
+ == Repository management
18
+
19
+ A repository can be created by simply typing the following.
20
+
21
+ require 'allegro_graph'
22
+
23
+ server = AllegroGraph::Server.new :username => "user", :password => "pass"
24
+ repository = AllegroGraph::Repository.new server, "test_repository"
25
+ repository.create_if_missing!
26
+
27
+ The code will try to connect to a AllegroGraph server running at <tt>localhost:10035</tt> with the credentials
28
+ <tt>user</tt> and <tt>pass</tt>, and creates a repository named <tt>test_repository</tt> if it's not already existing.
29
+
30
+ == Adding and Removing triples
31
+
32
+ Once a repository is created, statements can be added.
33
+
34
+ repository.statements.create "<a_subject>", "<a_predicate>", "<an_object>", "<context>"
35
+
36
+ The last argument is optional and defines a context for the given triple. This context can be used to define named
37
+ graphs inside the repository.
38
+
39
+ To delete statements, matching options can be passed to the delete method.
40
+
41
+ repository.statements.delete :predicate => "<a_predicate>"
42
+
43
+ This will delete all statements with the predicate <tt><a_predicate></tt>.
44
+
45
+ == Searching for statements
46
+
47
+ The <tt>find</tt> method provides an easy way to find specified statements.
48
+
49
+ repository.statements.find :subject => "<a_subject>"
50
+
51
+ The result will be an array that holds arrays of all matching statements.
52
+
53
+ [
54
+ [ "<a_subject>", "<a_predicate>", "<a_object>" ],
55
+ [ "<a_subject>", "<another_predicate>", "<another_object>" ]
56
+ ]
57
+
58
+ == Searching for statements by geo-spatial queries
59
+
60
+ === Adding coordinates to the database
61
+
62
+ Before coordinates can be added, a fitting type has to be requested from the database. Coordinates can have the
63
+ cartesian (x and y) or spherical (latitude and longitude) type. When creating the type, also a range has to be defined.
64
+
65
+ cartesian_type = repository.geo.cartesian_type 1, 0, 0, 100, 100
66
+
67
+ The first argument specifies the strip width and the last four the top-left and bottom-right corner of the rectangle
68
+ that represent the boundaries for any coordinate.
69
+
70
+ Afterwards, the return type can be used add geometric nodes (subjects or objects) to the database.
71
+
72
+ repository.statements.create "<a_subject>", "<a_predicate>", "\"+20+20\"^^#{cartesian_type}"
73
+
74
+ === Finding statements by geo-spatial queries
75
+
76
+ To find statements by thier assigned coordinates, the following methods are provided.
77
+
78
+ repository.geo.inside_box
79
+ repository.geo.inside_circle
80
+ repository.geo.inside_haversine
81
+ repository.geo.inside_polygon
82
+
83
+ The previously create statement will be returned by
84
+
85
+ repository.geo.inside_box cartesian_type, "<a_predicate>", 10, 10, 30, 30
86
+
87
+ == Transactions
88
+
89
+ agraph also allows you to perform transaction. All operations performed within a transaction will committed at once. If
90
+ an error occurs during the transaction, all operations will be rolled back.
91
+
92
+ repository.transaction do
93
+ statements.create "<a_subject>", "<a_predicate>", "<an_object>"
94
+ statements.create "<a_subject>", "<a_predicate>", "<another_object>"
95
+ # ... if an error is raised here, no operation will be committed
96
+ end
97
+ # ... if no error has occured, all operations will be committed
98
+
99
+ == SparQL and Prolog queries
100
+
101
+ The perform a SparQL or Prolog query, simply set the language that the query is written in and pass the query string to
102
+ the following method.
103
+
104
+ repository.query.language = :sparql
105
+ repository.query.perform "SELECT ?s WHERE { ?s ?p ?o . }"
106
+
107
+ At the moment only <tt>:sparql</tt> and <tt>:prolog</tt> queries are supported.
108
+
109
+ The result will look like this.
110
+
111
+ { "names" => [ "s" ], "values" => [ [ "<a_subject>" ], [ "<another_subject>" ] ] }
112
+
113
+ == Mapping of data types
114
+
115
+ To add and remove mapping between types and it's encodings, the following methods can be used.
116
+
117
+ repository.mapping.create "<time>", "<http://www.w3.org/2001/XMLSchema#dateTime>"
118
+ repository.mapping.delete "<time>"
119
+
120
+ With such a type defined, it's possible to perform range queries like
121
+
122
+ repository.statements.create "<event_one>", "<occurs>", "\"2010-03-29T11:40:00\"^^<time>"
123
+ repository.statements.create "<event_two>", "<occurs>", "\"2010-03-29T17:40:00\"^^<time>"
124
+
125
+ repository.statements.find :predicate => "<occurs>", :object => [ "\"2010-03-29T11:00:00\"^^<time>", "\"2010-03-29T12:00:00\"^^<time>" ]
126
+
127
+ Only the second event would be returned.
128
+
129
+ == More Examples
130
+
131
+ More examples can be found in the integration specs (spec/integration)
132
+
133
+ == Contribution
134
+
135
+ Any contribution - especially bug reports - is welcome.
136
+
137
+ Fork or send mail to b.phifty@gmail.com
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ require 'spec'
4
+ require "rake/rdoctask"
5
+ require 'rake/gempackagetask'
6
+ require 'spec/rake/spectask'
7
+
8
+ task :default => :spec
9
+
10
+ specification = Gem::Specification.new do |specification|
11
+ specification.name = "agraph"
12
+ specification.version = "0.1.0.beta1"
13
+ specification.date = "2010-03-29"
14
+ specification.email = "b.phifty@gmail.com"
15
+ specification.homepage = "http://github.com/phifty/agraph"
16
+ specification.summary = "Client for the AllegroGraph 4.x graph database."
17
+ specification.description = "The gem provides a client for the AllegroGraph 4.x RDF graph database. Features like searching geo-spatial data, type mapping and transactions are supported."
18
+ specification.has_rdoc = true
19
+ specification.authors = [ "Philipp Bruell" ]
20
+ specification.files = [ "README.rdoc", "Rakefile" ] + Dir["{lib,spec}/**/*"]
21
+ specification.extra_rdoc_files = [ "README.rdoc" ]
22
+ specification.require_path = "lib"
23
+ end
24
+
25
+ Rake::GemPackageTask.new(specification) do |package|
26
+ package.gem_spec = specification
27
+ end
28
+
29
+ desc "Generate the rdoc"
30
+ Rake::RDocTask.new do |rdoc|
31
+ rdoc.rdoc_files.add [ "README.rdoc", "lib/**/*.rb" ]
32
+ rdoc.main = "README.rdoc"
33
+ rdoc.title = "Client for the AllegroGraph 4.x graph database."
34
+ end
35
+
36
+ desc "Run all specs in spec directory"
37
+ Spec::Rake::SpecTask.new do |task|
38
+ task.spec_files = FileList["spec/lib/**/*_spec.rb"]
39
+ end
40
+
41
+ namespace :spec do
42
+
43
+ desc "Run all integration specs in spec/integration directory"
44
+ Spec::Rake::SpecTask.new(:integration) do |task|
45
+ task.spec_files = FileList["spec/integration/**/*_spec.rb"]
46
+ end
47
+
48
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "allegro_graph", "server"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "allegro_graph", "catalog"))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "allegro_graph", "repository"))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "allegro_graph", "session"))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), "allegro_graph", "federation"))
@@ -0,0 +1,39 @@
1
+ require File.join(File.dirname(__FILE__), "repository")
2
+
3
+ module AllegroGraph
4
+
5
+ class Catalog
6
+
7
+ attr_reader :server
8
+ attr_accessor :name
9
+
10
+ def initialize(server, name, options = { })
11
+ @server = server
12
+ @name = name
13
+ @root = options[:root] || false
14
+ end
15
+
16
+ def ==(other)
17
+ other.is_a?(self.class) && self.server == other.server && self.root? == other.root? && self.name == other.name
18
+ end
19
+
20
+ def path
21
+ self.root? ? "" : "/catalogs/#{@name}"
22
+ end
23
+
24
+ def root?
25
+ !!@root
26
+ end
27
+
28
+ def exists?
29
+ @server.catalogs.include? self
30
+ end
31
+
32
+ def repositories
33
+ repositories = @server.request :get, self.path + "/repositories", :expected_status_code => 200
34
+ repositories.map { |repository| Repository.new self, repository["id"] }
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,72 @@
1
+ require File.join(File.dirname(__FILE__), "transport")
2
+ require File.join(File.dirname(__FILE__), "proxy", "statements")
3
+ require File.join(File.dirname(__FILE__), "proxy", "query")
4
+ require File.join(File.dirname(__FILE__), "proxy", "geo")
5
+ require File.join(File.dirname(__FILE__), "proxy", "mapping")
6
+
7
+ module AllegroGraph
8
+
9
+ class Federation
10
+
11
+ attr_reader :server
12
+ attr_accessor :name
13
+ attr_accessor :repository_names
14
+ attr_accessor :repository_urls
15
+
16
+ attr_reader :statements
17
+ attr_reader :query
18
+ attr_reader :geo
19
+ attr_reader :mapping
20
+
21
+ def initialize(server, name, options = { })
22
+ @server, @name = server, name
23
+
24
+ @repository_names = options[:repository_names]
25
+ @repository_urls = options[:repository_urls]
26
+
27
+ @statements = Proxy::Statements.new self
28
+ @query = Proxy::Query.new self
29
+ @geo = Proxy::Geo.new self
30
+ @mapping = Proxy::Mapping.new self
31
+ end
32
+
33
+ def ==(other)
34
+ other.is_a?(self.class) && @server == other.server && @name == other.name
35
+ end
36
+
37
+ def path
38
+ "/federated/#{@name}"
39
+ end
40
+
41
+ def request(http_method, path, options = { })
42
+ @server.request http_method, path, options
43
+ end
44
+
45
+ def exists?
46
+ @server.federations.include? self
47
+ end
48
+
49
+ def create!
50
+ parameters = { }
51
+ parameters.merge! :repo => @repository_names if @repository_names
52
+ parameters.merge! :url => @repository_urls if @repository_urls
53
+ @server.request :put, self.path, :parameters => parameters, :expected_status_code => 204
54
+ true
55
+ end
56
+
57
+ def create_if_missing!
58
+ create! unless exists?
59
+ end
60
+
61
+ def delete!
62
+ @server.request :delete, self.path, :expected_status_code => 204
63
+ true
64
+ end
65
+
66
+ def delete_if_exists!
67
+ delete! if exists?
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,117 @@
1
+
2
+ module AllegroGraph
3
+
4
+ module Proxy
5
+
6
+ class Geo
7
+
8
+ attr_reader :resource
9
+
10
+ def initialize(resource)
11
+ @resource = resource
12
+ end
13
+
14
+ def path
15
+ "#{@resource.path}/geo"
16
+ end
17
+
18
+ def cartesian_type(strip_width, x_min, y_min, x_max, y_max)
19
+ parameters = {
20
+ :stripWidth => strip_width.to_s,
21
+ :xmin => x_min.to_s,
22
+ :ymin => y_min.to_s,
23
+ :xmax => x_max.to_s,
24
+ :ymax => y_max.to_s
25
+ }
26
+ type = @resource.request :post, self.path + "/types/cartesian", :parameters => parameters, :expected_status_code => 200
27
+ type.sub! /^.*</, "<"
28
+ type.sub! />.*$/, ">"
29
+ type
30
+ end
31
+
32
+ def spherical_type(strip_width, unit, latitude_min, longitude_min, latitude_max, longitude_max)
33
+ parameters = {
34
+ :stripWidth => strip_width.to_s,
35
+ :unit => unit.to_s,
36
+ :latmin => latitude_min.to_s,
37
+ :longmin => longitude_min.to_s,
38
+ :latmax => latitude_max.to_s,
39
+ :longmax => longitude_max.to_s
40
+ }
41
+ type = @resource.request :post, self.path + "/types/spherical", :parameters => parameters, :expected_status_code => 200
42
+ type.sub! /^.*</, "<"
43
+ type.sub! />.*$/, ">"
44
+ type
45
+ end
46
+
47
+ def create_polygon(name, type, points)
48
+ raise ArgumentError, "at least three points has to defined" unless points.is_a?(Array) && points.size >= 3
49
+ parameters = {
50
+ :resource => "\"#{name}\"",
51
+ :point => points.map{ |point| "\"%+g%+g\"^^%s" % [ point[0], point[1], type ] }
52
+ }
53
+ @resource.request :put, self.path + "/polygon", :parameters => parameters, :expected_status_code => 204
54
+ true
55
+ end
56
+
57
+ def inside_box(type, predicate, x_min, y_min, x_max, y_max)
58
+ parameters = {
59
+ :type => type,
60
+ :predicate => predicate,
61
+ :xmin => x_min.to_s,
62
+ :ymin => y_min.to_s,
63
+ :xmax => x_max.to_s,
64
+ :ymax => y_max.to_s
65
+ }
66
+ @resource.request :get, self.path + "/box", :parameters => parameters, :expected_status_code => 200
67
+ end
68
+
69
+ def inside_circle(type, predicate, x, y, radius)
70
+ parameters = {
71
+ :type => type,
72
+ :predicate => predicate,
73
+ :x => x.to_s,
74
+ :y => y.to_s,
75
+ :radius => radius.to_s
76
+ }
77
+ @resource.request :get, self.path + "/circle", :parameters => parameters, :expected_status_code => 200
78
+ end
79
+
80
+ def inside_haversine(type, predicate, latitude, longitude, radius, unit = :km)
81
+ parameters = {
82
+ :type => type,
83
+ :predicate => predicate,
84
+ :lat => float_to_iso_6709(latitude, 2),
85
+ :long => float_to_iso_6709(longitude, 3),
86
+ :radius => radius.to_s,
87
+ :unit => unit.to_s
88
+ }
89
+ @resource.request :get, self.path + "/haversine", :parameters => parameters, :expected_status_code => 200
90
+ end
91
+
92
+ def inside_polygon(type, predicate, name)
93
+ parameters = {
94
+ :type => type,
95
+ :predicate => predicate,
96
+ :polygon => "\"#{name}\""
97
+ }
98
+ @resource.request :get, self.path + "/polygon", :parameters => parameters, :expected_status_code => 200
99
+ end
100
+
101
+ private
102
+
103
+ def float_to_iso_6709(value, digits)
104
+ sign = "+"
105
+ if value < 0
106
+ sign = "-"
107
+ value = -value
108
+ end
109
+ floor = value.to_i
110
+ sign + (("%%0%dd" % digits) % floor) + (".%07d" % ((value - floor) * 10000000))
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,34 @@
1
+
2
+ module AllegroGraph
3
+
4
+ module Proxy
5
+
6
+ class Mapping
7
+
8
+ attr_reader :resource
9
+
10
+ def initialize(resource)
11
+ @resource = resource
12
+ end
13
+
14
+ def path
15
+ "#{@resource.path}/mapping"
16
+ end
17
+
18
+ def create(type, encoding)
19
+ parameters = { :type => type, :encoding => encoding }
20
+ @resource.request :put, self.path + "/type", :parameters => parameters, :expected_status_code => 204
21
+ true
22
+ end
23
+
24
+ def delete(type)
25
+ parameters = { :type => type }
26
+ @resource.request :delete, self.path + "/type", :parameters => parameters, :expected_status_code => 204
27
+ true
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end