File: tutorial.html

package info (click to toggle)
tracker 3.4.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,096 kB
  • sloc: ansic: 57,908; javascript: 15,606; python: 6,272; cs: 242; perl: 106; sh: 98; xml: 29; makefile: 20
file content (964 lines) | stat: -rw-r--r-- 41,636 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
<!DOCTYPE html>
<html lang="en">
<head>

<base href=".">

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">


<title>SPARQL Tutorial</title>

<link rel="stylesheet" href="assets/css/custom_bootstrap.css" type="text/css">
<link rel="stylesheet" href="assets/css/bootstrap-toc.min.css" type="text/css">
<link rel="stylesheet" href="assets/css/frontend.css" type="text/css">
<link rel="stylesheet" href="assets/css/jquery.mCustomScrollbar.min.css">
<link rel="stylesheet" href="assets/js/search/enable_search.css" type="text/css">

<link rel="stylesheet" href="assets/css/tracker-custom.css" type="text/css">
<link rel="stylesheet" href="assets/css/prism.css" type="text/css">
<link rel="stylesheet" href="assets/css/devhelp.css" type="text/css">

<script src="assets/js/mustache.min.js"></script>
<script src="assets/js/jquery.js"></script>
<script src="assets/js/bootstrap.js"></script>
<script src="assets/js/scrollspy.js"></script>
<script src="assets/js/typeahead.jquery.min.js"></script>
<script src="assets/js/search.js"></script>
<script src="assets/js/compare-versions.js"></script>
<script src="assets/js/jquery.mCustomScrollbar.concat.min.js"></script>
<script src="assets/js/bootstrap-toc.min.js"></script>
<script src="assets/js/jquery.touchSwipe.min.js"></script>
<script src="assets/js/anchor.min.js"></script>
<script src="assets/js/tag_filtering.js"></script>
<script src="assets/js/language_switching.js"></script>

<script src="assets/js/lines_around_headings.js"></script>

<script src="assets/js/prism-core.js"></script>
<script src="assets/js/prism-autoloader.js"></script>
<script src="assets/js/prism_autoloader_path_override.js"></script>
<script src="assets/js/trie.js"></script>


</head>

<body class="no-script
">

<script>
$('body').removeClass('no-script');
</script>

<nav class="navbar navbar-fixed-top navbar-default" id="topnav">
	<div class="container-fluid">
		<div class="navbar-right">
			<a id="toc-toggle">
				<span class="glyphicon glyphicon-menu-right"></span>
				<span class="glyphicon glyphicon-menu-left"></span>
			</a>
			<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-wrapper" aria-expanded="false">
				<span class="sr-only">Toggle navigation</span>
				<span class="icon-bar"></span>
				<span class="icon-bar"></span>
				<span class="icon-bar"></span>
			</button>
			<form class="navbar-form pull-right" id="navbar-search-form">
                               <div class="form-group has-feedback">
                                       <input type="text" class="form-control input-sm" name="search" id="sidenav-lookup-field" placeholder="search" disabled>
				       <span class="glyphicon glyphicon-search form-control-feedback" id="search-mgn-glass"></span>
                               </div>
                        </form>
		</div>
		<div class="navbar-header">
			<a id="sidenav-toggle">
				<span class="glyphicon glyphicon-menu-right"></span>
				<span class="glyphicon glyphicon-menu-left"></span>
			</a>
			<a id="home-link" href="index.html" class="hotdoc-navbar-brand">
				<img src="assets/images/home.svg" alt="Home">
			</a>
		</div>
		<div class="navbar-collapse collapse" id="navbar-wrapper">
			<ul class="nav navbar-nav" id="menu">
				
<li>
    <a href="https://gnome.pages.gitlab.gnome.org/tracker/">Website</a>
</li>

			</ul>
			<div class="hidden-xs hidden-sm navbar-text navbar-center">
							</div>
		</div>
	</div>
</nav>

<main>
<div data-extension="core" data-hotdoc-in-toplevel="True" data-hotdoc-project="Tracker" data-hotdoc-ref="tutorial.html" class="page_container" id="page-wrapper" data-hotdoc-meta-gi-languages="['c', 'javascript', 'python']">
<script src="assets/js/utils.js"></script>

<div class="panel panel-collapse oc-collapsed" id="sidenav" data-hotdoc-role="navigation">
	<script src="assets/js/navigation.js"></script>
	<script src="assets/js/sitemap.js"></script>
</div>

<div id="body">
	<div id="main">
				    <div id="page-description" data-hotdoc-role="main">
        <h1 id="sparql-tutorial">SPARQL Tutorial</h1>
<p>This tutorial aims to introduce you to RDF and SPARQL from the ground
up. All examples come from the Nepomuk ontology, and even though
the tutorial aims to be generic enough, it mentions things
specific to Tracker, those are clearly spelled out.</p>
<p>If you are reading this tutorial, you might also have Tracker installed
in your system, if that is the case you can for example start a fresh
empty SPARQL service for local testing:</p>
<pre><code class="language-bash">$ tracker3 endpoint --dbus-service a.b.c --ontology nepomuk
</code></pre>
<p>The queries can be run in this specific service with:</p>
<pre><code class="language-bash">$ tracker3 sparql --dbus-service a.b.c --query $SPARQL_QUERY
</code></pre>
<h2 id="rdf-triples">RDF Triples</h2>
<p>RDF data define a graph, composed by vertices and edges. This graph is
directed, because edges point from one vertex to another, and it is
labeled, as those edges have a name. The unit of data in RDF is a
triple of the form:</p>
<pre><code>subject  predicate  object
</code></pre>
<p>Or expressed visually:</p>
<p><img src="images/triple-graph-1.png" alt="Triple Graph" id="triple-graph"></p>
<p>Subject and object are 2 graph vertices and the predicate is the edge,
the accumulation of those triples form the full graph. For example,
the following triples:</p>
<pre><code class="language-turtle">&lt;http://example.com/Song&gt; a nfo:FileDataObject .
&lt;http://example.com/Song&gt; a nmm:MusicPiece .
&lt;http://example.com/Song&gt; nie:title "Images" .
&lt;http://example.com/Song&gt; nmm:musicAlbum &lt;http://example.com/Album&gt; .
&lt;http://example.com/Song&gt; nmm:albumArtist &lt;http://example.com/Jason&gt; .
&lt;http://example.com/Song&gt; nmm:albumArtist &lt;http://example.com/Marty&gt; .
&lt;http://example.com/Song&gt; nmm:performer &lt;http://example.com/Band&gt; .

&lt;http://example.com/Album&gt; a nmm:MusicAlbum .
&lt;http://example.com/Album&gt; nie:title "Go Off!" .

&lt;http://example.com/Jason&gt; a nmm:Artist .
&lt;http://example.com/Jason&gt; nmm:artistName "Jason Becker" .

&lt;http://example.com/Marty&gt; a nmm:Artist .
&lt;http://example.com/Marty&gt; nmm:artistName "Marty Friedman" .

&lt;http://example.com/Band&gt; a nmm:Artist .
&lt;http://example.com/Band&gt; nmm:artistName "Cacophony" .
</code></pre>
<p>Would visually generate the following graph:</p>
<p><img src="images/triple-graph-2.png" alt="Triple Graph" id="triple-graph1"></p>
<p>The dot after each triple is not (just) there for legibility, but is
part of the syntax. The RDF triples in full length are quite
repetitive and cumbersome to write, luckily they can be shortened by
providing multiple objects (with <code>,</code> separator) or multiple
predicate/object pairs (with <code>;</code> separator), the previous RDF could be
shortened into:</p>
<pre><code class="language-turtle">&lt;http://example.com/Song&gt; a nfo:FileDataObject, nmm:MusicPiece .
&lt;http://example.com/Song&gt; nie:title "Images" .
&lt;http://example.com/Song&gt; nmm:musicAlbum &lt;http://example.com/Album&gt; .
&lt;http://example.com/Song&gt; nmm:albumArtist &lt;http://example.com/Jason&gt; , &lt;http://example.com/Marty&gt; .
&lt;http://example.com/Song&gt; nmm:performer &lt;http://example.com/Band&gt; .

&lt;http://example.com/Album&gt; a nmm:MusicAlbum .
&lt;http://example.com/Album&gt; nie:title "Go Off!" .

&lt;http://example.com/Jason&gt; a nmm:Artist .
&lt;http://example.com/Jason&gt; nmm:artistName "Jason Becker" .

&lt;http://example.com/Marty&gt; a nmm:Artist .
&lt;http://example.com/Marty&gt; nmm:artistName "Marty Friedman" .

&lt;http://example.com/Band&gt; a nmm:Artist .
&lt;http://example.com/Band&gt; nmm:artistName "Cacophony" .
</code></pre>
<p>And further into:</p>
<pre><code class="language-turtle">&lt;http://example.com/Song&gt; a nfo:FileDataObject, nmm:MusicPiece ;
    nie:title "Images" ;
    nmm:musicAlbum &lt;http://example.com/Album&gt; ;
    nmm:albumArtist &lt;http://example.com/Jason&gt; , &lt;http://example.com/Marty&gt; ;
    nmm:performer &lt;http://example.com/Band&gt; .

&lt;http://example.com/Album&gt; a nmm:MusicAlbum ;
    nie:title "Go Off!" .

&lt;http://example.com/Jason&gt; a nmm:Artist ;
    nmm:artistName "Jason Becker" .

&lt;http://example.com/Marty&gt; a nmm:Artist ;
    nmm:artistName "Marty Friedman" .

&lt;http://example.com/Band&gt; a nmm:Artist ;
    nmm:artistName "Cacophony" .
</code></pre>
<h2 id="sparql">SPARQL</h2>
<p>SPARQL is the definition of a query language for RDF data. How does a query
language for graphs work? Naturally by providing a graph to be matched, it
is conveniently called the "graph pattern".</p>
<p>SPARQL extends over the RDF concepts and syntax, once familiar with RDF the
basic data insertion syntax should be fairly self-explanatory:</p>
<pre><code class="language-SPARQL">INSERT DATA {
    &lt;http://example.com/Song&gt; a nfo:FileDataObject, nmm:MusicPiece ;
        nie:title "Images" ;
        nmm:musicAlbum &lt;http://example.com/Album&gt; ;
        nmm:albumArtist &lt;http://example.com/Jason&gt; , &lt;http://example.com/Marty&gt; ;
        nmm:performer &lt;http://example.com/Band&gt; .

    &lt;http://example.com/Album&gt; a nmm:MusicAlbum ;
        nie:title "Go Off!" .

    &lt;http://example.com/Jason&gt; a nmm:Artist ;
        nmm:artistName "Jason Becker" .

    &lt;http://example.com/Marty&gt; a nmm:Artist ;
        nmm:artistName "Marty Friedman" .

    &lt;http://example.com/Band&gt; a nmm:Artist ;
        nmm:artistName "Cacophony" .
}
</code></pre>
<p>Same with simple data deletion:</p>
<pre><code class="language-SPARQL">DELETE DATA {
    &lt;http://example.com/Resource&gt; a rdfs:Resource ;
}
</code></pre>
<p>And simple graph testing:</p>
<pre><code class="language-SPARQL"># Tell me whether this RDF data exists in the store
ASK {
    &lt;http://example.com/Song&gt; nie:title "Images" ;
        nmm:albumArtist &lt;http://example.com/Jason&gt; ;
        nmm:musicAlbum &lt;http://example.com/Album&gt; .
    &lt;http://example.com/Album&gt; nie:title "Go Off!" .
    &lt;http://example.com/Jason&gt; nmm:artistName "Jason Becker"
}
</code></pre>
<p>Which would result in <code>true</code>, as the triple does exist. The ASK query
syntax results in a single boolean row/column containing whether the
provided graph exists in the store or not.</p>
<h2 id="queries-and-variables">Queries and variables</h2>
<p>Of course, the deal of a query language is being able to obtain the
stored data, not just testing whether data exists.</p>
<p>The <code>SELECT</code> query syntax is used for that, and variables are denoted with
a <code>?</code> prefix (or <code>$</code>, although that is less widely used), variables act
as "placeholders" where any data will match and be available to the resultset
or within the query as that variable name.</p>
<p>These variables can be set anywhere as the subject, predicate or object of
a triple. For example, the following query could be considered the opposite
to the simple boolean testing the that <code>ASK</code> provides:</p>
<pre><code class="language-SPARQL"># Give me every known triple
SELECT * {
    ?subject ?predicate ?object
}
</code></pre>
<p>What does this query do? it provides a triple with 3 variables, that
every known triple in the database will match. The <code>*</code> is a shortcut
for all queried variables, the query could also be expressed as:</p>
<pre><code class="language-SPARQL">SELECT ?subject ?predicate ?object {
    ?subject ?predicate ?object
}
</code></pre>
<p>However, querying for all known data is most often hardly useful, this
got unwieldly soon! Luckily, that is not necessarily the case, the
variables may be used anywhere in the triple definition, with other
triple elements consisting of literals you want to match for, e.g.:</p>
<pre><code class="language-SPARQL"># Give me the title of the song (Result: "Images")
SELECT ?songName {
    &lt;http://example.com/Song&gt; nie:title ?songName
}
</code></pre>
<pre><code class="language-SPARQL"># What is this text to the album? (Result: the nie:title)
SELECT ?predicate {
    &lt;http://example.com/Album&gt; ?predicate "Go Off!"
}
</code></pre>
<pre><code class="language-SPARQL"># What is the resource URI of this fine musician? (Result: &lt;http://example.com/Marty&gt;)
SELECT ?subject {
    ?subject nmm:artistName "Marty Friedman"
}
</code></pre>
<pre><code class="language-SPARQL"># Give me all resources that are a music piece (Result: &lt;http://example.com/Song&gt;)
SELECT ?song {
    ?song a nmm:MusicPiece
}
</code></pre>
<p>And also combinations of them, for example:</p>
<pre><code class="language-SPARQL"># Give me all predicate/object pairs for the given resource
SELECT ?pred ?obj {
    &lt;http://example.com/Song&gt; ?pred ?obj
}
</code></pre>
<pre><code class="language-SPARQL"># The Answer to the Ultimate Question of Life, the Universe, and Everything
SELECT ?subj ?pred {
    ?subj ?pred 42
}
</code></pre>
<pre><code class="language-SPARQL"># Give me all resources that have a title, and their title.
SELECT ?subj ?obj {
    ?subj nie:title ?obj
}
</code></pre>
<p>And of course, the graph pattern can hold more complex triple
definitions, that will be matched as a whole across the stored
data. for example:</p>
<pre><code class="language-SPARQL"># Give me all songs from this fine album
SELECT ?song {
    ?album nie:title "Go Off!" .
    ?song nmm:musicAlbum ?album
}
</code></pre>
<pre><code class="language-SPARQL"># Give me all song resources, their title, and their album title
SELECT ?song ?songTitle ?albumTitle {
    ?song a nmm:MusicPiece ;
        nmm:musicAlbum ?album ;
        nie:title ?songTitle .
    ?album nie:title ?albumTitle
}
</code></pre>
<p>Stop a bit to think on the graph pattern expressed in the last query:
<img src="images/triple-graph-3.png" alt="Graph Pattern" id="graph-pattern"></p>
<p>This pattern on one hand consists of specified data (eg. <code>?song</code> must be
a <code>nmm:MusicPiece</code>, it must have a <code>nmm:musicAlbum</code> and a <code>nie:title</code>,
<code>?album</code> must have a <code>nie:title</code>, which must all apply for a match
to happen.</p>
<p>On the other hand, the graph pattern contains a number of variables,
some only used internally in the graph pattern, as a temporary
variable of sorts (?album, in order to express the relation between
?song and its album title), while other variables are requested in the
result set.</p>
<h2 id="uris-urns-and-iris">URIs, URNs and IRIs</h2>
<p>In RDF data, everything that can be identified does so by
a URI. URIs uniquely identify an element in the RDF set, have
properties that are defined by URIs themselves, and point to
data that may either be other resources (identified by URI) or
literal values.</p>
<p>Given URIs are central to RDF data and SPARQL queries, there are
a number of ways to write, generalize and shorten them. URIs can
of course be defined in their full form:</p>
<pre><code class="language-SPARQL">ASK {
    &lt;http://example.com/a&gt; rdf:type rdfs:Resource .
    &lt;http://example.com/sub/b&gt; rdfs:label 'One' .
}
</code></pre>
<p>Sadly, not everything in the world can be trivially mapped to
a URI, as an aide Tracker offers helpers to generate URIs based
on UUIDv4 identifiers like <a href="tracker-misc.html#tracker_sparql_get_uuid_urn" data-gi-href-javascript="tracker-misc.html#tracker_sparql_get_uuid_urn" data-gi-title-javascript="Tracker.prototype.sparql_get_uuid_urn" data-gi-href-python="tracker-misc.html#tracker_sparql_get_uuid_urn" data-gi-title-python="Tracker.sparql_get_uuid_urn">tracker_sparql_get_uuid_urn</a>,
these generated strings are typically called URNs.</p>
<p>The <code>BASE</code> keyword allows setting a common prefix for all URIs
in the query:</p>
<pre><code class="language-SPARQL">BASE &lt;http://example.com/&gt;

ASK {
    &lt;a&gt; rdf:type rdfs:Resource .
    &lt;sub/b&gt; rdfs:label 'One' .
}
</code></pre>
<p>Or different prefixes can be defined with the <code>PREFIX</code> keyword:</p>
<pre><code class="language-SPARQL">PREFIX ex: &lt;http://example.com/&gt;
PREFIX sub: &lt;http://example.com/sub/&gt;

ASK {
    ex:a rdf:type rdfs:Resource .
    sub:b rdfs:label 'One' .
}
</code></pre>
<p>Notice how in the first triple, subject/predicate/object now look the
same? That is because they now all are prefixed names, here the <code>rdf</code>
and <code>rdfs</code> prefixes are simply builtin.</p>
<p>Taking the opposite path (i.e. expanding every term), this query could
be written as:</p>
<pre><code class="language-SPARQL">ASK {
    &lt;https://example/a&gt; &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#type&gt; &lt;http://www.w3.org/2000/01/rdf-schema#Resource&gt; .
    &lt;https://example/sub/b&gt; &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#label&gt; 'One' .
}
</code></pre>
<p>For the sake of familiarity, these elements have been referred to as
"URIs", but these are actually IRIs, thus the unicode range is available:</p>
<pre><code class="language-SPARQL">INSERT DATA {
    &lt;http://example.com/💣&gt; a rdfs:Resource
}
</code></pre>
<h2 id="filtering-data">Filtering data</h2>
<p>With some practice and experimentation, it should be quick to get the hang
of graph patterns, and how do they match the stored RDF data. But it also
quickly comes with the realization that it is completely binary, every piece
of RDF data that matches the graph pattern is returned and everything else
is ignored.</p>
<p>The <code>FILTER</code> keyword adds the missing further expressiveness, allowing to
define arbitrary expressions on the variables defined in the graph
pattern. E.g.:</p>
<pre><code class="language-SPARQL"># Get images larger than 800x600, at 4:3 ratio
SELECT ?image {
    ?image a nfo:Image ;
        nfo:width ?width ;
        nfo:height ?height .
    FILTER (?width &gt; 800 &amp;&amp;
            ?height &gt; 600 &amp;&amp;
            (?width / ?height = 4/3)) .
}
</code></pre>
<p>Conceptually, every solution given by the graph pattern runs through these
filters, providing finer control over the final set of solutions. These
filters are ultimately interpreted as boolean values, for example the
following query:</p>
<pre><code class="language-SPARQL"># This returns nothing!
SELECT * {
   ?subject ?predicate ?object .
   FILTER (false)
}
</code></pre>
<p>Would return no results, as every solution is filtered out.</p>
<p>The SPARQL language also provides a number of
<a href="https://www.w3.org/TR/sparql11-query/#SparqlOps">builtin functions</a> that
are suitable for use in filters (e.g. for string checks and manipulation),
these complement the relational expressions <code>= != &gt; &gt;= &lt; &lt;=</code>.</p>
<p>It is also possible to provide a list of possible values for variables to
be filtered in or out via the operators <code>IN</code> and <code>NOT IN</code>:</p>
<pre><code class="language-SPARQL"># Give me every folder, except those named "Music" or "Downloads"
SELECT ?song {
    ?folder a nfo:Folder ;
        nfo:fileName ?name .
    FILTER (?name NOT IN ('Music', 'Downloads'))
}
</code></pre>
<h2 id="aggregate-functions">Aggregate functions</h2>
<p>Aggregate functions are those that work over groups of solutions, e.g.:</p>
<pre><code class="language-SPARQL"># Tell me how many songs do I have
SELECT (COUNT (?song) AS ?count) {
    ?song a nmm:MusicPiece .
}
</code></pre>
<p>By default, there is a single group for all solutions, a different
grouping can be provided with the <code>GROUP BY</code> clause.</p>
<pre><code class="language-SPARQL"># Get the run time of each of my albums, the sum of their music pieces.
SELECT ?album (SUM (?duration) AS ?runtime) {
    ?song a nmm:MusicPiece ;
        nfo:duration ?duration ;
        nmm:musicAlbum ?album .
}
GROUP BY ?album
</code></pre>
<p>For numeric operations, SPARQL defines the <code>COUNT / MIN / MAX / SUM / AVG</code>
functions. For strings, concatenation is available via the <code>GROUP_CONCAT</code>
function. The <code>SAMPLE</code> function can be used to pick one of the values at
random.</p>
<pre><code class="language-SPARQL"># Give me a list of directors, with one of their movies.
SELECT ?director (SAMPLE (?movie) AS ?sample) {
    ?movie a nmm:Movie ;
        nmm:director ?director .
}
GROUP BY ?director
</code></pre>
<p>Sometimes, it is desirable to apply filters on these aggregate values.
The <code>HAVING</code> clause behaves like <code>FILTER</code>, except it can be used
to apply filters on these aggregate values:</p>
<pre><code class="language-SPARQL"># Get music albums, but filter out singles and bonus discs (an arbitrary
# minimum of 4 songs is used for this)
SELECT ?album {
    ?song a nmm:MusicPiece .
        nmm:musicAlbum ?album
}
GROUP BY ?album
HAVING (COUNT (?song) &gt;= 4)
</code></pre>
<h2 id="optional-data">Optional data</h2>
<p>As we have seen, the graph pattern provided in <code>SELECT</code> queries match as
a whole, stored RDF data either is a match (and becomes a possible solution),
or it does not. Sometimes the available data is not all that regular, so it
is desirable to create a graph pattern that can find solutions where some
variables are left blank.</p>
<p>The <code>OPTIONAL</code> clause can be used for this:</p>
<pre><code class="language-SPARQL"># Get songs, and their known performer(s). But audio files are often mis/unlabeled!
SELECT ?song ?performer {
    ?song a nmm:MusicPiece .
    OPTIONAL {
        ?song nmm:performer ?performer .
    }
}
</code></pre>
<p>This query will always return all music pieces, but as the song's <code>nmm:performer</code>
property is obtained inside the <code>OPTIONAL</code> clause, it does not become mandatory
to be part of the set of solutions. Contrast with:</p>
<pre><code class="language-SPARQL">SELECT ?song ?performer {
    ?song a nmm:MusicPiece ;
        nmm:performer ?performer .
}
</code></pre>
<p>Which will only return music pieces that have a <code>nmm:performer</code>.</p>
<p>It is worth pointing out that the content of <code>OPTIONAL { }</code> is itself also a graph
pattern, so it also either matches as a whole or it does not. When fetching multiple
optional properties, it is a common pitfall to do:</p>
<pre><code class="language-SPARQL"># BAD ❌: Only songs that have both performer *and* composer will match the
# optional graph pattern. Otherwise these 2 variables will be null.
SELECT ?song ?performer ?composer {
    ?song a nmm:MusicPiece .
    OPTIONAL {
        ?song nmm:performer ?performer ;
            nmm:composer ?composer .
    }
}
</code></pre>
<p>If there are multiple optional pieces of data, these must happen in separate
<code>OPTIONAL</code> clauses:</p>
<pre><code class="language-SPARQL"># GOOD ✅: Songs may have none/either/both of performer/composer.
SELECT ?song ?performer ?composer
    ?song a nmm:MusicPiece .
    OPTIONAL {
        ?song nmm:performer ?performer .
    } .
    OPTIONAL {
        ?song nmm:composer ?composer .
    }
}
</code></pre>
<h2 id="property-paths">Property paths</h2>
<p>Up till now, we have been defining triples in the graph pattern as
sets of <code>subject predicate object</code>. A single predicate like that
is the simplest property path there is, it relates subject and object
directly via a labeled arrow.</p>
<p><img src="images/triple-graph-1.png" alt=""></p>
<p>Property paths make it possible to define more complex connections
between subject and object (literally, paths of properties). The <code>/</code>
operator may be used for concatenation:</p>
<pre><code class="language-SPARQL"># Get songs and their performer artist name, jumping across
# the intermediate nmm:Artist resource.
SELECT ?song ?artistName {
    ?song nmm:performer/nmm:artistName ?artistName
}
</code></pre>
<p>The <code>|</code> operator may be used for providing optional paths:</p>
<pre><code class="language-SPARQL"># Get songs and their performer/composer artist names, jumping across
# the intermediate nmm:Artist resources.
SELECT ?song ?artistName {
    ?song (nmm:performer|nmm:composer)/nmm:artistName ?artistName
}
</code></pre>
<p>The unary <code>*</code> and <code>+</code> operators may be used to define recursive paths,
<code>*</code> allows a 0-length property path (essentially, subject equals object),
while <code>+</code> requires that the property path should happen at least once.</p>
<pre><code class="language-SPARQL"># Get the XDG music folder, plus all its recursive contents.
SELECT ?file {
    ?file a nfo:FileDataObject ;
        (nfo:belongsToContainer/nie:isStoredAs)* ?root .
    FILTER (?root = 'file:///home/.../Music')
}
</code></pre>
<p>The unary <code>?</code> operator makes portions of the property path optional,
matching paths with either length 0 or 1:</p>
<pre><code class="language-SPARQL"># Get the XDG music folder, plus all its direct contents.
SELECT ?file {
    ?file a nfo:FileDataObject ;
        (nfo:belongsToContainer/nie:isStoredAs)? ?root .
    FILTER (?root = 'file:///home/.../Music')
}
</code></pre>
<p>The <code>^</code> operator inverts the direction of the relation expressed
by the property path:</p>
<pre><code class="language-SPARQL"># The arrow goes in the other direction!
SELECT ?song ?artistName {
    ?artistName ^nmm:artistName ?song
}
</code></pre>
<p>The <code>!</code> operator inverts the meaning of the match:</p>
<pre><code class="language-SPARQL"># Give me everything except the title
SELECT ?song ?value {
    ?song !nie:title ?value .
}
</code></pre>
<p>These operators may be all nested with <code>( )</code>, allowing full
expressiveness when defining the ways subject and object are
interrelated.</p>
<h2 id="ontologies">Ontologies</h2>
<p>In the RDF world, an "ontology" defines the characteristics of
the data that a RDF database can hold. RDF triples that fit in its
view of the world are accepted, while data that does not is rejected.</p>
<pre><code class="language-SPARQL"># This is good
INSERT DATA {
    &lt;a&gt; rdf:type rdfs:Resource
};

# This is bad, this property is unknown
INSERT DATA {
    &lt;a&gt; ex:nonExistentProperty 1000
}
</code></pre>
<p>Tracker defines all its ontologies on top of the RDF Schema, which
provides the basic blocks to define classes (i.e. the types of
the resources being stored) and the properties that each of these
can have.</p>
<p>Basic types and literals have their own builtin classes (e.g.
<code>xsd:string</code> for string literals), properties can point to either
one of these builtin classes (e.g. the <code>rdfs:label</code> property points
to string literals), or any other class defined in the ontology.
This ability to "define the type" of properties allows to define
the structure of the data:</p>
<pre><code class="language-SPARQL"># This is consistent, the nmm:musicAlbum property
# must point to a nmm:MusicAlbum resource
INSERT DATA {
    &lt;album&gt; a nmm:MusicAlbum .
    &lt;song1&gt; a nmm:MusicPiece ;
        nmm:musicAlbum &lt;album&gt;
}

# This is inconsistent, agenda contacts are not albums
INSERT DATA {
    &lt;contact&gt; a nco:Contact .
    &lt;song2&gt; a nmm:MusicPiece ;
        nmm:musicAlbum &lt;contact&gt;
}
</code></pre>
<p>The ontology is defined via RDF itself, and Tracker makes it part
of the data set, there are thus full introspection capabilities
builtin:</p>
<pre><code class="language-SPARQL"># Query all available classes
SELECT ?class {
    ?class a rdfs:Class
}
</code></pre>
<pre><code class="language-SPARQL"># Query all available properties, get
# the class they belong to, and the class
# type they point to.
SELECT ?definedOn ?property ?pointsTo {
    ?property a rdf:Property ;
        rdfs:domain ?definedOn ;
        rdfs:range ?pointsTo .
}
</code></pre>
<p>It is even possible to use these introspection capabilities while
querying the stored data:</p>
<pre><code class="language-SPARQL"># Get all string properties of any song.
SELECT ?song ?value {
    ?song a nmm:MusicPiece ;
        ?p ?value .
    ?p rdfs:range xsd:string .
}
</code></pre>
<p>To learn more about how ontologies are done, read the documentation about
<a href="ontologies.html">defining ontologies</a>. Tracker also provides a stock
<a href="nepomuk.html">Nepomuk</a> ontology, ready for use.</p>
<h2 id="inserting-data">Inserting data</h2>
<p>Once you know how RDF data is written, and are moderately acquainted with
the ontology used by the database you are targeting, the syntax to insert
data should be quite evident:</p>
<pre><code class="language-SPARQL"># Add a new music piece
INSERT DATA {
    &lt;song&gt; a nmm:MusicPiece
}
</code></pre>
<p>Just like with SELECT queries, the RDF data in a <code>INSERT DATA</code> clause
may represent complex graphs:</p>
<pre><code class="language-SPARQL"># Add a new music piece, with more information
INSERT DATA {
    &lt;song&gt; a nmm:MusicPiece ;
        nfo:duration 360 ;
        nfo:codec 'MP3' ;
        nmm:dlnaProfile 'MP3' ;
        nie:title "It's a long way to the top" ;
        nmm:musicAlbum &lt;album&gt; ;
        nmm:artist &lt;artist&gt; .
    &lt;album&gt; a nmm:MusicAlbum ;
        nmm:albumDuration 5000 ;
        nie:title "T.N.T." .
    &lt;artist&gt; nmm:artistName "AC DC" .
}
</code></pre>
<h2 id="updating-and-deleting-data">Updating and deleting data</h2>
<p>We have already seen the <code>INSERT DATA</code> and <code>DELETE DATA</code> syntax
that allows specifying pure RDF data to manipulate the stored
data.</p>
<p>It is additionally possible to perform bulk insertions and deletions
over the already stored data. It is worth introducing first to the
full syntax for updates and deletions:</p>
<pre><code class="language-SPARQL">DELETE {
   # Triple data
} INSERT {
   # Triple data
} WHERE {
   # Graph pattern
}
</code></pre>
<p>This query looks for all RDF data that matches the WHERE clause,
and proceeds to deleting and inserting the given triple data for
each of the matches.</p>
<p>The <code>DELETE</code> and <code>INSERT</code> clauses of this query form are optional,
allowing to simply insert data:</p>
<pre><code class="language-SPARQL"># My favorite song is... all of them!
INSERT {
    ?song nao:hasTag nao:predefined-tag-favorite .
} WHERE {
    ?song a nmm:MusicPiece .
}
</code></pre>
<p>Or delete it:</p>
<pre><code class="language-SPARQL"># Delete all songs
DELETE {
    ?song a rdfs:Resource .
} WHERE {
    ?song a nmm:MusicPiece .
}
</code></pre>
<p>This last form can be further reduced if the RDF data
to look for is also the data that should be deleted:</p>
<pre><code class="language-SPARQL"># Delete all songs, again
DELETE WHERE {
    ?song a nmm:MusicPiece, rdfs:Resource .
}
</code></pre>
<p>Going back to the full query form, there is no relation
required between the data being deleted and the data being
inserted. Its use can range from minor updates:</p>
<pre><code class="language-SPARQL"># Replace title of a song
DELETE {
    ?song nie:title ?title
} INSERT {
    ?song nie:title 'Two'
} WHERE {
    ?song a nmm:MusicPiece ;
        nie:title ?title .
    FILTER (?title = 'One')
}
</code></pre>
<p>To outright data substitutions:</p>
<pre><code class="language-SPARQL"># These are not songs, but personal recordings!
DELETE {
    # Delete anything that makes it look like a music piece
    ?notasong a nmm:MusicPiece .
    ?performer a rdfs:Resource .
    ?composer a rdfs:Resource .
} INSERT {
    # And insert my own data
    ?notasong a nfo:Note ;
        nie:title 'My Notes' .
} WHERE {
    ?notasong a nmm:MusicPiece ;
        nmm:performer ?performer ;
        nmm:composer ?composer .
}
</code></pre>
<h2 id="blank-nodes">Blank nodes</h2>
<p>Blank nodes (or anonymous nodes) are nodes without an specified URI, these
are given a query-local name via the special <code>_:</code> prefix:</p>
<pre><code class="language-SPARQL"># Insert a blank node
INSERT DATA {
    _:anon a nmm:MusicPiece
}
</code></pre>
<p>Note that running this query multiple times will insert a different blank
node each time. Unlike e.g.:</p>
<pre><code class="language-SPARQL">INSERT DATA {
    &lt;http://example.com/song1&gt; a nmm:MusicPiece
}
</code></pre>
<p>Where any second insert would be redundantly attempting to add the same
triple to the store.</p>
<p>By default, Tracker deviates from the SPARQL standard in the handling
of blank nodes, these are considered a generator of URIs. The
<a href="tracker-sparql-connection.html#TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES" data-gi-href-javascript="tracker-sparql-connection.html#TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES" data-gi-title-javascript="Tracker.SparqlConnectionFlags.ANONYMOUS_BNODES" data-gi-href-python="tracker-sparql-connection.html#TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES" data-gi-title-python="Tracker.SparqlConnectionFlags.ANONYMOUS_BNODES">TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES</a> flag may be used to
make Tracker honor the SPARQL 1.1 standard with those. The standard
defines blank nodes as truly anonymous, you can only use them to determine
that there is something that matches the graph pattern you defined. The
practical difference could be seen with this query:</p>
<pre><code class="language-SPARQL">SELECT ?u {
    ?u a rdfs:Resource .
    FILTER (isBlank (?u))
}
</code></pre>
<p>Tracker by default will provide you with URNs that can be fed into other
SPARQL queries as URIs. With <a href="tracker-sparql-connection.html#TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES" data-gi-href-javascript="tracker-sparql-connection.html#TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES" data-gi-title-javascript="Tracker.SparqlConnectionFlags.ANONYMOUS_BNODES" data-gi-href-python="tracker-sparql-connection.html#TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES" data-gi-title-python="Tracker.SparqlConnectionFlags.ANONYMOUS_BNODES">TRACKER_SPARQL_CONNECTION_FLAGS_ANONYMOUS_BNODES</a>
enabled, the returned elements will be temporary names that can only be used to
determine the existence of a distinct match. There, blank nodes can match named
nodes, but named nodes do not match with blank nodes.</p>
<p>This nature of blank nodes is however useful to query for elements whose
resource URI is irrelevant, e.g.:</p>
<pre><code class="language-SPARQL"># Query songs, and their disc's artist name
SELECT ?song ?artistName {
    ?song a nmm:MusicPiece ;
        nmm:musicAlbum _:album .
    _:album a nmm:MusicAlbum ;
        nmm:albumArtist _:artist ;
    _:artist a nmm:Artist ;
        nmm:artistName ?artistName .
}
</code></pre>
<p>In this query there is no interest in getting the intermediate album
and artist resources, so blank nodes can be used for them.</p>
<p>Blank nodes have an in-place <code>[ ]</code> syntax, which declares a distinct
empty blank node, it can also contain predicate/object pairs to further
define the structure of the blank node. E.g. the previous query could
be rewritten like:</p>
<pre><code class="language-SPARQL">SELECT ?song ?artistName {
    ?song a nmm:MusicPiece ;
        nmm:musicAlbum [
            a nmm:MusicAlbum ;
            nmm:albumArtist [
                a nmm:Artist ;
                nmm:artistName ?artistName
            ]
        ]
}
</code></pre>
<h2 id="named-graphs">Named graphs</h2>
<p>If SPARQL had a motto, it would be "Everything is a graph". Until
this point of the tutorial we have been talking about a singular "graph",
the triple store holds "a graph", graph patterns match against "the graph",
et cetera.</p>
<p>This is not entirely accurate, or rather, it is an useful simplification.
SPARQL is actually able to work over sets of graphs, or their
union/intersection.</p>
<p>As with everything that has a name in RDF, URIs are also used to reference
named graphs. The <code>GRAPH</code> clause is used to specify the named graph, in
either inserts/deletes:</p>
<pre><code class="language-SPARQL">INSERT DATA {
    GRAPH &lt;http://example.com/MyGraph&gt; {
        &lt;http://example.com/MySong&gt; a nmm:MusicPiece ;
            nie:title "My song" .
    }
}
</code></pre>
<p>Or in queries:</p>
<pre><code class="language-SPARQL">SELECT ?song {
    GRAPH &lt;http://example.com/MyGraph&gt; {
        ?song a nmm:MusicPiece .
    }
}
</code></pre>
<p>So what have been doing all these queries up to this point in the tutorial?
Without any specified graph, all insertions and deletes happen on an anonymous
graph, while all queries happen on a "default" graph that Tracker defines as
the union of all graphs.</p>
<p>These named graphs can exist independently of each other, and they can also
overlap (e.g. specific RDF triples may exist in more than one graph). This
makes it possible to match for specific pieces of data in specific graphs:</p>
<pre><code class="language-SPARQL"># Get info from different graphs, ?file is known in both of them, but none
# has the full information.
SELECT ?fileName ?title {
    GRAPH tracker:FileSystem {
        ?file a nfo:FileDataObject ;
            nfo:fileName ?fileName .
    }
    GRAPH tracker:Pictures {
        ?photo a nmm:Photo ;
            nie:isStoredAs ?file ;
            nie:title ?title .
    }
}
</code></pre>
<p>Tracker heavily relies on these named graph characteristics for its
sandboxing support, where the availability of graphs may be restricted.</p>
<p>It is also possible to use the <code>GRAPH</code> clause with a variable, making it
possible to query for the graph(s) where the graph pattern solutions are
found:</p>
<pre><code class="language-SPARQL"># Query all known graphs, everything matches an empty graph pattern!
SELECT ?graph { GRAPH ?graph { } }
</code></pre>
<h2 id="services">Services</h2>
<p>RDF triple stores are sometimes available as "endpoints", distinct
services available externally via HTTP or other remote protocols, able
to run SPARQL queries.</p>
<p>The SPARQL language itself defines the interoperation with other such
endpoints within SPARQL queries. Since everything is RDF data, and
everything is identified with a URI, this "foreign" RDF data might be
conceptually dealt with as just another graph:</p>
<pre><code class="language-SPARQL"># Get data from the wikidata SPARQL service for my local albums, using
# an intermediate external reference.
SELECT ?album ?wikiPredicate ?wikiObject {
    ?album a nfo:MusicAlbum ;
        tracker:externalReference ?externalReference .
    ?externalReference tracker:referenceSource 'wikidata' .

    SERVICE &lt;http://query.wikidata.org/sparql&gt; {
        ?externalReference ?wikiPredicate ?wikiObject
    }
}
</code></pre>
<p>Tracker provides means to make RDF triple stores publicly available as SPARQL
endpoints both via HTTP(S) and D-Bus protocols. By default, triple stores
are not exported as endpoints, and are considered private to a process.</p>
<h2 id="importing-and-exporting-data">Importing and exporting data</h2>
<p>Bulk insertions of RDF data are available with the <code>LOAD</code> clause:</p>
<pre><code class="language-SPARQL"># Load a file containing RDF data into a named graph
LOAD &lt;file:///path/to/data.rdf&gt; INTO GRAPH &lt;http://example.com/MyGraph&gt;
</code></pre>
<p>Bulk extraction of data can be obtained with the <code>DESCRIBE</code> query clause,
the following query would return all subject/predicate/object triples inserted
by the previous <code>LOAD</code> clause:</p>
<pre><code class="language-SPARQL"># Describe all objects in the named graph
DESCRIBE ?resource {
    GRAPH &lt;http://example.com/MyGraph&gt; {
        ?resource a rdfs:Resource .
    }
}
</code></pre>
<p>The <code>DESCRIBE</code> syntax can also be used to get all triple information describing
specific resources:</p>
<pre><code class="language-SPARQL"># Get all information around the XDG music folder's URI
DESCRIBE &lt;file:///.../Music&gt;
</code></pre>
<p>The data returned by <code>DESCRIBE</code> is RDF triple data, so it can be serialized as
such.</p>
<p>There may also be situations where it is convenient to transform data to other
RDF formats (e.g. a different ontology) from the get go, the <code>CONSTRUCT</code> syntax
exists for this purpose:</p>
<pre><code class="language-SPARQL"># Convert portions of the Nepomuk ontology into another fictional one.
PREFIX ex: &lt;http://example.com&gt;
CONSTRUCT {
    ?file ex:newTitleProperty ?title ;
        ex:newFileNameProperty ?fileName .
} WHERE {
    ?song a nmm:MusicPiece ;
        nie:isStoredAs ?file ;
        nie:title ?title .
}
</code></pre>
<p>The data returned by this syntax is also triple data, but ready for consumption
in the other end.</p>
<h2 id="conclusion">Conclusion</h2>
<p>SPARQL is a rather deep language that takes its purpose (making it possible
to query information in RDF data graphs) very thoroughly, it also has some
unusual features that make it able to scale from small private databases
to large distributed ones.</p>
<p>This is not all that there is, and perhaps the "everything is a graph" mindset
takes a while to think intuitively about to anyone with a background in
relational databases. The purpose that this tutorial hopefully achieved is
that SPARQL queries will now look familiar, and became approachable to reason
about.</p>

    </div>
        




		
	</div>
	<div id="search_results">
		<p>The results of the search are</p>
	</div>
	<div id="footer">
		    

	</div>
</div>

<div id="toc-column">
	
		<div class="edit-button">
		

	</div>
		<div id="toc-wrapper">
		<nav id="toc"></nav>
	</div>
</div>
</div>
</main>


<script src="assets/js/navbar_offset_scroller.js"></script>
</body>
</html>