├── .gitignore ├── LICENSE ├── README.md ├── images ├── astra-cql-console.gif ├── astra-create-2.png ├── astra-create-3.png ├── astra-create-4.png ├── astra-create-5.png ├── astra-create-6.png ├── astra-create-7.png ├── astra-create-launch-now.png ├── astra-create-login.png ├── astra-create-register.png ├── astra-login-cql-console.png ├── astra-use-cql-console-create-tables.png ├── astra-use-cql-console-create-users-by-city.png ├── astra-use-cql-console-delete-from-comments-by-video.png ├── astra-use-cql-console-delete-from-cred.png ├── astra-use-cql-console-desc-keyspace.png ├── astra-use-cql-console-describe-tables.png ├── astra-use-cql-console-insert-into-cred.png ├── astra-use-cql-console-select-from-tables.png ├── astra-use-cql-console-update-comments-by-video.png ├── astra-use-cql-console-use-killrvideo.png ├── astra-use-cql-console.png ├── astra-view-cql-console.png ├── badge │ └── intro-to-cassandra.png ├── cluster_basics.png ├── cql │ ├── 01_desc_keyspaces.png │ ├── 02_use_chatsandra.png │ ├── 03_user_table_created.png │ ├── 04_post_tables_created.png │ ├── 05_selects.png │ ├── 06_updated.png │ └── 07_deleting.png ├── getting-started-with-cassandra.png └── tutorials │ ├── astra-create-db.gif │ └── astra_signup.gif └── slides └── Presentation.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎓🔥 Intro to Apache Cassandra for Developers 🔥🎓 2 | 3 | Welcome to the 'Intro to Cassandra for Developers' workshop! In this two-hour workshop, the Developer Advocate team of DataStax shows the most important fundamentals and basics of the powerful distributed NoSQL database Apache Cassandra. Using Astra DB, the cloud based Cassandra-as-a-Service platform delivered by DataStax, we will cover the very first steps for every developer who wants to try to learn a new database: creating tables and CRUD operations. 4 | 5 | It doesn't matter if you join our workshop live or you prefer to do at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop: 6 | 7 | - Materials used during presentations 8 | - Hands-on exercises (see below) 9 | - [Workshop video](https://www.youtube.com/watch?v=wOyQlbFM1Uk) 10 | - [Discord chat](https://dtsx.io/discord) 11 | - [Questions and Answers](https://community.datastax.com/) 12 | 13 | ## Homework 14 | 15 | To complete the workshop and get a verified badge, follow these simple steps: 16 | 17 | - Watch the workshop live or recorded. 18 | - Complete the workshop practice as described below and make the screenshot of the last step (result of the `DELETE` in "Execute CRUD", see [here](#homework-note)). 19 | - Complete the mini-course [Cassandra Query Language](https://killercoda.com/datastaxdevs/course/cassandra-fundamentals/cql) and take a screenshot of the final screen (the one with buttons "Back"/"Restart" ... + console on the right). 20 | - Complete the mini-course "Cassandra Data Modeling / Digital Library": [lessons](https://www.datastax.com/learn/data-modeling-by-example/digital-library-data-model) and [practice](https://killercoda.com/datastaxdevs/course/cassandra-data-modeling/music-data). Take a screenshot of the final screen of the practice, with the console output at the right. 21 | - [Submit the Homework through this form](https://dtsx.io/homework-intro-to-cassandra) and attach the screenshot(s) you took. 22 | - Give us a few days to review your submission and relax: your well-earned badge will soon land in your mailbox! 23 | 24 | ## Table of Contents 25 | 26 | | Title | Description 27 | |---|---| 28 | | **Slide deck** | [Slide deck for the workshop](slides/Presentation.pdf) | 29 | | **1. Create your Astra DB instance** | [Create your Astra DB instance](#1-create-your-astra-db-instance) | 30 | | **2. Create tables** | [Create tables](#2-create-tables) | 31 | | **3. Execute CRUD (Create, Read, Update, Delete) operations** | [Execute CRUD operations](#3-execute-crud-operations) | 32 | 33 | 34 | ## 1. Create your Astra DB instance 35 | 36 | _**`ASTRA DB`** is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, $25.00 USD credit every month, meaning 20M read/write operations and about 80GB storage monthly - sufficient to run small production workloads._ 37 | 38 | ✅ Register (if needed) and Sign In to Astra DB [https://astra.datastax.com](https://astra.datastax.com): You can use your `Github`, `Google` accounts or register with an `email`. 39 | 40 | _Make sure to chose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character_ 41 | 42 | ✅ Choose "Start Free Now" 43 | 44 | Choose the "Start Free Now" plan, then "Get Started" to work in the free tier. 45 | 46 | You will have plenty of free initial credit (renewed each month!), roughly corresponding 47 | to 80 GB of storage and 20M read/write operations. 48 | 49 | > If this is not enough for you, congratulations! You are most likely running a mid- to large-sized business! In that case you should switch to a paid plan. 50 | 51 | (You can follow this [guide](https://docs.datastax.com/en/astra/docs/creating-your-astra-database.html) to set up your free-tier database with the $25 monthly credit.) 52 | 53 | ![astra-db-signup](images/tutorials/astra_signup.gif) 54 | 55 | To create the database, please note that _the `db_name` and `ks_name` in the above image are just placeholders_: 56 | 57 | - **For the database name** - use `workshops`. While Astra DB allows you to fill in these fields with values of your own choosing, please follow our recommendations to ensure the application runs properly. 58 | 59 | - **For the keyspace name** - use `chatsandra`. Please stick to this name, it will make the following steps much easier (you have to customize here and there otherwise). In short: 60 | 61 | _Note_: if you already have a `workshops` database, for instance from a previous workshop with us, you can simply create the keyspace with the `Add Keyspace` button in your Astra DB dashboard: the new keyspace will be available in few seconds. 62 | 63 | | Parameter | Value 64 | |---|---| 65 | | Database name | workshops | 66 | | Keyspace name | chatsandra | 67 | 68 | - **For provider and region**: Choose any provider (either GCP, AWS or Azure). Region is where your database will reside physically (choose one close to you or your users). 69 | 70 | - **Create the database**. Review all the fields to make sure they are as shown, and click the `Create Database` button. 71 | 72 | You will see your new database as `Pending` in the Dashboard; 73 | the status will change to `Active` when the database is ready. This will only take 2-3 minutes 74 | (you will also receive an email when it is ready). 75 | 76 | > **⚠️ Important** 77 | > ``` 78 | > The instructor might show you on screen how to create a token 79 | > but will have to destroy to token immediately for security reasons. 80 | > ``` 81 | 82 | 83 | ## 2. Create tables 84 | Ok, now that you have a database created the next step is to create tables to work with. 85 | 86 | > _General Methodology Note_: We'll work with a (rather simplified) "chat application" called **ChatSandra**: 87 | > users, identified by a unique ID, write posts in several "rooms". 88 | > Rooms are also uniquely identified by their name, such as `#gardening`. The design of our application is such 89 | > that we need to be able to (a) retrieve all posts by a given user, sorted by descending date, 90 | > and (b) retrieve all posts for a given room, sorted by descending date. 91 | > As dictated by the best practices of data modeling with Cassandra, these requirements are satisfied by creating _two_ very similar tables (denormalization), 92 | > as you'll see momentarily: they will contain the same posts, but stored (a.k.a. partitioned) in two different ways; 93 | > and it will be our (that is, the application's) responsibility to maintain them aligned. 94 | > Of course, we also need a `users` table - we will start with this one indeed. 95 | 96 | **✅ Step 2a. Navigate to the CQL Console and login to the database** 97 | 98 | In the Summary screen for your database, select **_CQL Console_** from the top menu in the main window. This will take you to the CQL Console and automatically log you in. 99 | 100 |
101 | Show me! 102 | 103 |
104 | 105 | > _Note_: if you are working with your own Cassandra cluster (other than Astra DB), you will reach the CQL Console differently. 106 | > Moreover, in that case you have to manually create the keyspace once in the CQL Console: this is done with a command similar to 107 | > `CREATE KEYSPACE chatsandra WITH REPLICATION = {'class': 'NetworkTopologyStrategy', 'replication_factor': 3};`. 108 | > See the Cassandra documentation for more details on this. 109 | 110 | **✅ Step 2b. Describe keyspaces and USE one of them** 111 | 112 | Ok, now we're ready to rock. Creating tables is quite easy, but before we create one we need to tell the database which keyspace we are working with. 113 | 114 | First, let's **_DESCRIBE_** all of the keyspaces that are in the database. This will give us a list of the available keyspaces. 115 | 116 | 📘 **Command to execute** 117 | ```sql 118 | DESC KEYSPACES; 119 | ``` 120 | _"desc" is short for "describe", either is valid._ 121 | 122 | > CQL commands usually end with a semicolon `;`. If you hit Enter, nothing happens and you don't even get your prompt back, most likely it's because you have not closed the command with `;`. If in trouble, you can always get back to the prompt with `Ctrl-C` and start typing the command anew. 123 | 124 | 📗 **Expected output** 125 | 126 | ![Keyspaces in CQL](images/cql/01_desc_keyspaces.png) 127 | 128 | Depending on your setup you might see a different set of keyspaces than in the image. The one we care about for now is **_chatsandra_**. From here, execute the **_USE_** command with the **_chatsandra_** keyspace to tell the database our context is within **_chatsandra_**. 129 | 130 | > Take advantage of the TAB-completion in the CQL Console. Try typing `use cha` and then pressing TAB, for example. 131 | 132 | 📘 **Command to execute** 133 | ```sql 134 | USE chatsandra; 135 | ``` 136 | 137 | 📗 **Expected output** 138 | 139 | ![USE keyspace](images/cql/02_use_chatsandra.png) 140 | 141 | Notice how the prompt displays ```@cqlsh:chatsandra>``` informing us we are **using** the **_chatsandra_** keyspace. Now we are ready to create our table. 142 | 143 | **✅ Step 2c. Create the users table** 144 | 145 | At this point we can execute a command to create the **users** table. 146 | Just copy/paste the following command into your CQL console at the prompt. 147 | Try to identify the primary key, the partition key and the clustering columns 148 | (if any) for this table in the command: 149 | 150 | 📘 **Command to execute** 151 | 152 | ```sql 153 | CREATE TABLE IF NOT EXISTS users ( 154 | email TEXT, 155 | name TEXT, 156 | password TEXT, 157 | user_id UUID, 158 | PRIMARY KEY (( email )) 159 | ); 160 | ``` 161 | 162 | Then **_DESCRIBE_** your keyspace tables to ensure it is there. 163 | 164 | 📘 **Command to execute** 165 | 166 | ```sql 167 | DESC TABLES; 168 | ``` 169 | 📗 **Expected output** 170 | 171 | ![A table created](images/cql/03_user_table_created.png) 172 | 173 | Aaaand **BOOM**, you created a table in your database. That's it. 174 | Now let's go ahead and create a couple more tables before we do 175 | something interesting with the data. 176 | 177 | **✅ Step 2d. Create the tables for posts** 178 | 179 | Let us create two more tables, which will contain the _posts_. 180 | As remarked earlier, we will store the posts in two tables which 181 | differ in how they are partitioned: look at the commands below, 182 | the differences mostly lie in the `PRIMARY KEY` specification: 183 | 184 | 📘 **Command to execute** 185 | 186 | ```sql 187 | CREATE TABLE IF NOT EXISTS posts_by_user ( 188 | user_id UUID, 189 | post_id TIMEUUID, 190 | room_id TEXT, 191 | text TEXT, 192 | PRIMARY KEY ((user_id), post_id) 193 | ) WITH CLUSTERING ORDER BY (post_id DESC); 194 | 195 | CREATE TABLE IF NOT EXISTS posts_by_room ( 196 | room_id TEXT, 197 | post_id TIMEUUID, 198 | user_id UUID, 199 | text TEXT, 200 | PRIMARY KEY ((room_id), post_id) 201 | ) WITH CLUSTERING ORDER BY (post_id DESC); 202 | ``` 203 | 204 | Then **_DESCRIBE_** your keyspace tables: you should see all three listed. 205 | 206 | 📘 **Command to execute** 207 | 208 | ```sql 209 | DESC TABLES; 210 | ``` 211 | 212 | 📗 **Expected output** 213 | 214 | ![A table created](images/cql/04_post_tables_created.png) 215 | 216 | _You may wonder, how did we arrive at this particular structure for the post tables? 217 | The answer lies in the methodology for data modeling 218 | with Cassandra, which, at its very core, states: **first look at the application's needs, 219 | determine the required workflows, then map them to a number of queries, finally design a table around each query**. 220 | We create table **_posts_by_user_** to support a query such as "get all posts by a user X"; 221 | then we also need table **_posts_by_room_** for a query of type "get all posts in room Y". 222 | The two tables have the same columns, but the different choice of partition key is what 223 | will make the two queries possible on the respective tables._ 224 | 225 | [🏠 Back to Table of Contents](#table-of-contents) 226 | 227 | ## 3. Execute CRUD operations 228 | CRUD stands for "**create, read, update, and delete**". Simply put, they are the basic types of commands you need to work with ANY database in order to maintain data for your applications. 229 | 230 | **✅ Step 3a. (C)RUD = create = insert data, users** 231 | 232 | Our tables are in place so let's put some data in them. This is done with the **INSERT** statement. We'll start by inserting three rows into the **_users_** table. 233 | 234 | > _Note_ that we have three users in this example: "111...", "555..." and "999...", which are having some pleasant conversations. In a real application, you would probably 235 | > generate user IDs at the application level or with the `UUID()` primitive offered by CQL. 236 | > See the [documentation](https://docs.datastax.com/en/cql-oss/3.3/cql/cql_reference/timeuuid_functions_r.html) for more details on time/uuid-related CQL functions. 237 | 238 | Copy and paste the following in your CQL Console: 239 | _(Once you have carefully examined the first of the following **INSERT** statements below, you can simply copy/paste the others which are very similar.)_ 240 | 241 | 📘 **Commands to execute** 242 | 243 | ```sql 244 | INSERT INTO users ( 245 | email, // TEXT 246 | name, // TEXT 247 | password, // TEXT 248 | user_id // UUID: id of a user 249 | ) 250 | VALUES ( 251 | 'otzi@mail.com', 252 | 'Otzi Oney', 253 | '123456', 254 | 11111111-1111-1111-1111-111111111111 255 | ); 256 | 257 | INSERT INTO users (email, name, password, user_id) VALUES ( 258 | 'fred@qmail.net', 'Fred Fivey', 'qwerty', 259 | 55555555-5555-5555-5555-555555555555 260 | ); 261 | INSERT INTO users (email, name, password, user_id) VALUES ( 262 | 'nina@zmail.org', 'Nina Niney', 's3cr3t', 263 | 99999999-9999-9999-9999-999999999999 264 | ); 265 | ``` 266 | 267 | **✅ Step 3b. (C)RUD = create = insert data, posts** 268 | 269 | Let's run some more **INSERT** statements, this time for **posts**. We'll insert data into the **_posts_by_user_** table. 270 | _(Once you have carefully examined the first of the following **INSERT** statements below, you can simply copy/paste the others which are very similar.)_ 271 | 272 | > _Note_: in the following, we are using `TIMEUUID`s crafted by hand, to make things easier to visualize. In a real application, you would generate them at application 273 | > level or, in some cases, using the `NOW()` primitive offered by CQL. In the values below, you can just pay attention to the first octet of hex digits. 274 | 275 | 📘 **Commands to execute** 276 | 277 | ```sql 278 | // Insert some data in the "posts_by_user" table 279 | 280 | INSERT INTO posts_by_user ( 281 | user_id, // UUID: unique id for a user 282 | post_id, // TIMEUUID: unique uuid + timestamp 283 | room_id, // TEXT: id of a chat room 284 | text // TEXT: the post content itself 285 | ) 286 | VALUES ( 287 | 11111111-1111-1111-1111-111111111111, 288 | 22222222-5cff-11ec-be16-1fedb0dfd057, 289 | '#hiking', 290 | 'I climbed Mt. Gumbo yesterday ...' 291 | ); 292 | 293 | INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 294 | 11111111-1111-1111-1111-111111111111, 295 | 77777777-5cff-11ec-be16-1fedb0dfd057, 296 | '#running', 'Who likes marathons here?' 297 | ); 298 | INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 299 | 11111111-1111-1111-1111-111111111111, 300 | aaaaaaaa-5cff-11ec-be16-1fedb0dfd057, 301 | '#hiking', '... and Mt. Gumbo was easy!!!' 302 | ); 303 | INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 304 | 55555555-5555-5555-5555-555555555555, 305 | bbbbbbbb-5cff-11ec-be16-1fedb0dfd057, 306 | '#hiking', 'For us humans Gumbo is a tough one...!' 307 | ); 308 | INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 309 | 99999999-9999-9999-9999-999999999999, 310 | cccccccc-5cff-11ec-be16-1fedb0dfd057, 311 | '#running', 'I just love marathons.' 312 | ); 313 | INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 314 | 11111111-1111-1111-1111-111111111111, 315 | eeeeeeee-5cff-11ec-be16-1fedb0dfd057, 316 | '#running', 'Same here!' 317 | ); 318 | INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 319 | 55555555-5555-5555-5555-555555555555, 320 | ffffffff-5cff-11ec-be16-1fedb0dfd057, 321 | '#hiking', 'I have to buy new boots.' 322 | ); 323 | ``` 324 | 325 | Ok, we have a lovely bunch of posts in our chat application. 326 | But **wait**: data is denormalized and the very same posts have to be inserted 327 | in table **_posts_by_room_** as well! Let's do it with the following command 328 | (please note that the `INSERT` statements are exactly the same as above, 329 | with only the table name changed): 330 | 331 | 📘 **Commands to execute** 332 | 333 | ```sql 334 | // Insert some data in the "posts_by_room" table 335 | 336 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 337 | 11111111-1111-1111-1111-111111111111, 338 | 22222222-5cff-11ec-be16-1fedb0dfd057, 339 | '#hiking', 'I climbed Mt. Gumbo yesterday ...' 340 | ); 341 | 342 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 343 | 11111111-1111-1111-1111-111111111111, 344 | 77777777-5cff-11ec-be16-1fedb0dfd057, 345 | '#running', 'Who likes marathons here?' 346 | ); 347 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 348 | 11111111-1111-1111-1111-111111111111, 349 | aaaaaaaa-5cff-11ec-be16-1fedb0dfd057, 350 | '#hiking', '... and Mt. Gumbo was easy!!!' 351 | ); 352 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 353 | 55555555-5555-5555-5555-555555555555, 354 | bbbbbbbb-5cff-11ec-be16-1fedb0dfd057, 355 | '#hiking', 'For us humans Gumbo is a tough one...!' 356 | ); 357 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 358 | 99999999-9999-9999-9999-999999999999, 359 | cccccccc-5cff-11ec-be16-1fedb0dfd057, 360 | '#running', 'I just love marathons.' 361 | ); 362 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 363 | 11111111-1111-1111-1111-111111111111, 364 | eeeeeeee-5cff-11ec-be16-1fedb0dfd057, 365 | '#running', 'Same here!' 366 | ); 367 | INSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES ( 368 | 55555555-5555-5555-5555-555555555555, 369 | ffffffff-5cff-11ec-be16-1fedb0dfd057, 370 | '#hiking', 'I have to buy new boots.' 371 | ); 372 | ``` 373 | 374 | **✅ Step 3c. C(R)UD = read = read data** 375 | 376 | Now that we've inserted a set of rows (two sets, to be precise), let's take a look at how to read the data back out. This is done with a **SELECT** statement. In its simplest form we could just execute a statement like the following **_**cough_** **_**cough_**: 377 | ```sql 378 | // Read all rows from "posts_by_user" table (careful with this ...) 379 | SELECT * FROM posts_by_user; 380 | ``` 381 | 382 | You may have noticed my coughing fit a moment ago. Even though you can execute a **SELECT** statement with no partition key defined, this is NOT something you should do when using Apache Cassandra. We are doing it here for illustration purposes only and because our whole dataset is just a handful of values. 383 | Given the data we inserted earlier, a more proper statement would be something like (while we are at it, we also explicitly specify which columns we want back): 384 | ```sql 385 | // Read (some columns of) rows in a certain partition of "posts_by_user" table 386 | SELECT post_id, room_id, text FROM posts_by_user 387 | WHERE user_id = 11111111-1111-1111-1111-111111111111; 388 | ``` 389 | 390 | The key is to ensure we are **always selecting by some partition key** at a minimum, so to avoid the dreaded _full-cluster scans_ which yield performances that are generally unacceptable in production. 391 | 392 | Ok, with that out of the way we can **READ** the data from the other table as well - remember we **INSERT**ed on both tables? 393 | 394 | 📘 **Commands to execute** 395 | 396 | ```sql 397 | // Read the whole "posts_by_room" table 398 | // (warning: not suitable for large tables in production) 399 | SELECT * FROM posts_by_room; 400 | 401 | // Read (some columns of) posts from a certain room (= a certain partition) 402 | SELECT user_id, text FROM posts_by_room WHERE room_id = '#hiking'; 403 | ``` 404 | 405 | (again, in the second **SELECT** we specify some columns - it is something we may want to do in most cases). 406 | 407 | 📗 **Expected output** 408 | 409 | ![SELECT in CQL](images/cql/05_selects.png) 410 | 411 | _Notice how the two tables contain the same set of posts, but group them differently: 412 | table `posts_by_user` is partitioned by user, while table `posts_by_room` is partitioned by room - and the corresponding outputs 413 | reflect this fact. 414 | This is very much related to the fact that these two tables, in the data modeling process, were designed 415 | to answer two different questions, "what are the posts by user X?" and "what are the posts in room Y?" respectively. 416 | Moreover, within any partition in both tables, right as we required when creating the table, 417 | posts are kept (and displayed) sorted by decreasing `post_id` (which, due to the nature of `TIMEUUID`s, 418 | implies a time-ordering as well)._ 419 | 420 | Once you execute the above **SELECT** statements you should see something like the expected output above. We have now **READ** the data we **INSERTED** earlier. Awesome job! 421 | 422 | _BTW, just a little extra for those who are interested. Since we used a [TIMEUUID](https://docs.datastax.com/en/cql-oss/3.3/cql/cql_reference/timeuuid_functions_r.html) type for our **post_id** field we can use the **dateOf()** function to determine the timestamp from the value. Check it out._ 423 | 424 | ```sql 425 | // Read all data from the posts_by_room table, 426 | // convert post_id into a timestamp, and label the column "post_date" 427 | SELECT user_id, dateOf(post_id) AS post_date, text FROM posts_by_room 428 | WHERE room_id = '#hiking'; 429 | ``` 430 | 431 | **✅ Step 3d. CR(U)D = update = update data** 432 | 433 | At this point we've **_CREATED_** and **_READ_** some data, but what happens when you want to change some existing data to some new value? That's where **UPDATE** comes into play. 434 | _The use case is as follows: in our chat app, users are allowed to edit their previous posts._ 435 | 436 | Let's take one of the records we created earlier and modify it. Recall that we **_INSERTED_** the following record in the **_posts_by_user_** table. 437 | ```sql 438 | // ** Just for reference: ** 439 | // INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES ( 440 | // 11111111-1111-1111-1111-111111111111, 441 | // aaaaaaaa-5cff-11ec-be16-1fedb0dfd057, 442 | // '#hiking', '... and Mt. Gumbo was easy!!!' 443 | // ); 444 | ``` 445 | 446 | Let's also take a look at how the **_posts_by_user_** table was created. In order to **UPDATE** an existing record, indeed, we need to know the primary key we defined when we **CREATE**d the table. 447 | ```sql 448 | // ** Just for reference: ** 449 | // CREATE TABLE IF NOT EXISTS posts_by_user ( 450 | // user_id UUID, 451 | // post_id TIMEUUID, 452 | // room_id TEXT, 453 | // text TEXT, 454 | // PRIMARY KEY ((user_id), post_id) 455 | // ) WITH CLUSTERING ORDER BY (post_id DESC); 456 | ``` 457 | 458 | > Let's say that user "111..." has noticed the remark by "555..." and, perhaps a bit ashamed by their own boasting, wants to correct their assessment on the hike difficulty! 459 | 460 | Looking at ```PRIMARY KEY ((user_id), post_id)```, we know that both **user_id** and **post_id** are used to define uniqueness of the row. 461 | We'll need both to update our record (plus, of course, some of the data columns, otherwise we are not changing anything in that row!). 462 | 463 | _You may remember that we used hardcoded values for **post_id** when we created these records (a real application would generate them live, one way or the other). 464 | Imagine the UX for editing an existing post: when the user clicks the "edit" button, both **user_id** and **post_id** are known and can be provided to 465 | the backend, where they ultimately become part of an **UPDATE** statement._ 466 | 467 | So we can run the following **UPDATE** statement and help user "111..." fix their post on table **_posts_by_user_** 468 | (we also subsequently read back the data as a check): 469 | 470 | 📘 **Commands to execute** 471 | 472 | ```sql 473 | UPDATE posts_by_user 474 | SET text = '... and Mt. Gumbo was NOT SO easy!!!' 475 | WHERE user_id = 11111111-1111-1111-1111-111111111111 476 | AND post_id = aaaaaaaa-5cff-11ec-be16-1fedb0dfd057; 477 | 478 | SELECT post_id, room_id, text FROM posts_by_user 479 | WHERE user_id = 11111111-1111-1111-1111-111111111111; 480 | ``` 481 | 482 | 📗 **Expected output** 483 | 484 | ![Updating in CQL](images/cql/06_updated.png) 485 | 486 | But **wait**: data, again, is denormalized! This means that we have to make sure 487 | such an edit is performed on table **_posts_by_room_** as well. 488 | Since the primary key of that table is given as `PRIMARY KEY ((room_id), post_id)`, 489 | these are the fields to provide, along with `text` itself, to the **UPDATE** statement. 490 | 491 | And we _could_ run an **UPDATE**. But, lo and behold, in Cassandra **UPDATE**s 492 | and **INSERT**s are (almost) the same, as a consequence of its architecture and 493 | the way storage and write logic are structured. We can then update the row with 494 | an **INSERT** statement like the following (note that we provide: primary key + 495 | any field that we want to modify; and leave out the other, unchanged fields): 496 | 497 | 📘 **Commands to execute** 498 | ```sql 499 | INSERT INTO posts_by_room (room_id, post_id, text) VALUES ( 500 | '#hiking', 501 | aaaaaaaa-5cff-11ec-be16-1fedb0dfd057, 502 | '... and Mt. Gumbo was NOT SO easy!!!' 503 | ); 504 | 505 | SELECT post_id, user_id, text FROM posts_by_room WHERE room_id = '#hiking'; 506 | ``` 507 | 508 | That's it, we successfully edited a post (on both tables). 509 | All that's left now is to **DELETE** some data. 510 | 511 | **✅ Step 3e. CRU(D) = delete = remove data** 512 | 513 | The final operation from our **CRUD** acronym is **DELETE**. This is the operation we use when we want to remove data from the database. 514 | In Apache Cassandra you can **DELETE** from the cell level all the way up to the partition 515 | _(meaning I could remove a single column in a single row or I could remove a whole partition)_ using the same **DELETE** command. 516 | 517 | _Generally speaking, it's best to perform as few delete operations as possible on the largest amount of data. Think of it this way, if you want to delete ALL data in a table, don't delete each individual cell, just **TRUNCATE** the table. If you need to delete all the rows in a partition, don't delete each row, **DELETE** the partition, and so on._ 518 | 519 | > User "555..." notices the post by "111..." being edited and wants to remove their snarky remark. Let's help them! 520 | 521 | When deleting a row on a given table, we have to specify the values of the primary key for that table. And don't forget 522 | that, in our data model, a post appears as two separate rows in the two tables, so we have to perform 523 | two different **DELETE** operations! 524 | 525 | 📘 **Commands to execute** 526 | 527 | ```sql 528 | SELECT post_id, room_id, text FROM posts_by_user 529 | WHERE user_id = 55555555-5555-5555-5555-555555555555; 530 | 531 | SELECT post_id, user_id, text FROM posts_by_room WHERE room_id = '#hiking'; 532 | 533 | DELETE FROM posts_by_user 534 | WHERE user_id = 55555555-5555-5555-5555-555555555555 535 | AND post_id = bbbbbbbb-5cff-11ec-be16-1fedb0dfd057; 536 | 537 | DELETE FROM posts_by_room 538 | WHERE room_id = '#hiking' 539 | AND post_id = bbbbbbbb-5cff-11ec-be16-1fedb0dfd057; 540 | 541 | SELECT post_id, room_id, text FROM posts_by_user 542 | WHERE user_id = 55555555-5555-5555-5555-555555555555; 543 | 544 | SELECT post_id, user_id, text FROM posts_by_room WHERE room_id = '#hiking'; 545 | ``` 546 | 547 | (Notice in the above, for your convenience, we read the tables, then delete the rows, then read them again). 548 | 549 | 📗 **Expected output** 550 | 551 | ![Deleting in CQL](images/cql/07_deleting.png) 552 | 553 | Notice the rows are now removed from both tables: it is as simple as that. 554 | 555 | ### Homework note 556 | 557 | To submit the **homework**, please take a screenshot of the CQL Console showing the rows in tables 558 | `posts_by_user` and `posts_by_room` before _and_ after executing the DELETE statements. 559 | 560 | ## 4. Wrapping up 561 | We've just scratched the surface of what you can do using Astra DB, built on Apache Cassandra. 562 | Go take a look at [DataStax for Developers](https://www.datastax.com/dev) to see what else is possible. 563 | There's plenty to dig into! 564 | 565 | # Done? 566 | 567 | Congratulations: you made to the end of today's workshop. 568 | 569 | Don't forget to [submit your homework](https://dtsx.io/homework-intro-to-cassandra) and be awarded a nice verified badge! 570 | 571 | ![Badge](images/badge/intro-to-cassandra.png) 572 | 573 | **... and see you at our next workshop!** 574 | 575 | > Sincerely yours, The DataStax Developers 576 | -------------------------------------------------------------------------------- /images/astra-cql-console.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-cql-console.gif -------------------------------------------------------------------------------- /images/astra-create-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-2.png -------------------------------------------------------------------------------- /images/astra-create-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-3.png -------------------------------------------------------------------------------- /images/astra-create-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-4.png -------------------------------------------------------------------------------- /images/astra-create-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-5.png -------------------------------------------------------------------------------- /images/astra-create-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-6.png -------------------------------------------------------------------------------- /images/astra-create-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-7.png -------------------------------------------------------------------------------- /images/astra-create-launch-now.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-launch-now.png -------------------------------------------------------------------------------- /images/astra-create-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-login.png -------------------------------------------------------------------------------- /images/astra-create-register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-create-register.png -------------------------------------------------------------------------------- /images/astra-login-cql-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-login-cql-console.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-create-tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-create-tables.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-create-users-by-city.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-create-users-by-city.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-delete-from-comments-by-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-delete-from-comments-by-video.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-delete-from-cred.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-delete-from-cred.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-desc-keyspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-desc-keyspace.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-describe-tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-describe-tables.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-insert-into-cred.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-insert-into-cred.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-select-from-tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-select-from-tables.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-update-comments-by-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-update-comments-by-video.png -------------------------------------------------------------------------------- /images/astra-use-cql-console-use-killrvideo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console-use-killrvideo.png -------------------------------------------------------------------------------- /images/astra-use-cql-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-use-cql-console.png -------------------------------------------------------------------------------- /images/astra-view-cql-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/astra-view-cql-console.png -------------------------------------------------------------------------------- /images/badge/intro-to-cassandra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/badge/intro-to-cassandra.png -------------------------------------------------------------------------------- /images/cluster_basics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cluster_basics.png -------------------------------------------------------------------------------- /images/cql/01_desc_keyspaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/01_desc_keyspaces.png -------------------------------------------------------------------------------- /images/cql/02_use_chatsandra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/02_use_chatsandra.png -------------------------------------------------------------------------------- /images/cql/03_user_table_created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/03_user_table_created.png -------------------------------------------------------------------------------- /images/cql/04_post_tables_created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/04_post_tables_created.png -------------------------------------------------------------------------------- /images/cql/05_selects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/05_selects.png -------------------------------------------------------------------------------- /images/cql/06_updated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/06_updated.png -------------------------------------------------------------------------------- /images/cql/07_deleting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/cql/07_deleting.png -------------------------------------------------------------------------------- /images/getting-started-with-cassandra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/getting-started-with-cassandra.png -------------------------------------------------------------------------------- /images/tutorials/astra-create-db.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/tutorials/astra-create-db.gif -------------------------------------------------------------------------------- /images/tutorials/astra_signup.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/images/tutorials/astra_signup.gif -------------------------------------------------------------------------------- /slides/Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-intro-to-cassandra/fafe619d6a7726367c906819d9916684da48fc76/slides/Presentation.pdf --------------------------------------------------------------------------------