Roy
Roy

Reputation: 313

Best approach to create URIs with Sparql (like auto increment)

I am currently writing a service which creates new items (data) by user input. To save these items in a RDF Graph Store (currently using Sesame via Sparql 1.1), I need to add a subject URI to the data. My approach is to use a number that is incremented for each new item. So e.g.:

<http://example.org/item/15> dct:title "Example Title" .
<http://example.org/item/16> dct:title "Other Item" .

What's the best approach to get an incremented number for new items (like auto incement in MySQL/MongoDB) via Sparql? Or to issue some data and the endpoint autmatically creates a URI by a template (like done for blank nodes). But I don't want to use blank nodes as subjects for these items. Is there a better solution than using an incremented number? My users don't care about the the URI.... and I don't want to handle collisions like created by hashing the data and using the hash as part of the subject.

Upvotes: 3

Views: 2049

Answers (3)

Damyan Ognyanov
Damyan Ognyanov

Reputation: 786

If you maintain a designated counter during updates, then something along these lines will do it,

fisrt insert a counter into your dataset

insert data {
    graph <urn:counters> {<urn:Example> <urn:count> 1 }
}

then a typical update should looks like:

PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
delete {
    #remove the old value of the counter
    graph <urn:counters> {<urn:Example> <urn:count> ?old}
} 
insert {
    #update the new value of the counter
    graph <urn:counters> {<urn:Example> <urn:count> ?new}

    # add your new data using the constructed IRI
    GRAPH <http://example.com> {
        ?id dct:title "Example Title" ;
            a <http://example.org/ontology/Example> .
    } .
} where {
    # retrieve the counter
    graph <urn:counters> {<urn:Example> <urn:count> ?old}

    # compute the new value
    bind(?old+1 as ?new)    

    #construct the IRI
    bind(IRI(concat("http://example.org/item/", str(?old))) as ?id)
}

Upvotes: 3

cygri
cygri

Reputation: 9482

You said you are open to other options than an auto-incremented number. One good alternative is to use UUIDs.

If you don't care at all what the URI looks like, you can use the UUID function:

INSERT {
    ?uri dct:title "Example Title"
}
WHERE {
    BIND (UUID() AS ?uri)
}

This will generate URIs like <urn:uuid:b9302fb5-642e-4d3b-af19-29a8f6d894c9>.

If you'd rather have HTTP URIs in your own namespace, you can use strUUID:

INSERT {
    ?uri dct:title "Example Title"
}
WHERE {
    BIND (IRI(CONCAT("http://example.org/item/", strUUID())) AS ?uri)
}

This will generate URIs like http://example.org/item/73cd4307-8a99-4691-a608-b5bda64fb6c1.

UUIDs are pretty good. Collision risk is negligible. The functions are part of the SPARQL standard. The only downside really is that they are long and ugly.

Upvotes: 2

mommi84
mommi84

Reputation: 654

Assuming the class of your items is http://example.org/ontology/Example, the query becomes the following. Note: items must be inserted one by one, as only one new URI is computed at each transaction.

PREFIX dct: <http://purl.org/dc/terms/>
INSERT {
    GRAPH <http://example.com> {
        ?id dct:title "Example Title" ;
            a <http://example.org/ontology/Example> .
    } .
} WHERE {
    SELECT ?id WHERE {
        {
            SELECT (count(*) AS ?c) WHERE {
                GRAPH <http://example.com> { ?s a <http://example.org/ontology/Example> }
            }
        }
        BIND(IRI(CONCAT("http://example.org/item/", STR(?c))) AS ?id)
    }
}

(Tested with GraphDB 8.4.0 using RDF4J 2.2.2)

Upvotes: 2

Related Questions