| .. Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| .. use this file except in compliance with the License. You may obtain a copy of |
| .. the License at |
| .. |
| .. http://www.apache.org/licenses/LICENSE-2.0 |
| .. |
| .. Unless required by applicable law or agreed to in writing, software |
| .. distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| .. WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| .. License for the specific language governing permissions and limitations under |
| .. the License. |
| |
| ======================== |
| JSON Structure Reference |
| ======================== |
| |
| The following appendix provides a quick reference to all the JSON structures |
| that you can supply to CouchDB, or get in return to requests. |
| |
| All Database Documents |
| ====================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | total_rows | Number of documents in the database/view | |
| +--------------------------------+---------------------------------------------+ |
| | offset | Offset where the document list started | |
| +--------------------------------+---------------------------------------------+ |
| | update_seq (optional) | Current update sequence for the database | |
| +--------------------------------+---------------------------------------------+ |
| | rows [array] | Array of document object | |
| +--------------------------------+---------------------------------------------+ |
| |
| Bulk Document Response |
| ====================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | docs [array] | Bulk Docs Returned Documents | |
| +--------------------------------+---------------------------------------------+ |
| | id | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | error | Error type | |
| +--------------------------------+---------------------------------------------+ |
| | reason | Error string with extended reason | |
| +--------------------------------+---------------------------------------------+ |
| |
| Bulk Documents |
| ============== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | all_or_nothing (optional) | Sets the database commit mode to use | |
| | | all-or-nothing semantics | |
| +--------------------------------+---------------------------------------------+ |
| | docs [array] | Bulk Documents Document | |
| +--------------------------------+---------------------------------------------+ |
| | _id (optional) | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev (optional) | Revision ID (when updating an existing | |
| | | document) | |
| +--------------------------------+---------------------------------------------+ |
| | _deleted (optional) | Whether the document should be deleted | |
| +--------------------------------+---------------------------------------------+ |
| |
| Changes information for a database |
| ================================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | last_seq | Last change sequence number | |
| +--------------------------------+---------------------------------------------+ |
| | results [array] | Changes made to a database | |
| +--------------------------------+---------------------------------------------+ |
| | seq | Update sequence number | |
| +--------------------------------+---------------------------------------------+ |
| | id | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | changes [array] | List of changes, field-by-field, for this | |
| | | document | |
| +--------------------------------+---------------------------------------------+ |
| |
| CouchDB Document |
| ================ |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | _id (optional) | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev (optional) | Revision ID (when updating an existing | |
| | | document) | |
| +--------------------------------+---------------------------------------------+ |
| |
| CouchDB Error Status |
| ==================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | id | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | error | Error type | |
| +--------------------------------+---------------------------------------------+ |
| | reason | Error string with extended reason | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. _dbinfo_object: |
| |
| CouchDB database information object |
| =================================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | db_name | The name of the database. | |
| +--------------------------------+---------------------------------------------+ |
| | committed_update_seq | The number of committed update. | |
| +--------------------------------+---------------------------------------------+ |
| | doc_count | A count of the documents in the specified | |
| | | database. | |
| +--------------------------------+---------------------------------------------+ |
| | doc_del_count | Number of deleted documents | |
| +--------------------------------+---------------------------------------------+ |
| | compact_running | Set to true if the database compaction | |
| | | routine is operating on this database. | |
| +--------------------------------+---------------------------------------------+ |
| | disk_format_version | The version of the physical format used for | |
| | | the data when it is stored on disk. | |
| +--------------------------------+---------------------------------------------+ |
| | disk_size | Size in bytes of the data as stored on the | |
| | | disk. Views indexes are not included in the | |
| | | calculation. | |
| +--------------------------------+---------------------------------------------+ |
| | instance_start_time | Timestamp of when the database was opened, | |
| | | expressed in microseconds since the epoch. | |
| +--------------------------------+---------------------------------------------+ |
| | purge_seq | The number of purge operations on the | |
| | | database. | |
| +--------------------------------+---------------------------------------------+ |
| | update_seq | The current number of updates to the | |
| | | database. | |
| +--------------------------------+---------------------------------------------+ |
| |
| Design Document |
| =============== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | _id | Design Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev | Design Document Revision | |
| +--------------------------------+---------------------------------------------+ |
| | views | View | |
| +--------------------------------+---------------------------------------------+ |
| | viewname | View Definition | |
| +--------------------------------+---------------------------------------------+ |
| | map | Map Function for View | |
| +--------------------------------+---------------------------------------------+ |
| | reduce (optional) | Reduce Function for View | |
| +--------------------------------+---------------------------------------------+ |
| |
| Design Document Information |
| =========================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | name | Name/ID of Design Document | |
| +--------------------------------+---------------------------------------------+ |
| | view_index | View Index | |
| +--------------------------------+---------------------------------------------+ |
| | compact_running | Indicates whether a compaction routine is | |
| | | currently running on the view | |
| +--------------------------------+---------------------------------------------+ |
| | disk_size | Size in bytes of the view as stored on disk | |
| +--------------------------------+---------------------------------------------+ |
| | language | Language for the defined views | |
| +--------------------------------+---------------------------------------------+ |
| | purge_seq | The purge sequence that has been processed | |
| +--------------------------------+---------------------------------------------+ |
| | signature | MD5 signature of the views for the design | |
| | | document | |
| +--------------------------------+---------------------------------------------+ |
| | update_seq | The update sequence of the corresponding | |
| | | database that has been indexed | |
| +--------------------------------+---------------------------------------------+ |
| | updater_running | Indicates if the view is currently being | |
| | | updated | |
| +--------------------------------+---------------------------------------------+ |
| | waiting_clients | Number of clients waiting on views from this| |
| | | design document | |
| +--------------------------------+---------------------------------------------+ |
| | waiting_commit | Indicates if there are outstanding commits | |
| | | to the underlying database that need to | |
| | | processed | |
| +--------------------------------+---------------------------------------------+ |
| |
| Document with Attachments |
| ========================= |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | _id (optional) | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev (optional) | Revision ID (when updating an existing | |
| | | document) | |
| +--------------------------------+---------------------------------------------+ |
| | _attachments (optional) | Document Attachment | |
| +--------------------------------+---------------------------------------------+ |
| | filename | Attachment information | |
| +--------------------------------+---------------------------------------------+ |
| | content_type | MIME Content type string | |
| +--------------------------------+---------------------------------------------+ |
| | data | File attachment content, Base64 encoded | |
| +--------------------------------+---------------------------------------------+ |
| |
| List of Active Tasks |
| ==================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | tasks [array] | Active Task | |
| +--------------------------------+---------------------------------------------+ |
| | pid | Process ID | |
| +--------------------------------+---------------------------------------------+ |
| | status | Task status message | |
| +--------------------------------+---------------------------------------------+ |
| | task | Task name | |
| +--------------------------------+---------------------------------------------+ |
| | type | Operation Type | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. _replication-settings: |
| |
| Replication Settings |
| ==================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | source | Source database name or URL | |
| +--------------------------------+---------------------------------------------+ |
| | target | Target database name or URL | |
| +--------------------------------+---------------------------------------------+ |
| | create_target (optional) | Creates the target database | |
| +--------------------------------+---------------------------------------------+ |
| | continuous (optional) | Configure the replication to be continuous | |
| +--------------------------------+---------------------------------------------+ |
| | cancel (optional) | Cancels the replication | |
| +--------------------------------+---------------------------------------------+ |
| | doc_ids (optional) | Array of document IDs to be synchronized | |
| +--------------------------------+---------------------------------------------+ |
| | proxy (optional) | Address of a proxy server through which | |
| | | replication should occur | |
| +--------------------------------+---------------------------------------------+ |
| | since_seq (optional) | Sequence from which the replication should | |
| | | start | |
| +--------------------------------+---------------------------------------------+ |
| | filter (optional) | name of the filter function in the form of | |
| | | ddoc/myfilter | |
| +--------------------------------+---------------------------------------------+ |
| | query_params (optional) | query parameter that are passed to the | |
| | | filter function; value should be a document | |
| | | containing parameters as members | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. _replication-status: |
| |
| Replication Status |
| ================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | ok | Replication status | |
| +--------------------------------+---------------------------------------------+ |
| | session_id | Unique session ID | |
| +--------------------------------+---------------------------------------------+ |
| | source_last_seq | Last sequence number read from source | |
| | | database | |
| +--------------------------------+---------------------------------------------+ |
| | history [array] | Replication History | |
| +--------------------------------+---------------------------------------------+ |
| | session_id | Session ID for this replication operation | |
| +--------------------------------+---------------------------------------------+ |
| | recorded_seq | Last recorded sequence number | |
| +--------------------------------+---------------------------------------------+ |
| | docs_read | Number of documents read | |
| +--------------------------------+---------------------------------------------+ |
| | docs_written | Number of documents written to target | |
| +--------------------------------+---------------------------------------------+ |
| | doc_write_failures | Number of document write failures | |
| +--------------------------------+---------------------------------------------+ |
| | start_time | Date/Time replication operation started | |
| +--------------------------------+---------------------------------------------+ |
| | start_last_seq | First sequence number in changes stream | |
| +--------------------------------+---------------------------------------------+ |
| | end_time | Date/Time replication operation completed | |
| +--------------------------------+---------------------------------------------+ |
| | end_last_seq | Last sequence number in changes stream | |
| +--------------------------------+---------------------------------------------+ |
| | missing_checked | Number of missing documents checked | |
| +--------------------------------+---------------------------------------------+ |
| | missing_found | Number of missing documents found | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. _request_object: |
| |
| Request object |
| ============== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | body | Request body data as `string`. | |
| | | If request method is `GET` method contains | |
| | | this field contains ``"undefined"`` value, | |
| | | while if `DELETE` or `HEAD` value is ``""`` | |
| | | (empty string) | |
| +--------------------------------+---------------------------------------------+ |
| | cookie | Cookies `object`. | |
| +--------------------------------+---------------------------------------------+ |
| | form | Form data `object`. | |
| | | Contains decoded body as key-value pairs if | |
| | | `Content-Type` header was | |
| | | ``application/x-www-form-urlencoded``. | |
| +--------------------------------+---------------------------------------------+ |
| | headers | Request headers `object`. | |
| +--------------------------------+---------------------------------------------+ |
| | id | Requested document id `string` if it was | |
| | | specified or ``null`` otherwise. | |
| +--------------------------------+---------------------------------------------+ |
| | info | :ref:`Database information <dbinfo_object>` | |
| +--------------------------------+---------------------------------------------+ |
| | method | Request method as `string` or `array`. | |
| | | String value is method is one of: `HEAD`, | |
| | | `GET`, `POST`, `PUT`, `DELETE`, `OPTIONS`, | |
| | | and `TRACE`, otherwise it will be | |
| | | represented as array of char codes. | |
| +--------------------------------+---------------------------------------------+ |
| | path | List of requested path sections. | |
| +--------------------------------+---------------------------------------------+ |
| | peer | Request source IP address. | |
| +--------------------------------+---------------------------------------------+ |
| | query | URL query parameters `object`. | |
| | | Note that multiple keys not supported and | |
| | | last key value suppress others. | |
| +--------------------------------+---------------------------------------------+ |
| | requested_path | List of actual requested path section. | |
| +--------------------------------+---------------------------------------------+ |
| | raw_path | Raw requested path `string`. | |
| +--------------------------------+---------------------------------------------+ |
| | secObj | :ref:`security_object`. | |
| +--------------------------------+---------------------------------------------+ |
| | userCtx | :ref:`userctx_object`. | |
| +--------------------------------+---------------------------------------------+ |
| | uuid | Generated UUID by specified algorithm in | |
| | | config file. | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. code-block:: javascript |
| |
| { |
| "body": "undefined", |
| "cookie": { |
| "AuthSession": "cm9vdDo1MDZBRjQzRjrfcuikzPRfAn-EA37FmjyfM8G8Lw", |
| "m": "3234" |
| }, |
| "form": {}, |
| "headers": { |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", |
| "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.3", |
| "Accept-Encoding": "gzip,deflate,sdch", |
| "Accept-Language": "en-US,en;q=0.8", |
| "Connection": "keep-alive", |
| "Cookie": "m=3234:t|3247:t|6493:t|6967:t|34e2:|18c3:t|2c69:t|5acb:t|ca3:t|c01:t|5e55:t|77cb:t|2a03:t|1d98:t|47ba:t|64b8:t|4a01:t; AuthSession=cm9vdDo1MDZBRjQzRjrfcuikzPRfAn-EA37FmjyfM8G8Lw", |
| "Host": "127.0.0.1:5984", |
| "User-Agent": "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7" |
| }, |
| "id": "foo", |
| "info": { |
| "committed_update_seq": 2701412, |
| "compact_running": false, |
| "data_size": 7580843252, |
| "db_name": "mailbox", |
| "disk_format_version": 6, |
| "disk_size": 14325313673, |
| "doc_count": 2262757, |
| "doc_del_count": 560, |
| "instance_start_time": "1347601025628957", |
| "purge_seq": 0, |
| "update_seq": 2701412 |
| }, |
| "method": "GET", |
| "path": [ |
| "mailbox", |
| "_design", |
| "request", |
| "_show", |
| "dump", |
| "foo" |
| ], |
| "peer": "127.0.0.1", |
| "query": {}, |
| "raw_path": "/mailbox/_design/request/_show/dump/foo", |
| "requested_path": [ |
| "mailbox", |
| "_design", |
| "request", |
| "_show", |
| "dump", |
| "foo" |
| ], |
| "secObj": { |
| "admins": { |
| "names": [ |
| "Bob" |
| ], |
| "roles": [] |
| }, |
| "members": { |
| "names": [ |
| "Mike", |
| "Alice" |
| ], |
| "roles": [] |
| } |
| }, |
| "userCtx": { |
| "db": "mailbox", |
| "name": "Mike", |
| "roles": [ |
| "user" |
| ] |
| }, |
| "uuid": "3184f9d1ea934e1f81a24c71bde5c168" |
| } |
| |
| |
| .. _response_object: |
| |
| Response object |
| =============== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | code | HTTP status code `number`. | |
| +--------------------------------+---------------------------------------------+ |
| | json | JSON encodable `object`. | |
| | | Implicitly sets `Content-Type` header as | |
| | | ``application/json``. | |
| +--------------------------------+---------------------------------------------+ |
| | body | Raw response text `string`. | |
| | | Implicitly sets `Content-Type` header as | |
| | | ``text/html; charset=utf-8``. | |
| +--------------------------------+---------------------------------------------+ |
| | base64 | Base64 encoded `string`. | |
| | | Implicitly sets `Content-Type` header as | |
| | | ``application/binary``. | |
| +--------------------------------+---------------------------------------------+ |
| | headers | Response headers `object`. | |
| | | `Content-Type` header from this object | |
| | | overrides any implicitly assigned one. | |
| +--------------------------------+---------------------------------------------+ |
| | stop | `boolean` signal to stop iteration over | |
| | | view result rows (for list functions only) | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. warning:: |
| ``body``, ``base64`` and ``json`` object keys are overlaps each other and |
| the last wins. Since most realizations of key-value objects doesn't preserve |
| key order mixing them may create confusing situation. Try to use only one of |
| them. |
| |
| .. note:: |
| Any custom property makes CouchDB raise internal exception. |
| Also `Response object` could be a simple string value which would be |
| implicitly wrapped into ``{"body": ...}`` object. |
| |
| |
| Returned CouchDB Document with Detailed Revision Info |
| ===================================================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | _id (optional) | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev (optional) | Revision ID (when updating an existing | |
| | | document) | |
| +--------------------------------+---------------------------------------------+ |
| | _revs_info [array] | CouchDB Document Extended Revision Info | |
| +--------------------------------+---------------------------------------------+ |
| | rev | Full revision string | |
| +--------------------------------+---------------------------------------------+ |
| | status | Status of the revision | |
| +--------------------------------+---------------------------------------------+ |
| |
| Returned CouchDB Document with Revision Info |
| ============================================ |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | _id (optional) | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev (optional) | Revision ID (when updating an existing | |
| | | document) | |
| +--------------------------------+---------------------------------------------+ |
| | _revisions | CouchDB Document Revisions | |
| +--------------------------------+---------------------------------------------+ |
| | ids [array] | Array of valid revision IDs, in reverse | |
| | | order (latest first) | |
| +--------------------------------+---------------------------------------------+ |
| | start | Prefix number for the latest revision | |
| +--------------------------------+---------------------------------------------+ |
| |
| Returned Document with Attachments |
| ================================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | _id (optional) | Document ID | |
| +--------------------------------+---------------------------------------------+ |
| | _rev (optional) | Revision ID (when updating an existing | |
| | | document) | |
| +--------------------------------+---------------------------------------------+ |
| | _attachments (optional) | Document Attachment | |
| +--------------------------------+---------------------------------------------+ |
| | filename | Attachment | |
| +--------------------------------+---------------------------------------------+ |
| | stub | Indicates whether the attachment is a stub | |
| +--------------------------------+---------------------------------------------+ |
| | content_type | MIME Content type string | |
| +--------------------------------+---------------------------------------------+ |
| | length | Length (bytes) of the attachment data | |
| +--------------------------------+---------------------------------------------+ |
| | revpos | Revision where this attachment exists | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. _security_object: |
| |
| Security Object |
| =============== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | admins | Roles/Users with admin privileges | |
| +--------------------------------+---------------------------------------------+ |
| | roles [array] | List of roles with parent privilege | |
| +--------------------------------+---------------------------------------------+ |
| | users [array] | List of users with parent privilege | |
| +--------------------------------+---------------------------------------------+ |
| | readers | Roles/Users with reader privileges | |
| +--------------------------------+---------------------------------------------+ |
| | roles [array] | List of roles with parent privilege | |
| +--------------------------------+---------------------------------------------+ |
| | users [array] | List of users with parent privilege | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. code-block:: javascript |
| |
| { |
| "admins": { |
| "names": [ |
| "Bob" |
| ], |
| "roles": [] |
| }, |
| "members": { |
| "names": [ |
| "Mike", |
| "Alice" |
| ], |
| "roles": [] |
| } |
| } |
| |
| |
| .. _userctx_object: |
| |
| User Context Object |
| =================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | db | Database name in context of provided | |
| | | operation. | |
| +--------------------------------+---------------------------------------------+ |
| | name | User name. | |
| +--------------------------------+---------------------------------------------+ |
| | roles | List of user roles. | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. code-block:: javascript |
| |
| { |
| "db": "mailbox", |
| "name": null, |
| "roles": [ |
| "_admin" |
| ] |
| } |
| |
| |
| .. _view_head_info_object: |
| |
| View Head Information |
| ===================== |
| |
| +--------------------------------+---------------------------------------------+ |
| | Field | Description | |
| +================================+=============================================+ |
| | total_rows | Number of documents in the view | |
| +--------------------------------+---------------------------------------------+ |
| | offset | Offset where the document list started | |
| +--------------------------------+---------------------------------------------+ |
| |
| .. code-block:: javascript |
| |
| { |
| "total_rows": 42, |
| "offset": 3 |
| } |
| |
| .. _number-handling: |
| |
| Number Handling |
| =============== |
| |
| Any numbers defined in JSON that contain a decimal point or exponent |
| will be passed through the Erlang VM's idea of the "double" data type. |
| Any numbers that are used in views will pass through the views idea of |
| a number (the common JavaScript case means even integers pass through |
| a double due to JavaScript's definition of a number). |
| |
| Consider this document that we write to CouchDB: |
| |
| .. code-block:: javascript |
| |
| { |
| "_id":"30b3b38cdbd9e3a587de9b8122000cff", |
| "number": 1.1 |
| } |
| |
| Now let’s read that document back from CouchDB: |
| |
| .. code-block:: javascript |
| |
| { |
| "_id":"30b3b38cdbd9e3a587de9b8122000cff", |
| "_rev":"1-f065cee7c3fd93aa50f6c97acde93030", |
| "number":1.1000000000000000888 |
| } |
| |
| |
| What happens is CouchDB is changing the textual representation of the |
| result of decoding what it was given into some numerical format. In most |
| cases this is an `IEEE 754`_ double precision floating point number which |
| is exactly what almost all other languages use as well. |
| |
| .. _IEEE 754: https://en.wikipedia.org/wiki/IEEE_754-2008 |
| |
| What CouchDB does a bit differently than other languages is that it |
| does not attempt to pretty print the resulting output to use the |
| shortest number of characters. For instance, this is why we have this |
| relationship: |
| |
| .. code-block:: erlang |
| |
| ejson:encode(ejson:decode(<<"1.1">>)). |
| <<"1.1000000000000000888">> |
| |
| What can be confusing here is that internally those two formats |
| decode into the same IEEE-754 representation. And more importantly, it |
| will decode into a fairly close representation when passed through all |
| major parsers that I know about. |
| |
| While we've only been discussing cases where the textual |
| representation changes, another important case is when an input value |
| is contains more precision than can actually represented in a double. |
| (You could argue that this case is actually "losing" data if you don't |
| accept that numbers are stored in doubles). |
| |
| Here's a log for a couple of the more common JSON libraries I happen |
| to have on my machine: |
| |
| Spidermonkey:: |
| |
| $ js -h 2>&1 | head -n 1 |
| JavaScript-C 1.8.5 2011-03-31 |
| $ js |
| js> JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890")) |
| "1.0123456789012346" |
| js> var f = JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890")) |
| js> JSON.stringify(JSON.parse(f)) |
| "1.0123456789012346" |
| |
| Node:: |
| |
| $ node -v |
| v0.6.15 |
| $ node |
| JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890")) |
| '1.0123456789012346' |
| var f = JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890")) |
| undefined |
| JSON.stringify(JSON.parse(f)) |
| '1.0123456789012346' |
| |
| Python:: |
| |
| $ python |
| Python 2.7.2 (default, Jun 20 2012, 16:23:33) |
| [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin |
| Type "help", "copyright", "credits" or "license" for more information. |
| import json |
| json.dumps(json.loads("1.01234567890123456789012345678901234567890")) |
| '1.0123456789012346' |
| f = json.dumps(json.loads("1.01234567890123456789012345678901234567890")) |
| json.dumps(json.loads(f)) |
| '1.0123456789012346' |
| |
| Ruby:: |
| |
| $ irb --version |
| irb 0.9.5(05/04/13) |
| require 'JSON' |
| => true |
| JSON.dump(JSON.load("[1.01234567890123456789012345678901234567890]")) |
| => "[1.01234567890123]" |
| f = JSON.dump(JSON.load("[1.01234567890123456789012345678901234567890]")) |
| => "[1.01234567890123]" |
| JSON.dump(JSON.load(f)) |
| => "[1.01234567890123]" |
| |
| |
| .. note:: A small aside on Ruby, it requires a top level object or array, so I just |
| wrapped the value. Should be obvious it doesn't affect the result of |
| parsing the number though. |
| |
| |
| Ejson (CouchDB's current parser) at CouchDB sha 168a663b:: |
| |
| $ ./utils/run -i |
| Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:2:2] [rq:2] |
| [async-threads:4] [hipe] [kernel-poll:true] |
| |
| Eshell V5.8.5 (abort with ^G) |
| 1> ejson:encode(ejson:decode(<<"1.01234567890123456789012345678901234567890">>)). |
| <<"1.0123456789012346135">> |
| 2> F = ejson:encode(ejson:decode(<<"1.01234567890123456789012345678901234567890">>)). |
| <<"1.0123456789012346135">> |
| 3> ejson:encode(ejson:decode(F)). |
| <<"1.0123456789012346135">> |
| |
| |
| As you can see they all pretty much behave the same except for Ruby |
| actually does appear to be losing some precision over the other |
| libraries. |
| |
| The astute observer will notice that ejson (the CouchDB JSON library) |
| reported an extra three digits. While its tempting to think that this |
| is due to some internal difference, its just a more specific case of |
| the 1.1 input as described above. |
| |
| The important point to realize here is that a double can only hold a |
| finite number of values. What we're doing here is generating a string |
| that when passed through the "standard" floating point parsing |
| algorithms (ie, strtod) will result in the same bit pattern in memory |
| as we started with. Or, slightly different, the bytes in a JSON |
| serialized number are chosen such that they refer to a single specific |
| value that a double can represent. |
| |
| The important point to understand is that we're mapping from one |
| infinite set onto a finite set. An easy way to see this is by |
| reflecting on this:: |
| |
| 1.0 == 1.00 == 1.000 = 1.(infinite zeroes) |
| |
| Obviously a computer can't hold infinite bytes so we have to |
| decimate our infinitely sized set to a finite set that can be |
| represented concisely. |
| |
| The game that other JSON libraries are playing is merely: |
| |
| "How few characters do I have to use to select this specific value for a double" |
| |
| And that game has lots and lots of subtle details that are difficult |
| to duplicate in C without a significant amount of effort (it took |
| Python over a year to get it sorted with their fancy build systems |
| that automatically run on a number of different architectures). |
| |
| Hopefully we've shown that CouchDB is not doing anything "funky" by |
| changing input. Its behaving the same as any other common JSON library |
| does, its just not pretty printing its output. |
| |
| On the other hand, if you actually are in a position where an IEEE-754 |
| double is not a satisfactory datatype for your numbers, then the |
| answer as has been stated is to not pass your numbers through this |
| representation. In JSON this is accomplished by encoding them as a |
| string or by using integer types (although integer types can still |
| bite you if you use a platform that has a different integer |
| representation than normal, ie, JavaScript). |
| |
| Also, if anyone is really interested in changing this behavior, I'm |
| all ears for contributions to `jiffy`_ (which is theoretically going to |
| replace ejson when I get around to updating the build system). The |
| places I've looked for inspiration are TCL and Python. If you know a |
| decent implementation of this float printing algorithm give me a |
| holler. |
| |
| .. _jiffy: https://github.com/davisp/jiffy |