Description
The SaveDocument API allows a User to save or update a Document with the given parameters.
There are two ways to save a document - with an defined schema, or without one. Users wanting more flexibility and control over files and security should create documents with a schema.
Note that “schema-less” documents are actually documents with a default schema. This default schema contains the field "apsdb_attachments" that can be used to attach a file to the schema-less document.
Creating Documents
To create a document, users should create fields that make up this document. Creating a field can be done by providing [fieldname]=[fieldValue] pairs and setting the apsdb.multivalueAppend parameter=[fieldname] to append multiple values else the last value of the fieldname will be set only.
When specifying fields, users can send a fieldType. This will ensure the field value will be parsed and validated. The fieldType defaults to "string" if not sent. Any other values than the predefined types (string, text, numeric, date, file, geospatial) are not allowed.
Note that all fieldNames starting with apsdb or ending with apsdb.fieldType or apsdb.fieldDateFormat cannot be sent more than once, else an error (DUPLICATE_PARAMETER_VALUE) will be returned.
When saving a document, it should be specified to what store it is saving. When saving a document, users can send a document key. If the key is not sent, then apstrata database will generate a store-bound unique id, called a document key, for each document. This key will be used when updating the document later.
Please remember that apstrata database uses the document key to index its documents, so it is necessary to have random document keys. More importantly, the first few letters of the document key should be random and unique. The reason for random document keys is to optimize the indexing process and make your data storage and retrieval operations faster. For example:
Good document keys:
123_john, 234_john, 345_john, 456_john, …
Bad document keys:
john_123, john_234, john_345, john_456, …
Field of type 'date':
When saving a field of type 'date', the system needs to know the format used to parse the date sent in the request. There are three ways to do so:
1- Use the default system format:
The system accepts two date formats by default:
yyyy-MM-dd'T'HH:mm:ssZ (e.g. 2012-04-13T13:01:02+0000)
yyyy-MM-dd (e.g. 2012-04-13)
Unless otherwise specified, the fields of type 'date' sent in the SaveDocument request will be expected to use one of the two default formats and will be parsed accordingly.
An error is returned if the date does not match either of these formats.
Below is an example of a javascript implementation that generates a date in the default system format:
new Date().toISOString().split(".")[0]+"+0000"
2- Use the apsdb.globalDateFormat request parameter:
Sending the apsdb.globalDateFormat parameter in the SaveDocument request informs the system to expect all 'date' fields of the current document to use the specified format. The dates will be parsed using this new format and an error will be returned if one of the fields cannot be parsed.
The format specified in the apsdb.globalDateFormat parameter always overrides the default system date format for the current document.
3- Use the [fieldName].apsdb.fieldDateFormat request parameter:
Sending a date format in the [fieldName].apsdb.fieldDateFormat parameter informs the system that the value of field [fieldName] uses the specified format and should be parsed accordingly. If the format of the date sent in this field is not the one specified in the parameter then an error will be returned.
The format specified in the [fieldName].apsdb.fieldDateFormat parameter always overrides both the default system date format and the format sent in the apsdb.globalDateFormat parameter for the specified field ([fieldName]).
Please note that the server time is GMT.
Field of type 'file':
Saving a field of type "file" is done differently if the document is bound to a schema or not.
1- Document is schemaless:
Only the special field "apsdb_attachments" can be used to save a file to a document that is not bound to a schema. It is possible to attach multiple files to the "apsdb_attachments" field by sending its name in the apsdb.multivalueAppend parameter (ex: apsdb.multivalueAppend=apsdb_attachments).
If a user sends a file in a different field than “apsdb_attachments”, an error will occur with code "INVALID_FIELD_VALUE".
2- Document is bound to a schema:
If the document being saved is bound to a schema then files can only be saved under fields of type "file" defined in the schema. Trying to save a file under a field that is not defined in the schema or under the special field "apsdb_attachments" will result in an error with code "FIELD_FILE_NOT_DEFINED".
To save a file to a document, you need to send it in a multipart request. This can either be done in an HTML form with an input of type “file” representing the field under which the file will be saved (ex: <input type=”file” name=”myFileFieldName”>) or by sending a raw HTTP request as follows:
POST /apsdb/rest/[authenticationkey]/SaveDocument?apsws.time=1245681044 &apsws.authSig=5cf178fed074ec9dfa202627d860123f0e7daf0f HTTP/1.0 Host: sandbox.apstrata.com Content-Type: multipart/form-data; boundary=---------------------e7b6ef072f Content-length: 1516 -----------------------e7b6ef072f Content-Disposition: form-data; name="apsdb.documentKey" documentKey -----------------------e7b6ef072f Content-Disposition: form-data; name="apsdb.store" myStore -----------------------e7b6ef072f Content-Disposition: form-data; name="newNumericField4" 1200 -----------------------e7b6ef072f Content-Disposition: form-data; name="newNumericField4" 1100 -----------------------e7b6ef072f Content-Disposition: form-data; name="newNumericField4.apsdb.fieldType" numeric -----------------------e7b6ef072f Content-Disposition: form-data; name="field1" myField1 -----------------------e7b6ef072f Content-Disposition: form-data; name="field1.apsdb.fieldType" string -----------------------e7b6ef072f Content-Disposition: form-data; name="apsdb_attachments"; filename="myFile.txt" Content-Type: application/octet-stream my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents, my file contents -----------------------e7b6ef072f Content-Disposition: form-data; name="apsdb.ftsFields" newNumericField4,field1,apsdb_attachments -----------------------e7b6ef072f--
Field of type 'geospatial':
Saving a field of type "geospatial" requires providing a pair of longitude and latitude coordinates represented in decimal degrees.
[fieldname]=[LATITUDE],[LONGITUDE]
Where LATITUDE and LONGITUDE should match numbers that have at most 3 digits before a decimal and 4 digits after, and optionally leading + or -.
Example:
EiffelTowerCoordinates = 48.8580,2.2951
StatueOfLibertyCoordinates =40.6892,-74.0447
Creating A Scheduling Document
A scheduling document contains a defined set of reserved fields that control the execution of a script at a specific date and time.
Fields Name | Description |
---|---|
apsST_scriptName | The name of the script to be scheduled |
apsST_execAt | The date and time at which the script will run |
apsST_execAt.apsdb.fieldType | This should be set to "date" to indicate the type of the apsST.execAt field |
apsST_saveResult | A boolean indicating whether to save the result of calling the script in the scheduling document |
Please refer to Scheduled Script for more details about scheduling scripts.
Updating a document
Documents submitted for updating must include the document key of the document apsdb.documentKey=[key]. The update parameter can take the following values: [true|false]. Any different value will not be allowed.
The update parameter apsdb.update and the document key parameter apsdb.documentKey are handled in the following way when sent:
| and update parameter is not sent | and update parameter is set to true | and update parameter is set to false |
---|---|---|---|
In case a document key is not sent… | create a document | not allowed | create a document |
In case a document key is sent empty… | not allowed | not allowed | not allowed |
In case a document key is sent that maps to an existing document… | update the document | update the document | not allowed |
In case a document key is sent that doesn’t map to an existing document… | create a document | not allowed | create a document |
When updating a field in a document, if the user tries to append to an existing value, the value type should match the existing field type or else the request will not be allowed. Note, however, that replacing the existing value with a new value of a different type is allowed. Replacing a value is done by sending a request with the document key and the same fieldName with a different value. Note, however, that updating a multivalue field to a single value will remove the multivalue fields.
If the user wishes to append to an existing field in an existing document, he can send the new values and set the apsdb.multivalueAppend to this fieldname. apsdb.multivalueAppend should contain a comma separated list of fieldNames the user wishes to append.
Appending will only add unique values to one field. If the user tries to create a duplicate field value, the action is ignored. Appending to a single value will make the field multivalued.
Users cannot append more than one file attachment per field, but can attach more than one file per document by creating multiple file attachment fields.
Deleting a field or a field value
Finally, to delete a field (single or multi-valued), the update request should send the field with an empty value. Note that deleting a field will delete all its values by sending fieldName with no value (e.g. apsdb.fieldName=)
Users can also delete fields by sending fieldName.apsdb.delete=valueToBeDeleted.
If the user sends fieldName.apsdb.delete with no value, the field with all its values will be deleted.
ACLs
When saving a document, field ACLs are verified
- Adding fields in the fts fields to be indexed cannot be done on fields the user doesn't have read access on
- Saving fields the user doesn't have write access on is not allowed
- Saving file fields the user doesn't have write access on is not allowed
- If a user has no write access to any field group including the dynamic fields, then he may not create/update a document.
Note: if you try to create/ or update a schema-less document but the configuration of your account does not allow for schema-less documents, you will get a "PERMISSION_DENIED" error when calling "SaveDocument" even if your credentials are valid.
Overriding Schema and Default ACLs
In specific cases, the need to override ACLs set in the schema is required. One example of an application with such a use case is a wiki. First, let’s assume that all wiki pages are documents based on the same schema. Let us also assume that the schema allows only the creator to write to the wiki pages.
However, someone who created a page in a wiki may require adding contributors to help him update the page and write to it. This is the case where the user would need to override the ACLs set in the schema and give write ACLs to contributors. Please find the below example to better explain this concept.
<schema> <aclGroups> <aclGroup name="wikiPage"> <read>all</read> <write>creator</write> <fields> <field>field1</field> <field>picture</field> </fields> </aclGroup> <defaultAcl> <read>all</read> <write>creator</write> </defaultAcl> <schemaAcl> <read>user1;user2;group:group1</read> <write>user1;user2;group:group1</write> <delete>user1;user2;group:group1</delete> </schemaAcl> </aclGroups> <fields> <field name="field1" type="string" /> <field name="picture" type="file" /> </fields> </schema>
The ACLs in the documents will take priority over the ones in the schema. So in order to override the ACLs in the “wikiPage” ACL group, the user can send wikiPage.writeACL parameter in the request. In order to override default ACLs, the user can send default.readACL or default.writeACL parameters in the request. To override the default delete ACL, the user must send document.deleteACL parameter in the request. Moreover, the document.readACL and document.writeACL parameters can be used to grant or deny read and write access to the document as a whole, and they override the field groups and dynamic fields (default) ACLs.
The following are the possible scenarios for overriding schema ACLs:
- A user who is in the "document writeACL" can modify who else is in the "document writeACL" and "document readACL"
- A user who is in the "document deleteACL" can modify who else is in the "document deleteACL"
- A user who is in the "default writeACL" can modify who else is in the "default writeACL" and "default readACL"
- A user who is in a "field group writeACL" can modify who else is in the "field group writeACL" and "field group readACL"
The following are the possible scenarios for overriding "schema-less" ACLs:
- A user who is in the "document writeACL" can modify who else is in the "document writeACL" and "document readACL"
- A user who is in the "document deleteACL" can modify who else is in the "document deleteACL"
Note that for "schema-less" documents, the "default writeACL" and the "default readACL" are meaningless. Instead, the "document writeACL" and the "document writeACL" should be used. In "schema-less" documents, all fields are supposed to be dynamic fields. However, and for technical reasons, the attached files fields cannot be dynamic, and hence needed to be defined in a field group in the default schema. This means that even if you try to override the "default readACL" and "default writeACL", this does not affect attached files fields. As a result, you are supposed to use the "document readACL" and "document writeCL" instead.
Also note that the "document writeACL" restricts EVERYTHING. That is, if a user is NOT in the "document writeACL", then he cannot do anything on the document even though he is a member of other ACLs. By default, the "document readACL" and "document writeACL" imply everyone in case they are not set.
Last but not least, the following rules apply for viewing the ACLs persisted with the document:
- A user who is in the "document readACL" can view the "document writeACL" and "document readACL" and "document deleteACL"
- A user who is in the "default readACL" can view the "default writeACL" and "default readACL"
- A user who is in a "field group readACL" can view the "field group writeACL" and "field group readACL"
Versioning
Versioning is an extra step that may occur when updating a document. It can be used to keep track of all changes to a document or to create new versions of a document at specific milestones.
A document is uniquely identified by its dockey and version number. Multiple versions of the same document have the same dockey, but different version numbers.
A document's schema may specify the type of versioning that the document supports. (Schemaless documents have disabled versioning). API calls to SaveDocument may use the following parameters to take advantage of this feature: apsdb.versioning and apsdb.latestVersion. Note that the first time a document is created, it is by default the first version. The versioning parameters should not be used when creating this first version --- versioning requires the apsdb.update parameter to also be true.
The apsdb.versioning parameter specifies whether this call of SaveDocument should create a new version of the document instead of updating the existing document.
The apsdb.latestVersion parameter specifies what is currently expected to be the latest version of the document. If it does not match what the actual latest version is, then the SaveDocument call will fail with a CANNOT_CREATE_VERSION ServiceException.
If you are not creating a new version of a document or if you are creating a new document, but you also specify the apsdb.latestVersion parameter, then an INVALID_REQUEST ServiceException will be thrown.
Schema Versioning | SaveDocument Result |
---|---|
Disabled | This document (or any other document that follows the schema) cannot be versioned and will be updated instead. |
Forced | A new version of the document will be created. |
Enabled | A new version of the document will be created if the parameter apsdb.versioning is true, else the current document will be updated. |
Anything that causes a contradiction to the above table will lead to an INVALID_PARAMETER_VALUE ServiceException being thrown. For instance, if versioning is disabled, but the apsdb.versioning parameter is sent as true, or if versioning is forced but apsdb.versioning is false, etc.
Note that the latest version of the document will inherit all ACLs from its previous version. If the previous version contains a document.writeACL that differs from the one specified in its schema, then the current values of its document.writeACL field will be saved under a new field called document.writeACL.previous, and its document.writeACL will be set to "nobody". This results in old versions read-only, and a backup of the writeACL that was used. The following example illustrates this behavior:
Before Versioning | Document Version 1 document.writeACL = "user1" | Document Version 2 does not exist yet |
After Versioning | Document Version 1 document.writeACL = "nobody" document.writeACL.previous = "user1" | Document Version 2 document.writeACL = "user1" |
Specific Request Parameters
(Refer to Common Request Parameters)
Name | Description | Required | Default | Possible Values |
---|---|---|---|---|
apsdb.store | The store name in which you are to save your document. | No |
| |
apsdb.update | Specifies whether or not to create a new document if an existing document with the same “apsdb.documentKey” is found | No |
| true |
apsdb.documentKey | The unique document identifier. It is unique in its store | No |
|
|
apsdb.multivalueAppend | Specifies that the given field values will be appended to, instead of replacing, the current field values. Contains a comma separated list of field names to which the values specified will be appended. | No |
|
|
[fieldName] | This parameter name in the request is the name of the field to be stored, and its value is the value of the field to be stored. | No |
|
|
[fieldName].apsdb.fieldType | The type of the data to be stored in the corresponding field. | No | string | string |
apsdb.schema | Represents the name of a previously set schema to apply to the document. | No |
|
|
apsdb.runAs | It allows the owner to run a service as one of his own users. The possible values are any of the usernames. | No |
|
|
[fieldName].apsdb.delete | Parameter used to delete a field or a specific value | No |
|
|
apsdb.ftsFields | Comma separated list of fields to index. This parameter can be used for schema-less document only. To specify that a field is searchable for a document that uses a schema, it should be specified in the schema definition itself. Note that if this parameter is sent with an empty value then nothing will be indexed. | No |
|
|
apsdb.revisionNumber | This parameter can only be sent when updating a document. It indicates the revision number of the document that the user expects to be modifying. If the current document has a different revision number than the one specified, then the update will fail and the user will be informed. | No |
|
|
apsdb.versioning | Specifies whether this call of SaveDocument should create a new version of the document instead of updating the existing document. | No | false | true |
apsdb.latestVersion | This parameter can only be sent when versioning a document. It indicates the latest version of the document that the user expects to be modifying. If the latest document has a different version number than the one specified, then the update will fail and the user will be informed. | No | numeric | |
apsdb.authToken | This parameter is used to sign the request with a token that was initially obtained by calling VerifyCredentails API. For more details on signing requests, please refer to the page entitled Authentication. | No |
Specific Response Elements
(Refer to Common Response Elements)
The following specific "result" element is a child of the common root element "response" and a sibling of the common "metadata" element:
{ "result":{ "document":{ "key":"the key of the document being created; this will be auto-generated if not provided by the user", "versionNumber":"the version number of the document being created" } } }
Specific Logical Errors
(Refer to Common Logical Error Codes)
Error | Message | Status Code |
---|---|---|
INVALID_STORE_NAME | Store name is invalid. Please refer to the online documentation for a detailed list of acceptable values | 400 |
DUPLICATE_STORE_NAME | Store already exists, please choose another name | 400 |
STORE_NOT_FOUND | The store was not found | 404 |
CANNOT_MODIFY_DOCUMENT_SCHEMA | Document schema cannot be updated | 400 |
INVALID_DOCUMENT_KEY |
| 400 |
DOCUMENT_KEY_EMPTY |
| 400 |
DUPLICATE_DOCUMENT_KEY |
| 400 |
DOCUMENT_NOT_FOUND |
| 404 |
INVALID_FIELD_VALUE | Incorrect value for fields: [fieldNames] | 400 |
PARAMETER_REQUIRED |
| 400 |
INVALID_FIELD_NAME |
| 400 |
SCHEMA_NOT_FOUND | The schema [schemaName] cannot be found | 404 |
INVALID_PARAMETER_VALUE | Incorrect value for parameter [parameterName]. | 400 |
INVALID_PARAMETER_VALUE | Cannot create a new version, versioning is disabled. | 400 |
INVALID_PARAMETER_VALUE | Must create a new version, versioning is forced. | 400 |
CANNOT_MODIFY_DOCUMENT | Revision number sent is different from revision number saved in document | 400 |
INVALID_APPEND_NEW_DOCUMENT | Cannot append values to a new document | 400 |
DUPLICATE_FIELD_VALUE | Field [fieldName] has duplicate values | 400 |
DUPLICATE_FIELD_VALUE | Trying to add duplicate values for field [fieldName] | 400 |
MAX_VALUES_PER_FIELD_EXCEEDED | Field [fieldName] cannot have more than [maximumValue] values | 400 |
FIELD_NOT_MULTIVALUED | Trying to add multiple values to a non-multivalued field [fieldName]: [list of values] | 400 |
DATE_REQUIRED |
| 400 |
INCORRECT_DATE_FORMAT |
| 400 |
ACL_GROUP_NOT_FOUND |
| 404 |
INCONSISTENT_FIELD_TYPE | Field [fieldName] must have the type [fileType] instead of the supplied [suppliedType] | 400 |
INVALID_FIELD_TYPE | Incorrect value [fieldType] for parameter [fieldName] | 400 |
RESTRICTED_FIELD_NAME |
| 400 |
FIELD_NOT_FOUND | Field [fieldName] does not exist. | 404 |
VALUE_NOT_FOUND | Value [filedValue] does not exist. | 404 |
INVALID_FILE_FIELD | Can only add files to the field apsdb_attachments in schemaless documents | 400 |
FIELD_NAME_REQUIRED |
| 400 |
MAX_ATTACHMENTS_SIZE_EXCEEDED |
| 400 |
FIELD_FILE_NOT_DEFINED |
| 400 |
FILE_WITH_SAME_NAME_ALREADY_EXIST | 400 | |
FILE_ALREADY_EXISTS | The file [fileName] already exists. | 400 |
MAX_FIELDS_EXCEEDED |
| 400 |
INVALID_REQUEST | Incomplete script trigger parameters | 400 |
INVALID_REQUEST | Invalid versioning parameters. apsdb.latestVersion can only be used when creating a new version | 400 |
PERMISSION_DENIED | Script trigger required fields are not in an ACL group | 403 |
PERMISSION_DENIED | Script trigger required fields are not writeable | 403 |
INVALID_PARAMETER | Script trigger handback must be a string value with length <= 1024 bytes | 400 |
INVALID_PARAMETER | Script trigger cronSpec value is not valid | 400 |
INVALID_PARAMETER | Script trigger execAt must be a valid date value | 400 |
CANNOT_CREATE_VERSION | Latest version number sent [versionNumberSent] is different from latest version number of document [latestVersionDocument] | 400 |
Examples
Sample Request
Request URL: http://sandbox.apstrata.com/apsdb/rest/[authenticationkey]/SaveDocument?apsws.time=[timestamp]&apsws.authSig=[signature]
POST parameters:
apsdb.store apsdb.update=[true|false] apsdb.documentKey apsdb.globalDateFormat [fieldName]=[fieldValue] [fieldName].apsdb.fieldType=[fieldType] [fieldName].apsdb.fieldDateFormat=[dateFormat] apsdb.multivalueAppend=[fieldName1],[fieldName2],...[fieldNameN]
Sample XML Response
Success XML response:
<response xmlns="http://www.apstrata.com/services/schemas/apstrata_database_response.xsd"> <metadata> <requestId>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</requestId> <status>success</status> </metadata> <result> <document key="[document_key]" versionNumber="[document_versionNumber]"/> </result> </response>
Failure XML response:
<response xmlns="http://www.apstrata.com/services/schemas/apstrata_database_response.xsd"> <metadata> <requestId>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</requestId> <status>failure</status> <errorCode>[errorCode]</errorCode> <errorDetail>[failMsg]</errorDetail> </metadata> </response>
Sample JSON Response
{"response": { "metadata": { "requestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "status": "success" }, "result": { "document": { "key": "00A84E82A116C2BE1FD57E5399E8F8D4", "versionNumber": "1.0" } } }}