Merge lp:~thisfred/u1db/documentation-update into lp:u1db

Proposed by Eric Casteleijn
Status: Merged
Approved by: Eric Casteleijn
Approved revision: 356
Merged at revision: 354
Proposed branch: lp:~thisfred/u1db/documentation-update
Merge into: lp:u1db
Diff against target: 753 lines (+199/-191)
9 files modified
CMakeLists.txt (+5/-1)
doc/sqlite_schema.txt (+5/-5)
html-docs/conflicts.rst (+44/-43)
html-docs/high-level-api.rst (+73/-70)
html-docs/index.rst (+14/-14)
html-docs/philosophy.rst (+31/-32)
html-docs/quickstart.rst (+14/-14)
html-docs/reference-implementation.rst (+4/-4)
u1db/__init__.py (+9/-8)
To merge this branch: bzr merge lp:~thisfred/u1db/documentation-update
Reviewer Review Type Date Requested Status
Lucio Torre (community) Approve
Review via email: mp+115187@code.launchpad.net

Commit message

Added make doctest to make check, so the documentation has a higher chance of not lying. Corrected the documentation to pass doctests and tell the truth.

Description of the change

Added make doctest to make check, so the documentation has a higher chance of not lying. Corrected the documentation to pass doctests and tell the truth.

To post a comment you must log in.
Revision history for this message
Lucio Torre (lucio.torre) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2012-07-05 16:00:16 +0000
+++ CMakeLists.txt 2012-07-16 17:22:17 +0000
@@ -40,9 +40,13 @@
40 ${CMAKE_CURRENT_BINARY_DIR}40 ${CMAKE_CURRENT_BINARY_DIR}
41 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )41 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
4242
43add_custom_target(doctests
44 COMMAND cd html-docs && make doctest && cd ..
45)
46
43add_custom_target(check47add_custom_target(check
44 COMMAND python -m testtools.run discover48 COMMAND python -m testtools.run discover
45 DEPENDS build-inplace49 DEPENDS build-inplace doctests
46)50)
4751
48add_custom_target(build-inplace52add_custom_target(build-inplace
4953
=== modified file 'doc/sqlite_schema.txt'
--- doc/sqlite_schema.txt 2011-12-02 10:15:55 +0000
+++ doc/sqlite_schema.txt 2012-07-16 17:22:17 +0000
@@ -25,7 +25,7 @@
25It is intended to be a list of fields, and possibly mappings on those fields.25It is intended to be a list of fields, and possibly mappings on those fields.
26Something like::26Something like::
2727
28 CREATE_INDEX(mydb, "myindex", ["field", "other.subfield", "number(third)"]) 28 CREATE_INDEX(mydb, "myindex", ["field", "other.subfield", "number(third)"])
2929
3030
31Recommended Implementation31Recommended Implementation
@@ -83,7 +83,7 @@
8383
84 {"lastname": "pedroni", "firstname": "john"}84 {"lastname": "pedroni", "firstname": "john"}
8585
86Which should not match the above query. 86Which should not match the above query.
8787
88We also want an SQL index on this table, something like [#]_::88We also want an SQL index on this table, something like [#]_::
8989
@@ -134,7 +134,7 @@
1343) It isn't hard to map nested fields into this structure. And you have1343) It isn't hard to map nested fields into this structure. And you have
135 the nice property that you don't have to change the data to add/remove an135 the nice property that you don't have to change the data to add/remove an
136 index.136 index.
137 137
1384) It isn't 100% clear how we handle mapped fields in this structure. Something1384) It isn't 100% clear how we handle mapped fields in this structure. Something
139 like ``lower(lastname)``. It is possible that we could only support the set139 like ``lower(lastname)``. It is possible that we could only support the set
140 of mappings that we can do with SQL on the live data. However, that will140 of mappings that we can do with SQL on the live data. However, that will
@@ -147,7 +147,7 @@
147 seem to support turning "SELECT * FROM table WHERE value LIKE 'p%'" into an147 seem to support turning "SELECT * FROM table WHERE value LIKE 'p%'" into an
148 index query. Even though value is in a btree, it doesn't use it. However,148 index query. Even though value is in a btree, it doesn't use it. However,
149 you could use >= and < to get a range query. Something like::149 you could use >= and < to get a range query. Something like::
150 150
151 SELECT * FROM table WHERE value >= 'p' AND value < 'q'151 SELECT * FROM table WHERE value >= 'p' AND value < 'q'
152152
153 Since sqlite supports closed and open ended ranges, we don't have to play153 Since sqlite supports closed and open ended ranges, we don't have to play
@@ -168,7 +168,7 @@
168-----------------168-----------------
169169
170The same schema as defined above, except you always put every field into the170The same schema as defined above, except you always put every field into the
171document_fields table. 171document_fields table.
172172
173Discussion173Discussion
174~~~~~~~~~~174~~~~~~~~~~
175175
=== modified file 'html-docs/conflicts.rst'
--- html-docs/conflicts.rst 2011-12-21 16:09:22 +0000
+++ html-docs/conflicts.rst 2012-07-16 17:22:17 +0000
@@ -7,26 +7,26 @@
7Conflicts7Conflicts
8-------------8-------------
99
10If two u1dbs are synced, and then the same document is changed in different ways10If two u1dbs are synced, and then the same document is changed in different
11in each u1db, and then they are synced again, there will be a *conflict*. This11ways in each u1db, and then they are synced again, there will be a *conflict*.
12does not block synchronisation: the document is registered as being in conflict,12This does not block synchronisation: the document is registered as being in
13and resolving that is up to the u1db-using application.13conflict, and resolving that is up to the u1db-using application.
1414
15Importantly, **conflicts are not synced**. If *machine A* initiates a sync with15Importantly, **conflicts are not synced**. If *machine A* initiates a sync with
16*machine B*, and this sync results in a conflict, the conflict **only registers16*machine B*, and this sync results in a conflict, the conflict **only registers
17on machine A**. This policy is sometimes called "other wins": the machine you17on machine A**. This policy is sometimes called "other wins": the machine you
18synced *to* wins conflicts, and the document will have machine B's content on18synced *to* wins conflicts, and the document will have machine B's content on
19both machine A and machine B. However, on machine A the document is marked19both machine A and machine B. However, on machine A the document is marked as
20as having conflicts, and must be resolved there:20having conflicts, and must be resolved there:
2121
22.. testsetup ::22.. testsetup ::
2323
24 import u1db, json24 import u1db, json
25 db=u1db.open(':memory:', True)25 db=u1db.open(':memory:', True)
26 docFromA=u1db.Document('test','machineA:1',json.dumps({'camefrom':'machineA'}))26 docFromA=u1db.Document('test','machineA:1',json.dumps({'camefrom':'machineA'}))
27 db.put_doc_if_newer(docFromA, save_conflict=True)27 db._put_doc_if_newer(docFromA, save_conflict=True, replica_uid='machineA', replica_gen=1)
28 docFromB=u1db.Document('test','machineB:1',json.dumps({'camefrom':'machineB'}))28 docFromB=u1db.Document('test','machineB:1',json.dumps({'camefrom':'machineB'}))
29 db.put_doc_if_newer(docFromB, save_conflict=True)29 db._put_doc_if_newer(docFromB, save_conflict=True, replica_uid='machineB', replica_gen=1)
3030
31.. doctest ::31.. doctest ::
3232
@@ -35,66 +35,67 @@
35 >>> docFromB.has_conflicts # the document is in conflict35 >>> docFromB.has_conflicts # the document is in conflict
36 True36 True
37 >>> conflicts = db.get_doc_conflicts(docFromB.doc_id)37 >>> conflicts = db.get_doc_conflicts(docFromB.doc_id)
38 >>> print conflicts38 >>> conflicts
39 [(u'machineB:1', u'{"camefrom": "machineB"}'), (u'machineA:1', u'{"camefrom": "machineA"}')]39 [Document(test, machineB:1, conflicted, u'{"camefrom": "machineB"}'), Document(test, machineA:1, u'{"camefrom": "machineA"}')]
40 >>> db.resolve_doc(docFromB, [x[0] for x in conflicts]) # resolve in favour of B40 >>> db.resolve_doc(docFromB, [d.rev for d in conflicts]) # resolve in favour of B
41 >>> doc_is_now = db.get_doc("test")41 >>> doc_is_now = db.get_doc("test")
42 >>> doc_is_now.content # the content has been updated to doc's content42 >>> doc_is_now.content # the content has been updated to doc's content
43 u'{"camefrom": "machineB"}'43 {u'camefrom': u'machineB'}
44 >>> db.get_doc_conflicts(docFromB.doc_id)
45 []
44 >>> doc_is_now.has_conflicts # and is no longer in conflict46 >>> doc_is_now.has_conflicts # and is no longer in conflict
45 False47 False
4648
47Note that ``put_doc`` will fail because we got conflicts from a sync, but it49Note that ``put_doc`` will fail because we got conflicts from a sync, but it
48may also fail for another reason. If you acquire a document before a sync and 50may also fail for another reason. If you acquire a document before a sync and
49then sync, and the sync updates that document, then re-putting that document 51then sync, and the sync updates that document, then re-putting that document
50with modified content will also fail, because the revision is not the current 52with modified content will also fail, because the revision is not the current
51one. This will raise a ``RevisionConflict`` error.53one. This will raise a ``RevisionConflict`` error.
5254
53Revisions55Revisions
54----------56----------
5557
56As an app developer, you should treat a ``Document``'s ``revision`` as an opaque58As an app developer, you should treat a ``Document``'s ``revision`` as an
57cookie; do not try and deconstruct it or edit it. It is for your u1db 59opaque cookie; do not try and deconstruct it or edit it. It is for your u1db
58implementation's use. You can therefore ignore the rest of this section.60implementation's use. You can therefore ignore the rest of this section.
5961
60If you are writing a new u1db implementation, understanding revisions is 62If you are writing a new u1db implementation, understanding revisions is
61important, and this is where you find out about them.63important, and this is where you find out about them.
6264
63To keep track of document revisions u1db uses vector versions. Each65To keep track of document revisions u1db uses vector versions. Each
64synchronized instance of the same database is called a replica and has66synchronized instance of the same database is called a replica and has a unique
65a unique identifier (``replica uid``) assigned to it (currently the67identifier (``replica uid``) assigned to it (currently the reference
66reference implementation by default uses UUID4s for that); a68implementation by default uses UUID4s for that); a revision is a mapping
67revision is a mapping between ``replica uids`` and ``edit numbers``: ``rev =69between ``replica uids`` and ``edit numbers``: ``rev
68<replica_uid:edit_num...>``, or using a functional notation70= <replica_uid:edit_num...>``, or using a functional notation
69``rev(replica_uid) = edit_num``. The current concrete format is a string71``rev(replica_uid) = edit_num``. The current concrete format is a string built
70built out of each ``replica_uid`` concatenated with ``':'`` and with its edit72out of each ``replica_uid`` concatenated with ``':'`` and with its edit number
71number in decimal, sorted lexicographically by ``replica_uid`` and then73in decimal, sorted lexicographically by ``replica_uid`` and then all joined
72all joined with ``'|'``, for example: ``'replicaA:1|replicaB:3'`` . Absent74with ``'|'``, for example: ``'replicaA:1|replicaB:3'`` . Absent ``replica
73``replica uids`` in a revision mapping are implicitly mapped to edit75uids`` in a revision mapping are implicitly mapped to edit number 0.
74number 0.
7576
76The new revision of a document modified locally in a replica, is the77The new revision of a document modified locally in a replica, is the
77modification of the old revision where the edit number mapped for the78modification of the old revision where the edit number mapped for the editing
78editing ``replica uid`` is increased by 1.79``replica uid`` is increased by 1.
7980
80When syncing one needs to establish whether an incoming revision is81When syncing one needs to establish whether an incoming revision is newer than
81newer than the current one or in conflict. A revision 82the current one or in conflict. A revision
8283
83``rev1 = <replica_1i:edit_num1i|i=1..n>``84``rev1 = <replica_1i:edit_num1i|i=1..n>``
8485
85is newer than a different 86is newer than a different
8687
87``rev2 = <replica_2j:edit_num2j|j=1..m>``88``rev2 = <replica_2j:edit_num2j|j=1..m>``
8889
89if for all ``i=1..n``, ``rev2(replica_1i) <= edit_num1i`` 90if for all ``i=1..n``, ``rev2(replica_1i) <= edit_num1i``
9091
91and for all ``j=1..m``, ``rev1(replica_2j) >= edit_num2j``. 92and for all ``j=1..m``, ``rev1(replica_2j) >= edit_num2j``.
9293
93Two revisions which are not equal nor one newer than the94Two revisions which are not equal nor one newer than the other are in conflict.
94other are in conflict.95
9596When resolving a conflict locally in a replica ``replica_resol``, starting from
96When resolving a conflict locally in a replica ``replica_resol``, starting from 97``rev1...revN`` in conflict, the resulting revision ``rev_resol`` is obtained
97``rev1...revN`` in conflict, the resulting revision ``rev_resol`` is obtained by:98by:
9899
99 ``R`` is the set the of all replicas explicitly mentioned in ``rev1..revN``100 ``R`` is the set the of all replicas explicitly mentioned in ``rev1..revN``
100101
101102
=== modified file 'html-docs/high-level-api.rst'
--- html-docs/high-level-api.rst 2011-12-21 11:09:04 +0000
+++ html-docs/high-level-api.rst 2012-07-16 17:22:17 +0000
@@ -4,23 +4,22 @@
4##################4##################
55
6The U1DB API has three separate sections: document storage and retrieval,6The U1DB API has three separate sections: document storage and retrieval,
7querying, and sync. Here we describe the high-level API. Remember that you7querying, and sync. Here we describe the high-level API. Remember that you will
8will need to choose an implementation, and exactly how this API is defined8need to choose an implementation, and exactly how this API is defined is
9is implementation-specific, in order that it fits with the language's 9implementation-specific, in order that it fits with the language's conventions.
10conventions.
1110
12Document storage and retrieval11Document storage and retrieval
13##############################12##############################
1413
15U1DB stores documents. A document is a set of nested key-values; basically,14U1DB stores documents. A document is a set of nested key-values; basically,
16anything you can express with JSON. Implementations are likely to provide a 15anything you can express with JSON. Implementations are likely to provide
17Document object "wrapper" for these documents; exactly how the wrapper works16a Document object "wrapper" for these documents; exactly how the wrapper works
18is implementation-defined.17is implementation-defined.
1918
20Creating and editing documents19Creating and editing documents
21------------------------------20------------------------------
2221
23To create a document, use ``create_doc()``. Code examples below are from 22To create a document, use ``create_doc()``. Code examples below are from
24:ref:`reference-implementation` in Python.23:ref:`reference-implementation` in Python.
2524
26.. testcode ::25.. testcode ::
@@ -33,12 +32,12 @@
3332
34.. testoutput ::33.. testoutput ::
3534
36 {"key": "value"}35 {'key': 'value'}
37 testdoc36 testdoc
3837
39Editing an *existing* document is done with ``put_doc()``. This is separate from38Editing an *existing* document is done with ``put_doc()``. This is separate
40``create_doc()`` so as to avoid accidental overwrites. ``put_doc()`` takes a39from ``create_doc()`` so as to avoid accidental overwrites. ``put_doc()`` takes
41``Document`` object, because the object encapsulates revision information for40a ``Document`` object, because the object encapsulates revision information for
42a particular document.41a particular document.
4342
44.. testcode ::43.. testcode ::
@@ -52,15 +51,16 @@
52 except u1db.errors.RevisionConflict:51 except u1db.errors.RevisionConflict:
53 print "There was a conflict when creating the doc!"52 print "There was a conflict when creating the doc!"
54 print "Now editing the doc with the doc object we got back..."53 print "Now editing the doc with the doc object we got back..."
55 data = json.loads(doc1.content)54 doc1.content["key1"] = "edited"
56 data["key1"] = "edited"
57 doc1.content = json.dumps(data)
58 db.put_doc(doc1)55 db.put_doc(doc1)
56 doc2 = db.get_doc(doc1.doc_id)
57 print doc2.content
5958
60.. testoutput ::59.. testoutput ::
6160
62 There was a conflict when creating the doc!61 There was a conflict when creating the doc!
63 Now editing the doc with the doc object we got back...62 Now editing the doc with the doc object we got back...
63 {u'key1': u'edited'}
6464
65Finally, deleting a document is done with ``delete_doc()``.65Finally, deleting a document is done with ``delete_doc()``.
6666
@@ -70,9 +70,14 @@
70 db = u1db.open(":memory:", create=True)70 db = u1db.open(":memory:", create=True)
71 doc = db.create_doc(json.dumps({"key": "value"}))71 doc = db.create_doc(json.dumps({"key": "value"}))
72 db.delete_doc(doc)72 db.delete_doc(doc)
73 print db.get_doc(doc.doc_id)
74 doc = db.get_doc(doc.doc_id, include_deleted=True)
75 print doc.content
7376
74.. testoutput ::77.. testoutput ::
7578
79 None
80 None
7681
77Retrieving documents82Retrieving documents
78--------------------83--------------------
@@ -90,7 +95,7 @@
9095
91.. testoutput ::96.. testoutput ::
9297
93 {"key": "value"}98 {u'key': u'value'}
94 testdoc99 testdoc
95100
96And it's also possible to retrieve many documents by ``doc_id``.101And it's also possible to retrieve many documents by ``doc_id``.
@@ -140,7 +145,7 @@
140 {"firstname": "Alan", "surname", "Hansen", "position": "defence"} ID ah145 {"firstname": "Alan", "surname", "Hansen", "position": "defence"} ID ah
141 {"firstname": "John", "surname", "Wayne", "position": "filmstar"} ID jw146 {"firstname": "John", "surname", "Wayne", "position": "filmstar"} ID jw
142147
143an index expression of ``["firstname"]`` will create an index that looks 148an index expression of ``["firstname"]`` will create an index that looks
144(conceptually) like this149(conceptually) like this
145150
146 ====================== ===========151 ====================== ===========
@@ -152,25 +157,25 @@
152 John jw157 John jw
153 ====================== ===========158 ====================== ===========
154159
155and that index is created with ``create_index("by-firstname", ["firstname"])`` - that is,160and that index is created with ``create_index("by-firstname", "firstname")``
156create an index with a name and a list of index expressions. (Exactly how to161-- that is, create an index with a name and a list of index expressions.
157pass the name and the list of index expressions is something specific to162(Exactly how to pass the name and the list of index expressions is something
158each implementation.)163specific to each implementation.)
159164
160Index expressions165Index expressions
161^^^^^^^^^^^^^^^^^166^^^^^^^^^^^^^^^^^
162167
163An index expression describes how to get data from a document; you can think168An index expression describes how to get data from a document; you can think of
164of it as describing a function which, when given a document, returns a value,169it as describing a function which, when given a document, returns a value,
165which is then used as the index key.170which is then used as the index key.
166171
167**Name a field.** A basic index expression is a dot-delimited list of nesting172**Name a field.** A basic index expression is a dot-delimited list of nesting
168fieldnames, so the index expression ``field.sub1.sub2`` applied to a document 173fieldnames, so the index expression ``field.sub1.sub2`` applied to a document
169with ID ``doc1`` and content::174with ID ``doc1`` and content::
170175
171 {176 {
172 "field": { 177 "field": {
173 "sub1": { 178 "sub1": {
174 "sub2": "hello"179 "sub2": "hello"
175 "sub3": "not selected"180 "sub3": "not selected"
176 }181 }
@@ -187,11 +192,11 @@
187192
188**Name a list.** If an index expression names a field whose contents is a list193**Name a list.** If an index expression names a field whose contents is a list
189of strings, the doc will have multiple entries in the index, one per entry in194of strings, the doc will have multiple entries in the index, one per entry in
190the list. So, the index expression ``field.tags`` applied to a document with 195the list. So, the index expression ``field.tags`` applied to a document with ID
191ID "doc2" and content::196"doc2" and content::
192197
193 {198 {
194 "field": { 199 "field": {
195 "tags": [ "tag1", "tag2", "tag3" ]200 "tags": [ "tag1", "tag2", "tag3" ]
196 }201 }
197 }202 }
@@ -206,25 +211,30 @@
206 tag3 doc2211 tag3 doc2
207 ========= ======212 ========= ======
208213
209**Transformation functions.** An index expression may be wrapped in any number of214**Transformation functions.** An index expression may be wrapped in any number
210transformation functions. A function transforms the result of the contained215of transformation functions. A function transforms the result of the contained
211index expression: for example, if an expression ``name.firstname`` generates 216index expression: for example, if an expression ``name.firstname`` generates
212"John" when applied to a document, then ``lower(name.firstname)`` generates 217"John" when applied to a document, then ``lower(name.firstname)`` generates
213"john".218"john".
214219
215Available transformation functions are:220Available transformation functions are:
216221
217 * ``lower(index_expression)`` - lowercase the value222 * ``lower(index_expression)`` - lowercase the value
218 * ``splitwords(index_expression)`` - split the value on whitespace; will act like a 223 * ``splitwords(index_expression)`` - split the value on whitespace; will act
219 list and add multiple entries to the index224 like a list and add multiple entries to the index
220 * ``is_null(index_expression)`` - True if value is null or not a string or the field 225 * ``number(index_expression, width)`` - takes an integer value, and turns it
221 is absent, otherwise false226 into a string, left padded with zeroes, to make it at least as wide as
227 width.
228 * ``bool(index_expression)`` - takes a boolean value and turns it into '0' if
229 false and '1' if true.
230 * ``is_null(index_expression)`` - True if value is null or not a string or the
231 field is absent, otherwise false
222232
223So, the index expression ``splitwords(lower(field.name))`` applied to a document with 233So, the index expression ``splitwords(lower(field.name))`` applied to
224ID "doc3" and content::234a document with ID "doc3" and content::
225235
226 {236 {
227 "field": { 237 "field": {
228 "name": "Bruce David Grobbelaar"238 "name": "Bruce David Grobbelaar"
229 }239 }
230 }240 }
@@ -243,25 +253,18 @@
243Querying an index253Querying an index
244-----------------254-----------------
245255
246Pass a list of tuples of index keys to ``get_from_index``; the last index key in256Pass an index key or a tuple of index keys (if the index is on multiple fields)
247each tuple (and *only* the last one) can end with an asterisk, which matches 257to ``get_from_index``; the last index key in each tuple (and *only* the last
248initial substrings. So, querying our ``by-firstname`` index from above::258one) can end with an asterisk, which matches initial substrings. So, querying
249259our ``by-firstname`` index from above::
250 get_from_index(260
251 "by-firstname", # name of index261 get_from_index("by-firstname", "John")
252 [ # begin the list of index keys262
253 ("John", ) # an index key263
254 ] # end the list264will return the documents with ids: 'jw', 'jb'.
255 )265
256266``get_from_index("by_firstname", "J*")`` will match all index keys beginning
257267with "J", and so will return the documents with ids: 'jw', 'jb', 'jm'.
258will return ``[ 'jw', 'jb' ]`` - that is, a list of document IDs.
259
260``get_from_index("by_firstname", [("J*")])`` will match all index keys beginning
261with "J", and so will return ``[ 'jw', 'jb', 'jm' ]``.
262
263``get_from_index("by_firstname", [("Jan"), ("Alan")])`` will match both the
264queried index keys, and so will return ``[ 'jm', 'ah' ]``.
265268
266269
267Index functions270Index functions
@@ -277,30 +280,30 @@
277#######280#######
278281
279U1DB is a syncable database. Any U1DB can be synced with any U1DB server; most282U1DB is a syncable database. Any U1DB can be synced with any U1DB server; most
280U1DB implementations are capable of being run as a server. Syncing brings283U1DB implementations are capable of being run as a server. Syncing brings both
281both the server and the client up to date with one another; save data into a284the server and the client up to date with one another; save data into a local
282local U1DB whether online or offline, and then sync when online.285U1DB whether online or offline, and then sync when online.
283286
284Pass an HTTP URL to sync with that server.287Pass an HTTP URL to sync with that server.
285288
286Syncing databases which have been independently changed may produce conflicts.289Syncing databases which have been independently changed may produce conflicts.
287Read about the U1DB conflict policy and more about syncing at :ref:`conflicts`.290Read about the U1DB conflict policy and more about syncing at :ref:`conflicts`.
288291
289Running your own U1DB server is implementation-specific. :ref:`reference-implementation` 292Running your own U1DB server is implementation-specific.
290is able to be run as a server.293:ref:`reference-implementation` is able to be run as a server.
291294
292Dealing with conflicts295Dealing with conflicts
293----------------------296----------------------
294297
295Syncing a database can result in conflicts; if your user changes the same 298Syncing a database can result in conflicts; if your user changes the same
296document in two different places and then syncs again, that document will be299document in two different places and then syncs again, that document will be
297''in conflict'', meaning that it has incompatible changes. If this is the case,300''in conflict'', meaning that it has incompatible changes. If this is the case,
298``doc.has_conflicts`` will be true, and put_doc to a conflicted doc will give a301``doc.has_conflicts`` will be true, and put_doc to a conflicted doc will give
299``ConflictedDoc`` error. To get a list of conflicted versions of the302a ``ConflictedDoc`` error. To get a list of conflicted versions of the
300document, do ``get_doc_conflicts(doc_id)``. Deciding what the final unconflicted303document, do ``get_doc_conflicts(doc_id)``. Deciding what the final
301document should look like is obviously specific to the user's application; once304unconflicted document should look like is obviously specific to the user's
302decided, call ``resolve_doc(doc, list_of_conflicted_revisions)`` to resolve and305application; once decided, call ``resolve_doc(doc, list_of_conflicted_revisions)``
303set the final resolved content.306to resolve and set the final resolved content.
304307
305Syncing functions308Syncing functions
306^^^^^^^^^^^^^^^^^309^^^^^^^^^^^^^^^^^
307310
=== modified file 'html-docs/index.rst'
--- html-docs/index.rst 2011-12-21 13:49:41 +0000
+++ html-docs/index.rst 2012-07-16 17:22:17 +0000
@@ -1,29 +1,29 @@
1U1DB1U1DB
2####2####
33
4U1DB is a database API for synchronised databases of JSON documents. It's 4U1DB is a database API for synchronised databases of JSON documents. It's
5simple to use in applications, and allows apps to store documents and 5simple to use in applications, and allows apps to store documents and
6synchronise them between machines and devices. U1DB itself is not a database: 6synchronise them between machines and devices. U1DB itself is not a database:
7instead, it's an API which can be backed by any database for storage. This means that you 7instead, it's an API which can be backed by any database for storage. This
8can use u1db on different platforms, from different languages, and backed 8means that you can use u1db on different platforms, from different languages,
9on to different databases, and sync between all of them.9and backed on to different databases, and sync between all of them.
1010
11The API for U1DB looks similar across all different implementations. This API11The API for U1DB looks similar across all different implementations. This API
12is described at :ref:`high-level-api`. To actually use U1DB you'll need an 12is described at :ref:`high-level-api`. To actually use U1DB you'll need an
13implementation; a version of U1DB made available on your choice of platform, 13implementation; a version of U1DB made available on your choice of platform, in
14in your choice of language, and on your choice of backend database.14your choice of language, and on your choice of backend database.
1515
16If you're interested in using U1DB in an application, look at 16If you're interested in using U1DB in an application, look at
17:ref:`high-level-api` first, and then choose one of the :ref:`implementations` 17:ref:`high-level-api` first, and then choose one of the :ref:`implementations`
18and read about exactly how the U1DB API is made available in that 18and read about exactly how the U1DB API is made available in that
19implementation. Get going quickly with the :ref:`quickstart`.19implementation. Get going quickly with the :ref:`quickstart`.
2020
21If you're interested in hacking on U1DB itself, read about the 21If you're interested in hacking on U1DB itself, read about the
22:ref:`rules for U1DB <philosophy>` and :ref:`reference-implementation`.22:ref:`rules for U1DB <philosophy>` and :ref:`reference-implementation`.
2323
24.. toctree::24.. toctree::
25 :maxdepth: 125 :maxdepth: 1
26 26
27 quickstart27 quickstart
28 high-level-api28 high-level-api
29 reference-implementation29 reference-implementation
3030
=== modified file 'html-docs/philosophy.rst'
--- html-docs/philosophy.rst 2011-12-21 13:11:20 +0000
+++ html-docs/philosophy.rst 2012-07-16 17:22:17 +0000
@@ -5,52 +5,51 @@
55
6Some notes on what u1db is for, how it works, and how it should be used.6Some notes on what u1db is for, how it works, and how it should be used.
77
8U1DB is a cross-platform, cross-device, syncable database API. In order to be this8U1DB is a cross-platform, cross-device, syncable database API. In order to be
9way, there's a philosophy behind it. Key to this philosophy is that u1db can9this way, there's a philosophy behind it. Key to this philosophy is that u1db
10be implemented in many languages and on top of many back ends: this means that10can be implemented in many languages and on top of many back ends: this means
11the API needs to be, as much as possible, portable between very different11that the API needs to be, as much as possible, portable between very different
12languages. Each implementation should implement :ref:`high-level-api` in the12languages. Each implementation should implement :ref:`high-level-api` in the
13way appropriate to that language (Python uses tuples all over the place,13way appropriate to that language (Python uses tuples all over the place, Vala/C
14Vala/C use a Document object for most things, and so on), but it's important14use a Document object for most things, and so on), but it's important that an
15that an implementation not diverge from the API. Because u1db is a syncable15implementation not diverge from the API. Because u1db is a syncable database,
16database, it's quite likely that an app developer using it will be building their16it's quite likely that an app developer using it will be building their app on
17app on multiple platforms at once. Knowledge that an app developer has from17multiple platforms at once. Knowledge that an app developer has from having
18having built a u1db app on one platform should be transferable to another18built a u1db app on one platform should be transferable to another platform.
19platform. This means that querying is the same across platforms; storing and19This means that querying is the same across platforms; storing and retrieving
20retrieving docs is the same across platforms; syncing is the same across20docs is the same across platforms; syncing is the same across platforms. U1DB
21platforms. U1DB is also syncable to Ubuntu One, which is a very large 21is also syncable to Ubuntu One, which is a very large server installation; the
22server installation; the API needs to be suitable to run at scales from a22API needs to be suitable to run at scales from a mobile phone up to a large
23mobile phone up to a large server installation.23server installation.
2424
25For similar reasons, u1db is *schemaless*. Documents stored in u1db do not25For similar reasons, u1db is *schemaless*. Documents stored in u1db do not need
26need to contain any pre-defined list of fields; this way, an application can26to contain any pre-defined list of fields; this way, an application can store
27store whatever it wants, however it wants; development is faster and changing27whatever it wants, however it wants; development is faster and changing how
28how data is stored is simpler.28data is stored is simpler.
2929
30What this means is that u1db is for user-specific data. A desktop app or a 30What this means is that u1db is for user-specific data. A desktop app or
31mobile app storing data for a user is the ideal use case. A web app which 31a mobile app storing data for a user is the ideal use case. A web app which
32holds data for many users should be using and syncing a separate u1db for32holds data for many users should be using and syncing a separate u1db for each
33each user. U1DB isn't designed to be the backend database for the next 33user. U1DB isn't designed to be the backend database for the next Facebook.
34Facebook.
3534
36To this end, there are a few guidelines. Primarily, the guideline the u1db team35To this end, there are a few guidelines. Primarily, the guideline the u1db team
37used for the largest u1db is somewhere around 10,000 documents. It's important36used for the largest u1db is somewhere around 10,000 documents. It's important
38to note that this is not an *enforced* limit; an app dev can store a zillion37to note that this is not an *enforced* limit; an app dev can store a zillion
39documents in a u1db if they want. However, the implementations are allowed to38documents in a u1db if they want. However, the implementations are allowed to
40assume that there aren't a zillion documents; in particular, suggestions for39assume that there aren't a zillion documents; in particular, suggestions for
41API changes which make things more annoying for a 1,000 documents use-case40API changes which make things more annoying for a 1,000 documents use-case in
42in order to help with a zillion documents are not likely to be adopted.41order to help with a zillion documents are not likely to be adopted.
4342
44Similarly, suggested changes to the high-level API which are very difficult to43Similarly, suggested changes to the high-level API which are very difficult to
45implement in static languages like C are also unlikely to be adopted, in order44implement in static languages like C are also unlikely to be adopted, in order
46to maintain the goal of knowledge on one platform transferring to another.45to maintain the goal of knowledge on one platform transferring to another.
4746
48U1DB is designed so that implementations are built by creating small layers on 47U1DB is designed so that implementations are built by creating small layers on
49top of existing storage solutions. It isn't a database in itself; it's an API48top of existing storage solutions. It isn't a database in itself; it's an API
50layer which sits on top of a native database to that platform. This means that49layer which sits on top of a native database to that platform. This means that
51the platform provides the actual database functionality and u1db takes advantage50the platform provides the actual database functionality and u1db takes
52of it. SQLite where available, localStorage for JavaScript in the web browser;51advantage of it. SQLite where available, localStorage for JavaScript in the web
53u1db should work with the platform, not be ported to it.52browser; u1db should work with the platform, not be ported to it.
5453
55It should be easy to sync a u1db from place to place. There is a direct server54It should be easy to sync a u1db from place to place. There is a direct server
56HTTP API, which allows an app to work with a u1db on the server without any55HTTP API, which allows an app to work with a u1db on the server without any
5756
=== modified file 'html-docs/quickstart.rst'
--- html-docs/quickstart.rst 2011-12-21 11:09:04 +0000
+++ html-docs/quickstart.rst 2012-07-16 17:22:17 +0000
@@ -18,8 +18,8 @@
18Use from source control18Use from source control
19^^^^^^^^^^^^^^^^^^^^^^^19^^^^^^^^^^^^^^^^^^^^^^^
2020
21u1db is `maintained in bazaar in Launchpad <http://launchpad.net/u1db/>`_. To fetch the latest version,21u1db is `maintained in bazaar in Launchpad <http://launchpad.net/u1db/>`_. To
22`bzr branch lp:u1db`.22fetch the latest version, `bzr branch lp:u1db`.
2323
24Starting u1db24Starting u1db
25-------------25-------------
@@ -28,29 +28,29 @@
2828
29 >>> import u1db, json, tempfile29 >>> import u1db, json, tempfile
30 >>> db = u1db.open(":memory:", create=True)30 >>> db = u1db.open(":memory:", create=True)
31 31
32 >>> content = json.dumps({"name": "Alan Hansen"}) # create a document32 >>> content = json.dumps({"name": "Alan Hansen"}) # create a document
33 >>> doc = db.create_doc(content)33 >>> doc = db.create_doc(content)
34 >>> print doc.content34 >>> doc.content
35 {"name": "Alan Hansen"}35 {'name': 'Alan Hansen'}
36 >>> doc.content = json.dumps({"name": "Alan Hansen", "position": "defence"}) # update the document's content36 >>> doc.content = json.dumps({"name": "Alan Hansen", "position": "defence"}) # update the document's content
37 >>> rev = db.put_doc(doc)37 >>> rev = db.put_doc(doc)
38 38
39 >>> content = json.dumps({"name": "John Barnes", "position": "forward"}) # create more documents39 >>> content = json.dumps({"name": "John Barnes", "position": "forward"}) # create more documents
40 >>> doc2 = db.create_doc(content)40 >>> doc2 = db.create_doc(content)
41 >>> content = json.dumps({"name": "Ian Rush", "position": "forward"})41 >>> content = json.dumps({"name": "Ian Rush", "position": "forward"})
42 >>> doc2 = db.create_doc(content)42 >>> doc2 = db.create_doc(content)
43 43
44 >>> db.create_index("by-position", ("position",)) # create an index by passing an index expression44 >>> db.create_index("by-position", "position") # create an index by passing a field name
45 45
46 >>> results = db.get_from_index("by-position", [("forward",)]) # query that index by passing a list of tuples of queries46 >>> results = db.get_from_index("by-position", "forward") # query that index by passing a value
47 >>> len(results)47 >>> len(results)
48 248 2
49 >>> data = [json.loads(result.content) for result in results]49 >>> data = [result.content for result in results]
50 >>> names = [item["name"] for item in data]50 >>> names = [item["name"] for item in data]
51 >>> sorted(names)51 >>> sorted(names)
52 [u'Ian Rush', u'John Barnes']52 [u'Ian Rush', u'John Barnes']
53 53
54Running a server54Running a server
55----------------55----------------
5656
@@ -80,7 +80,7 @@
80 >>> import u1db80 >>> import u1db
81 >>> db = u1db.open(":memory:", create=True)81 >>> db = u1db.open(":memory:", create=True)
82 >>> generation = db.sync("http://127.0.0.1:43632/example.u1db")82 >>> generation = db.sync("http://127.0.0.1:43632/example.u1db")
83 83
84or from the command line84or from the command line
8585
86.. code-block:: bash86.. code-block:: bash
@@ -88,4 +88,4 @@
88 ~/u1db/trunk$ ./u1db-client init-db someother.u1db88 ~/u1db/trunk$ ./u1db-client init-db someother.u1db
89 ~/u1db/trunk$ ./u1db-client sync someother.u1db http://127.0.0.1:43632/example.u1db89 ~/u1db/trunk$ ./u1db-client sync someother.u1db http://127.0.0.1:43632/example.u1db
9090
91 91
9292
=== modified file 'html-docs/reference-implementation.rst'
--- html-docs/reference-implementation.rst 2011-12-21 11:09:04 +0000
+++ html-docs/reference-implementation.rst 2012-07-16 17:22:17 +0000
@@ -4,10 +4,10 @@
4#############################4#############################
55
6The u1db reference implementation is written in Python, with a SQLite back end.6The u1db reference implementation is written in Python, with a SQLite back end.
7It can be used as a real working implementation by Python code. It is also used 7It can be used as a real working implementation by Python code. It is also used
8to document and test how u1db should work; it has a comprehensive test suite. 8to document and test how u1db should work; it has a comprehensive test suite.
9Implementation authors should port the u1db reference test suite in order to 9Implementation authors should port the u1db reference test suite in order to
10test that their implementation is correct; in particular, sync conformance is 10test that their implementation is correct; in particular, sync conformance is
11defined as being able to sync with the reference implementation.11defined as being able to sync with the reference implementation.
1212
13Fetch with ``bzr branch lp:u1db`` or from `Launchpad <http://launchpad.net/u1db>`_.13Fetch with ``bzr branch lp:u1db`` or from `Launchpad <http://launchpad.net/u1db>`_.
1414
=== modified file 'u1db/__init__.py'
--- u1db/__init__.py 2012-07-12 17:21:15 +0000
+++ u1db/__init__.py 2012-07-16 17:22:17 +0000
@@ -60,8 +60,9 @@
60 returned as documents by the database.60 returned as documents by the database.
6161
62 :param factory: A function that returns an object which at minimum must62 :param factory: A function that returns an object which at minimum must
63 satisfy the same interface as does the class DocumentBase. Subclassing63 satisfy the same interface as does the class DocumentBase.
64 that class is the easiest way to create such a function.64 Subclassing that class is the easiest way to create such
65 a function.
65 """66 """
66 raise NotImplementedError(self.set_document_factory)67 raise NotImplementedError(self.set_document_factory)
6768
@@ -169,11 +170,11 @@
169 and the index generated.170 and the index generated.
170171
171 :name: A unique name which can be used as a key prefix172 :name: A unique name which can be used as a key prefix
172 :index_expressions: index expressions defining the index173 :index_expressions: index expressions defining the index information.
173 information. Examples:174 Examples:
174 "fieldname" to index alphabetically sorted on field.175 "fieldname" to index alphabetically sorted on field.
175 "number(fieldname, width)", "lower(fieldname)",176 "number(fieldname, width)", "lower(fieldname)",
176 "fieldname.subfieldname"177 "fieldname.subfieldname"
177 """178 """
178 raise NotImplementedError(self.create_index)179 raise NotImplementedError(self.create_index)
179180
@@ -243,7 +244,7 @@
243 raise NotImplementedError(self.get_index_keys)244 raise NotImplementedError(self.get_index_keys)
244245
245 def get_doc_conflicts(self, doc_id):246 def get_doc_conflicts(self, doc_id):
246 """Get the list of conflict texts for the given document.247 """Get the list of conflicts for the given document.
247248
248 The order of the conflicts is such that the first entry is the value249 The order of the conflicts is such that the first entry is the value
249 that would be returned by "get_doc".250 that would be returned by "get_doc".

Subscribers

People subscribed via source and target branches