Friday, December 16, 2005

Think Ning Triples : How to build an object content store searcher thingamus

I've been thinking about patterns a lot more recently. I'm really fond of the Ning style of chaining together objects magically.

$foo = Foo::factory()->name("bar")->age("old");

I'm also really fond of thinking in triples: I just haven't gotten PHP to do triples nicely. In other RDF handling code it's awfully complicated when you start getting to literals, resources, and all sorts: you get dazed and confused before you know it.

In an effort to better grok a php + triples + ning pattern, I gave the below a bit of a bash.


<?php
class Relation {

public $s;
public $p;
public $o;

public static function factory() {
return new Relation();
}


public function p($predicate) {

$this->p = &$predicate;

return $this;
}

public function s($subject) {

$this->s = &$subject;

return $this;
}

public function o($object) {

$this->o = &$object;

return $this;
}

public function __toString() {

ob_start();

print $this->s;
print ' ';
print $this->p;
print ' ';
print $this->o;

return ob_get_clean();
}
}


class Person {

public $email;

public function __construct($email) {
$this->email = $email;
}

public function __toString() {
return $this->email;
}
}

$bob = new Person("mailto:bob@bob.com");
$susan = new Person("mailto:susan@susan.com");
$frank = new Person("mailto:frank@frank.com");

$graph = array();
$graph[0] = Relation::factory()->s($susan)->p("likes")->o($bob);
$graph[1] = Relation::factory()->s($graph[0])->p("=")->o(.50);

$graph[2] = Relation::factory()->s($bob)->p("hates")->o($frank);
$graph[3] = Relation::factory()->s($bob)->p("hates")->o($susan);

$graph[4] = Relation::factory()->s($graph[2])->p("=")->o(.77);
$graph[5] = Relation::factory()->s($graph[3])->p("=")->o(.11);

function whoLikesBob($graph) {
$bob = new Person("mailto:bob@bob.com");
$susan = new Person("mailto:susan@susan.com");

$target = Relation::factory()->p("likes")->o($bob);

$pattern = array("s" => false, "p" => true, "o" => true);
$triple = find($graph, $target, $pattern);

print "Who likes bob? ";
print $triple;
}

function find($graph, $targetTriple,
$matchPattern = array("s" => true, "p" => true, "o" => true)) {
$match = array("s" => false, "p" => false, "o" => false);
foreach ($graph as $triple) {

foreach (array("s","p","o") as $property) {
if (isset($targetTriple->$property)) {
if ($triple->$property == $targetTriple->$property) {
$match[$property] = true;
}
}
}

if ($match == $matchPattern) {
return $triple;
}
}
}

foreach ($graph as $triple) {
print $triple;
print "\n";
}


whoLikesBob($graph);
?>



What does it do? The triple pattern (Foo::s(), Foo::p(), Foo::o()) allows us to just stick anything into anywhere and define a predicate of any kind.
find() is a really simple method to get an array of triples, and a target triple and return that. Not wanting to build a SPARQL parser or anything that large, we've just got a simple match pattern.
find($graph, $triple, array("s" => true, "p" => false, "o" => true)) simply matches the subject (s) and object (o) properties, and doesn't even inspect the predicate.

find() could be quite readily changed to return an array of triples, which you then can render, manipulate, etc.

No comments: