{"id":18772443,"url":"https://github.com/eftec/documentstoreone","last_synced_at":"2025-06-20T06:36:21.609Z","repository":{"id":56975560,"uuid":"144415697","full_name":"EFTEC/DocumentStoreOne","owner":"EFTEC","description":"A flat document store for PHP that allows multiples concurrencies","archived":false,"fork":false,"pushed_at":"2024-12-31T10:52:05.000Z","size":299,"stargazers_count":12,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-10T11:37:27.788Z","etag":null,"topics":["bigdata","database","mapreduce","php","php-library"],"latest_commit_sha":null,"homepage":"https://www.escuelainformatica.cl","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EFTEC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-11T20:18:31.000Z","updated_at":"2024-12-31T10:51:45.000Z","dependencies_parsed_at":"2024-06-21T03:14:49.717Z","dependency_job_id":"53efb211-228e-4aed-bdfc-c0de4d8ea4fc","html_url":"https://github.com/EFTEC/DocumentStoreOne","commit_stats":{"total_commits":80,"total_committers":3,"mean_commits":"26.666666666666668","dds":0.07499999999999996,"last_synced_commit":"a8435c709d629c4c7ee6a66cce40ab10deb8326a"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2FDocumentStoreOne","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2FDocumentStoreOne/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2FDocumentStoreOne/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2FDocumentStoreOne/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EFTEC","download_url":"https://codeload.github.com/EFTEC/DocumentStoreOne/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248683267,"owners_count":21144877,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bigdata","database","mapreduce","php","php-library"],"created_at":"2024-11-07T19:29:08.871Z","updated_at":"2025-04-13T08:27:15.085Z","avatar_url":"https://github.com/EFTEC.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DocumentStoreOne\r\nA document store for PHP that allows multiples concurrencies. It is a minimalist alternative to MongoDB or CouchDB without the overhead of installing a new service.  \r\n\r\nIt also works as a small footprint database.\r\n\r\n[![Packagist](https://img.shields.io/packagist/v/eftec/documentstoreone.svg)](https://packagist.org/packages/eftec/documentstoreone)\r\n[![Total Downloads](https://poser.pugx.org/eftec/documentstoreone/downloads)](https://packagist.org/packages/eftec/documentstoreone)\r\n[![License](https://img.shields.io/badge/license-LGPLV3-blue.svg)]()\r\n[![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)]()\r\n[![composer](https://img.shields.io/badge/composer-%3E2.0-blue.svg)]()\r\n[![php](https://img.shields.io/badge/php-7.4-green.svg)]()\r\n[![php](https://img.shields.io/badge/php-8.4-green.svg)]()\r\n[![Doc](https://img.shields.io/badge/docs-62%25-green.svg)]()\r\n\r\n\r\n\u003c!-- TOC --\u003e\r\n* [DocumentStoreOne](#documentstoreone)\r\n  * [Key features](#key-features)\r\n  * [Test](#test-)\r\n  * [Concurrency test](#concurrency-test)\r\n  * [Usage](#usage)\r\n  * [Methods](#methods)\r\n    * [Constructor($baseFolder,$collection,$strategy=DocumentStoreOne::DSO_AUTO,$server=\"\",$serializeStrategy = false,$keyEncryption = '')](#constructorbasefoldercollectionstrategydocumentstoreonedso_autoserverserializestrategy--falsekeyencryption--)\r\n    * [isCollection($collection)](#iscollectioncollection)\r\n    * [collection($collection)](#collectioncollection)\r\n    * [autoSerialize($value=true,$strategy='php')](#autoserializevaluetruestrategyphp-)\r\n    * [createCollection($collection)](#createcollectioncollection-)\r\n    * [insertOrUpdate($id,$document,[$tries=-1])](#insertorupdateiddocumenttries-1)\r\n    * [insert($id,$document,[$tries=-1])](#insertiddocumenttries-1)\r\n    * [update($id,$document,[$tries=-1])](#updateiddocumenttries-1)\r\n    * [get($id,[$tries=-1],$default=false)](#getidtries-1defaultfalse)\r\n    * [getFiltered($id,[$tries=-1],$default=false,$condition=[],$reindex=true)](#getfilteredidtries-1defaultfalseconditionreindextrue)\r\n    * [public function appendValue($name,$addValue,$tries=-1)](#public-function-appendvaluenameaddvaluetries-1)\r\n    * [getNextSequence($name=\"seq\",$tries=-1,$init=1,$interval=1,$reserveAdditional=0)](#getnextsequencenameseqtries-1init1interval1reserveadditional0)\r\n    * [getSequencePHP()](#getsequencephp)\r\n    * [ifExist($id,[$tries=-1])](#ifexistidtries-1)\r\n    * [delete($id,[$tries=-1])](#deleteidtries-1)\r\n    * [select($mask=\"*\")](#selectmask)\r\n    * [copy($idorigin,$iddestination,[$tries=-1])](#copyidoriginiddestinationtries-1)\r\n    * [rename($idorigin,$iddestination,[$tries=-1])](#renameidoriginiddestinationtries-1)\r\n    * [fixCast (util class)](#fixcast-util-class)\r\n  * [DocumentStoreOne Fields](#documentstoreone-fields)\r\n  * [MapReduce](#mapreduce)\r\n  * [Limits](#limits)\r\n* [Strategy of Serialization](#strategy-of-serialization)\r\n  * [NONE](#none)\r\n  * [PHP](#php)\r\n  * [PHP_ARRAY](#php_array)\r\n  * [JSON_ARRAY and JSON_OBJECT](#json_array-and-json_object)\r\n* [Control of Error](#control-of-error)\r\n* [Working with CSV](#working-with-csv)\r\n* [Version list](#version-list)\r\n  * [Pending](#pending)\r\n\u003c!-- TOC --\u003e\r\n\r\n## Key features\r\n- Single key based.\r\n- Fast. However, it's not an alternative to a relational database. It's optimized to store a moderated number documents instead of millions of rows.\r\n- **Allows multiple concurrences by locking and unlocking a document**. If the document is locked then, it retries until the document is unlocked or fails after a number of retries.\r\n- One single class with no dependencies.\r\n- Automatic unlock document locked (by default, every 2 minutes if the file was left locked).\r\n- It could use **MapReduce** See [example](https://github.com/EFTEC/DocumentStoreOne/blob/master/examples/4_example_read_mapreduce.php)\r\n\r\n## Test \r\n\r\nIn average, an SMB generates 100 invoices per month. So, let's say that an SMB generates 12000 invoices per decade.  \r\n\r\nTesting generating 12000 invoices with customer, details (around 1-5 lines per detail) and date on an i7/ssd/16gb/windows 64bits.\r\n\r\n* Store 12000 invoices 45.303 seconds (reserving a sequence range)  \r\n* Store 12000 invoices  73.203 seconds (reading a sequence for every new invoice)\r\n* Store 12000 invoices 49.0286 seconds (reserving a sequence range and using igbinary)   \r\n* Reading all invoices 60.2332 seconds. (only reading) \r\n* MapReduce all invoices per customers 64.0569 seconds.  \r\n* MapReduce all invoices per customers 32.9869 seconds (igbinary)\r\n* Reading all invoices from a customer **0.3 seconds.** (including render the result, see image)\r\n* Adding a new invoice without recalculating all the MapReduce 0.011 seconds.\r\n\r\n![mapreduce example](https://github.com/EFTEC/DocumentStoreOne/blob/master/doc/mapreduce.jpg \"mapreduce on php\")\r\n\r\n## Concurrency test\r\n\r\nA test with 100 concurrent test (write and read), 10 times.\r\n\r\n| N°  | Reads | (ms) | Reads | Error |\r\n|-----|-------|------|-------|-------|\r\n| 1   | 100   | 7471 | 100   | 0     |\r\n| 2   | 100   | 7751 | 100   | 0     |\r\n| 3   | 100   | 7490 | 100   | 0     |\r\n| 4   | 100   | 7480 | 100   | 0     |\r\n| 5   | 100   | 8199 | 100   | 0     |\r\n| 6   | 100   | 7451 | 100   | 0     |\r\n| 7   | 100   | 7476 | 100   | 0     |\r\n| 8   | 100   | 7244 | 100   | 0     |\r\n| 9   | 100   | 7573 | 100   | 0     |\r\n| 10  | 100   | 7818 | 100   | 0     |\r\n\r\n## Usage\r\n\r\n```php\r\ninclude \"lib/DocumentStoreOne.php\";\r\nuse eftec\\DocumentStoreOne\\DocumentStoreOne;\r\ntry {\r\n    $flatcon = new DocumentStoreOne(\"base\", 'tmp');    \r\n    // or you could use:\r\n    // $flatcon = new DocumentStoreOne(__DIR__ . \"/base\", 'tmp');  \r\n} catch (Exception $e) {\r\n    die(\"Unable to create document store. Please, check the folder\");\r\n}\r\n$flatcon-\u003einsertOrUpdate(\"somekey1\",json_encode(array(\"a1\"=\u003e'hello',\"a2\"=\u003e'world'))); // or you could use serialize/igbinary_serialize\r\n$doc=$flatcon-\u003eget(\"somekey1\");\r\n$listKeys=$flatcon-\u003eselect();\r\n$flatcon-\u003edelete(\"somekey1\");\r\n```\r\n\r\n```php\r\ninclude \"lib/DocumentStoreOne.php\";\r\nuse eftec\\DocumentStoreOne\\DocumentStoreOne;\r\n$doc=new DocumentStoreOne(\"base\",\"task\",'folder');\r\n//also: $doc=new DocumentStoreOne(__DIR__.\"/base\",\"task\",'folder');\r\n$doc-\u003eserializeStrategy='php'; // it sets the strategy of serialization to php\r\n$doc-\u003eautoSerialize(true); // autoserialize\r\n\r\n$flatcon-\u003einsertOrUpdate(\"somekey1\",array(\"a1\"=\u003e'hello',\"a2\"=\u003e'world')); \r\n```\r\n\r\n## Methods\r\n\r\n### Constructor($baseFolder,$collection,$strategy=DocumentStoreOne::DSO_AUTO,$server=\"\",$serializeStrategy = false,$keyEncryption = '')\r\n\r\nIt creates the DocumentStoreOne instance. \r\n\r\n* **$baseFolder**: should be a folder\r\n* **$collection**: (a subfolder) is optional.\r\n* **$strategy**: It is the strategy used to determine if the file is in use or not.\r\n\r\n| strategy   | type                                                                                                     | server         | benchmark      |\r\n|------------|----------------------------------------------------------------------------------------------------------|----------------|----------------|\r\n| DSO_AUTO   | It sets the best available strategy (default)                                                            | depends        | -              |\r\n| DSO_FOLDER | It uses a folder for lock/unlock a document                                                              | -              | 0.3247         |\r\n| DSO_APCU   | It uses APCU for lock/unlock a document                                                                  | -              | 0.1480         |\r\n| DSO_REDIS  | It uses REDIS for lock/unlock a document                                                                 | localhost:6379 | 2.5403 (worst) |\r\n| DSO_NONE   | It uses nothing to lock/unlock a document. It is the fastest method but it is unsafe for multiples users |                | 0              |\r\n\r\n* **$server**: It is used by REDIS. You can set the server used by the strategy.\r\n* **$serializeStrategy**: If false then it does not serialize the information. \r\n\r\n| strategy                 | type                                                                                                                             |\r\n|--------------------------|----------------------------------------------------------------------------------------------------------------------------------|\r\n| php                      | it serializes using serialize() function                                                                                         |\r\n| php_array                | it serializes using include()/var_export()function. The result could be cached on OpCache because the result is a PHP code file. |\r\n| json_object              | it is serialized using json (as object)                                                                                          |\r\n| json_array               | it is serialized using json (as array)                                                                                           |\r\n| csv                      | it serializes using a csv file.                                                                                                  |\r\n| igbinary                 | it serializes using a igbinary file.                                                                                             |\r\n| **none** (default value) | it is not serialized. Information must be serialized/de-serialized manually                                                      |\r\n\r\nExamples:\r\n\r\n```php\r\n$flatcon = new DocumentStoreOne(__DIR__ . \"/base\"); // new instance, using the folder /base, without serialization and with the default data\r\n\r\n$flatcon = new DocumentStoreOne(__DIR__ . \"/base\", '','auto','','php_array'); // new instance and serializing using php_array\r\n```\r\n\r\nBenchmark how much time (in seconds) it takes to add 100 inserts.   \r\n\r\n```php\r\nuse eftec\\DocumentStoreOne\\DocumentStoreOne;\r\ninclude \"lib/DocumentStoreOne.php\";\r\ntry {\r\n    $flatcon = new DocumentStoreOne(__DIR__ . \"/base\", 'tmp');\r\n} catch (Exception $e) {\r\n    die(\"Unable to create document store.\".$e-\u003egetMessage());\r\n}\r\n```\r\n\r\n```php\r\nuse eftec\\DocumentStoreOne\\DocumentStoreOne;\r\ninclude \"lib/DocumentStoreOne.php\";\r\ntry {\r\n    $flatcon = new DocumentStoreOne(\"/base\", 'tmp',DocumentStoreOne::DSO_APCU);\r\n} catch (Exception $e) {\r\n    die(\"Unable to create document store.\".$e-\u003egetMessage());\r\n}\r\n```\r\n\r\n### isCollection($collection)\r\n\r\nReturns true if collection is valid (a sub-folder).\r\n```php\r\n$ok=$flatcon-\u003eisCollection('tmp');\r\n```\r\n### collection($collection)\r\n\r\nIt sets the current collection\r\n```php\r\n$flatcon-\u003ecollection('newcollection'); // it sets a collection.\r\n```\r\nThis command could be nested.  \r\n\r\n```php\r\n$flatcon-\u003ecollection('newcollection')-\u003eselect(); // it sets and return a query\r\n```\r\n\r\n\u003e Note, it doesn't validate if the collection is correct or exists.  You must use **isCollection()** to verify if it's right.\r\n\r\n### autoSerialize($value=true,$strategy='php') \r\nIt sets if we want to auto serialize the information, and we set how it is serialized. You can also set using the constructor.\r\n\r\n| strategy                   | type                                                                                                                       |\r\n|----------------------------|----------------------------------------------------------------------------------------------------------------------------|\r\n| php                        | it serializes using serialize() function.                                                                                  |\r\n| php_array                  | it serializes using include()/var_export()function. The result could be cached on OpCache because the result is a php file |\r\n| json_object                | it is serialized using json (as object)                                                                                    |\r\n| json_array                 | it is serialized using json (as array)                                                                                     |\r\n| csv                        | it serializes using a csv file.                                                                                            |\r\n| igbinary                   | it serializes using a igbinary file.                                                                                       |\r\n| **none** (default value)   | it is not serialized. Information must be serialized/de-serialized manually                                                |\r\n\r\n\r\n\r\n### createCollection($collection) \r\n\r\nIt creates a collection (a new folder inside the base folder). It returns false if the operation fails; otherwise it returns true\r\n\r\n```php\r\n$flatcon-\u003ecreateCollection('newcollection'); \r\n$flatcon-\u003ecreateCollection('/folder1/folder2'); \r\n```\r\n\r\n### insertOrUpdate($id,$document,[$tries=-1])\r\n\r\ninserts a new document (string) in the **$id** indicated. If the document exists, then it's updated.  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of attempts).  \r\n\r\n```php\r\n// if we are not using auto serialization\r\n$doc=json_encode([\"a1\"=\u003e'hello',\"a2\"=\u003e'world']);\r\n$flatcon-\u003einsertOrUpdate(\"1\",$doc); // it will create a document called 1.dson in the base folder.\r\n\r\n// if we are using auto serialization\r\n$flatcon-\u003einsertOrUpdate(\"1\",[\"a1\"=\u003e'hello',\"a2\"=\u003e'world']);\r\n```\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivalent to 10 seconds)\r\n\r\n\u003e It's faster than insert or update.\r\n\r\n### insert($id,$document,[$tries=-1])\r\n\r\nInserts a new document (string) in the **$id** indicated. If the document exists, then it returns false.  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of attempts).  \r\n\r\n```php\r\n// if we are not using auto serialization\r\n$doc=json_encode(array(\"a1\"=\u003e'hello',\"a2\"=\u003e'world'));\r\n$flatcon-\u003einsert(\"1\",$doc);\r\n\r\n// if we are using auto serialization\r\n$flatcon-\u003einsert(\"1\",[\"a1\"=\u003e'hello',\"a2\"=\u003e'world']);\r\n```\r\n\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivalent to 10 seconds)\r\n\r\n### update($id,$document,[$tries=-1])\r\n\r\nUpdate a document (string) in the **$id** indicated. If the document doesn't exist, then it returns false  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of attempts).  \r\n\r\n```php\r\n// if we are not using auto serialization\r\n$doc=json_encode([\"a1\"=\u003e'hello',\"a2\"=\u003e'world']);\r\n$flatcon-\u003eupdate(\"1\",$doc);\r\n// if we are using auto serialization\r\n$flatcon-\u003eupdate(\"1\",[\"a1\"=\u003e'hello',\"a2\"=\u003e'world']);\r\n```\r\n\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivales to 10 seconds)\r\n\r\n\r\n### get($id,[$tries=-1],$default=false)\r\n\r\nIt reads the document **$id**.  If the document doesn't exist, or it's unable to read it, then it returns false.  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of attempts).  \r\n\r\n```php\r\n$doc=$flatcon-\u003eget(\"1\"); // the default value is false\r\n\r\n$doc=$flatcon-\u003eget(\"1\",-1,'empty');\r\n```\r\n\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivalent to 10 seconds)\r\n\r\n\r\n### getFiltered($id,[$tries=-1],$default=false,$condition=[],$reindex=true)\r\n\r\nIt reads the document **$id** filtered.  If the document doesn't exist, or it's unable to read it, then it returns false.  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of attempts).  \r\n\r\n```php\r\n// data in rows [['id'=\u003e1,'cat'=\u003e'vip'],['id'=\u003e2,'cat'=\u003e'vip'],['id'=\u003e3,'cat'=\u003e'normal']];\r\n$data=$this-\u003egetFiltered('rows',-1,false,['cat'=\u003e'normal']); // [['id'=\u003e3,'cat'=\u003e'normal']]\r\n$data=$this-\u003egetFiltered('rows',-1,false,['type'=\u003e'busy'],false); // [2=\u003e['id'=\u003e3,'cat'=\u003e'normal']]\r\n```\r\n\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivalent to 10 seconds)\r\n\r\n### public function appendValue($name,$addValue,$tries=-1)\r\n\r\nIt adds a value to a document with name **$name**. The new value is added, so it avoids to create the whole document. It is useful, for example, for a log file.\r\n\r\na) If the value doesn't exist, then it's created with $addValue. Otherwise, it will return true  \r\nb) If the value exists, then **$addValue** is added, and it'll return true  \r\nc) Otherwise, it will return false  \r\n\r\n```php\r\n$seq=$flatcon-\u003eappendValue(\"log\",date('c').\" new log\");\r\n```\r\n\r\n### getNextSequence($name=\"seq\",$tries=-1,$init=1,$interval=1,$reserveAdditional=0)\r\n\r\nIt reads or generates a new sequence.\r\n\r\na) If the sequence exists, then it's incremented by **$interval** and this value is returned.  \r\nb) If the sequence doesn't exist, then it's created with **$init**, and this value is returned.\r\nc) If the library is unable to create a sequence, unable to lock or the sequence exists but, it's unable to read, then it returns false\r\n\r\n```php\r\n$seq=$flatcon-\u003egetNextSequence();\r\n```\r\n\r\n\u003e You could peek a sequence with $id=get('genseq_\u003cname\u003e') however it's not recommended.\r\n\r\n\u003e If the sequence is corrupt then it's reset to $init\r\n\r\n\u003e If you need to reserve a list of sequences, you could use **$reserveAdditional**\r\n\r\n```php\r\n$seq=$flatcon-\u003egetNextSequence(\"seq\",-1,1,1,100); // if $seq=1, then it's reserved up to the 101. The next value will be 102.\r\n```\r\n###  getSequencePHP()\r\n\r\nIt returns a unique sequence (64bit integer) based on time, a random value and a serverId.\r\n\r\n\u003e The chances of collision (a generation of the same value) is 1/4095 (per two operations executed every 0.0001 second).\r\n\r\n```php\r\n$this-\u003enodeId=1; // if it is not set then it uses a random value each time.\r\n$unique=$flatcon-\u003egetSequencePHP(); \r\n```\r\n\r\n\r\n### ifExist($id,[$tries=-1])\r\n\r\nIt checks if the document **$id** exists.  It returns true if the document exists. Otherwise, it returns false.  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of tries).  \r\n\u003eThe validation only happens if the document is fully unlocked.  \r\n\r\n```php\r\n$found=$flatcon-\u003eifExist(\"1\");\r\n```\r\n\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivales to 10 seconds)\r\n\r\n### delete($id,[$tries=-1])\r\n\r\nIt deletes the document **$id**.  If the document doesn't exist, or it's unable to delete, then it returns false.  \r\n**$tries** indicates the number of tries. The default value is -1 (default number of tries).  \r\n\r\n```php\r\n$doc=$flatcon-\u003edelete(\"1\");\r\n```\r\n\u003e If the document is locked then it retries until it is available or after a \"nth\" number of tries (by default it's 100 tries that equivales to 10 seconds)\r\n\r\n### select($mask=\"*\")\r\n\r\nIt returns all the IDs stored on a collection.  \r\n\r\n```php\r\n$listKeys=$flatcon-\u003eselect();\r\n$listKeys=$flatcon-\u003eselect(\"invoice_*\");\r\n```\r\n\u003e It includes locked documents.\r\n\r\n### copy($idorigin,$iddestination,[$tries=-1])\r\n\r\nCopy the document **$idorigin** in **$iddestination** \r\n\r\n```php\r\n$bool=$flatcon-\u003ecopy(20,30);\r\n```\r\n\r\n\u003e If the document destination exists then its replaced\r\n\r\n### rename($idorigin,$iddestination,[$tries=-1])\r\n\r\nRename the document **$idorigin** as **$iddestination** \r\n\r\n```php\r\n$bool=$flatcon-\u003erename(20,30);\r\n```\r\n\r\n\r\n\u003e If the document destination exists then the operation fails.\r\n\r\n### fixCast (util class)\r\n\r\nIt converts a stdclass to a specific class. \r\n\r\n```php\r\n$inv=new Invoice();\r\n$invTmp=$doc-\u003eget('someid'); //$invTmp is a stdClass();\r\nDocumentStoreOne::fixCast($inv,$invTmp); \r\n```\r\n\r\n\u003e It doesn't work with members that are array of objects.  The array is kept as stdclass.\r\n\r\n## DocumentStoreOne Fields\r\n\r\nThe next fields are public, and they could be changed during runtime\r\n\r\n| field                        | Type                                                                                                                                           |\r\n|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|\r\n| $database                    | string root folder of the database                                                                                                             |\r\n| $collection                  | string Current collection (subfolder) of the database                                                                                          |\r\n| $maxLockTime=120             | int Maximium duration of the lock (in seconds). By default it's 2 minutes                                                                      |\r\n| $defaultNumRetry=100         | int Default number of retries. By default it tries 100x0.1sec=10 seconds                                                                       |\r\n| $intervalBetweenRetry=100000 | int Interval (in microseconds) between retries. 100000 means 0.1 seconds                                                                       |\r\n| $docExt=\".dson\"              | string Default extension (with dot) of the document                                                                                            |\r\n| $keyEncryption=\"\"            | string Indicates if the key is encrypted or not when it's stored (the file name). Empty means, no encryption. You could use md5,sha1,sha256,.. |\r\n\r\nExample: \r\n```php\r\n$ds=new DocumentStoreOne();\r\n$ds-\u003emaxLockTime=300;\r\n```\r\n\r\n```php\r\n$ds=new DocumentStoreOne();\r\n$ds-\u003einsert('1','hello'); // it stores the document 1.dson\r\n$ds-\u003ekeyEncryption='SHA256';\r\n$ds-\u003einsert('1','hello'); // it stores the document 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b.dson\r\n```\r\n\r\n\r\n## MapReduce\r\n\r\nIt could be done manually. The system allows to store a pre-calculated value that could be easily accesses (instead of read all values).\r\n\r\nLet's say the next exercise, we have a list of purchases\r\n\r\n| id  | customer | age | sex | productpurchase | amount |\r\n|-----|----------|-----|-----|-----------------|--------|\r\n| 14  | john     | 33  | m   | 33              | 3      |\r\n| 25  | anna     | 22  | f   | 32              | 1      |\r\n\r\n| productcode | unitprice |\r\n|-------------|-----------|\r\n| 32          | 23.3      |\r\n| 33          | 30        |\r\n\r\n\r\nJohn purchased 3 products with the code 33.  The products 33 costs $23.3 per unit.\r\n\r\nQuestion, how much every customer paid?.\r\n\r\n\u003e It's a simple exercise, it's more suitable for a relational database (select * from purchases inner join products).\r\n\u003e However, if the document is long or complex to store in the database then it's here where a document store shines.\r\n\r\n```php\r\n// 1) open the store\r\n$ds=new DocumentStoreOne('base','purchases'); // we open the document store and selected the collection purchase.\r\n$ds-\u003eautoSerialize(true,'auto');\r\n// 2) reading all products\r\n// if the list of products holds in memory then, we could store the whole list in a single document (listproducts key)\r\n$products=$ds-\u003ecollection('products')-\u003eget('listproducts');\r\n// 3) we read the keys of every purchases. It could be slow and it should be a limited set (\u003c100k rows)    \r\n$purchases=$ds-\u003ecollection('purchases')-\u003eselect(); // they are keys such as 14,15...\r\n\r\n$customerXPurchase=[];\r\n// 4) We read every purchase. It is also slow.  Then we merge the result and obtained the final result\r\nforeach($purchases as $k) {\r\n    $purchase=$ds-\u003eget($k);\r\n    @$customerXPurchase[$purchase-\u003ecustomer]+=($purchase-\u003eamount * @$products[$purchase-\u003eproductpurchase]); // we add the amount\r\n}\r\n// 5) Finally, we store the result.\r\n$ds-\u003ecollection('total')-\u003einsertOrUpdate($customerXPurchase,'customerXPurchase'); // we store the result.\r\n```\r\n\r\n| customer | value |\r\n|----------|-------|\r\n| john     | 69.9  |\r\n| anna     | 30    |\r\n\r\nSince it's done on code then it's possible to create a hybrid system (relational database+store+memory cache)\r\n\r\n## Limits\r\n- Keys should be of the type A-a,0-9. In windows, keys are not case-sensitive. \r\n- The limit of documents that a collection could hold is based on the document system used. NTFS allows 2 million\r\n of documents per collection.  \r\n\r\n\r\n\r\n# Strategy of Serialization\r\n\r\nLet's say we want to serialize the next information:\r\n\r\n```php\r\n$input=[['a1'=\u003e1,'a2'=\u003e'a'],['a1'=\u003e2,'a2'=\u003e'b']];\r\n```\r\n\r\n## NONE\r\n\r\nThe values are not serialized, so it is not possible to serialize an object, array or other structure. It only works with strings.\r\n\r\nHow values are stored\r\n\r\n```\r\nhelloworld\r\n```\r\n\r\nHow values are returned\r\n\r\n```php\r\n\"helloworld\"\r\n```\r\n\r\n\r\n\r\n## PHP\r\n\r\nThe serialization of PHP is one of the faster way to serialize and de-serialize, and it always returns the same value with the same structure (classes, array, fields)\r\n\r\nHowever, the value stored could be long.\r\n\r\nHow the values are stored:\r\n\r\n```\r\na:2:{i:0;a:2:{s:2:\"a1\";i:1;s:2:\"a2\";s:1:\"a\";}i:1;a:2:{s:2:\"a1\";i:2;s:2:\"a2\";s:1:\"b\";}}\r\n```\r\n\r\nHow the values are returned:\r\n\r\n```\r\n[['a1'=\u003e1,'a2'=\u003e'a'],['a1'=\u003e2,'a2'=\u003e'b']]\r\n```\r\n\r\n## PHP_ARRAY\r\n\r\nThis serialization generates a PHP code. This code is verbose however, it has some nice features:\r\n\r\n* It could be cached by PHP's OPcache.\r\n* It's fast to load.\r\n\r\nHow the values are stored:\r\n\r\n```php\r\n\u003c?php /** @generated */\r\nreturn array (\r\n  0 =\u003e \r\n  array (\r\n    'a1' =\u003e 1,\r\n    'a2' =\u003e 'a',\r\n  ),\r\n  1 =\u003e \r\n  array (\r\n    'a1' =\u003e 2,\r\n    'a2' =\u003e 'b',\r\n  ),\r\n);\r\n```\r\n\r\nHow the values are returned:\r\n\r\n```\r\n[['a1'=\u003e1,'a2'=\u003e'a'],['a1'=\u003e2,'a2'=\u003e'b']]\r\n```\r\n\r\n## JSON_ARRAY and JSON_OBJECT\r\n\r\nBoth methods work with JSON for the serialization and de-serialization but the first on returns always an associative\r\narray while the other could return an object (stdClass)\r\n\r\nPro: \r\n\r\n* JSON is fast (but not as fast a PHP's serialization)\r\n* JSON is compatible across different platforms.\r\n* JSON uses fewer space than PHP?s serialization.\r\n\r\nCons:\r\n\r\n* It is a big slower than PHP's serialization\r\n* The result could vary, and it could return a different structure (objects are always returned as stdClass)\r\n\r\nHow the values are stored:\r\n\r\n```\r\n[{\"a1\":1,\"a2\":\"a\"},{\"a1\":2,\"a2\":\"b\"}]\r\n```\r\n\r\nHow the values are returned:\r\n\r\n```php\r\n[['a1'=\u003e1,'a2'=\u003e'a'],['a1'=\u003e2,'a2'=\u003e'b']] // array\r\n[stdClass{'a1'=\u003e1,'a2'=\u003e'a'},stdClass{'a1'=\u003e2,'a2'=\u003e'b'}] // object\r\n```\r\n\r\n\r\n\r\n# Control of Error\r\n\r\nBy default, this library throws errors when an error or exception happens. Some methods allow to avoid throwing errors but most of them could throw an error.\r\n\r\nThe errors are try/catch catch-ables.\r\n\r\n```php\r\n// throw an error:\r\n$this-\u003ethrowable=true; // (default value is true) If false, then the errors are stored in $this-\u003elatestError\r\ntry {\r\n    $this-\u003einsert('id1','values'); \r\n} catch($ex) {\r\n    var_dump($ex);\r\n}\r\n```\r\n\r\n```php\r\n// not throw an error:\r\n$this-\u003ethrowable=false; \r\n$this-\u003einsert('id1','values'); \r\nif($this-\u003elatestError) {\r\n\tvar_dump($this-\u003elatestError);\r\n}\r\n$this-\u003eresetError();\r\n\r\n```\r\n\r\nOr you could also use to avoid throwing an exception:\r\n\r\n```php\r\n// not throw an error:\r\n$this-\u003e-\u003enoThrowOnError()-\u003einsert('id1','values'); \r\n// but you can still see the latest error:\r\nif($this-\u003elatestError) {\r\n\tvar_dump($this-\u003elatestError);\r\n}\r\n```\r\n\r\n\r\n\r\n# Working with CSV\r\n\r\nYou can work with CSV as follows:\r\n\r\n```php\r\n$doc=new DocumentStoreOne(__DIR__ . \"/base\",'','none','','csv'); // set your strategy to csv.\r\n$doc-\u003edocExt='.csv'; // (optional), you can set the extension of the document\r\n$doc-\u003ecsvPrefixColumn='col_'; // (optional), you can set the name of the columns (if the csv doesn't have columns)\r\n$doc-\u003ecsvStyle(); // (optional) not needing, but you can use to set your own specifications of csv, for example tab-separated, etc.\r\n$doc-\u003eregionalStyle(); // (optional) not needing, but you can use to set your own regional settings.\r\n$values=[\r\n    ['name'=\u003e'john1','age'=\u003e22],\r\n    ['name'=\u003e'john2','age'=\u003e22],\r\n    ['name'=\u003e'john3','age'=\u003e22],\r\n    ];\r\n$doc-\u003edelete('csv1');\r\n$doc-\u003einsert('csv1',$values);\r\n```\r\n\r\n\r\n\r\n# Version list\r\n* 1.28 2024-12-31\r\n  * Updated to PHP 8.4 \r\n* 1.27 2024-07-19\r\n  * Fixed a problem with unlock (folder strategy), when the folder does not exist anymore. \r\n* 1.26 2024-02-13\r\n  * composer.json ig-binary is suggested, not required \r\n* 1.25.1 2023-06-04\r\n  * fixed a bug in the constructor. Now it generates the folders if they don't exist. \r\n* 1.25 2023-06-04\r\n  * added DocumentStoreOne::isRelativePath()\r\n  * now you can specify a relative path in the constructor\r\n* 1.24 2022-06-29\r\n  * deleteCollection() deletes the collection including it's content. \r\n* 1.23 2022-03-20\r\n  * **[new]** It allows to obtain an instance (if any) of DocumentStoreOne using the static method DocumentStoreOne::instance()\r\n* 1.22.1 2022-03-12\r\n  * getTimeStamp() Fixed: returns a warning if the file does not exist.\r\n* 1.22 2022-03-12\r\n  * added setTimeStamp() \r\n* 1.21 2022-02-07\r\n  * compatibility with PHP 7.2 and higher. This library is not compatible with PHP 5.6 anymore, but you can use an old version of the library.\r\n  * Tested the compatibility with PHP 8.1\r\n  * **[added]** method noThrowOnError() \r\n- 1.20 2021-12-11\r\n  - add igbinary\r\n- 1.19 2021-12-08\r\n  * **[added]** more controls over the errors.\r\n- 1.18 2021-12-08\r\n  * **[added]** csv as serialization strategy\r\n  * Some optimizations\r\n  * Memcache is removed.\r\n- 1.16.2 2020-09-20\r\n    * getTimeStamp() throws an exception when the file doesn't exist. Now it returns false.   \r\n- 1.16 2020-09-20\r\n    * new method getTimeStamp()   \r\n- 1.15 2020-09-13\r\n    * method get() now unlocks a document correctly (using method php_array)     \r\n    * method appendValue() is more efficient with json_object,json_array, and it works with php_array.   \r\n    * method appendValue() now generates an array of values.\r\n- 1.14 2020-09-13\r\n    * Fixed composer.json. However, the previous composer.json poisoned installations, so it removed all the previous\r\n     version from packagist. \r\n    * Maybe you should delete \"composer.lock\" and the folder vendor\\efted\\documentstoreone and runs composer update.     \r\n\u003e   [RuntimeException]\r\n\u003e   Could not load package eftec/documentstoreone in repo.packagist.org: [UnexpectedValueException] Could not parse version constraint ^5.6.*: Invalid version string \"^5.6.*\"          \r\n\r\n\r\n- 1.13 2020-07-12   \r\n    * method appendValue() now serializes information and works with most method but php_array.    \r\n- 1.12 2020-04-18\r\n    * method get() has a default value\r\n    * method unlock() removed the argument $forced\r\n    * new method getFiltered()\r\n- 1.11 2019-10-23\r\n    * new method setObjectIndex() It sets the default index field for insertObject() and insertOrUpdateObject()\r\n    * new method insertObject() \r\n    * new method insertOrUpdateObject()\r\n    * method select() now could return a list of indexes of a list of documents\r\n- 1.10 2019-08-30 Some cleaning. Added getSequencePHP() and field nodeId\r\n- 1.9 2019-02-10 Unlock now tries to unlock. Manuallock field is not used anymore.\r\n- 1.8 2018-02-03 field neverLock (for fast access a read only database) also phpunit\r\n- 1.7.3 2018-02-03 Updated composer.json \r\n- 1.7.1 2018-10-20 Removed an incorrect echo on lock()\r\n- 1.7 2018-10-20 Added key encryption (optional)\r\n- 1.6 2018-10-19 \r\n- - Reduced the default time from 30 seconds to 10 seconds because usually PHP is configured to a timeout of 30 seconds.\r\n- - Method ifExist locks the resource and never releases. Now it releases as expected.\r\n- 1.5 2018-10-13 Maintenance update. Fixed the automatic strategy\r\n- 1.4 2018-08-26 function rename\r\n- 1.3 2018-08-15 Added strategy of lock.\r\n- 1.2 2018-08-12 Small fixes.\r\n- 1.1 2018-08-12 Changed schema with collection.\r\n- 1.0 2018-08-11 first version\r\n\r\n## Pending\r\n\r\n- Transactional (allows to commit or rollback a multiple step transaction). It's in evaluation.\r\n- ~~Different strategy of lock (folder,redis and apcu)~~\r\n- Msgpack and ~~igbinary~~\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feftec%2Fdocumentstoreone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feftec%2Fdocumentstoreone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feftec%2Fdocumentstoreone/lists"}