Skip to content

Commit

Permalink
Merge branch 'master' of github.com:mcollina/levelgraph-jsonld
Browse files Browse the repository at this point in the history
  • Loading branch information
jmatsushita committed Jan 14, 2017
2 parents 5996f54 + ed3469e commit 349649c
Show file tree
Hide file tree
Showing 6 changed files with 679 additions and 95 deletions.
52 changes: 24 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,10 @@ var levelup = require("levelup"),
db = jsonld(levelgraph(yourDB), opts);
```

By default `update`s (and `delete`s) **delete all triples associated to the `@id` of the document before updating it**, the `'preserve'` option will ensure that all `put`s and `delete`s only update the triples that are mentioned in the document:


```javascript
var levelup = require("levelup"),
yourDB = levelup("./yourdb"),
levelgraph = require('levelgraph'),
jsonld = require('levelgraph-jsonld'),
opts = {
base: 'http://matteocollina.com/base',
preserve: true
},
db = jsonld(levelgraph(yourDB), opts);
```
> From v1, overwriting and deleting is more conservative. If you rely on the previous behavior you can set the `overwrite` option to `true` (when creating the db or as options to `put` and `del`) to:
> - overwrite all existing triples when using `put`
> - delete all blank nodes recursively when using `del` (cf upcoming `cut` function)
> This old api will be phased out.
### Put

Expand Down Expand Up @@ -153,14 +143,6 @@ db.jsonld.put(nested, function(err, obj) {
});
```

By default, `put` **deletes all triples associated to the `@id` of the document before updating it**. If you want to instead only update the triples that are part of the document you can use the `{ preserve : true }` option (you can also [set it globally when you create the DB](#usage)):

```javascript
db.jsonld.put(nested, { preserve : true }, function(err, obj) {
// do something...
});
```

### Get

Retrieving a JSON-LD object from the store requires its `'@id'`:
Expand Down Expand Up @@ -212,19 +194,33 @@ db.jsonld.put(nested, function(err, obj) {

### Delete

In order to delete an object, you can just pass it's `'@id'` to the
`'@del'` method:
In order to delete an object, you need to pass the document to the `'del'` method which will delete only the properties specified in the document:
```javascript
db.jsonld.del(manu['@id'], function(err) {
db.jsonld.del(manu, function(err) {
// do something after it is deleted!
});
```

By default, `del` **deletes all triples associated to the `@id`**. If you want to instead only delete the triples that are part of the document, you can use the `{ preserve : true }` option (you can also [set it globally when you create the DB](#usage)):
Note that blank nodes are ignored, so to delete blank nodes you need to pass the `cut: true` option (you can also add the `recurse: true`option) or use the `'cut'` method below.

> Note that since v1 `'del'` doesn't support passing an IRI anymore.
### Cut

In order to delete the blank nodes object, you can just pass it's `'@id'` to the
`'cut'` method:
```javascript
db.jsonld.del(nested, { preserve : true }, function(err) {
// do something...
db.jsonld.cut(manu['@id'], function(err) {
// do something after it is cut!
});
```

You can also pass an object, but in this case the properties are not used to determine which triples will be deleted and only the `@id`s are considered.

Using the `recurse` option you can follow all links and blank nodes (which might result in deleting more data than you expect)
```javascript
db.jsonld.cut(manu['@id'], { recurse: true }, function(err) {
// do something after it is cut!
});
```

Expand Down
109 changes: 66 additions & 43 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ function levelgraphJSONLD(db, jsonldOpts) {

function doDel(obj, options, callback) {
var blanks = {};

jsonld.expand(obj, options, function(err, expanded) {
if (err) {
return callback && callback(err);
Expand All @@ -136,6 +135,7 @@ function levelgraphJSONLD(db, jsonldOpts) {
var stream = graphdb.delStream();
stream.on('close', callback);
stream.on('error', callback);

if (options.base) {
if (expanded['@context']) {
expanded['@context']['@base'] = options.base;
Expand Down Expand Up @@ -190,6 +190,38 @@ function levelgraphJSONLD(db, jsonldOpts) {
})
}

function doCut(obj, options, callback) {
var iri = obj;
if (typeof obj !=='string') {
iri = obj['@id'];
}
if (iri === undefined) {
return callback && callback(null);
}

var stream = graphdb.delStream();
stream.on('close', callback);
stream.on('error', callback);

(function delAllTriples(iri, done) {
graphdb.get({ subject: iri }, function(err, triples) {
async.each(triples, function(triple, cb) {
stream.write(triple);
if (triple.object.indexOf('_:') === 0 || (options.recurse && N3Util.isIRI(triple.object))) {
delAllTriples(triple.object, cb);
} else {
cb();
}
}, done);
});
})(iri, function(err) {
if (err) {
return callback(err);
}
stream.end();
});
}

graphdb.jsonld.put = function(obj, options, callback) {

if (typeof obj === 'string') {
Expand All @@ -202,68 +234,59 @@ function levelgraphJSONLD(db, jsonldOpts) {
}

options.base = options.base || this.options.base;
options.preserve = options.preserve || this.options.preserve || false;
options.overwrite = options.overwrite !== undefined ? options.overwrite : ( this.options.overwrite !== undefined ? this.options.overwrite : false );

graphdb.jsonld.del(obj, options, function(err) {
if (err) {
return callback && callback(err);
}
if (!options.overwrite) {
doPut(obj, options, callback);
});
} else {
graphdb.jsonld.del(obj, options, function(err) {
if (err) {
return callback && callback(err);
}
});
doPut(obj, options, callback);
}
};

graphdb.jsonld.del = function(obj, options, callback) {
var blanks = {};

if (typeof options === 'function') {
callback = options;
options = {};
}

options.cut = options.cut !== undefined ? options.cut : ( this.options.cut !== undefined ? this.options.cut : false );
options.recurse = options.recurse !== undefined ? options.recurse : ( this.options.recurse !== undefined ? this.options.recurse : false );

if (typeof obj === 'string') {
try {
obj = JSON.parse(obj);
} catch (e) {
// Handle case where we're trying to delete by passing an IRI
if (!N3Util.isIRI(obj)) {
throw e
if (N3Util.isIRI(obj) && !options.cut) {
callback(new Error("Passing an IRI to del is not supported anymore. Please pass a JSON-LD document."))
}
}
}

if (!options.cut) {
doDel(obj, options, callback)
} else {
doCut(obj, options, callback)
}
};


graphdb.jsonld.cut = function(obj, options, callback) {

if (typeof options === 'function') {
callback = options;
options = {};
}

options.preserve = options.preserve || this.options.preserve || false;

if (options.preserve === false) {
var iri = obj;
if (typeof obj !=='string') {
iri = obj['@id'];
}

var stream = graphdb.delStream();
stream.on('close', callback);
stream.on('error', callback);
options.recurse = options.recurse || this.options.recurse || false;

(function delAllTriples(iri, done) {
graphdb.get({ subject: iri }, function(err, triples) {
async.each(triples, function(triple, cb) {
stream.write(triple);
if (triple.object.indexOf('_:') === 0) {
delAllTriples(triple.object, cb);
} else {
cb();
}
}, done);
});
})(iri, function(err) {
if (err) {
return callback(err);
}
stream.end();
});
} else {
doDel(obj, options, callback)
}
};
doCut(obj, options, callback);
}

// http://json-ld.org/spec/latest/json-ld-api/#data-round-tripping
function getCoercedObject(object) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"async": "^2.1.4",
"jsonld": "~0.4.6",
"memdb": "^1.3.1",
"n3": "^0.9.0",
"n3": "iilab/N3.js#fix/object-is-not-IRI",
"uuid": "^3.0.1"
},
"peerDependencies": {
Expand Down
131 changes: 131 additions & 0 deletions test/cut_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
var expect = require('chai').expect;
var helper = require('./helper');

describe('jsonld.cut', function() {

var db, manu, tesla;

beforeEach(function() {
db = helper.getDB({ jsonld: { base: 'http://levelgraph.io/' } });
manu = helper.getFixture('manu.json');
tesla = helper.getFixture('tesla.json');
});

afterEach(function(done) {
db.close(done);
});

it('should accept a done callback', function(done) {
db.jsonld.cut(manu, done);
});

it('should cut a basic object', function(done) {
db.jsonld.put(manu, function() {
db.jsonld.cut(manu, function() {
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.be.empty;
done();
});
});
});
});

it('should cut nothing', function(done) {
db.jsonld.put(manu, function() {
db.jsonld.cut({}, function() {
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(2);
done();
});
});
});
});

it('should cut a complex object', function(done) {
db.jsonld.put(tesla, function() {
db.jsonld.cut(tesla, function() {
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(0);
done();
});
});
});
});

it('should del an iri with the cut option', function(done) {
db.jsonld.put(manu, function() {
db.jsonld.del(manu['@id'], function(err) {
expect(err && err.message).to.equal("Passing an IRI to del is not supported anymore. Please pass a JSON-LD document.")
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(2);
done();
});
});
});
});


it('should del a single object leaving blank nodes', function(done) {
db.jsonld.put(manu, function() {
db.jsonld.put(tesla, function() {
db.jsonld.del(tesla, function() {
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(10); // 2 triples from Manu and 8 from tesla blanks.
done();
});
});
});
});
});

it('should del a single object with the cut option leaving blank nodes', function(done) {
// This should also be deprecated in favor of using a `cut` option or the `cut` function.
db.jsonld.put(manu, function() {
db.jsonld.put(tesla, function() {
db.jsonld.del(tesla, {cut: true}, function() {
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(2); // 2 triples from Manu.
done();
});
});
});
});
});

it('should del a single object with no blank nodes completely', function(done) {
var library = helper.getFixture('library_framed.json');

db.jsonld.put(manu, function() {
db.jsonld.put(library, function() {
db.jsonld.del(library, function() {
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(2);
done();
});
});
});
});
});

it('should del obj passed as stringified JSON', function(done) {
var jld = {"@context": { "@vocab": "https://schema.org/"}, "name": "BigBlueHat"};

db.jsonld.put(JSON.stringify(jld), function() {
db.jsonld.del(JSON.stringify(jld), function(err) {
expect(err).to.not.exist;
db.get({}, function(err, triples) {
// getting the full db
expect(triples).to.have.length(1);
done();
});
});
});
});
});
Loading

0 comments on commit 349649c

Please sign in to comment.