SemWeb Documentation

Selecting Statements

Using statement templates

In the previous tutorial you saw the Select method of StatementSources, which streams all of the statements into a sink. The name "Select" was derived from SQL syntax, where it's used to retrieve rows that match a criteria. In SemWeb, Select is used to retreive statements matching a filter. (And as with SQL, when no criteria is given all statements are retrieved.)

A template is a Statement but with subject, predicate, and object possibly null. nulls are wildcards. So here are a few examples:

store.Select(sink);  // selects all statements, streaming them into sink
store.Select(new Statement(null, null, null), sink);  // the same
store.Select(Statement.All, sink);  // the same, but shorthand

store.Select(new Statement(subj, null, null), sink);  // any statement with that particular subject entity
store.Select(new Statement(subj, pred, null), sink);  // any statement with that particular subject and predicate
store.Select(new Statement(subj, pred, obj), sink);  // just that statement, if it exists in the store

The sink can be any StatementSink. This includes RdfWriters, which would let you write out just a part of a store to a file, and Stores like the MemoryStore so that you can move statements between data sources.

Stores provide a few convenience methods. Two methods are provided for getting all of the subjects found with a given predicate and object, and similarly all objects found with a given subject and predicate. This can be used to move around in a graph:

foreach (Resource r in store.SelectObjects(person, foafname))
	Console.WriteLine("His name is: " + r);

Other convenience methods are overrides of Select that rather than sending the results to a sink, load them into memory so that you may for-each over them:

foreach (Statement statement in store.Select(new Statement(null, rdftype, foafPerson))) {
	...
}

You obviously shouldn't use these on data sources of unbounded size as you wouldn't necessarily want to load the results all into memory.

These convenience methods are only provided in the Store class and its subclasses. If you want to use them on data from a file or other data source that doesn't extend Store, load the data into a MemoryStore:

Store store = new MemoryStore();
store.Import(RdfReader.LoadFromUri(new Uri("http://dannyayers.com/misc/foaf/foaf.rdf")));

Here's an example program that loads a FOAF document and extracts some information using Select:

using System;
using SemWeb;

public class Select {
    const string RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
    const string FOAF = "http://xmlns.com/foaf/0.1/";
    
    static readonly Entity rdftype = RDF+"type";
    static readonly Entity foafPerson = FOAF+"Person";
    static readonly Entity foafknows = FOAF+"knows";
    static readonly Entity foafname = FOAF+"name";

    public static void Main() {
        Store store = new MemoryStore();
        store.Import(RdfReader.LoadFromUri(new Uri("http://dannyayers.com/misc/foaf/foaf.rdf")));
        
        Console.WriteLine("These are the people in the file:");
        foreach (Statement s in store.Select(new Statement(null, rdftype, foafPerson))) {
            foreach (Resource r in store.SelectObjects(s.Subject, foafname))
                Console.WriteLine(r);
        }
        Console.WriteLine();

        Console.WriteLine("And here's RDF/XML just for some of the file:");
        using (RdfWriter w = new RdfXmlWriter(Console.Out)) {
            store.Select(new Statement(null, foafname, null), w);
            store.Select(new Statement(null, foafknows, null), w);
        }
        Console.WriteLine();    
    }
}

Other notes

RDF collections, like Bag, Alt, and Seq, in RDF/XML use a strange rdf:li pseudo-property. rdf:li isn't actually a property. It is an abbreviation for rdf:_1, rdf:_2, etc. in that order. Thus when you select for members of a collection, you can't use rdf:li. However, RDFS defines the property rdfs:member which rdf:_## properties are all subproperties of. The SemWeb stores all recognize the rdfs:member predicate and will match it to any of the rdf:_## predicates.

In addition, the SelectObjects method of the Store class will automatically sort the objects by their collection order, where possible, when you call the method with the rdfs:member predicate.

Here's an example of that:

// This example deals with RDF containers.  You can use the rdfs:member
// property to match any rdf:_### (i.e. rdf:li) property.  Or,
// use SelectObjects on the Store, which will return the items
// in sorted order.

using System;
using SemWeb;

public class Containers {

    const string RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
    const string RDFS = "http://www.w3.org/2000/01/rdf-schema#";
    
    public static void Main() {
        MemoryStore store = new MemoryStore();
        
        Entity container = new Entity("http://www.example.org/#container");
        
        store.Add(new Statement(container, RDF+"type", (Entity)(RDF+"Bag")));
        store.Add(new Statement(container, RDF+"_3", (Literal)"Three"));
        store.Add(new Statement(container, RDF+"_2", (Literal)"Two"));
        store.Add(new Statement(container, RDF+"_1", (Literal)"One"));
        
        // use the rdfs:member property to match for any rdf:_### predicates.
        Entity rdfs_member = (Entity)(RDFS+"member");
        
        using (RdfWriter writer = new N3Writer(Console.Out)) {
            writer.Namespaces.AddNamespace(RDF, "rdf");
            store.Select(new Statement(container, rdfs_member, null), writer);
        }
        
        foreach (Resource r in store.SelectObjects(container, rdfs_member))
            Console.WriteLine(r);
    }
}