Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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