Imagine a data table, where each row is an item and each column is a property.
It might look like this:
url | name | published |
---|---|---|
http://example.com/1 | Item One | 2015–09–10 |
http://example.com/2 | Item Two | 2015–09–11 |
The table is a representation of a collection of objects, each with several properties.
Using JavaScript notation, they would look like this:
[
{
url: 'http://example.com/1',
name: 'Item One',
published: '2015-09-10',
},
{
url: 'http://example.com/2',
name: 'Item Two',
published: '2015-09-11',
}
]
An abstract definition of the object, using Polymer’s notation, would look like this:
{
properties: {
url: {
type: URL
},
name: {
type: String
},
published: {
type: String
}
}
}
You might notice that the published
property is represented as a String, when it would be easier to use as a Date object. To convert it, you could add a “computed property”: a function that takes one or more existing properties as input, and outputs a new property:
{
properties: {
url: {
type: URL
},
name: {
type: String
},
published: {
type: String
},
publishedDate: {
type: Date,
computed: function(published) {
return new Date(published)
}
}
}
}
From this definition, you can see that the publishedDate
property has a dependency on the published
property: any computed properties should be updated when any of its dependencies are updated. In this case, when the published
property is updated, the publishedDate
property is also updated.
This is fine when the dependencies are all stored locally, but it’s also possible to imagine data that’s stored elsewhere. For example, this object might have associated metrics data counting how many times it’s been viewed:
{
properties: {
url: URL,
name: String,
published: String,
publishedDate: {
type: Date,
computed: function(published) {
return new Date(published)
}
},
viewCount: {
type: Number,
computed: function(url) {
return Resource(url).get('json').then(function(data) {
return data.views
});
}
}
}
}
The Resource
object used above is a Web Resource, part of a library I built to make it easier to fetch and parse remote resources. If it helps, an alternative using the standard Fetch API would look like this:
return fetch(url).then(function(response) {
return response.json()
}).then(function(data) {
return data.views
});
In either of those cases, the data is being fetched asynchronously, and a Promise is returned. Once the Promise is resolved, the viewCount
property is updated. If this property was bound to the original table, you would see the new values being filled in as the data arrives!
url | name | published | views |
---|---|---|---|
http://example.com/1 | Item One | 2015–09–10 | 1000 |
http://example.com/2 | Item Two | 2015–09–11 | 2000 |
Implementations
I talked about this kind of thing at XTech in 2008, illustrating the object as a Katamari Damacy-style of “ball of stuff”, being passed around various different services and accumulating properties as it goes.
vege-table is an implementation of asynchronous composable resources: it’s an interface for adding computed properties to a collection of items and fetching the data asynchronously into a table.
Talis’ data platform had a similar feature, where results from a SPARQL query could be augmented by passing each result through another data store, matching on identifiers and adding selected properties each time.
The SERVICE feature of Wikidata’s SPARQL endpoint is also similar: it takes an object in each result and passes it to a specific service, assigning the resulting data to a specified property.
In a Google Sheet, adding a column/property using the IMPORT*
functions will fetch data from a remote resource, inserting the value into the table when it arrives.
In OpenRefine, remote data can be fetched from web services and added to each item in the background.
Are there any others?