Merge lp:~thisfred/u1db/documentation-update into lp:u1db
- documentation-update
- Merge into trunk
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 |
Related bugs: |
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
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2012-07-05 16:00:16 +0000 | |||
3 | +++ CMakeLists.txt 2012-07-16 17:22:17 +0000 | |||
4 | @@ -40,9 +40,13 @@ | |||
5 | 40 | ${CMAKE_CURRENT_BINARY_DIR} | 40 | ${CMAKE_CURRENT_BINARY_DIR} |
6 | 41 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) | 41 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) |
7 | 42 | 42 | ||
8 | 43 | add_custom_target(doctests | ||
9 | 44 | COMMAND cd html-docs && make doctest && cd .. | ||
10 | 45 | ) | ||
11 | 46 | |||
12 | 43 | add_custom_target(check | 47 | add_custom_target(check |
13 | 44 | COMMAND python -m testtools.run discover | 48 | COMMAND python -m testtools.run discover |
15 | 45 | DEPENDS build-inplace | 49 | DEPENDS build-inplace doctests |
16 | 46 | ) | 50 | ) |
17 | 47 | 51 | ||
18 | 48 | add_custom_target(build-inplace | 52 | add_custom_target(build-inplace |
19 | 49 | 53 | ||
20 | === modified file 'doc/sqlite_schema.txt' | |||
21 | --- doc/sqlite_schema.txt 2011-12-02 10:15:55 +0000 | |||
22 | +++ doc/sqlite_schema.txt 2012-07-16 17:22:17 +0000 | |||
23 | @@ -25,7 +25,7 @@ | |||
24 | 25 | It is intended to be a list of fields, and possibly mappings on those fields. | 25 | It is intended to be a list of fields, and possibly mappings on those fields. |
25 | 26 | Something like:: | 26 | Something like:: |
26 | 27 | 27 | ||
28 | 28 | CREATE_INDEX(mydb, "myindex", ["field", "other.subfield", "number(third)"]) | 28 | CREATE_INDEX(mydb, "myindex", ["field", "other.subfield", "number(third)"]) |
29 | 29 | 29 | ||
30 | 30 | 30 | ||
31 | 31 | Recommended Implementation | 31 | Recommended Implementation |
32 | @@ -83,7 +83,7 @@ | |||
33 | 83 | 83 | ||
34 | 84 | {"lastname": "pedroni", "firstname": "john"} | 84 | {"lastname": "pedroni", "firstname": "john"} |
35 | 85 | 85 | ||
37 | 86 | Which should not match the above query. | 86 | Which should not match the above query. |
38 | 87 | 87 | ||
39 | 88 | We also want an SQL index on this table, something like [#]_:: | 88 | We also want an SQL index on this table, something like [#]_:: |
40 | 89 | 89 | ||
41 | @@ -134,7 +134,7 @@ | |||
42 | 134 | 3) It isn't hard to map nested fields into this structure. And you have | 134 | 3) It isn't hard to map nested fields into this structure. And you have |
43 | 135 | the nice property that you don't have to change the data to add/remove an | 135 | the nice property that you don't have to change the data to add/remove an |
44 | 136 | index. | 136 | index. |
46 | 137 | 137 | ||
47 | 138 | 4) It isn't 100% clear how we handle mapped fields in this structure. Something | 138 | 4) It isn't 100% clear how we handle mapped fields in this structure. Something |
48 | 139 | like ``lower(lastname)``. It is possible that we could only support the set | 139 | like ``lower(lastname)``. It is possible that we could only support the set |
49 | 140 | of mappings that we can do with SQL on the live data. However, that will | 140 | of mappings that we can do with SQL on the live data. However, that will |
50 | @@ -147,7 +147,7 @@ | |||
51 | 147 | seem to support turning "SELECT * FROM table WHERE value LIKE 'p%'" into an | 147 | seem to support turning "SELECT * FROM table WHERE value LIKE 'p%'" into an |
52 | 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, |
53 | 149 | you could use >= and < to get a range query. Something like:: | 149 | you could use >= and < to get a range query. Something like:: |
55 | 150 | 150 | ||
56 | 151 | SELECT * FROM table WHERE value >= 'p' AND value < 'q' | 151 | SELECT * FROM table WHERE value >= 'p' AND value < 'q' |
57 | 152 | 152 | ||
58 | 153 | Since sqlite supports closed and open ended ranges, we don't have to play | 153 | Since sqlite supports closed and open ended ranges, we don't have to play |
59 | @@ -168,7 +168,7 @@ | |||
60 | 168 | ----------------- | 168 | ----------------- |
61 | 169 | 169 | ||
62 | 170 | The same schema as defined above, except you always put every field into the | 170 | The same schema as defined above, except you always put every field into the |
64 | 171 | document_fields table. | 171 | document_fields table. |
65 | 172 | 172 | ||
66 | 173 | Discussion | 173 | Discussion |
67 | 174 | ~~~~~~~~~~ | 174 | ~~~~~~~~~~ |
68 | 175 | 175 | ||
69 | === modified file 'html-docs/conflicts.rst' | |||
70 | --- html-docs/conflicts.rst 2011-12-21 16:09:22 +0000 | |||
71 | +++ html-docs/conflicts.rst 2012-07-16 17:22:17 +0000 | |||
72 | @@ -7,26 +7,26 @@ | |||
73 | 7 | Conflicts | 7 | Conflicts |
74 | 8 | ------------- | 8 | ------------- |
75 | 9 | 9 | ||
80 | 10 | If two u1dbs are synced, and then the same document is changed in different ways | 10 | If two u1dbs are synced, and then the same document is changed in different |
81 | 11 | in each u1db, and then they are synced again, there will be a *conflict*. This | 11 | ways in each u1db, and then they are synced again, there will be a *conflict*. |
82 | 12 | does not block synchronisation: the document is registered as being in conflict, | 12 | This does not block synchronisation: the document is registered as being in |
83 | 13 | and resolving that is up to the u1db-using application. | 13 | conflict, and resolving that is up to the u1db-using application. |
84 | 14 | 14 | ||
85 | 15 | Importantly, **conflicts are not synced**. If *machine A* initiates a sync with | 15 | Importantly, **conflicts are not synced**. If *machine A* initiates a sync with |
86 | 16 | *machine B*, and this sync results in a conflict, the conflict **only registers | 16 | *machine B*, and this sync results in a conflict, the conflict **only registers |
87 | 17 | on machine A**. This policy is sometimes called "other wins": the machine you | 17 | on machine A**. This policy is sometimes called "other wins": the machine you |
88 | 18 | synced *to* wins conflicts, and the document will have machine B's content on | 18 | synced *to* wins conflicts, and the document will have machine B's content on |
91 | 19 | both machine A and machine B. However, on machine A the document is marked | 19 | both machine A and machine B. However, on machine A the document is marked as |
92 | 20 | as having conflicts, and must be resolved there: | 20 | having conflicts, and must be resolved there: |
93 | 21 | 21 | ||
94 | 22 | .. testsetup :: | 22 | .. testsetup :: |
95 | 23 | 23 | ||
96 | 24 | import u1db, json | 24 | import u1db, json |
97 | 25 | db=u1db.open(':memory:', True) | 25 | db=u1db.open(':memory:', True) |
98 | 26 | docFromA=u1db.Document('test','machineA:1',json.dumps({'camefrom':'machineA'})) | 26 | docFromA=u1db.Document('test','machineA:1',json.dumps({'camefrom':'machineA'})) |
100 | 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) |
101 | 28 | docFromB=u1db.Document('test','machineB:1',json.dumps({'camefrom':'machineB'})) | 28 | docFromB=u1db.Document('test','machineB:1',json.dumps({'camefrom':'machineB'})) |
103 | 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) |
104 | 30 | 30 | ||
105 | 31 | .. doctest :: | 31 | .. doctest :: |
106 | 32 | 32 | ||
107 | @@ -35,66 +35,67 @@ | |||
108 | 35 | >>> docFromB.has_conflicts # the document is in conflict | 35 | >>> docFromB.has_conflicts # the document is in conflict |
109 | 36 | True | 36 | True |
110 | 37 | >>> conflicts = db.get_doc_conflicts(docFromB.doc_id) | 37 | >>> conflicts = db.get_doc_conflicts(docFromB.doc_id) |
114 | 38 | >>> print conflicts | 38 | >>> conflicts |
115 | 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"}')] |
116 | 40 | >>> db.resolve_doc(docFromB, [x[0] for x in conflicts]) # resolve in favour of B | 40 | >>> db.resolve_doc(docFromB, [d.rev for d in conflicts]) # resolve in favour of B |
117 | 41 | >>> doc_is_now = db.get_doc("test") | 41 | >>> doc_is_now = db.get_doc("test") |
118 | 42 | >>> doc_is_now.content # the content has been updated to doc's content | 42 | >>> doc_is_now.content # the content has been updated to doc's content |
120 | 43 | u'{"camefrom": "machineB"}' | 43 | {u'camefrom': u'machineB'} |
121 | 44 | >>> db.get_doc_conflicts(docFromB.doc_id) | ||
122 | 45 | [] | ||
123 | 44 | >>> doc_is_now.has_conflicts # and is no longer in conflict | 46 | >>> doc_is_now.has_conflicts # and is no longer in conflict |
124 | 45 | False | 47 | False |
125 | 46 | 48 | ||
126 | 47 | Note that ``put_doc`` will fail because we got conflicts from a sync, but it | 49 | Note that ``put_doc`` will fail because we got conflicts from a sync, but it |
130 | 48 | may also fail for another reason. If you acquire a document before a sync and | 50 | may also fail for another reason. If you acquire a document before a sync and |
131 | 49 | then sync, and the sync updates that document, then re-putting that document | 51 | then sync, and the sync updates that document, then re-putting that document |
132 | 50 | with modified content will also fail, because the revision is not the current | 52 | with modified content will also fail, because the revision is not the current |
133 | 51 | one. This will raise a ``RevisionConflict`` error. | 53 | one. This will raise a ``RevisionConflict`` error. |
134 | 52 | 54 | ||
135 | 53 | Revisions | 55 | Revisions |
136 | 54 | ---------- | 56 | ---------- |
137 | 55 | 57 | ||
140 | 56 | As an app developer, you should treat a ``Document``'s ``revision`` as an opaque | 58 | As an app developer, you should treat a ``Document``'s ``revision`` as an |
141 | 57 | cookie; do not try and deconstruct it or edit it. It is for your u1db | 59 | opaque cookie; do not try and deconstruct it or edit it. It is for your u1db |
142 | 58 | implementation's use. You can therefore ignore the rest of this section. | 60 | implementation's use. You can therefore ignore the rest of this section. |
143 | 59 | 61 | ||
145 | 60 | If you are writing a new u1db implementation, understanding revisions is | 62 | If you are writing a new u1db implementation, understanding revisions is |
146 | 61 | important, and this is where you find out about them. | 63 | important, and this is where you find out about them. |
147 | 62 | 64 | ||
148 | 63 | To keep track of document revisions u1db uses vector versions. Each | 65 | To keep track of document revisions u1db uses vector versions. Each |
160 | 64 | synchronized instance of the same database is called a replica and has | 66 | synchronized instance of the same database is called a replica and has a unique |
161 | 65 | a unique identifier (``replica uid``) assigned to it (currently the | 67 | identifier (``replica uid``) assigned to it (currently the reference |
162 | 66 | reference implementation by default uses UUID4s for that); a | 68 | implementation by default uses UUID4s for that); a revision is a mapping |
163 | 67 | revision is a mapping between ``replica uids`` and ``edit numbers``: ``rev = | 69 | between ``replica uids`` and ``edit numbers``: ``rev |
164 | 68 | <replica_uid:edit_num...>``, or using a functional notation | 70 | = <replica_uid:edit_num...>``, or using a functional notation |
165 | 69 | ``rev(replica_uid) = edit_num``. The current concrete format is a string | 71 | ``rev(replica_uid) = edit_num``. The current concrete format is a string built |
166 | 70 | built out of each ``replica_uid`` concatenated with ``':'`` and with its edit | 72 | out of each ``replica_uid`` concatenated with ``':'`` and with its edit number |
167 | 71 | number in decimal, sorted lexicographically by ``replica_uid`` and then | 73 | in decimal, sorted lexicographically by ``replica_uid`` and then all joined |
168 | 72 | all joined with ``'|'``, for example: ``'replicaA:1|replicaB:3'`` . Absent | 74 | with ``'|'``, for example: ``'replicaA:1|replicaB:3'`` . Absent ``replica |
169 | 73 | ``replica uids`` in a revision mapping are implicitly mapped to edit | 75 | uids`` in a revision mapping are implicitly mapped to edit number 0. |
159 | 74 | number 0. | ||
170 | 75 | 76 | ||
171 | 76 | The new revision of a document modified locally in a replica, is the | 77 | The new revision of a document modified locally in a replica, is the |
174 | 77 | modification of the old revision where the edit number mapped for the | 78 | modification of the old revision where the edit number mapped for the editing |
175 | 78 | editing ``replica uid`` is increased by 1. | 79 | ``replica uid`` is increased by 1. |
176 | 79 | 80 | ||
179 | 80 | When syncing one needs to establish whether an incoming revision is | 81 | When syncing one needs to establish whether an incoming revision is newer than |
180 | 81 | newer than the current one or in conflict. A revision | 82 | the current one or in conflict. A revision |
181 | 82 | 83 | ||
182 | 83 | ``rev1 = <replica_1i:edit_num1i|i=1..n>`` | 84 | ``rev1 = <replica_1i:edit_num1i|i=1..n>`` |
183 | 84 | 85 | ||
185 | 85 | is newer than a different | 86 | is newer than a different |
186 | 86 | 87 | ||
187 | 87 | ``rev2 = <replica_2j:edit_num2j|j=1..m>`` | 88 | ``rev2 = <replica_2j:edit_num2j|j=1..m>`` |
188 | 88 | 89 | ||
198 | 89 | if for all ``i=1..n``, ``rev2(replica_1i) <= edit_num1i`` | 90 | if for all ``i=1..n``, ``rev2(replica_1i) <= edit_num1i`` |
199 | 90 | 91 | ||
200 | 91 | and for all ``j=1..m``, ``rev1(replica_2j) >= edit_num2j``. | 92 | and for all ``j=1..m``, ``rev1(replica_2j) >= edit_num2j``. |
201 | 92 | 93 | ||
202 | 93 | Two revisions which are not equal nor one newer than the | 94 | Two revisions which are not equal nor one newer than the other are in conflict. |
203 | 94 | other are in conflict. | 95 | |
204 | 95 | 96 | When resolving a conflict locally in a replica ``replica_resol``, starting from | |
205 | 96 | When resolving a conflict locally in a replica ``replica_resol``, starting from | 97 | ``rev1...revN`` in conflict, the resulting revision ``rev_resol`` is obtained |
206 | 97 | ``rev1...revN`` in conflict, the resulting revision ``rev_resol`` is obtained by: | 98 | by: |
207 | 98 | 99 | ||
208 | 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`` |
209 | 100 | 101 | ||
210 | 101 | 102 | ||
211 | === modified file 'html-docs/high-level-api.rst' | |||
212 | --- html-docs/high-level-api.rst 2011-12-21 11:09:04 +0000 | |||
213 | +++ html-docs/high-level-api.rst 2012-07-16 17:22:17 +0000 | |||
214 | @@ -4,23 +4,22 @@ | |||
215 | 4 | ################## | 4 | ################## |
216 | 5 | 5 | ||
217 | 6 | The U1DB API has three separate sections: document storage and retrieval, | 6 | The U1DB API has three separate sections: document storage and retrieval, |
222 | 7 | querying, and sync. Here we describe the high-level API. Remember that you | 7 | querying, and sync. Here we describe the high-level API. Remember that you will |
223 | 8 | will need to choose an implementation, and exactly how this API is defined | 8 | need to choose an implementation, and exactly how this API is defined is |
224 | 9 | is implementation-specific, in order that it fits with the language's | 9 | implementation-specific, in order that it fits with the language's conventions. |
221 | 10 | conventions. | ||
225 | 11 | 10 | ||
226 | 12 | Document storage and retrieval | 11 | Document storage and retrieval |
227 | 13 | ############################## | 12 | ############################## |
228 | 14 | 13 | ||
229 | 15 | U1DB stores documents. A document is a set of nested key-values; basically, | 14 | U1DB stores documents. A document is a set of nested key-values; basically, |
232 | 16 | anything you can express with JSON. Implementations are likely to provide a | 15 | anything you can express with JSON. Implementations are likely to provide |
233 | 17 | Document object "wrapper" for these documents; exactly how the wrapper works | 16 | a Document object "wrapper" for these documents; exactly how the wrapper works |
234 | 18 | is implementation-defined. | 17 | is implementation-defined. |
235 | 19 | 18 | ||
236 | 20 | Creating and editing documents | 19 | Creating and editing documents |
237 | 21 | ------------------------------ | 20 | ------------------------------ |
238 | 22 | 21 | ||
240 | 23 | To create a document, use ``create_doc()``. Code examples below are from | 22 | To create a document, use ``create_doc()``. Code examples below are from |
241 | 24 | :ref:`reference-implementation` in Python. | 23 | :ref:`reference-implementation` in Python. |
242 | 25 | 24 | ||
243 | 26 | .. testcode :: | 25 | .. testcode :: |
244 | @@ -33,12 +32,12 @@ | |||
245 | 33 | 32 | ||
246 | 34 | .. testoutput :: | 33 | .. testoutput :: |
247 | 35 | 34 | ||
249 | 36 | {"key": "value"} | 35 | {'key': 'value'} |
250 | 37 | testdoc | 36 | testdoc |
251 | 38 | 37 | ||
255 | 39 | Editing an *existing* document is done with ``put_doc()``. This is separate from | 38 | Editing an *existing* document is done with ``put_doc()``. This is separate |
256 | 40 | ``create_doc()`` so as to avoid accidental overwrites. ``put_doc()`` takes a | 39 | from ``create_doc()`` so as to avoid accidental overwrites. ``put_doc()`` takes |
257 | 41 | ``Document`` object, because the object encapsulates revision information for | 40 | a ``Document`` object, because the object encapsulates revision information for |
258 | 42 | a particular document. | 41 | a particular document. |
259 | 43 | 42 | ||
260 | 44 | .. testcode :: | 43 | .. testcode :: |
261 | @@ -52,15 +51,16 @@ | |||
262 | 52 | except u1db.errors.RevisionConflict: | 51 | except u1db.errors.RevisionConflict: |
263 | 53 | print "There was a conflict when creating the doc!" | 52 | print "There was a conflict when creating the doc!" |
264 | 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..." |
268 | 55 | data = json.loads(doc1.content) | 54 | doc1.content["key1"] = "edited" |
266 | 56 | data["key1"] = "edited" | ||
267 | 57 | doc1.content = json.dumps(data) | ||
269 | 58 | db.put_doc(doc1) | 55 | db.put_doc(doc1) |
270 | 56 | doc2 = db.get_doc(doc1.doc_id) | ||
271 | 57 | print doc2.content | ||
272 | 59 | 58 | ||
273 | 60 | .. testoutput :: | 59 | .. testoutput :: |
274 | 61 | 60 | ||
275 | 62 | There was a conflict when creating the doc! | 61 | There was a conflict when creating the doc! |
276 | 63 | Now editing the doc with the doc object we got back... | 62 | Now editing the doc with the doc object we got back... |
277 | 63 | {u'key1': u'edited'} | ||
278 | 64 | 64 | ||
279 | 65 | Finally, deleting a document is done with ``delete_doc()``. | 65 | Finally, deleting a document is done with ``delete_doc()``. |
280 | 66 | 66 | ||
281 | @@ -70,9 +70,14 @@ | |||
282 | 70 | db = u1db.open(":memory:", create=True) | 70 | db = u1db.open(":memory:", create=True) |
283 | 71 | doc = db.create_doc(json.dumps({"key": "value"})) | 71 | doc = db.create_doc(json.dumps({"key": "value"})) |
284 | 72 | db.delete_doc(doc) | 72 | db.delete_doc(doc) |
285 | 73 | print db.get_doc(doc.doc_id) | ||
286 | 74 | doc = db.get_doc(doc.doc_id, include_deleted=True) | ||
287 | 75 | print doc.content | ||
288 | 73 | 76 | ||
289 | 74 | .. testoutput :: | 77 | .. testoutput :: |
290 | 75 | 78 | ||
291 | 79 | None | ||
292 | 80 | None | ||
293 | 76 | 81 | ||
294 | 77 | Retrieving documents | 82 | Retrieving documents |
295 | 78 | -------------------- | 83 | -------------------- |
296 | @@ -90,7 +95,7 @@ | |||
297 | 90 | 95 | ||
298 | 91 | .. testoutput :: | 96 | .. testoutput :: |
299 | 92 | 97 | ||
301 | 93 | {"key": "value"} | 98 | {u'key': u'value'} |
302 | 94 | testdoc | 99 | testdoc |
303 | 95 | 100 | ||
304 | 96 | And it's also possible to retrieve many documents by ``doc_id``. | 101 | And it's also possible to retrieve many documents by ``doc_id``. |
305 | @@ -140,7 +145,7 @@ | |||
306 | 140 | {"firstname": "Alan", "surname", "Hansen", "position": "defence"} ID ah | 145 | {"firstname": "Alan", "surname", "Hansen", "position": "defence"} ID ah |
307 | 141 | {"firstname": "John", "surname", "Wayne", "position": "filmstar"} ID jw | 146 | {"firstname": "John", "surname", "Wayne", "position": "filmstar"} ID jw |
308 | 142 | 147 | ||
310 | 143 | an index expression of ``["firstname"]`` will create an index that looks | 148 | an index expression of ``["firstname"]`` will create an index that looks |
311 | 144 | (conceptually) like this | 149 | (conceptually) like this |
312 | 145 | 150 | ||
313 | 146 | ====================== =========== | 151 | ====================== =========== |
314 | @@ -152,25 +157,25 @@ | |||
315 | 152 | John jw | 157 | John jw |
316 | 153 | ====================== =========== | 158 | ====================== =========== |
317 | 154 | 159 | ||
322 | 155 | and that index is created with ``create_index("by-firstname", ["firstname"])`` - that is, | 160 | and that index is created with ``create_index("by-firstname", "firstname")`` |
323 | 156 | create an index with a name and a list of index expressions. (Exactly how to | 161 | -- that is, create an index with a name and a list of index expressions. |
324 | 157 | pass the name and the list of index expressions is something specific to | 162 | (Exactly how to pass the name and the list of index expressions is something |
325 | 158 | each implementation.) | 163 | specific to each implementation.) |
326 | 159 | 164 | ||
327 | 160 | Index expressions | 165 | Index expressions |
328 | 161 | ^^^^^^^^^^^^^^^^^ | 166 | ^^^^^^^^^^^^^^^^^ |
329 | 162 | 167 | ||
332 | 163 | An index expression describes how to get data from a document; you can think | 168 | An index expression describes how to get data from a document; you can think of |
333 | 164 | of it as describing a function which, when given a document, returns a value, | 169 | it as describing a function which, when given a document, returns a value, |
334 | 165 | which is then used as the index key. | 170 | which is then used as the index key. |
335 | 166 | 171 | ||
336 | 167 | **Name a field.** A basic index expression is a dot-delimited list of nesting | 172 | **Name a field.** A basic index expression is a dot-delimited list of nesting |
338 | 168 | fieldnames, so the index expression ``field.sub1.sub2`` applied to a document | 173 | fieldnames, so the index expression ``field.sub1.sub2`` applied to a document |
339 | 169 | with ID ``doc1`` and content:: | 174 | with ID ``doc1`` and content:: |
340 | 170 | 175 | ||
341 | 171 | { | 176 | { |
344 | 172 | "field": { | 177 | "field": { |
345 | 173 | "sub1": { | 178 | "sub1": { |
346 | 174 | "sub2": "hello" | 179 | "sub2": "hello" |
347 | 175 | "sub3": "not selected" | 180 | "sub3": "not selected" |
348 | 176 | } | 181 | } |
349 | @@ -187,11 +192,11 @@ | |||
350 | 187 | 192 | ||
351 | 188 | **Name a list.** If an index expression names a field whose contents is a list | 193 | **Name a list.** If an index expression names a field whose contents is a list |
352 | 189 | of strings, the doc will have multiple entries in the index, one per entry in | 194 | of strings, the doc will have multiple entries in the index, one per entry in |
355 | 190 | the list. So, the index expression ``field.tags`` applied to a document with | 195 | the list. So, the index expression ``field.tags`` applied to a document with ID |
356 | 191 | ID "doc2" and content:: | 196 | "doc2" and content:: |
357 | 192 | 197 | ||
358 | 193 | { | 198 | { |
360 | 194 | "field": { | 199 | "field": { |
361 | 195 | "tags": [ "tag1", "tag2", "tag3" ] | 200 | "tags": [ "tag1", "tag2", "tag3" ] |
362 | 196 | } | 201 | } |
363 | 197 | } | 202 | } |
364 | @@ -206,25 +211,30 @@ | |||
365 | 206 | tag3 doc2 | 211 | tag3 doc2 |
366 | 207 | ========= ====== | 212 | ========= ====== |
367 | 208 | 213 | ||
372 | 209 | **Transformation functions.** An index expression may be wrapped in any number of | 214 | **Transformation functions.** An index expression may be wrapped in any number |
373 | 210 | transformation functions. A function transforms the result of the contained | 215 | of transformation functions. A function transforms the result of the contained |
374 | 211 | index expression: for example, if an expression ``name.firstname`` generates | 216 | index expression: for example, if an expression ``name.firstname`` generates |
375 | 212 | "John" when applied to a document, then ``lower(name.firstname)`` generates | 217 | "John" when applied to a document, then ``lower(name.firstname)`` generates |
376 | 213 | "john". | 218 | "john". |
377 | 214 | 219 | ||
378 | 215 | Available transformation functions are: | 220 | Available transformation functions are: |
379 | 216 | 221 | ||
380 | 217 | * ``lower(index_expression)`` - lowercase the value | 222 | * ``lower(index_expression)`` - lowercase the value |
385 | 218 | * ``splitwords(index_expression)`` - split the value on whitespace; will act like a | 223 | * ``splitwords(index_expression)`` - split the value on whitespace; will act |
386 | 219 | list and add multiple entries to the index | 224 | like a list and add multiple entries to the index |
387 | 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 |
388 | 221 | is absent, otherwise false | 226 | into a string, left padded with zeroes, to make it at least as wide as |
389 | 227 | width. | ||
390 | 228 | * ``bool(index_expression)`` - takes a boolean value and turns it into '0' if | ||
391 | 229 | false and '1' if true. | ||
392 | 230 | * ``is_null(index_expression)`` - True if value is null or not a string or the | ||
393 | 231 | field is absent, otherwise false | ||
394 | 222 | 232 | ||
397 | 223 | So, the index expression ``splitwords(lower(field.name))`` applied to a document with | 233 | So, the index expression ``splitwords(lower(field.name))`` applied to |
398 | 224 | ID "doc3" and content:: | 234 | a document with ID "doc3" and content:: |
399 | 225 | 235 | ||
400 | 226 | { | 236 | { |
402 | 227 | "field": { | 237 | "field": { |
403 | 228 | "name": "Bruce David Grobbelaar" | 238 | "name": "Bruce David Grobbelaar" |
404 | 229 | } | 239 | } |
405 | 230 | } | 240 | } |
406 | @@ -243,25 +253,18 @@ | |||
407 | 243 | Querying an index | 253 | Querying an index |
408 | 244 | ----------------- | 254 | ----------------- |
409 | 245 | 255 | ||
429 | 246 | Pass a list of tuples of index keys to ``get_from_index``; the last index key in | 256 | Pass an index key or a tuple of index keys (if the index is on multiple fields) |
430 | 247 | each tuple (and *only* the last one) can end with an asterisk, which matches | 257 | to ``get_from_index``; the last index key in each tuple (and *only* the last |
431 | 248 | initial substrings. So, querying our ``by-firstname`` index from above:: | 258 | one) can end with an asterisk, which matches initial substrings. So, querying |
432 | 249 | 259 | our ``by-firstname`` index from above:: | |
433 | 250 | get_from_index( | 260 | |
434 | 251 | "by-firstname", # name of index | 261 | get_from_index("by-firstname", "John") |
435 | 252 | [ # begin the list of index keys | 262 | |
436 | 253 | ("John", ) # an index key | 263 | |
437 | 254 | ] # end the list | 264 | will return the documents with ids: 'jw', 'jb'. |
438 | 255 | ) | 265 | |
439 | 256 | 266 | ``get_from_index("by_firstname", "J*")`` will match all index keys beginning | |
440 | 257 | 267 | with "J", and so will return the documents with ids: 'jw', 'jb', 'jm'. | |
422 | 258 | will return ``[ 'jw', 'jb' ]`` - that is, a list of document IDs. | ||
423 | 259 | |||
424 | 260 | ``get_from_index("by_firstname", [("J*")])`` will match all index keys beginning | ||
425 | 261 | with "J", and so will return ``[ 'jw', 'jb', 'jm' ]``. | ||
426 | 262 | |||
427 | 263 | ``get_from_index("by_firstname", [("Jan"), ("Alan")])`` will match both the | ||
428 | 264 | queried index keys, and so will return ``[ 'jm', 'ah' ]``. | ||
441 | 265 | 268 | ||
442 | 266 | 269 | ||
443 | 267 | Index functions | 270 | Index functions |
444 | @@ -277,30 +280,30 @@ | |||
445 | 277 | ####### | 280 | ####### |
446 | 278 | 281 | ||
447 | 279 | U1DB is a syncable database. Any U1DB can be synced with any U1DB server; most | 282 | U1DB is a syncable database. Any U1DB can be synced with any U1DB server; most |
451 | 280 | U1DB implementations are capable of being run as a server. Syncing brings | 283 | U1DB implementations are capable of being run as a server. Syncing brings both |
452 | 281 | both the server and the client up to date with one another; save data into a | 284 | the server and the client up to date with one another; save data into a local |
453 | 282 | local U1DB whether online or offline, and then sync when online. | 285 | U1DB whether online or offline, and then sync when online. |
454 | 283 | 286 | ||
455 | 284 | Pass an HTTP URL to sync with that server. | 287 | Pass an HTTP URL to sync with that server. |
456 | 285 | 288 | ||
457 | 286 | Syncing databases which have been independently changed may produce conflicts. | 289 | Syncing databases which have been independently changed may produce conflicts. |
458 | 287 | Read about the U1DB conflict policy and more about syncing at :ref:`conflicts`. | 290 | Read about the U1DB conflict policy and more about syncing at :ref:`conflicts`. |
459 | 288 | 291 | ||
462 | 289 | Running your own U1DB server is implementation-specific. :ref:`reference-implementation` | 292 | Running your own U1DB server is implementation-specific. |
463 | 290 | is able to be run as a server. | 293 | :ref:`reference-implementation` is able to be run as a server. |
464 | 291 | 294 | ||
465 | 292 | Dealing with conflicts | 295 | Dealing with conflicts |
466 | 293 | ---------------------- | 296 | ---------------------- |
467 | 294 | 297 | ||
469 | 295 | Syncing a database can result in conflicts; if your user changes the same | 298 | Syncing a database can result in conflicts; if your user changes the same |
470 | 296 | document in two different places and then syncs again, that document will be | 299 | document in two different places and then syncs again, that document will be |
471 | 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, |
478 | 298 | ``doc.has_conflicts`` will be true, and put_doc to a conflicted doc will give a | 301 | ``doc.has_conflicts`` will be true, and put_doc to a conflicted doc will give |
479 | 299 | ``ConflictedDoc`` error. To get a list of conflicted versions of the | 302 | a ``ConflictedDoc`` error. To get a list of conflicted versions of the |
480 | 300 | document, do ``get_doc_conflicts(doc_id)``. Deciding what the final unconflicted | 303 | document, do ``get_doc_conflicts(doc_id)``. Deciding what the final |
481 | 301 | document should look like is obviously specific to the user's application; once | 304 | unconflicted document should look like is obviously specific to the user's |
482 | 302 | decided, call ``resolve_doc(doc, list_of_conflicted_revisions)`` to resolve and | 305 | application; once decided, call ``resolve_doc(doc, list_of_conflicted_revisions)`` |
483 | 303 | set the final resolved content. | 306 | to resolve and set the final resolved content. |
484 | 304 | 307 | ||
485 | 305 | Syncing functions | 308 | Syncing functions |
486 | 306 | ^^^^^^^^^^^^^^^^^ | 309 | ^^^^^^^^^^^^^^^^^ |
487 | 307 | 310 | ||
488 | === modified file 'html-docs/index.rst' | |||
489 | --- html-docs/index.rst 2011-12-21 13:49:41 +0000 | |||
490 | +++ html-docs/index.rst 2012-07-16 17:22:17 +0000 | |||
491 | @@ -1,29 +1,29 @@ | |||
492 | 1 | U1DB | 1 | U1DB |
493 | 2 | #### | 2 | #### |
494 | 3 | 3 | ||
501 | 4 | U1DB is a database API for synchronised databases of JSON documents. It's | 4 | U1DB is a database API for synchronised databases of JSON documents. It's |
502 | 5 | simple to use in applications, and allows apps to store documents and | 5 | simple to use in applications, and allows apps to store documents and |
503 | 6 | synchronise them between machines and devices. U1DB itself is not a database: | 6 | synchronise them between machines and devices. U1DB itself is not a database: |
504 | 7 | instead, it's an API which can be backed by any database for storage. This means that you | 7 | instead, it's an API which can be backed by any database for storage. This |
505 | 8 | can use u1db on different platforms, from different languages, and backed | 8 | means that you can use u1db on different platforms, from different languages, |
506 | 9 | on to different databases, and sync between all of them. | 9 | and backed on to different databases, and sync between all of them. |
507 | 10 | 10 | ||
508 | 11 | The API for U1DB looks similar across all different implementations. This API | 11 | The API for U1DB looks similar across all different implementations. This API |
512 | 12 | is described at :ref:`high-level-api`. To actually use U1DB you'll need an | 12 | is described at :ref:`high-level-api`. To actually use U1DB you'll need an |
513 | 13 | implementation; a version of U1DB made available on your choice of platform, | 13 | implementation; a version of U1DB made available on your choice of platform, in |
514 | 14 | in your choice of language, and on your choice of backend database. | 14 | your choice of language, and on your choice of backend database. |
515 | 15 | 15 | ||
519 | 16 | If you're interested in using U1DB in an application, look at | 16 | If you're interested in using U1DB in an application, look at |
520 | 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` |
521 | 18 | and read about exactly how the U1DB API is made available in that | 18 | and read about exactly how the U1DB API is made available in that |
522 | 19 | implementation. Get going quickly with the :ref:`quickstart`. | 19 | implementation. Get going quickly with the :ref:`quickstart`. |
523 | 20 | 20 | ||
525 | 21 | If you're interested in hacking on U1DB itself, read about the | 21 | If you're interested in hacking on U1DB itself, read about the |
526 | 22 | :ref:`rules for U1DB <philosophy>` and :ref:`reference-implementation`. | 22 | :ref:`rules for U1DB <philosophy>` and :ref:`reference-implementation`. |
527 | 23 | 23 | ||
528 | 24 | .. toctree:: | 24 | .. toctree:: |
529 | 25 | :maxdepth: 1 | 25 | :maxdepth: 1 |
531 | 26 | 26 | ||
532 | 27 | quickstart | 27 | quickstart |
533 | 28 | high-level-api | 28 | high-level-api |
534 | 29 | reference-implementation | 29 | reference-implementation |
535 | 30 | 30 | ||
536 | === modified file 'html-docs/philosophy.rst' | |||
537 | --- html-docs/philosophy.rst 2011-12-21 13:11:20 +0000 | |||
538 | +++ html-docs/philosophy.rst 2012-07-16 17:22:17 +0000 | |||
539 | @@ -5,52 +5,51 @@ | |||
540 | 5 | 5 | ||
541 | 6 | Some notes on what u1db is for, how it works, and how it should be used. | 6 | Some notes on what u1db is for, how it works, and how it should be used. |
542 | 7 | 7 | ||
547 | 8 | U1DB is a cross-platform, cross-device, syncable database API. In order to be this | 8 | U1DB is a cross-platform, cross-device, syncable database API. In order to be |
548 | 9 | way, there's a philosophy behind it. Key to this philosophy is that u1db can | 9 | this way, there's a philosophy behind it. Key to this philosophy is that u1db |
549 | 10 | be implemented in many languages and on top of many back ends: this means that | 10 | can be implemented in many languages and on top of many back ends: this means |
550 | 11 | the API needs to be, as much as possible, portable between very different | 11 | that the API needs to be, as much as possible, portable between very different |
551 | 12 | languages. Each implementation should implement :ref:`high-level-api` in the | 12 | languages. Each implementation should implement :ref:`high-level-api` in the |
574 | 13 | way appropriate to that language (Python uses tuples all over the place, | 13 | way appropriate to that language (Python uses tuples all over the place, Vala/C |
575 | 14 | Vala/C use a Document object for most things, and so on), but it's important | 14 | use a Document object for most things, and so on), but it's important that an |
576 | 15 | that an implementation not diverge from the API. Because u1db is a syncable | 15 | implementation not diverge from the API. Because u1db is a syncable database, |
577 | 16 | database, it's quite likely that an app developer using it will be building their | 16 | it's quite likely that an app developer using it will be building their app on |
578 | 17 | app on multiple platforms at once. Knowledge that an app developer has from | 17 | multiple platforms at once. Knowledge that an app developer has from having |
579 | 18 | having built a u1db app on one platform should be transferable to another | 18 | built a u1db app on one platform should be transferable to another platform. |
580 | 19 | platform. This means that querying is the same across platforms; storing and | 19 | This means that querying is the same across platforms; storing and retrieving |
581 | 20 | retrieving docs is the same across platforms; syncing is the same across | 20 | docs is the same across platforms; syncing is the same across platforms. U1DB |
582 | 21 | platforms. U1DB is also syncable to Ubuntu One, which is a very large | 21 | is also syncable to Ubuntu One, which is a very large server installation; the |
583 | 22 | server installation; the API needs to be suitable to run at scales from a | 22 | API needs to be suitable to run at scales from a mobile phone up to a large |
584 | 23 | mobile phone up to a large server installation. | 23 | server installation. |
585 | 24 | 24 | ||
586 | 25 | For similar reasons, u1db is *schemaless*. Documents stored in u1db do not | 25 | For similar reasons, u1db is *schemaless*. Documents stored in u1db do not need |
587 | 26 | need to contain any pre-defined list of fields; this way, an application can | 26 | to contain any pre-defined list of fields; this way, an application can store |
588 | 27 | store whatever it wants, however it wants; development is faster and changing | 27 | whatever it wants, however it wants; development is faster and changing how |
589 | 28 | how data is stored is simpler. | 28 | data is stored is simpler. |
590 | 29 | 29 | ||
591 | 30 | What this means is that u1db is for user-specific data. A desktop app or a | 30 | What this means is that u1db is for user-specific data. A desktop app or |
592 | 31 | mobile app storing data for a user is the ideal use case. A web app which | 31 | a mobile app storing data for a user is the ideal use case. A web app which |
593 | 32 | holds data for many users should be using and syncing a separate u1db for | 32 | holds data for many users should be using and syncing a separate u1db for each |
594 | 33 | each user. U1DB isn't designed to be the backend database for the next | 33 | user. U1DB isn't designed to be the backend database for the next Facebook. |
573 | 34 | Facebook. | ||
595 | 35 | 34 | ||
596 | 36 | To this end, there are a few guidelines. Primarily, the guideline the u1db team | 35 | To this end, there are a few guidelines. Primarily, the guideline the u1db team |
597 | 37 | used for the largest u1db is somewhere around 10,000 documents. It's important | 36 | used for the largest u1db is somewhere around 10,000 documents. It's important |
598 | 38 | to note that this is not an *enforced* limit; an app dev can store a zillion | 37 | to note that this is not an *enforced* limit; an app dev can store a zillion |
599 | 39 | documents in a u1db if they want. However, the implementations are allowed to | 38 | documents in a u1db if they want. However, the implementations are allowed to |
600 | 40 | assume that there aren't a zillion documents; in particular, suggestions for | 39 | assume that there aren't a zillion documents; in particular, suggestions for |
603 | 41 | API changes which make things more annoying for a 1,000 documents use-case | 40 | API changes which make things more annoying for a 1,000 documents use-case in |
604 | 42 | in order to help with a zillion documents are not likely to be adopted. | 41 | order to help with a zillion documents are not likely to be adopted. |
605 | 43 | 42 | ||
606 | 44 | Similarly, suggested changes to the high-level API which are very difficult to | 43 | Similarly, suggested changes to the high-level API which are very difficult to |
607 | 45 | implement in static languages like C are also unlikely to be adopted, in order | 44 | implement in static languages like C are also unlikely to be adopted, in order |
608 | 46 | to maintain the goal of knowledge on one platform transferring to another. | 45 | to maintain the goal of knowledge on one platform transferring to another. |
609 | 47 | 46 | ||
611 | 48 | U1DB is designed so that implementations are built by creating small layers on | 47 | U1DB is designed so that implementations are built by creating small layers on |
612 | 49 | top of existing storage solutions. It isn't a database in itself; it's an API | 48 | top of existing storage solutions. It isn't a database in itself; it's an API |
613 | 50 | layer which sits on top of a native database to that platform. This means that | 49 | layer which sits on top of a native database to that platform. This means that |
617 | 51 | the platform provides the actual database functionality and u1db takes advantage | 50 | the platform provides the actual database functionality and u1db takes |
618 | 52 | of it. SQLite where available, localStorage for JavaScript in the web browser; | 51 | advantage of it. SQLite where available, localStorage for JavaScript in the web |
619 | 53 | u1db should work with the platform, not be ported to it. | 52 | browser; u1db should work with the platform, not be ported to it. |
620 | 54 | 53 | ||
621 | 55 | It should be easy to sync a u1db from place to place. There is a direct server | 54 | It should be easy to sync a u1db from place to place. There is a direct server |
622 | 56 | HTTP API, which allows an app to work with a u1db on the server without any | 55 | HTTP API, which allows an app to work with a u1db on the server without any |
623 | 57 | 56 | ||
624 | === modified file 'html-docs/quickstart.rst' | |||
625 | --- html-docs/quickstart.rst 2011-12-21 11:09:04 +0000 | |||
626 | +++ html-docs/quickstart.rst 2012-07-16 17:22:17 +0000 | |||
627 | @@ -18,8 +18,8 @@ | |||
628 | 18 | Use from source control | 18 | Use from source control |
629 | 19 | ^^^^^^^^^^^^^^^^^^^^^^^ | 19 | ^^^^^^^^^^^^^^^^^^^^^^^ |
630 | 20 | 20 | ||
633 | 21 | u1db is `maintained in bazaar in Launchpad <http://launchpad.net/u1db/>`_. To fetch the latest version, | 21 | u1db is `maintained in bazaar in Launchpad <http://launchpad.net/u1db/>`_. To |
634 | 22 | `bzr branch lp:u1db`. | 22 | fetch the latest version, `bzr branch lp:u1db`. |
635 | 23 | 23 | ||
636 | 24 | Starting u1db | 24 | Starting u1db |
637 | 25 | ------------- | 25 | ------------- |
638 | @@ -28,29 +28,29 @@ | |||
639 | 28 | 28 | ||
640 | 29 | >>> import u1db, json, tempfile | 29 | >>> import u1db, json, tempfile |
641 | 30 | >>> db = u1db.open(":memory:", create=True) | 30 | >>> db = u1db.open(":memory:", create=True) |
643 | 31 | 31 | ||
644 | 32 | >>> content = json.dumps({"name": "Alan Hansen"}) # create a document | 32 | >>> content = json.dumps({"name": "Alan Hansen"}) # create a document |
645 | 33 | >>> doc = db.create_doc(content) | 33 | >>> doc = db.create_doc(content) |
648 | 34 | >>> print doc.content | 34 | >>> doc.content |
649 | 35 | {"name": "Alan Hansen"} | 35 | {'name': 'Alan Hansen'} |
650 | 36 | >>> doc.content = json.dumps({"name": "Alan Hansen", "position": "defence"}) # update the document's content | 36 | >>> doc.content = json.dumps({"name": "Alan Hansen", "position": "defence"}) # update the document's content |
651 | 37 | >>> rev = db.put_doc(doc) | 37 | >>> rev = db.put_doc(doc) |
653 | 38 | 38 | ||
654 | 39 | >>> content = json.dumps({"name": "John Barnes", "position": "forward"}) # create more documents | 39 | >>> content = json.dumps({"name": "John Barnes", "position": "forward"}) # create more documents |
655 | 40 | >>> doc2 = db.create_doc(content) | 40 | >>> doc2 = db.create_doc(content) |
656 | 41 | >>> content = json.dumps({"name": "Ian Rush", "position": "forward"}) | 41 | >>> content = json.dumps({"name": "Ian Rush", "position": "forward"}) |
657 | 42 | >>> doc2 = db.create_doc(content) | 42 | >>> doc2 = db.create_doc(content) |
662 | 43 | 43 | ||
663 | 44 | >>> db.create_index("by-position", ("position",)) # create an index by passing an index expression | 44 | >>> db.create_index("by-position", "position") # create an index by passing a field name |
664 | 45 | 45 | ||
665 | 46 | >>> results = db.get_from_index("by-position", [("forward",)]) # query that index by passing a list of tuples of queries | 46 | >>> results = db.get_from_index("by-position", "forward") # query that index by passing a value |
666 | 47 | >>> len(results) | 47 | >>> len(results) |
667 | 48 | 2 | 48 | 2 |
669 | 49 | >>> data = [json.loads(result.content) for result in results] | 49 | >>> data = [result.content for result in results] |
670 | 50 | >>> names = [item["name"] for item in data] | 50 | >>> names = [item["name"] for item in data] |
671 | 51 | >>> sorted(names) | 51 | >>> sorted(names) |
672 | 52 | [u'Ian Rush', u'John Barnes'] | 52 | [u'Ian Rush', u'John Barnes'] |
674 | 53 | 53 | ||
675 | 54 | Running a server | 54 | Running a server |
676 | 55 | ---------------- | 55 | ---------------- |
677 | 56 | 56 | ||
678 | @@ -80,7 +80,7 @@ | |||
679 | 80 | >>> import u1db | 80 | >>> import u1db |
680 | 81 | >>> db = u1db.open(":memory:", create=True) | 81 | >>> db = u1db.open(":memory:", create=True) |
681 | 82 | >>> generation = db.sync("http://127.0.0.1:43632/example.u1db") | 82 | >>> generation = db.sync("http://127.0.0.1:43632/example.u1db") |
683 | 83 | 83 | ||
684 | 84 | or from the command line | 84 | or from the command line |
685 | 85 | 85 | ||
686 | 86 | .. code-block:: bash | 86 | .. code-block:: bash |
687 | @@ -88,4 +88,4 @@ | |||
688 | 88 | ~/u1db/trunk$ ./u1db-client init-db someother.u1db | 88 | ~/u1db/trunk$ ./u1db-client init-db someother.u1db |
689 | 89 | ~/u1db/trunk$ ./u1db-client sync someother.u1db http://127.0.0.1:43632/example.u1db | 89 | ~/u1db/trunk$ ./u1db-client sync someother.u1db http://127.0.0.1:43632/example.u1db |
690 | 90 | 90 | ||
692 | 91 | 91 | ||
693 | 92 | 92 | ||
694 | === modified file 'html-docs/reference-implementation.rst' | |||
695 | --- html-docs/reference-implementation.rst 2011-12-21 11:09:04 +0000 | |||
696 | +++ html-docs/reference-implementation.rst 2012-07-16 17:22:17 +0000 | |||
697 | @@ -4,10 +4,10 @@ | |||
698 | 4 | ############################# | 4 | ############################# |
699 | 5 | 5 | ||
700 | 6 | The u1db reference implementation is written in Python, with a SQLite back end. | 6 | The u1db reference implementation is written in Python, with a SQLite back end. |
705 | 7 | It can be used as a real working implementation by Python code. It is also used | 7 | It can be used as a real working implementation by Python code. It is also used |
706 | 8 | to document and test how u1db should work; it has a comprehensive test suite. | 8 | to document and test how u1db should work; it has a comprehensive test suite. |
707 | 9 | Implementation authors should port the u1db reference test suite in order to | 9 | Implementation authors should port the u1db reference test suite in order to |
708 | 10 | test that their implementation is correct; in particular, sync conformance is | 10 | test that their implementation is correct; in particular, sync conformance is |
709 | 11 | defined as being able to sync with the reference implementation. | 11 | defined as being able to sync with the reference implementation. |
710 | 12 | 12 | ||
711 | 13 | Fetch with ``bzr branch lp:u1db`` or from `Launchpad <http://launchpad.net/u1db>`_. | 13 | Fetch with ``bzr branch lp:u1db`` or from `Launchpad <http://launchpad.net/u1db>`_. |
712 | 14 | 14 | ||
713 | === modified file 'u1db/__init__.py' | |||
714 | --- u1db/__init__.py 2012-07-12 17:21:15 +0000 | |||
715 | +++ u1db/__init__.py 2012-07-16 17:22:17 +0000 | |||
716 | @@ -60,8 +60,9 @@ | |||
717 | 60 | returned as documents by the database. | 60 | returned as documents by the database. |
718 | 61 | 61 | ||
719 | 62 | :param factory: A function that returns an object which at minimum must | 62 | :param factory: A function that returns an object which at minimum must |
722 | 63 | satisfy the same interface as does the class DocumentBase. Subclassing | 63 | satisfy the same interface as does the class DocumentBase. |
723 | 64 | that class is the easiest way to create such a function. | 64 | Subclassing that class is the easiest way to create such |
724 | 65 | a function. | ||
725 | 65 | """ | 66 | """ |
726 | 66 | raise NotImplementedError(self.set_document_factory) | 67 | raise NotImplementedError(self.set_document_factory) |
727 | 67 | 68 | ||
728 | @@ -169,11 +170,11 @@ | |||
729 | 169 | and the index generated. | 170 | and the index generated. |
730 | 170 | 171 | ||
731 | 171 | :name: A unique name which can be used as a key prefix | 172 | :name: A unique name which can be used as a key prefix |
737 | 172 | :index_expressions: index expressions defining the index | 173 | :index_expressions: index expressions defining the index information. |
738 | 173 | information. Examples: | 174 | Examples: |
739 | 174 | "fieldname" to index alphabetically sorted on field. | 175 | "fieldname" to index alphabetically sorted on field. |
740 | 175 | "number(fieldname, width)", "lower(fieldname)", | 176 | "number(fieldname, width)", "lower(fieldname)", |
741 | 176 | "fieldname.subfieldname" | 177 | "fieldname.subfieldname" |
742 | 177 | """ | 178 | """ |
743 | 178 | raise NotImplementedError(self.create_index) | 179 | raise NotImplementedError(self.create_index) |
744 | 179 | 180 | ||
745 | @@ -243,7 +244,7 @@ | |||
746 | 243 | raise NotImplementedError(self.get_index_keys) | 244 | raise NotImplementedError(self.get_index_keys) |
747 | 244 | 245 | ||
748 | 245 | def get_doc_conflicts(self, doc_id): | 246 | def get_doc_conflicts(self, doc_id): |
750 | 246 | """Get the list of conflict texts for the given document. | 247 | """Get the list of conflicts for the given document. |
751 | 247 | 248 | ||
752 | 248 | The order of the conflicts is such that the first entry is the value | 249 | The order of the conflicts is such that the first entry is the value |
753 | 249 | that would be returned by "get_doc". | 250 | that would be returned by "get_doc". |