Skip to content

Commit f769079

Browse files
authored
Merge pull request #193 from share/delete-docs
Add FAQ with answer to permanent doc deletion question
2 parents 01c61ff + 4bb6f28 commit f769079

File tree

2 files changed

+10
-268
lines changed

2 files changed

+10
-268
lines changed

README.md

Lines changed: 1 addition & 268 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ documents. It is the realtime backend for the [DerbyJS web application
1010
framework](http://derbyjs.com/).
1111

1212
For questions, discussion and announcements, join the [ShareJS mailing
13-
list](https://groups.google.com/forum/?fromgroups#!forum/sharejs).
13+
list](https://groups.google.com/forum/?fromgroups#!forum/sharejs) or [check the FAQ](./docs/faq.md).
1414

1515
Please report any bugs you find to the [issue
1616
tracker](https://github.com/share/sharedb/issues).
1717

18-
1918
## Features
2019

2120
- Realtime synchronization of any JSON document
@@ -322,272 +321,6 @@ after a sequence of diffs are handled.
322321
(Only fires on subscription queries) `query.extra` changed.
323322

324323

325-
326-
<!-- Old docs from LiveDB:
327-
328-
## Using ShareDB
329-
330-
### Creating documents
331-
332-
To create a document, you need to submit a create operation to the
333-
document to set its type. In sharedb's world, a document doesn't exist until it
334-
has a type set.
335-
336-
A create operation looks like this: `{create:{type:TYPE, [data:INITIAL DATA]}, [v:VERSION]}`. The type should be something accessible in the map returned by require('ottypes'), for example `json0` or `http://sharejs.org/types/textv1`. Specifying initial data is optional. If provided, it is passed to the type's `create()` method. This does what you expect - for JSON documents, pass your initial object here. For text documents, pass a string containing the document's contents. As with all operations, the version is optional. You probably don't want to specify the version for a create message.
337-
338-
To submit any changes to documents, you use `sharedb.submit(cName, docName, opData, callback)`.
339-
340-
For example:
341-
342-
```javascript
343-
sharedb.submit('users', 'fred', {create:{type:'json0', data:[1,2,3]}}, function(err, version, transformedByOps, snapshot) {
344-
// I made a document, ma!
345-
});
346-
```
347-
348-
Since documents implicitly exist with no type at version 0, usually the create
349-
message will increment the version from 0 to 1. Not all documents you want to
350-
delete have a version of 0 - if a document is deleted, it will retain its
351-
version.
352-
353-
### Deleting documents
354-
355-
Deleting documents is similar to creating them. A deleted document has no type
356-
and no data, but will retain its version (actually, the delete operation will
357-
bump the document's version). A delete operation looks like this:
358-
`{del:true, [v:VERSION]}`.
359-
360-
You use the same submit function as above to delete documents:
361-
362-
```javascript
363-
sharedb.submit('users', 'fred', {del:true}, function(err) {
364-
//goneskies! Kapow!
365-
});
366-
```
367-
368-
### Editing documents
369-
370-
You edit a document by submitting an operation. Operations are OT type-specific
371-
JSON blobs. Refer to the documentation on the particular OT type for details.
372-
For example, text documents are documented
373-
[here](https://github.com/share/ottypes/blob/master/lib/text.js#L10-L16). If we
374-
had a text document stored in LiveDB and wanted to edit it, it might look like
375-
this:
376-
377-
```javascript
378-
sharedb.submit('love letters', 'dear fred', {op:[6, "You never return my calls!"], v:1002}, function(err) {
379-
// ...
380-
});
381-
```
382-
383-
You should always specify the version when submitting edit operations. The
384-
version is technically optional - if its missing, your operation will be
385-
submitted against the most recent version of the document in the server. This
386-
is useful for creating a document which may already exist, but for normal edits
387-
you should always specify the expected current version of the document.
388-
389-
390-
### Getting a document
391-
392-
You can fetch the most recent version of a document using `sharedb.fetch(cName, docName, callback)` or
393-
`sharedb.bulkFetch(request, callback)`. This will fetch the document(s) from the snapshot database
394-
and fetch all operations which may or may not have been committed.
395-
396-
Fetch returns a snapshot data object via its callback. The snapshot data object
397-
has the following fields:
398-
399-
- **v:** version. This is an integer (starting at 0) containing the version of the document
400-
- **type:** Document type, if set. This field is missing if the document does not exist.
401-
- **data:** The document's actual data. For JSON documents this is a JSON tree.
402-
For text documents this is a string. This field is missing if the document does not exist.
403-
404-
```javascript
405-
sharedb.fetch('users', 'fred', function(err, snapshot) {
406-
// snapshot has {v:123, type:'...', data:{name:'Fred Flintstone'}}
407-
// If the document doesn't exist, only the v:version field will exist in the data.
408-
});
409-
```
410-
411-
If you need to get many documents, its more efficient to issue bulk fetches. To
412-
pass the set of requested documents to bulkFetch, you need to make a request
413-
object which maps collection names to lists of documents you want in that
414-
collection. For example, to get 'red', 'green' and 'blue' from the colors
415-
collection, you would make a bulkFetch request of `{colors:['red', 'green',
416-
'blue']}`.
417-
418-
The response maps each collection name to a set of snapshots. Each set of
419-
snapshots maps document names to snapshot data objects. Continuing our colors
420-
example above, the response could be `{colors:{red:{v:0}, green:{v:10, type:..., data:"emerald"}, blue:{v:1, type:..., data:{favorite:true}}}}`.
421-
422-
For example:
423-
424-
```javascript
425-
sharedb.bulkFetch({users:['fred', 'wilma', 'homer'], admins:['zerocool']}, function(err, results) {
426-
// results will be {users:{fred:..., wilma:..., homer:...}, admins:{zerocool:...}}.
427-
// Each document has v and optional type and data fields like fetch (above).
428-
});
429-
```
430-
431-
432-
### Getting historic changes to a document
433-
434-
You can get old versions of a document (for playback or catching up a client)
435-
using `sharedb.getOps(cName, docName, from, to, callback)`. This will return
436-
all operations which have been applied to the named document in the requested range.
437-
The range is *open*, so `getOps('users', 'fred', 0, 3, ..)` will return all
438-
operations up to (but not including) 3. (Ie, operations 0, 1 and 2).
439-
440-
If you set the *to* field to null, getOps will get all operations up to the
441-
current version.
442-
443-
ShareDB documents always start at version 0, so you can get a document's entire history using `getOps('users', fred', 0, null, callback);`.
444-
445-
If you set *to* to a version in the future, behaviour is not defined.
446-
447-
Example usage:
448-
449-
```javascript
450-
sharedb.submit('users', 'fred', {create:{type:'json0', data:{name:'Fred'}}}, function(err) {
451-
sharedb.submit('users', 'fred', {v:1, op:[{p:['name', 4], si:' Flintstone'}]}, function(err) {
452-
// ...
453-
});
454-
});
455-
456-
// Sometime later...
457-
458-
sharedb.getOps('users', 'fred', 0, null, function(err, ops) {
459-
// ops contains the two operations which were submitted above:
460-
// [{v:0, create:{...}, {v:1, op:[...]}]
461-
});
462-
```
463-
464-
### Streaming changes to a document in realtime
465-
466-
You can subscribe to changes from a document using
467-
`sharedb.subscribe(cName, docName, v, callback)` or
468-
`sharedb.bulkSubscribe(request, callback)`. When you subscribe, you get an
469-
operation stream which gets packed with operations as they happen.
470-
471-
When you subscribe to a document, you need to specify which version you're
472-
subscribing to the document *from*. The version cannot be in the future.
473-
474-
The stream will be populated with each operation from the requested version
475-
onwards (to infinity and beyond). Each operation will appear in the stream
476-
exactly once. If you subscribe and request an old document version, all
477-
operations from that version to the current version will be buffered in the
478-
stream before the stream is returned to the callback.
479-
480-
You usually want to call *subscribe* after fetching a document. Pass the
481-
document version that you got from calling *fetch* into your call to
482-
*subscribe*.
483-
484-
For example:
485-
486-
```javascript
487-
sharedb.fetch('users', 'fred', function(err, data) {
488-
if (err) { ... }
489-
var version = data.v;
490-
491-
// ... Any amount of time later (literally).
492-
sharedb.subscribe('users', 'fred', version, function(err, stream) {
493-
if (err) { ... }
494-
495-
// stream is a nodejs ReadableStream with all operations that happen to
496-
// users.fred.
497-
498-
stream.on('data', function(opData) {
499-
// The opData is a JSON object, the same object you can pass to submit().
500-
// It always has a v: field.
501-
502-
// ShareDB exports a helper function to apply the operation to some
503-
// snapshot data:
504-
var err = ldb.ot.apply(data, opData);
505-
if (err) { ... }
506-
});
507-
});
508-
});
509-
```
510-
511-
**Important!** To avoid leaking memory, when you're done with a stream call `stream.destroy()` to clean it up.
512-
513-
There is a helper method which will both fetch and subscribe for you (cleverly
514-
called `fetchAndSubscribe(cName, docName, callback)`). It is defined like this:
515-
516-
```javascript
517-
ShareDB.prototype.fetchAndSubscribe = function(cName, docName, callback) {
518-
var self = this;
519-
this.fetch(cName, docName, function(err, data) {
520-
if (err) return callback(err);
521-
self.subscribe(cName, docName, data.v, function(err, stream) {
522-
callback(err, data, stream);
523-
});
524-
});
525-
};
526-
```
527-
528-
It calls your callback with `(err, snapshot, stream)`, giving you both the current document snapshot and the stream of operations from the current version.
529-
530-
#### Bulk Subscribe
531-
532-
If you want to subscribe to multiple documents at once, you should call
533-
`bulkSubscribe(request, callback)`. The bulk subscribe request is a map from
534-
cName -> map from docName -> version. For example, `{colors: {red:5, blue:6,
535-
green:0}}`. The response is a map from cName -> map from docName -> stream.
536-
For example, `{colors: {red:<stream>, blue:<stream>, green:<stream>}}`.
537-
bulkSubscribe will either return a stream for all requested objects or (if
538-
there was an error), none of them.
539-
540-
Again, remember to call `stream.destroy()` on all streams returned by bulk
541-
subscribe when you're done with them.
542-
543-
544-
### Queries
545-
546-
ShareDB supports running live queries against the database. It can re-run queries when it suspects that a query's results might have changed - and notify the caller with any changes to the result set.
547-
548-
This is incredibly inefficient and I want to completely rewrite / rework them. For now, I recommend against using live bound queries in a production app with a decent load. I'll document them when I'm happier with them.
549-
550-
551-
### Projections
552-
553-
ShareDB supports exposing a *projection* of a real collection, with a specified
554-
(limited) set of allowed fields. Once configured, the projected collection
555-
looks just like a real collection - except documents only have the fields
556-
you've requested.
557-
558-
Operations (gets, queries, sets, etc) on the fake collection work, but you only
559-
see a small portion of the data. You can use this to drop server & db load
560-
dramatically and speed up page times. Its similar to SQL VIEWs. For now, this
561-
only works on JSON documents. (I don't know what it would look like for text
562-
documents).
563-
564-
For example, you could make a `users_limited` projection which lets users view
565-
each other's names and profile pictures, but not password hashes. You would
566-
configure this by calling:
567-
568-
```javascript
569-
sharedb.addProjection('users_limited', 'users', 'json0', {name:true, profileUrl:true});
570-
```
571-
572-
However, be aware that on its own **this is not sufficient for access control**. If
573-
users are still allowed to make arbitrary mongo queries against the projected
574-
collection, they can find out any data in the hidden fields.
575-
576-
Configure a projection by calling `addProjection(projCName, realCName, type, fields)`.
577-
578-
- **projCName:** The projected collection name. (Eg, `users_limited`)
579-
- **realCName:** The underlying collection name
580-
- **type:** The OT type. Only JSON0 is supported for now.
581-
- **fields:** A map of the allowed fields in documents. The keys in this map
582-
represent the field names, and the values should be `true`.
583-
584-
Limitations:
585-
586-
- You can only whitelist fields (not blacklist them).
587-
- Projections can only limit / allow fields at the top level of the document
588-
589-
-->
590-
591324
## Error codes
592325

593326
ShareDB returns errors as plain JavaScript objects with the format:

docs/faq.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# FAQ
2+
3+
## Is it possible to completely delete documents from the db?
4+
5+
No, it is not possible to use the ShareDB API to fully delete data. In addition, the operation log is kept forever by default.
6+
7+
Maintaining persistence of snapshots and ops means that ShareDB can correctly deal with all cases where ops have been removed. Permanently removing the snapshot document could result in a corrupt state in some edge cases by not maintaining the current document version, which must be incremented on each commit. As well, if you delete ops and then a client reconnects needing those ops, you will break that client and it will be unable to submit any pending changes or bring itself up to date from its current state. If your usecase calls for complete deletion of operations, you'll need to ensure that no clients will ever need them again or deal with the error appropriately.
8+
9+
You can currently delete from your persistent datastore directly. For example, if you're using MongoDB you can delete the data by connecting to Mongo directly, not via the ShareDB API. If you do delete snapshot data, be sure that you delete not just the document snapshot but all operations associated with that document. Having operations with no corresponding snapshot would result in a corrupt state.

0 commit comments

Comments
 (0)