Introduction
We would urge you to first do this tutorial and then study the Allegro Prolog documentation if necessary. This is a basic tutorial on how to use Prolog with AllegroGraph 2.2.5. It should be enough to get you going but if you have any questions please write to us and we will help you. In this example we will focus mainly on how to use the following constructs..
- select
- q
- <--
- <-
As the basis for this tutorial we will use a tiny genealogy of the Kennedy family. (My first example was the Royal Dutch family but my colleague Sheng-Chuan noticed that it tended to confuse non-Dutch users so he created this wonderful example)
Please open the file kennedy.ntriples that came with this distribution in an editor or with TopBraidComposer and study the contents of the file. Notice that people in this file have a type, sometimes multiple children, multiple spouses, multiple professions, and go to multiple colleges or universities.
This tutorial uses Lisp as the base language but there is also a Java example with the same content.
First let us get AllegroGraph ready to use:
> (require :agraph)
;; .... output deleted.
> (in-package :triple-store-user)
#<The db.agraph.user package>
> (register-namespace "ex" "http://www.franz.com/simple#"
:errorp nil)
"http://www.franz.com/simple#"
Now we can create a triple-store and load it with data. The function create-triple-store creates a new triple-store and opens it. If you use the triple-store name "temp/test", then AllegroGraph will create a new directory named temp in your current directory (use the top-level command :pwd if you want to see what this is). It will then make another directory named test as a sub-directory of temp. All of this triple-store's data will be placed in this new directory temp/test:
> (defun fill-kennedy-db ()
(create-triple-store "temp/test"
:if-exists :supersede)
(time
(load-ntriples #p"sys:agraph;tutorial-files;kennedy.ntriples"))
(index-all-triples))
fill-kennedy-db
> (fill-kennedy-db)
;; .... output deleted.
So let us first look at person 1 in this database:
> (print-triples
(get-triples-list :s !ex:person1))
<http://www.franz.com/simple#person1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.franz.com/simple#person> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#first-name> <http://www.franz.com/simple#Joseph> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#middle-initial> <http://www.franz.com/simple#Patrick> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#last-name> <http://www.franz.com/simple#Kennedy> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#birth-year> <http://www.franz.com/simple#1888> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#death-year> <http://www.franz.com/simple#1969> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#sex> <http://www.franz.com/simple#male> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#spouse> <http://www.franz.com/simple#person2> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#suffix> <http://www.franz.com/simple#none> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person9> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person13> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person17> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person4> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person6> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person15> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person11> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person3> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#has-child> <http://www.franz.com/simple#person7> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#profession> <http://www.franz.com/simple#producer> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#profession> <http://www.franz.com/simple#banker> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#profession> <http://www.franz.com/simple#ambassador> .
<http://www.franz.com/simple#person1> <http://www.franz.com/simple#alma-mater> <http://www.franz.com/simple#Harvard> .
Now we are ready to try the select statement in combination with the Prolog q functor. Let us try to find all the children of person1. Just type the following in the listener. Afterward, I'll explain.
> (select (?x)
(q !ex:person1 !ex:has-child ?x))
(("http://www.franz.com/simple#person9")
("http://www.franz.com/simple#person13")
("http://www.franz.com/simple#person17")
("http://www.franz.com/simple#person4")
("http://www.franz.com/simple#person6")
("http://www.franz.com/simple#person15")
("http://www.franz.com/simple#person11")
("http://www.franz.com/simple#person3")
("http://www.franz.com/simple#person7"))
Select is a wrapper used around one or more Prolog statements. The first element after select is template for the format and variables that you want to bind and return. So in this example above we want to bind the variable ?x. The rest of the elements tell Prolog what we want to bind ?x to.
This select statement has only one clause, namely (q !ex:person1 !ex:has-child ?x)
If you have studied how get-triples works you probably can guess what happens here. q is a Prolog functor that is our link to the data in the triple-store. It calls get-triples and unifies the ?x with the objects of all triples with subject !ex:person1 and predicate !ex:has-child.
So let us make it a little bit more complex. Let us find all the children of the children of person1. Here is how you do it:
> (select (?y)
(q !ex:person1 !ex:has-child ?x)
(q ?x !ex:has-child ?y))
(("http://www.franz.com/simple#person33")
("http://www.franz.com/simple#person26")
("http://www.franz.com/simple#person28")
("http://www.franz.com/simple#person31")
("http://www.franz.com/simple#person25")
("http://www.franz.com/simple#person62")
("http://www.franz.com/simple#person56")
("http://www.franz.com/simple#person42")
("http://www.franz.com/simple#person47")
("http://www.franz.com/simple#person51") ...)
Although Prolog is a declarative language, a procedural reading of this query works better for most people. So the previous query can be read as
Find all triples that start with !ex:person1 !ex:has-child. For each match set ?x to the object of that triple; then for each triple that starts with ?x !ex:has-child find the ?y
The following example should now be easy to understand. Here we are trying to find all the spouses of the grand-children of ?z. Notice that we ignore the ?x and ?y in the query. The select will only return the ?z
> (select (?z)
(q !ex:person1 !ex:has-child ?x)
(q ?x !ex:has-child ?y)
(q ?y !ex:spouse ?z))
(("http://www.franz.com/simple#person34")
("http://www.franz.com/simple#person27")
("http://www.franz.com/simple#person30")
("http://www.franz.com/simple#person32")
("http://www.franz.com/simple#person63")
("http://www.franz.com/simple#person57")
("http://www.franz.com/simple#person43")
("http://www.franz.com/simple#person49")
("http://www.franz.com/simple#person48")
("http://www.franz.com/simple#person52") ...)
Now if you wanted to you could get the other variables back. Here is the same query but now you also want to see the grand-child.
> (select (?y ?z)
(q !ex:person1 !ex:has-child ?x)
(q ?x !ex:has-child ?y)
(q ?y !ex:spouse ?z))
(("http://www.franz.com/simple#person33" "http://www.franz.com/simple#person34")
("http://www.franz.com/simple#person26" "http://www.franz.com/simple#person27")
("http://www.franz.com/simple#person28" "http://www.franz.com/simple#person30")
("http://www.franz.com/simple#person31" "http://www.franz.com/simple#person32")
("http://www.franz.com/simple#person62" "http://www.franz.com/simple#person63")
("http://www.franz.com/simple#person56" "http://www.franz.com/simple#person57")
("http://www.franz.com/simple#person42" "http://www.franz.com/simple#person43")
("http://www.franz.com/simple#person47" "http://www.franz.com/simple#person49")
("http://www.franz.com/simple#person47" "http://www.franz.com/simple#person48")
("http://www.franz.com/simple#person51" "http://www.franz.com/simple#person52") ...)
So now we understand the select and the q statement. We are halfway there. Let us now define some Prolog functors.
The following defines a functor that says: ?x is a male if in the triple store I can find an ?x that has the !ex:sex !ex:male.
> (<-- (male ?x)
(q ?x !ex:sex !ex:male))
male
Let us try it out by finding all the sons of person1.
> (select (?x)
(q !ex:person1 !ex:has-child ?x)
(male ?x)) ;;; Note how we use NO q here!
(("http://www.franz.com/simple#person13")
("http://www.franz.com/simple#person17")
("http://www.franz.com/simple#person4")
("http://www.franz.com/simple#person3"))
Now this is not too exciting, and it is equivalent to the following:
(select (?x)
(q !ex:person1 !ex:has-child ?x)
(q ?x !ex:sex !ex:male))
So let us make it more complex:
> (<-- (female ?x)
(q ?x !ex:sex !ex:female))
female
> (<-- (father ?x ?y)
(male ?x)
(q ?x !ex:has-child ?y))
father
> (<-- (mother ?x ?y)
(female ?x)
(q ?x !ex:has-child ?y))
mother
The female, father, mother relations are all simple to understand. The following adds the idea of multiple rules (or functors). Notice how we define the parent relationship with two rules, where the first rule uses <-- and the second rule uses <-. The reason is that <-- means: wipe out all the previous rules that I had about parent and start anew whereas <- means, add to the existing rules for parent.
The following should be read as:
?x is the parent of ?y if ?x is the father of ?y or
?x is the parent of ?y if ?x is the mother of ?y.
> (<-- (parent ?x ?y)
(father ?x ?y))
parent
> (<- (parent ?x ?y)
(mother ?x ?y))
parent
So let us try it out by finding the grand children of person1
> (select (?y)
(parent !ex:person1 ?x)
(parent ?x ?y))
(("http://www.franz.com/simple#person33")
("http://www.franz.com/simple#person26")
("http://www.franz.com/simple#person28")
("http://www.franz.com/simple#person31")
("http://www.franz.com/simple#person25")
("http://www.franz.com/simple#person62")
("http://www.franz.com/simple#person56")
("http://www.franz.com/simple#person42")
("http://www.franz.com/simple#person47")
("http://www.franz.com/simple#person51") ...)
We could have done the same thing by defining a grandparent functor. See the next definition.
> (<-- (grandparent ?x ?y)
(parent ?x ?z)
(parent ?z ?y))
grandparent
> (<-- (grandchild ?x ?y)
(grandparent ?y ?x))
grandchild
And here it gets really interesting because we now go for the first time to a recursive functor.
> (<-- (ancestor ?x ?y)
(parent ?x ?y))
ancestor
> (<- (ancestor ?x ?y)
(parent ?x ?z)
(ancestor ?z ?y))
ancestor
Read the previous two expressions as
?x is the ancestor of ?y if
- ?x is the parent of ?y or
- ?x is the parent of some person ?z and ?z is the ancestor of ?y
A descendant is of course the reverse of ancestor
> (<-- (descendant ?x ?y)
(ancestor ?y ?x))
descendant
So if we want to find all the male descendants of person1 then here is how to do it.
> (select (?x)
(descendant ?x !ex:person1)
(male ?x))
(("http://www.franz.com/simple#person13")
("http://www.franz.com/simple#person17")
("http://www.franz.com/simple#person4")
("http://www.franz.com/simple#person3")
("http://www.franz.com/simple#person33")
("http://www.franz.com/simple#person28")
("http://www.franz.com/simple#person31")
("http://www.franz.com/simple#person25")
("http://www.franz.com/simple#person62")
("http://www.franz.com/simple#person47") ...)
And then here are some puzzles that you can work out for yourself.. Note the use of not and part= in these statements. 'not' can contain any expression. Part= will compare its two arguments as UPIs; It will not unify.
> (<-- (aunt ?x ?y)
(father ?z ?x)
(female ?x)
(father ?z ?w)
(not (part= ?x ?w))
(parent ?w ?y))
aunt
> (<-- (uncle ?x ?y)
(father ?z ?x)
(male ?x)
(father ?z ?w)
(not (part= ?x ?w))
(parent ?w ?y))
uncle
And the final query: find all the children of person1 that are uncles
> (select (?x ?y)
(parent !ex:person1 ?x)
(uncle ?x ?y))
(("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person33")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person26")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person28")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person31")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person25")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person62")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person56")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person42")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person47")
("http://www.franz.com/simple#person13"
"http://www.franz.com/simple#person51")
...)
>
-
Remember that
selectis a macro and that using it in the Lisp REPL will produce interpreted code. This means that selects run from the REPL may be significantly slower than those that you write inside of compiled functions. (Note also that both the HTTP and the Java interfaces to AllegroGraph ensure that any select calls get compiled before they run). ↩