Notable changes to @triply/triplydb.
Dataset.sparqlQuery(queryString) runs a SPARQL query over a dataset's own data. TriplyDB's
built-in engine answers it, so the dataset needs no service.
What it hands back has not sent anything yet — choose a result form on it, and that form is the
request: bindings() for a SELECT, statements() for a CONSTRUCT or DESCRIBE, boolean() for
an ASK, and toFile(path) for any of them.
const bindings = await dataset.sparqlQuery("select * { ?s ?p ?o } limit 10").bindings();
Each form is a single request carrying the whole result set, so the object is reusable and every call runs the query again. Asking for a form the query cannot answer in is refused rather than coerced.
toFile writes a SELECT or ASK as TSV and a CONSTRUCT or DESCRIBE as N-Triples — the query's
form decides, not the file name you pass — and gzips when the name ends in .gz, or when you
pass { compressed: true }.
Service.sparqlQuery(queryString), the same call against a running service, with the same
result forms. The service has to be running and to offer a SPARQL endpoint, which an
Elasticsearch service does not. It answers from the data it was built with, so one that has
fallen behind its dataset keeps answering from the older data until Service.update brings it up
to date.
Dataset.sparqlUpdate(updateString) applies a SPARQL Update to a dataset. The update is one
transaction — either every operation in it takes effect, or none does. It rewrites the dataset's
own data, so each of its services keeps serving the older data until you call Service.update.
await dataset.sparqlUpdate("insert data { <a:s> <a:p> <a:o> }");
Query.execute(variableValues?) applies a saved query whose text is a SPARQL Update, taking
values for its variables and falling back to the query's own defaults. It applies the version the
object is on, so Query.useVersion decides which update text runs. Only the built-in engine
applies an update, so a query saved to run on a service is refused; Query.results() on update
text is likewise refused, pointing here.
SparqlResults — the class both sparqlQuery methods return — and the request shapes
SparqlQueryInput, SparqlUpdateInput and SparqlRequestInput, all exported from
@triply/triplydb.
Account.addQuery and Query.addVersion accept SPARQL Update text in queryString, storing it
as an update. It was previously rejected, leaving the version
saved as invalid.Query.useVersion("latest") refetches the query, so getInfo() afterwards describes the latest
version rather than the one that was pinned before. This also fixes Query.addVersion on a
pinned query object, which carried the pinned version's settings over into the new version
instead of the latest one's.A major release: three behaviours changed in ways that can break a caller, each listed below with what to use instead. The additions are mostly the documentation itself — a published reference, and declarations that carry their doc comments into your editor.
App.get(idString) is removed, with no deprecation period. The idString notation is incompatible with future TriplyDB account notation for nested groups.
Use the typed getters instead. They have always been the same code underneath:
// before // after
app.get("account") app.getAccount()
app.get("account:my-account") app.getAccount("my-account")
app.get("user") app.getUser()
app.get("user:my-user") app.getUser("my-user")
app.get("group:my-group") app.getGroup("my-group")
app.get("dataset:my-dataset") (await app.getAccount()).getDataset("my-dataset")
app.get("query:my-query") (await app.getAccount()).getQuery("my-query")
app.get("story:my-story") (await app.getAccount()).getStory("my-story")
The static factory App.get(config) is unchanged and unrelated.
IncompatibleError now extends TriplyDbJsError. A single
catch (e) { if (e instanceof TriplyDbJsError) … } now catches every error this library throws,
including the version-incompatibility one it previously missed. Code that branches on
IncompatibleError after a TriplyDbJsError branch will now take the earlier branch — test
for IncompatibleError first.
Dataset.uploadAsset names an asset after the file, not the path. Passing a path used to
create an asset called ./data/logo.png; it is now called logo.png. Pass name explicitly to
keep the old value.
import { … } from "@triply/triplydb" now reaches the public surface from one place: the
classes, the options types their methods take, and the shapes their getInfo() methods return.
The deep paths (@triply/triplydb/Dataset and so on) still work, and
import App from "@triply/triplydb" is unchanged.ServiceConfigElastic and ServiceConfigJena, the shapes Dataset.addService takes as config.
Their documentation covers what an Elasticsearch service indexes without being configured, and
how index and component templates change that.User.accountType, which is "User". Together with Group.accountType it narrows an Account
to one or the other without a request.AsyncIteratorHelper is ResultIterator
and AsyncIteratorHelperWithToFile is QueryResultIterator, each exported from the package
root. The old module paths — @triply/triplydb/utils/AsyncIteratorHelper and
…/AsyncIteratorHelperWithToFile — still resolve, re-exporting the new classes, so nothing
breaks; they carry a deprecation pointing at the replacement.Dataset.importFromFiles no longer waits forever on an upload that goes quiet. A file that makes
no progress at all for five minutes fails the import, with an error naming the file and the job
to check.Graph.rename returns the graph when the new IRI is the one it already has, as it does for a
real rename, instead of undefined.Query.runAsSelectQueryJob throws a TriplyDbJsError on a query that is not a SELECT, like every
other failure in this library, instead of a plain Error.fileCache creates its directory when it does not exist, as it was documented to.Organizations became groups, and groups gained membership and nesting. Nested groups are an upcoming feature of triplyDB.
App.getGroup, User.getGroups,
User.getGroup, User.createGroup, User.ensureGroup, Account.asGroup, and a Group class
exported from @triply/triplydb/Group.Group.getMembers, Group.addMember, Group.removeMember,
Group.changeRole and Group.delete.User.createGroup and
User.ensureGroup, and afterwards through Group.update.parent:child.Group.accountType, which is "Group".Dataset.getInfo takes an options object as well as a boolean, with a signal for aborting the
request: getInfo({ refresh: true, signal }).Nothing was removed. The organization names still work and now carry a deprecation, so an editor
points at the replacement: asOrganization, createOrganization, getOrganizations,
getOrganization, ensureOrganization, App.getOrganization, App.get("org:…"), the
@triply/triplydb/Org module, and Group.type (use Group.accountType).
Account.importDataset failed outright when the target instance had assets switched off. It now
copies what it can and carries on..js extension — @triply/triplydb/Dataset.js, as 8.x accepted —
stopped resolving when the package moved to an exports map. They resolve again.Account.importDataset failed when the source dataset had topics and the target instance did not
support them.Query.runAsSelectQueryJob declared its results as bindings, which they are not — it yields one
parsed JSON object per line, the first naming the projected variables.Query.runAsSelectQueryJob, which runs a SELECT query as a batch job and streams the results, so
a query too large or slow to page through in process can still be read.Query.update accepts a Dataset for its dataset field, not only a dataset id.Query.getString() threw a TypeError on a saved query with no request payload, instead of the
"has no versions" error it means to report.