diff --git a/LICENSE b/LICENSE index 36e2b133..af62900b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,27 @@ -MIT License +Copyright (c) 2019, Tolu Aina +All rights reserved. -Copyright (c) 2019 Tolu Aina +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +* Neither the name of PGSync nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index f0d7f9a0..f7595438 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ It allows you to take advantage of the expressive power and scalability of You don't have to write complex queries and transformation pipelines. PGSync is lightweight, fast and flexible. -When we denormalize from relational to document, we loose meaning required to reconstruct any changes. +When we denormalize from relational to document, we lose meaning required to reconstruct any changes. Moreover, you shouldn't store your primary data in [Elasticsearch](https://www.elastic.co/products/elastic-stack). So how do you then get your data into [Elasticsearch](https://www.elastic.co/products/elastic-stack) in the first place? -Tools like Logstash and Kafka can aid this task but they still require a bit +Tools like [Logstash](https://www.elastic.co/products/logstash) and [Kafka](https://kafka.apache.org) can aid this task but they still require a bit of engineering and development. [ETL](https://en.wikipedia.org/wiki/Extract,_transform,_load) and [CDC](https://en.wikipedia.org/wiki/Change_data_capture) tools can be complex and expensive. @@ -34,17 +34,17 @@ Other benefits of PGSync include: - Reliable primary datastore/source of truth - Scale on-demand -SCHEMA DIAGRAM -Inline-style: -![alt text](architecture.jpeg "PGSync architecture") -- [TODO: add showterm.io demo here] +**PGSync Architecture**: + +![alt text](docs/architecture.jpeg "PGSync architecture") +![alt text](docs/demo.svg "PGSync demo") #### Why? At a high level, you have data in a Postgres database and you want to mirror it in Elasticsearch. -This means every change to your data (*Insert*, *Update*, *Delete* and *Truncate* statements) needs to be replicated to Elasticsearch. -At first, this seems easy and then it's not. Simply add some code to copy the data to Elasticsearch after updating the database (or dual writes). +This means every change to your data (***Insert***, ***Update***, ***Delete*** and ***Truncate*** statements) needs to be replicated to Elasticsearch. +At first, this seems easy and then it's not. Simply add some code to copy the data to Elasticsearch after updating the database (or so called dual writes). Writing SQL queries spanning multiple tables and involving multiple relationships are hard to write. Detecting changes within a nested document can also be quite hard. Of course, if your data never changed, then you could just take a snapshot in time and load it into Elasticsearch as a one-off operation. @@ -74,12 +74,11 @@ You can select any pivot table to be the root of your document. PGSync's query builder builds advanced queries dynamically against your schema. -PGSync operates both pull and event-driven model. -It creates a trigger for tables in your database to handle events. +PGSync operates in an event-driven model by creating triggers for tables in your database to handle notification events. *This is the only time PGSync will ever make any changes to your database.* -NOTE: **if you change your database schema, or PGSync's schema config, you would need to drop and rebuild your indexes.** +**NOTE**: **if you change your database schema, or PGSync's schema config, you would need to drop and rebuild your indexes.** There are plans to support zero-downtime migrations to streamline this process. @@ -97,7 +96,7 @@ Run: docker-compose up ``` -In another shell run +In another shell, run ``` docker-compose up exec -it pgsync ``` @@ -114,12 +113,12 @@ psql -f samples/data.sql Run PGSync ``` -pgsync +./bin/pgsync ``` Show the content in Elasticsearch ``` -curl -X GET http://localhost/index_name +curl -X GET http://localhost:9200/[index_name] ``` ##### Manual configuration @@ -151,16 +150,16 @@ Key features of PGSync are: - Transactionally consistent output in Elasticsearch. This means: writes appear only when they are committed to the database, insert, update and delete (TG_OP's) operations appear in the same order as they were committed (as opposed to eventual consistency). - Fault-tolerant: does not lose data, even if processes crash or a network interruption occurs, etc. The process can be recovered from the last checkpoint. - Returns the data directly as Postgres JSON from the database for speed -- Transformation support: a small subset of transforming the source data e.g rename labels in the document +- Transforms the data on the fly e.g rename labels before indexing. - Supports composite primary and foreign keys. -- Supports for an arbitrary depth of nested entities i.e Tables having long chain of relationship dependencies. -- Support for Postgres JSON data fields. This means: we can extract JSON fields in a database table as a separate field in the resulting document. -- Customize the document structure e.g Object vs List types. +- Supports an arbitrary depth of nested entities i.e Tables having long chain of relationship dependencies. +- Supports Postgres JSON data fields. This means: we can extract JSON fields in a database table as a separate field in the resulting document. +- Customize the document structure. #### Requirements - [Python](https://www.python.org) 3.7 -- [Postgres](https://www.postgresql.org) 9.4 +- [Postgres](https://www.postgresql.org) 9.4+ - [Redis](https://redis.io) 3.1.0 - [Elasticsearch](https://www.https://www.elastic.co/products/elastic-stack) 6.3.1 - [SQlAlchemy](https://www.sqlalchemy.org) 1.3.4 @@ -286,6 +285,16 @@ we wanted to change? - What if we delete or update an author? - What if we truncate an entire table? + +#### Benefits +- PGsync aims to be simple to use out of the box compared to other solutions. +- PGsync handles data deletions Unlike Logstash. +- PGSync requires little development effort. You simply define a config describing your data. +- PGsync generates advanced queries matching your schema directly. With Logstash you need to write this yourself. +- PGSync allows you to easily rebuild your index in case of a schema change. +- You can only expose the view of your data you require in Elasticsearch. + + #### Credits - This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template. @@ -295,24 +304,30 @@ we wanted to change? #### License -MIT License - -Copyright (c) 2019 Tolu Aina - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Copyright (c) 2019, Tolu Aina +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of PGSync nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/architecture.jpeg b/docs/architecture.jpeg similarity index 100% rename from architecture.jpeg rename to docs/architecture.jpeg diff --git a/docs/demo.svg b/docs/demo.svg new file mode 100644 index 00000000..28ed5c6b --- /dev/null +++ b/docs/demo.svg @@ -0,0 +1,525 @@ + + + + + + + + + + + Mon Aug 26$ Mon Aug 26$ s Mon Aug 26$ so Mon Aug 26$ sou Mon Aug 26$ sour Mon Aug 26$ source Mon Aug 26$ source . Mon Aug 26$ source .e Mon Aug 26$ source .en Mon Aug 26$ source .env Mon Aug 26$ source .envMon Aug 26$ c Mon Aug 26$ cr Mon Aug 26$ cre Mon Aug 26$ crea Mon Aug 26$ creat Mon Aug 26$ create Mon Aug 26$ created Mon Aug 26$ createdb Mon Aug 26$ createdb Mon Aug 26$ createdb b Mon Aug 26$ createdb bo Mon Aug 26$ createdb boo Mon Aug 26$ createdb book Mon Aug 26$ createdb book Mon Aug 26$ p Mon Aug 26$ ps Mon Aug 26$ psq Mon Aug 26$ psql Mon Aug 26$ psql Mon Aug 26$ psql - Mon Aug 26$ psql -d Mon Aug 26$ psql -d Mon Aug 26$ psql -d b Mon Aug 26$ psql -d bo Mon Aug 26$ psql -d boo Mon Aug 26$ psql -d book Mon Aug 26$ psql -d book Mon Aug 26$ psql -d book < Mon Aug 26$ psql -d book < Mon Aug 26$ psql -d book < b Mon Aug 26$ psql -d book < bo Mon Aug 26$ psql -d book < boo Mon Aug 26$ psql -d book < book.sql Mon Aug 26$ psql -d book < book.sql SETCREATE EXTENSIONCOMMENTCREATE FUNCTIONALTER FUNCTIONCREATE TABLEALTER TABLECREATE SEQUENCEALTER SEQUENCECOPY 4COPY 3 setval -------- 4(1 row) 3CREATE TRIGGERMon Aug 26$ pg Mon Aug 26$ p Mon Aug 26$ ps Mon Aug 26$ psq Mon Aug 26$ psql Mon Aug 26$ psql Mon Aug 26$ psql - Mon Aug 26$ psql -d Mon Aug 26$ psql -d Mon Aug 26$ psql -d b Mon Aug 26$ psql -d bo Mon Aug 26$ psql -d boo Mon Aug 26$ psql -d book Mon Aug 26$ psql -d book Mon Aug 26$ psql -d book - Mon Aug 26$ psql -d book -c Mon Aug 26$ psql -d book -c Mon Aug 26$ psql -d book -c " Mon Aug 26$ psql -d book -c "" Mon Aug 26$ psql -d book -c "" Mon Aug 26$ psql -d book -c "S" Mon Aug 26$ psql -d book -c "SE" Mon Aug 26$ psql -d book -c "SEL" Mon Aug 26$ psql -d book -c "SELE" Mon Aug 26$ psql -d book -c "SELEC" Mon Aug 26$ psql -d book -c "SELECT" Mon Aug 26$ psql -d book -c "SELECT " Mon Aug 26$ psql -d book -c "SELECT *" Mon Aug 26$ psql -d book -c "SELECT * " Mon Aug 26$ psql -d book -c "SELECT * F" Mon Aug 26$ psql -d book -c "SELECT * FR" Mon Aug 26$ psql -d book -c "SELECT * FRO" Mon Aug 26$ psql -d book -c "SELECT * FROM" Mon Aug 26$ psql -d book -c "SELECT * FROM " Mon Aug 26$ psql -d book -c "SELECT * FROM b" Mon Aug 26$ psql -d book -c "SELECT * FROM bo" Mon Aug 26$ psql -d book -c "SELECT * FROM boo" Mon Aug 26$ psql -d book -c "SELECT * FROM book" Mon Aug 26$ psql -d book -c "SELECT * FROM book" id | isbn | title | description | publisher_id ----+---------------+-----------------------------------+------------------------------------------------------------------------+-------------- 1 | 9785811243570 | Charlie and the chocolate factory | Willy Wonka’s famous chocolate factory is opening at last! | 2 2 | 9788374950978 | Kafka on the Shore | Kafka on the Shore is a 2002 novel by Japanese author Haruki Murakami. | 1 3 | 9781471331435 | 1984 | 1984 was George Orwell’s chilling prophecy about the dystopian future. | 3(3 rows)(END) Mon Aug 26$ Mon Aug 26$ psql -d book -c "SELECT * FROM book" Mon Aug 26$ psql -d book -c "SELECT * FROM book_" Mon Aug 26$ psql -d book -c "SELECT * FROM book_a" Mon Aug 26$ psql -d book -c "SELECT * FROM book_au" Mon Aug 26$ psql -d book -c "SELECT * FROM book_aut" Mon Aug 26$ psql -d book -c "SELECT * FROM book_auth" Mon Aug 26$ psql -d book -c "SELECT * FROM book_autho" Mon Aug 26$ psql -d book -c "SELECT * FROM book_author" Mon Aug 26$ psql -d book -c "SELECT * FROM book_author" id | book_isbn | author_id ----+---------------+----------- 1 | 9785811243570 | 1 2 | 9788374950978 | 2 3 | 9788374950978 | 3 4 | 9781471331435 | 4(4 rows)Mon Aug 26$ psql -d book -c "SELECT * FROM book_author" Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM book_author"Mon Aug 26$ psql -d book -c "SELECT * FROM bookauthor" Mon Aug 26$ psql -d book -c "SELECT * FROM booauthor" Mon Aug 26$ psql -d book -c "SELECT * FROM boauthor" Mon Aug 26$ psql -d book -c "SELECT * FROM bauthor" Mon Aug 26$ psql -d book -c "SELECT * FROM author" Mon Aug 26$ psql -d book -c "SELECT * FROM author" id | name ----+----------------- 1 | Roald Dahl 2 | Haruki Murakami 3 | Philip Gabriel 4 | George OrwellMon Aug 26$ psql -d book -c "SELECT * FROM author" Mon Aug 26$ psql -d book -c "SELECT * FROM author"Mon Aug 26$ psql -d book -c "SELECT * FROM autho" Mon Aug 26$ psql -d book -c "SELECT * FROM auth" Mon Aug 26$ psql -d book -c "SELECT * FROM aut" Mon Aug 26$ psql -d book -c "SELECT * FROM au" Mon Aug 26$ psql -d book -c "SELECT * FROM a" Mon Aug 26$ psql -d book -c "SELECT * FROM p" Mon Aug 26$ psql -d book -c "SELECT * FROM pu" Mon Aug 26$ psql -d book -c "SELECT * FROM pub" Mon Aug 26$ psql -d book -c "SELECT * FROM publ" Mon Aug 26$ psql -d book -c "SELECT * FROM publi" Mon Aug 26$ psql -d book -c "SELECT * FROM publis" Mon Aug 26$ psql -d book -c "SELECT * FROM publish" Mon Aug 26$ psql -d book -c "SELECT * FROM publishe" Mon Aug 26$ psql -d book -c "SELECT * FROM publisher" Mon Aug 26$ psql -d book -c "SELECT * FROM publisher" id | name ----+--------------- 1 | Oxford Press 2 | Penguin Books 3 | Pearson PressMon Aug 26$ b Mon Aug 26$ bo Mon Aug 26$ boo Mon Aug 26$ bootstrap Mon Aug 26$ bootstrap Mon Aug 26$ ca Mon Aug 26$ cat Mon Aug 26$ cat Mon Aug 26$ cat s Mon Aug 26$ cat sc Mon Aug 26$ cat schema Mon Aug 26$ cat schema. Mon Aug 26$ cat schema.j Mon Aug 26$ cat schema.json Mon Aug 26$ cat schema.json [ { "index": "book", "doc_type": "library", "nodes": [ { "table": "book" } ] }]Mon Aug 26$ pgs Mon Aug 26$ pgsy Mon Aug 26$ pgsync Mon Aug 26$ pgsync - book1)Pkeys : [1]Row :{'description': 'Willy Wonka’s famous chocolate factory is opening at last!', 'id': 1, 'isbn': '9785811243570', 'publisher_id': 2, 'title': 'Charlie and the chocolate factory'}----------2)Pkeys : [2]{'description': 'Kafka on the Shore is a 2002 novel by Japanese author Haruki ' 'Murakami.', 'id': 2, 'isbn': '9788374950978', 'publisher_id': 1, 'title': 'Kafka on the Shore'}3)Pkeys : [3]{'description': '1984 was George Orwell’s chilling prophecy about the ' 'dystopian future.', 'id': 3, 'isbn': '9781471331435', 'publisher_id': 3, 'title': '1984'}Mon Aug 26$ cur Mon Aug 26$ curl -XGET 'http://localhost:9200/book/_search?pretty=true&q=*:*&size=10' -H 'Content-Type: application/json' -d '{> "query": {> "match_all": {}> },> "_source": {> "excludes": [> "_private"> ]> }> }' > }' "max_score" : 1.0, "hits" : [ { "_index" : "book", "_type" : "library", "_id" : "2", "_score" : 1.0, "_source" : { "publisher_id" : 1, "isbn" : "9788374950978", "description" : "Kafka on the Shore is a 2002 novel by Japanese author Haruki Murakami.", "id" : 2, "title" : "Kafka on the Shore" } }, "_id" : "1", "publisher_id" : 2, "isbn" : "9785811243570", "description" : "Willy Wonka’s famous chocolate factory is opening at last!", "id" : 1, "title" : "Charlie and the chocolate factory" "_inde "_id" : "3", "publisher_id" : 3, "isbn" : "9781471331435", "description" : "1984 was George Orwell’s chilling prophecy about the dystopian future.", "id" : 3, "title" : "1984" } ] }}Mon Aug 26$ v Mon Aug 26$ vi Mon Aug 26$ vim Mon Aug 26$ vim Mon Aug 26$ vim s Mon Aug 26$ vim sh Mon Aug 26$ vim s Mon Aug 26$ vim sc Mon Aug 26$ vim sch Mon Aug 26$ vim sche Mon Aug 26$ vim schem Mon Aug 26$ vim schema Mon Aug 26$ vim schema. Mon Aug 26$ vim schema.j Mon Aug 26$ vim schema.json Mon Aug 26$ vim schema.json [ { "nodes": [ { "table": "book" } ] } ] ~ ~ "schema.json" 12L, 162C~ [ { "index": "book", "nodes": [ { } ] } ] "index": "book", } "doc_type": "library", "nodes": [ { } "table": "book" } ] } ] -- INSERT -- [ { "index": "book", "doc_type [ "doc_type": "library", "table": "book", "columns": [ "isbn", "title", "description" ] ] ] : : :w :wq :wq "schema.json" 16L, 300C written Mon Aug 26$ vim schema.json "query": { "match_all": {} }, "_source": { "excludes": [ "_private" ] }}' Mon Aug 26$ pgsync }'Mon Aug 26$ pgsync - book 1) Pkeys : [1] Row : {'description': 'Willy Wonka’s famous chocolate factory is opening at last!', 'isbn': '9785811243570', 'title': 'Charlie and the chocolate factory'} ---------- 2) Mon Aug 26$ vim schema.json Mon Aug 26$ curl -XGET 'http://localhost:9200/book/_search?pretty=true&q=*:*&size=10' -H 'Content-Type: application/json' -d '{ }' "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 3,Mon Aug 26$ vi Mon Aug 26$ vi s Mon Aug 26$ vi sc Mon Aug 26$ vi sch Mon Aug 26$ vi sche Mon Aug 26$ vi schem Mon Aug 26$ vi schema Mon Aug 26$ vi schema. Mon Aug 26$ vi schema.j Mon Aug 26$ vi schema.js Mon Aug 26$ vi schema.jso Mon Aug 26$ vi schema.json Mon Aug 26$ vi schema.json Mon Aug 26$ vi schema.json [ { "doc_type": "library", "nodes": [ { "table": "book", "columns": [ "isbn", "title", "description" ] } } ] ~ "schema.json" 16L, 300C~ [ ~ "isbn",~ --No lines in buffer-- "doc_type": "li ], "children": [ { "table": "publisher", "columns": [ "name" ], "relationship": { "variant": "object", "type": "one_to_one" } } ] "schema.json" 28L, 704C written - publisher 'publisher': {'name': 'Penguin Books'}, 'publisher': {'name': 'Oxford Press'}, 'publisher': {'name': 'Pearson Press'},Mon Aug 26$ vi schema.json "publisher" : { "name" : "Oxford Press" }, "name" : "Penguin Books" "name" : "Pearson Press"Mon Aug 26$ vi schema.json [ "description" ], "children": [ { "table": "publisher", "columns": [ "name" ], "relationship": { "variant": "object", "type": "one_to_one" "schema.json" 28L, 704C ~ ~ "variant": "scalar", "schema.json" 29L, 705C written - publisher 'publisher': 'Penguin Books', 'publisher': 'Oxford Press', 'publisher': 'Pearson Press', "successful" : 5, "publisher" : "Oxford Press", "publisher" : "Penguin Books", "publisher" : "Pearson Press",Mon Aug 26$ ci Mon Aug 26$ c Mon Aug 26$ v Mon Aug 26$ vi Mon Aug 26$ vi Mon Aug 26$ vi s Mon Aug 26$ vi sc Mon Aug 26$ vi sch Mon Aug 26$ vi sche Mon Aug 26$ vi schem Mon Aug 26$ vi schema Mon Aug 26$ vi schema. Mon Aug 26$ vi schema.j Mon Aug 26$ vi schema.js Mon Aug 26$ vi schema.jso Mon Aug 26$ vi schema.json Mon Aug 26$ vi schema.json [ { "index": "book", "nodes": [ { "columns": [ "isbn", "title", "description" ], "children": [ { "columns": [ "name" ], "relationship": { "variant": "scalar", "type": "one_to_one" } } ] } ]]"schema.json" 29L, 705C~ "isbn", ~ }, { "table": "author", "id", "name" "label": "authors", "relationship": {"type": "one_to_many", "variant": "object",-- INSERT (paste) -- "variant": "object", "through_tables": ["through_tables": ["book_author"]}}]}]] ]"schema.json" 42L, 1233C written Mon Aug 26$ pgsn Mon Aug 26$ pgs Mon Aug 26$ pgsy Mon Aug 26$ pgsyn Mon Aug 26$ pgsync |- publisher - author{'authors': [{'id': 1, 'name': 'Roald Dahl'}], 'description': 'Willy Wonka’s famous chocolate factory is opening at last!', 'publisher': 'Penguin Books',{'authors': [{'id': 2, 'name': 'Haruki Murakami'}, {'id': 3, 'name': 'Philip Gabriel'}], 'description': 'Kafka on the Shore is a 2002 novel by Japanese author Haruki '{'authors': [{'id': 4, 'name': 'George Orwell'}], 'description': '1984 was George Orwell’s chilling prophecy about the 'Mon Aug 26$ pgsync "_shards" : { "total" : 5, "title" : "Kafka on the Shore", "authors" : [ "name" : "Haruki Murakami", "id" : 2 }, "name" : "Philip Gabriel", "id" : 3 "title" : "Charlie and the chocolate factory", "name" : "Roald Dahl", "id" : 1 ] "title" : "1984", "name" : "George Orwell", "id" : 4 Mon Aug 26$ psql -d book -c "UPDATE author SET name='Tolu Aina' WHERE name='Philip Gabriel'"UPDATE 1 {'id': 3, 'name': 'Tolu Aina'}],Mon Aug 26$ psql -d book -c "UPDATE author SET name='Tolu Aina' WHERE name='Philip Gabriel'" "name" : "Tolu Aina",Mon Aug 26$ psql -d book -c Mon Aug 26$ psql -d book -c "DELETE FROM book_author WHERE author_id = (SELECT id FROM author WHERE name='Tolu Aina')"DELETE 1Mon Aug 26$ pgsy {'authors': [{'id': 2, 'name': 'Haruki Murakami'}],Mon Aug 26$ psql -d book -c "DELETE FROM book_author WHERE author_id = (SELECT id FROM author WHERE name='Tolu Aina')" "took" : 31, "timed_out" : false,Mon Aug 26$ e Mon Aug 26$ ex Mon Aug 26$ exi Mon Aug 26$ exit Mon Aug 26$ exitexit + \ No newline at end of file