SemWeb: Docs: Selecting Statements

SemWeb Documentation

# Selecting Statements

## Using statement templates

In the previous tutorial you saw the `Select` method of `StatementSource`s, 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`. `null`s 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);
        }
    }