Cassandra#

  • A production grade NoSQL database.

  • Can be distributed across servers, nodes, etc.

  • Replication of database is supported for high degree of redundancy and speed.

  • Uses CQL, a subset of SQL for querying.

  • Works seamlesly together with Spark and its corresponding distributed structure.

  • Installation of Cassandra is explained in the Installation chapter.

Spinning up a local Cassandra instance#

In a terminal, first time:
docker run --name my_cassandra -p 9042:9042 cassandra:latest
… and later:
docker start my_cassandra

… or in Docker Desktop:

  • Run the cassandra docker image with optional settings, opening 9042 port and setting a name.

  • Later, simply run the container with the name you chose.

https://github.com/khliland/IND320/blob/main/D2Dbook/images/Docker_images.png?raw=TRUE https://github.com/khliland/IND320/blob/main/D2Dbook/images/Docker_containers.png?raw=TRUE

Connect to the Cassandra cluster from Python.#

# Connecting to Cassandra
from cassandra.cluster import Cluster
cluster = Cluster(['localhost'], port=9042)
session = cluster.connect()

Keyspace#

  • In Cassandra database tables are stored in keyspaces (basically a distributed database).

  • These have parameters controlling their distribution on nodes/servers and redundancy.

  • We will use the simplest form locally.

# Set up new keyspace (first time only)
#                                              name of keyspace                        replication strategy           replication factor
session.execute("CREATE KEYSPACE IF NOT EXISTS my_first_keyspace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };")
<cassandra.cluster.ResultSet at 0x1139fdc40>

Create a table#

  • IF NOT EXISTS makes sure we do not overwrite existing tables

# Create a new table (first time only)
session.set_keyspace('my_first_keyspace')
session.execute("DROP TABLE IF EXISTS my_first_keyspace.my_first_table;") # Starting from scratch every time
session.execute("CREATE TABLE IF NOT EXISTS my_first_table (ind int PRIMARY KEY, company text, model text);")
<cassandra.cluster.ResultSet at 0x11411a7e0>

Inserting and reading data#

# Insert some data (ind is the primary key, must be unique)
session.execute("INSERT INTO my_first_table (ind, company, model) VALUES (1, 'Tesla', 'Model S');")
session.execute("INSERT INTO my_first_table (ind, company, model) VALUES (2, 'Tesla', 'Model 3');")
session.execute("INSERT INTO my_first_table (ind, company, model) VALUES (3, 'Polestar', '3');")
<cassandra.cluster.ResultSet at 0x11411b8c0>
# Query the data
rows = session.execute("SELECT * FROM my_first_table;")
for i in rows:
    print(i)
Row(ind=1, company='Tesla', model='Model S')
Row(ind=2, company='Tesla', model='Model 3')
Row(ind=3, company='Polestar', model='3')

Case sensitivity#

  • Cassandra is by default case insensitive in column names.

  • To use column names with capital letters, use double quotation marks both when creating tables and when inserting data.

  • The effect of insensitivity may be surprising.

    • Look carefully at the use of quotation marks and error message below.

session.set_keyspace('my_first_keyspace')
session.execute("DROP TABLE IF EXISTS my_first_keyspace.case_insensitive;") # Starting from scratch every time
session.execute("CREATE TABLE IF NOT EXISTS case_insensitive (Capital int PRIMARY KEY, Letters text, Everywhere text);")
session.execute("DROP TABLE IF EXISTS my_first_keyspace.case_sensitive;") # Starting from scratch every time
session.execute("CREATE TABLE IF NOT EXISTS case_sensitive (\"Capital\" int PRIMARY KEY, \"Letters\" text, \"Everywhere\" text);")
<cassandra.cluster.ResultSet at 0x114121d00>
session.execute("INSERT INTO case_insensitive (Capital, Letters, Everywhere) VALUES (1, 'Tesla', 'Model S');")
<cassandra.cluster.ResultSet at 0x113602db0>
session.execute("INSERT INTO case_sensitive (Capital, Letters, Everywhere) VALUES (1, 'Tesla', 'Model S');")
---------------------------------------------------------------------------
InvalidRequest                            Traceback (most recent call last)
Cell In[8], line 1
----> 1 session.execute("INSERT INTO case_sensitive (Capital, Letters, Everywhere) VALUES (1, 'Tesla', 'Model S');")

File ~/miniforge3/envs/ind320_25/lib/python3.12/site-packages/cassandra/cluster.py:2677, in Session.execute(self, query, parameters, timeout, trace, custom_payload, execution_profile, paging_state, host, execute_as)
   2634 def execute(self, query, parameters=None, timeout=_NOT_SET, trace=False,
   2635             custom_payload=None, execution_profile=EXEC_PROFILE_DEFAULT,
   2636             paging_state=None, host=None, execute_as=None):
   2637     """
   2638     Execute the given query and synchronously wait for the response.
   2639 
   (...)   2674     on a DSE cluster.
   2675     """
-> 2677     return self.execute_async(query, parameters, trace, custom_payload, timeout, execution_profile, paging_state, host, execute_as).result()

File ~/miniforge3/envs/ind320_25/lib/python3.12/site-packages/cassandra/cluster.py:4956, in ResponseFuture.result(self)
   4954     return ResultSet(self, self._final_result)
   4955 else:
-> 4956     raise self._final_exception

InvalidRequest: Error from server: code=2200 [Invalid query] message="Undefined column name capital in table my_first_keyspace.case_sensitive"
session.execute("INSERT INTO case_sensitive (\"Capital\", \"Letters\", \"Everywhere\") VALUES (1, 'Tesla', 'Model S');")
<cassandra.cluster.ResultSet at 0x114123470>
# Query the data
rows = session.execute("SELECT * FROM case_insensitive;")
for i in rows:
    print(i)
rows = session.execute("SELECT * FROM case_sensitive;")
for i in rows:
    print(i)
Row(capital=1, everywhere='Model S', letters='Tesla')
Row(Capital=1, Everywhere='Model S', Letters='Tesla')

Asyncronous writing#

  • If your application is very data intensive, waiting for a response is not productive.

  • Writing asyncronously sends the data but does not pause for reply.

session.execute_async("INSERT INTO my_first_table (ind, company, model) VALUES (5, 'Volkswagen', 'ID.3');")
<ResponseFuture: query='<SimpleStatement query="INSERT INTO my_first_table (ind, company, model) VALUES (5, 'Volkswagen', 'ID.3');", consistency=Not Set>' request_id=63 result=(no result yet) exception=None coordinator_host=None>
# Query the data
rows = session.execute("SELECT * FROM my_first_table;")
for i in rows:
    print(i)
Row(ind=5, company='Volkswagen', model='ID.3')
Row(ind=1, company='Tesla', model='Model S')
Row(ind=2, company='Tesla', model='Model 3')
Row(ind=3, company='Polestar', model='3')
# More specific query
prepared_statement = session.prepare("SELECT * FROM my_first_table WHERE company=? ALLOW FILTERING;")
teslas = session.execute(prepared_statement, ['Tesla'])
for i in teslas:
    print(i)
Row(ind=1, company='Tesla', model='Model S')
Row(ind=2, company='Tesla', model='Model 3')

Cassandra filtering#

Cassandra is inherently a distributed production database. Selecting as above may require downloading all data from a node, then filtering based on the WHERE part (only PRIMARY KEYs are centrally known). Solutions:

  • If the table is small or most of the data will satisfy the query, add ALLOW FILTERING at the end of the query (not recommended if not known).

  • Or make sure the WHERE clause points to one of the keys (see below).

# Create a new table (observe keys)
session.execute("DROP TABLE IF EXISTS my_first_keyspace.car_table;")
session.execute("CREATE TABLE IF NOT EXISTS car_table (company text, model text, PRIMARY KEY(company, model));")
<cassandra.cluster.ResultSet at 0x114153fb0>
# Insert some data (combination of company and model must be unique)
session.execute("INSERT INTO car_table (company, model) VALUES ('Tesla', 'Model S');")
session.execute("INSERT INTO car_table (company, model) VALUES ('Tesla', 'Model 3');")
session.execute("INSERT INTO car_table (company, model) VALUES ('Polestar', '3');")
session.execute("INSERT INTO car_table (company, model) VALUES ('Volkswagen', 'ID.4');")
<cassandra.cluster.ResultSet at 0x114130cb0>
# More specific query now works
prepared_statement = session.prepare("SELECT * FROM car_table WHERE company=?;")
teslas = session.execute(prepared_statement, ['Tesla'])
for i in teslas:
    print(i)
Row(company='Tesla', model='Model 3')
Row(company='Tesla', model='Model S')

Partitions#

  • Cassandra databases are usually replicated over different nodes.

  • Data is stored in partitions (subsets) which have local copys.

  • The primary key, e.g., PRIMARY KEY(company, model), is used in partitioning.

    • The first part, e.g., company, is most important.

    • All cars from a company will be located together, aiming for quicker queries.

Unique IDs#

  • In MySQL one could use the attribute AUTO_INCREMENT on integer IDs to automatically make a new unique index when inserting data.

  • This would cause unreasonable overhead in a distributed database.

  • UUIDs are used instead.

    • Universally Unique Identifiers are typically 128-bit random bit sequences with extremely low probability of duplication.

    • Cassandra uses a timeuuid type to combine a timestamp and uuid in one.

# Create a new table (first time only)
session.set_keyspace('my_first_keyspace')
session.execute("DROP TABLE IF EXISTS my_first_keyspace.table_with_uuid;")
session.execute("CREATE TABLE IF NOT EXISTS table_with_uuid (id timeuuid PRIMARY KEY, company text, model text, price float);")
<cassandra.cluster.ResultSet at 0x113faa630>
session.execute("INSERT INTO table_with_uuid (id, company, model, price) VALUES (now(), 'Tesla', 'Model S', 20000.0);")
session.execute("INSERT INTO table_with_uuid (id, company, model, price) VALUES (now(), 'Tesla', 'Model S', 21000.0);")
session.execute("INSERT INTO table_with_uuid (id, company, model, price) VALUES (now(), 'Oldsmobile', 'Model 6C', 135000.0);")
<cassandra.cluster.ResultSet at 0x1143c4980>
from cassandra.util import datetime_from_uuid1

# Query the data
rows = session.execute("SELECT * FROM table_with_uuid;")
for i in rows:
    print(i)
    # Extract the timestamp from Cassandra's timeuuid
    print("Datetime:", datetime_from_uuid1(i.id))
Row(id=UUID('dec86c00-c90d-11f0-9457-b396685dd964'), company='Tesla', model='Model S', price=21000.0)
Datetime: 2025-11-24 08:16:25.792000
Row(id=UUID('dec8e130-c90d-11f0-9457-b396685dd964'), company='Oldsmobile', model='Model 6C', price=135000.0)
Datetime: 2025-11-24 08:16:25.795000
Row(id=UUID('dec81de0-c90d-11f0-9457-b396685dd964'), company='Tesla', model='Model S', price=20000.0)
Datetime: 2025-11-24 08:16:25.790000

JSON in Cassandra#

Read previously saved JSON file forecast.json to memory#

import json
with open('../3_APIs/downloads/forecast.json', 'r') as f:
    forecast = json.load(f)
# Inspect JSON file
forecast.__str__()
"{'cod': '200', 'message': 0, 'cnt': 40, 'list': [{'dt': 1763974800, 'main': {'temp': 285.4, 'feels_like': 283.78, 'temp_min': 285.4, 'temp_max': 293.92, 'pressure': 1024, 'sea_level': 1024, 'grnd_level': 1007, 'humidity': 42, 'temp_kf': -8.52}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.98, 'deg': 280, 'gust': 0.96}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 09:00:00'}, {'dt': 1763985600, 'main': {'temp': 288.83, 'feels_like': 287.5, 'temp_min': 288.83, 'temp_max': 295.68, 'pressure': 1024, 'sea_level': 1024, 'grnd_level': 1007, 'humidity': 40, 'temp_kf': -6.85}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.99, 'deg': 246, 'gust': 3.17}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 12:00:00'}, {'dt': 1763996400, 'main': {'temp': 292.79, 'feels_like': 291.78, 'temp_min': 292.79, 'temp_max': 296.49, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 37, 'temp_kf': -3.7}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.83, 'deg': 293, 'gust': 5.39}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 15:00:00'}, {'dt': 1764007200, 'main': {'temp': 294.08, 'feels_like': 293.48, 'temp_min': 294.08, 'temp_max': 294.08, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1004, 'humidity': 48, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.91, 'deg': 308, 'gust': 5.97}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-24 18:00:00'}, {'dt': 1764018000, 'main': {'temp': 293.59, 'feels_like': 292.89, 'temp_min': 293.59, 'temp_max': 293.59, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 46, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.92, 'deg': 336, 'gust': 5.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-24 21:00:00'}, {'dt': 1764028800, 'main': {'temp': 292.46, 'feels_like': 291.78, 'temp_min': 292.46, 'temp_max': 292.46, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1004, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.62, 'deg': 43, 'gust': 2.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 00:00:00'}, {'dt': 1764039600, 'main': {'temp': 291.31, 'feels_like': 290.67, 'temp_min': 291.31, 'temp_max': 291.31, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.65, 'deg': 106, 'gust': 1.99}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 03:00:00'}, {'dt': 1764050400, 'main': {'temp': 289.88, 'feels_like': 289.31, 'temp_min': 289.88, 'temp_max': 289.88, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.71, 'deg': 113, 'gust': 1.37}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 06:00:00'}, {'dt': 1764061200, 'main': {'temp': 291.01, 'feels_like': 290.5, 'temp_min': 291.01, 'temp_max': 291.01, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 63, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 38}, 'wind': {'speed': 0.99, 'deg': 177, 'gust': 0.94}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 09:00:00'}, {'dt': 1764072000, 'main': {'temp': 294.29, 'feels_like': 293.66, 'temp_min': 294.29, 'temp_max': 294.29, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 46, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 51}, 'wind': {'speed': 2.04, 'deg': 247, 'gust': 1.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 12:00:00'}, {'dt': 1764082800, 'main': {'temp': 294.69, 'feels_like': 294.02, 'temp_min': 294.69, 'temp_max': 294.69, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 43, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 100}, 'wind': {'speed': 1.5, 'deg': 276, 'gust': 2.84}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 15:00:00'}, {'dt': 1764093600, 'main': {'temp': 291.57, 'feels_like': 291.09, 'temp_min': 291.57, 'temp_max': 291.57, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 62, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'clouds': {'all': 65}, 'wind': {'speed': 1.35, 'deg': 120, 'gust': 1.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 18:00:00'}, {'dt': 1764104400, 'main': {'temp': 289.57, 'feels_like': 289.43, 'temp_min': 289.57, 'temp_max': 289.57, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 83, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 37}, 'wind': {'speed': 4.84, 'deg': 117, 'gust': 4.76}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 21:00:00'}, {'dt': 1764115200, 'main': {'temp': 288.57, 'feels_like': 288.49, 'temp_min': 288.57, 'temp_max': 288.57, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 89, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}], 'clouds': {'all': 21}, 'wind': {'speed': 5.34, 'deg': 108, 'gust': 6.28}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 00:00:00'}, {'dt': 1764126000, 'main': {'temp': 288.77, 'feels_like': 288.35, 'temp_min': 288.77, 'temp_max': 288.77, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 75, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 2.61, 'deg': 79, 'gust': 3.05}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 03:00:00'}, {'dt': 1764136800, 'main': {'temp': 288.42, 'feels_like': 287.91, 'temp_min': 288.42, 'temp_max': 288.42, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 73, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 0.96, 'deg': 352, 'gust': 2}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 06:00:00'}, {'dt': 1764147600, 'main': {'temp': 289.48, 'feels_like': 288.97, 'temp_min': 289.48, 'temp_max': 289.48, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 1.41, 'deg': 176, 'gust': 0.42}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 09:00:00'}, {'dt': 1764158400, 'main': {'temp': 291.03, 'feels_like': 290.57, 'temp_min': 291.03, 'temp_max': 291.03, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 3}, 'wind': {'speed': 1.62, 'deg': 210, 'gust': 1.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 12:00:00'}, {'dt': 1764169200, 'main': {'temp': 291.59, 'feels_like': 291.03, 'temp_min': 291.59, 'temp_max': 291.59, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.23, 'deg': 256, 'gust': 2.53}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 15:00:00'}, {'dt': 1764180000, 'main': {'temp': 290.37, 'feels_like': 289.92, 'temp_min': 290.37, 'temp_max': 290.37, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 68, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4, 'deg': 158, 'gust': 3.98}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 18:00:00'}, {'dt': 1764190800, 'main': {'temp': 289.98, 'feels_like': 289.55, 'temp_min': 289.98, 'temp_max': 289.98, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 4.61, 'deg': 123, 'gust': 5.2}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 21:00:00'}, {'dt': 1764201600, 'main': {'temp': 289.93, 'feels_like': 289.41, 'temp_min': 289.93, 'temp_max': 289.93, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 6}, 'wind': {'speed': 4.13, 'deg': 106, 'gust': 4.45}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 00:00:00'}, {'dt': 1764212400, 'main': {'temp': 289.32, 'feels_like': 288.74, 'temp_min': 289.32, 'temp_max': 289.32, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 49}, 'wind': {'speed': 0.23, 'deg': 268, 'gust': 0.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 03:00:00'}, {'dt': 1764223200, 'main': {'temp': 288.87, 'feels_like': 288.33, 'temp_min': 288.87, 'temp_max': 288.87, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 43}, 'wind': {'speed': 1.43, 'deg': 294, 'gust': 1.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 06:00:00'}, {'dt': 1764234000, 'main': {'temp': 291.03, 'feels_like': 290.52, 'temp_min': 291.03, 'temp_max': 291.03, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 63, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02d'}], 'clouds': {'all': 18}, 'wind': {'speed': 1.22, 'deg': 303, 'gust': 1.68}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 09:00:00'}, {'dt': 1764244800, 'main': {'temp': 292.46, 'feels_like': 291.96, 'temp_min': 292.46, 'temp_max': 292.46, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02d'}], 'clouds': {'all': 11}, 'wind': {'speed': 2.09, 'deg': 248, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 12:00:00'}, {'dt': 1764255600, 'main': {'temp': 293.03, 'feels_like': 292.56, 'temp_min': 293.03, 'temp_max': 293.03, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 4.26, 'deg': 277, 'gust': 3.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 15:00:00'}, {'dt': 1764266400, 'main': {'temp': 292.08, 'feels_like': 291.54, 'temp_min': 292.08, 'temp_max': 292.08, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.52, 'deg': 288, 'gust': 3.78}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 18:00:00'}, {'dt': 1764277200, 'main': {'temp': 290.94, 'feels_like': 290.52, 'temp_min': 290.94, 'temp_max': 290.94, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.38, 'deg': 158, 'gust': 2.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 21:00:00'}, {'dt': 1764288000, 'main': {'temp': 289.9, 'feels_like': 289.56, 'temp_min': 289.9, 'temp_max': 289.9, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.03, 'deg': 121, 'gust': 3.58}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 00:00:00'}, {'dt': 1764298800, 'main': {'temp': 290, 'feels_like': 289.41, 'temp_min': 290, 'temp_max': 290, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 64, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.8, 'deg': 91, 'gust': 4.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 03:00:00'}, {'dt': 1764309600, 'main': {'temp': 289.87, 'feels_like': 289.11, 'temp_min': 289.87, 'temp_max': 289.87, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.7, 'deg': 44, 'gust': 2.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 06:00:00'}, {'dt': 1764320400, 'main': {'temp': 291.58, 'feels_like': 290.89, 'temp_min': 291.58, 'temp_max': 291.58, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 54, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.09, 'deg': 345, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 09:00:00'}, {'dt': 1764331200, 'main': {'temp': 293.32, 'feels_like': 292.72, 'temp_min': 293.32, 'temp_max': 293.32, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.33, 'deg': 270, 'gust': 1.95}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 12:00:00'}, {'dt': 1764342000, 'main': {'temp': 293.22, 'feels_like': 292.61, 'temp_min': 293.22, 'temp_max': 293.22, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.69, 'deg': 288, 'gust': 3.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 15:00:00'}, {'dt': 1764352800, 'main': {'temp': 292.82, 'feels_like': 292.2, 'temp_min': 292.82, 'temp_max': 292.82, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 52, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.82, 'deg': 314, 'gust': 4.13}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 18:00:00'}, {'dt': 1764363600, 'main': {'temp': 291.71, 'feels_like': 291.14, 'temp_min': 291.71, 'temp_max': 291.71, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.84, 'deg': 115, 'gust': 2.36}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 21:00:00'}, {'dt': 1764374400, 'main': {'temp': 290.51, 'feels_like': 290.02, 'temp_min': 290.51, 'temp_max': 290.51, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 66, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.54, 'deg': 114, 'gust': 3.39}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 00:00:00'}, {'dt': 1764385200, 'main': {'temp': 290.02, 'feels_like': 289.25, 'temp_min': 290.02, 'temp_max': 290.02, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.86, 'deg': 94, 'gust': 2.82}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 03:00:00'}, {'dt': 1764396000, 'main': {'temp': 289.22, 'feels_like': 288.42, 'temp_min': 289.22, 'temp_max': 289.22, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.64, 'deg': 4, 'gust': 1.79}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 06:00:00'}], 'city': {'id': 2561668, 'name': 'Agadir', 'coord': {'lat': 30.4202, 'lon': -9.5982}, 'country': 'MA', 'population': 698310, 'timezone': 3600, 'sunrise': 1763968323, 'sunset': 1764005913}}"

Raw JSON#

  • A simple, but not very efficient way of storing JSON data is to treat it as a text and save it directly to the database.

  • More efficient, with regard to transfer, is to compress the JSON data to a blob first.

    • Compression is automatic.

# Create a new table which treats the whole JSON as a blob, using the city id and the first dt as keys
session.set_keyspace('my_first_keyspace')
session.execute("DROP TABLE IF EXISTS my_first_keyspace.forecast_table;")
session.execute("CREATE TABLE IF NOT EXISTS forecast_table (city_id int, dt int, forecast blob, PRIMARY KEY(city_id, dt));")
<cassandra.cluster.ResultSet at 0x114119e20>

Insert the forecast data into the table as text blob#

session.execute("INSERT INTO forecast_table (city_id, dt, forecast) VALUES (%s, %s, textAsBlob(%s));", (forecast['city']['id'], forecast['list'][0]['dt'], forecast.__str__()))
<cassandra.cluster.ResultSet at 0x1143c2c60>
# Query the data
forecast_rows = session.execute("SELECT * FROM forecast_table;")
print(forecast_rows.one()) # <- only one row
Row(city_id=2561668, dt=1763974800, forecast=b"{'cod': '200', 'message': 0, 'cnt': 40, 'list': [{'dt': 1763974800, 'main': {'temp': 285.4, 'feels_like': 283.78, 'temp_min': 285.4, 'temp_max': 293.92, 'pressure': 1024, 'sea_level': 1024, 'grnd_level': 1007, 'humidity': 42, 'temp_kf': -8.52}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.98, 'deg': 280, 'gust': 0.96}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 09:00:00'}, {'dt': 1763985600, 'main': {'temp': 288.83, 'feels_like': 287.5, 'temp_min': 288.83, 'temp_max': 295.68, 'pressure': 1024, 'sea_level': 1024, 'grnd_level': 1007, 'humidity': 40, 'temp_kf': -6.85}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.99, 'deg': 246, 'gust': 3.17}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 12:00:00'}, {'dt': 1763996400, 'main': {'temp': 292.79, 'feels_like': 291.78, 'temp_min': 292.79, 'temp_max': 296.49, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 37, 'temp_kf': -3.7}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.83, 'deg': 293, 'gust': 5.39}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 15:00:00'}, {'dt': 1764007200, 'main': {'temp': 294.08, 'feels_like': 293.48, 'temp_min': 294.08, 'temp_max': 294.08, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1004, 'humidity': 48, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.91, 'deg': 308, 'gust': 5.97}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-24 18:00:00'}, {'dt': 1764018000, 'main': {'temp': 293.59, 'feels_like': 292.89, 'temp_min': 293.59, 'temp_max': 293.59, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 46, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.92, 'deg': 336, 'gust': 5.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-24 21:00:00'}, {'dt': 1764028800, 'main': {'temp': 292.46, 'feels_like': 291.78, 'temp_min': 292.46, 'temp_max': 292.46, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1004, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.62, 'deg': 43, 'gust': 2.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 00:00:00'}, {'dt': 1764039600, 'main': {'temp': 291.31, 'feels_like': 290.67, 'temp_min': 291.31, 'temp_max': 291.31, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.65, 'deg': 106, 'gust': 1.99}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 03:00:00'}, {'dt': 1764050400, 'main': {'temp': 289.88, 'feels_like': 289.31, 'temp_min': 289.88, 'temp_max': 289.88, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.71, 'deg': 113, 'gust': 1.37}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 06:00:00'}, {'dt': 1764061200, 'main': {'temp': 291.01, 'feels_like': 290.5, 'temp_min': 291.01, 'temp_max': 291.01, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 63, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 38}, 'wind': {'speed': 0.99, 'deg': 177, 'gust': 0.94}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 09:00:00'}, {'dt': 1764072000, 'main': {'temp': 294.29, 'feels_like': 293.66, 'temp_min': 294.29, 'temp_max': 294.29, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 46, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 51}, 'wind': {'speed': 2.04, 'deg': 247, 'gust': 1.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 12:00:00'}, {'dt': 1764082800, 'main': {'temp': 294.69, 'feels_like': 294.02, 'temp_min': 294.69, 'temp_max': 294.69, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 43, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 100}, 'wind': {'speed': 1.5, 'deg': 276, 'gust': 2.84}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 15:00:00'}, {'dt': 1764093600, 'main': {'temp': 291.57, 'feels_like': 291.09, 'temp_min': 291.57, 'temp_max': 291.57, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 62, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'clouds': {'all': 65}, 'wind': {'speed': 1.35, 'deg': 120, 'gust': 1.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 18:00:00'}, {'dt': 1764104400, 'main': {'temp': 289.57, 'feels_like': 289.43, 'temp_min': 289.57, 'temp_max': 289.57, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 83, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 37}, 'wind': {'speed': 4.84, 'deg': 117, 'gust': 4.76}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 21:00:00'}, {'dt': 1764115200, 'main': {'temp': 288.57, 'feels_like': 288.49, 'temp_min': 288.57, 'temp_max': 288.57, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 89, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}], 'clouds': {'all': 21}, 'wind': {'speed': 5.34, 'deg': 108, 'gust': 6.28}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 00:00:00'}, {'dt': 1764126000, 'main': {'temp': 288.77, 'feels_like': 288.35, 'temp_min': 288.77, 'temp_max': 288.77, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 75, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 2.61, 'deg': 79, 'gust': 3.05}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 03:00:00'}, {'dt': 1764136800, 'main': {'temp': 288.42, 'feels_like': 287.91, 'temp_min': 288.42, 'temp_max': 288.42, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 73, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 0.96, 'deg': 352, 'gust': 2}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 06:00:00'}, {'dt': 1764147600, 'main': {'temp': 289.48, 'feels_like': 288.97, 'temp_min': 289.48, 'temp_max': 289.48, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 1.41, 'deg': 176, 'gust': 0.42}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 09:00:00'}, {'dt': 1764158400, 'main': {'temp': 291.03, 'feels_like': 290.57, 'temp_min': 291.03, 'temp_max': 291.03, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 3}, 'wind': {'speed': 1.62, 'deg': 210, 'gust': 1.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 12:00:00'}, {'dt': 1764169200, 'main': {'temp': 291.59, 'feels_like': 291.03, 'temp_min': 291.59, 'temp_max': 291.59, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.23, 'deg': 256, 'gust': 2.53}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 15:00:00'}, {'dt': 1764180000, 'main': {'temp': 290.37, 'feels_like': 289.92, 'temp_min': 290.37, 'temp_max': 290.37, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 68, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4, 'deg': 158, 'gust': 3.98}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 18:00:00'}, {'dt': 1764190800, 'main': {'temp': 289.98, 'feels_like': 289.55, 'temp_min': 289.98, 'temp_max': 289.98, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 4.61, 'deg': 123, 'gust': 5.2}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 21:00:00'}, {'dt': 1764201600, 'main': {'temp': 289.93, 'feels_like': 289.41, 'temp_min': 289.93, 'temp_max': 289.93, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 6}, 'wind': {'speed': 4.13, 'deg': 106, 'gust': 4.45}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 00:00:00'}, {'dt': 1764212400, 'main': {'temp': 289.32, 'feels_like': 288.74, 'temp_min': 289.32, 'temp_max': 289.32, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 49}, 'wind': {'speed': 0.23, 'deg': 268, 'gust': 0.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 03:00:00'}, {'dt': 1764223200, 'main': {'temp': 288.87, 'feels_like': 288.33, 'temp_min': 288.87, 'temp_max': 288.87, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 43}, 'wind': {'speed': 1.43, 'deg': 294, 'gust': 1.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 06:00:00'}, {'dt': 1764234000, 'main': {'temp': 291.03, 'feels_like': 290.52, 'temp_min': 291.03, 'temp_max': 291.03, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 63, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02d'}], 'clouds': {'all': 18}, 'wind': {'speed': 1.22, 'deg': 303, 'gust': 1.68}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 09:00:00'}, {'dt': 1764244800, 'main': {'temp': 292.46, 'feels_like': 291.96, 'temp_min': 292.46, 'temp_max': 292.46, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02d'}], 'clouds': {'all': 11}, 'wind': {'speed': 2.09, 'deg': 248, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 12:00:00'}, {'dt': 1764255600, 'main': {'temp': 293.03, 'feels_like': 292.56, 'temp_min': 293.03, 'temp_max': 293.03, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 4.26, 'deg': 277, 'gust': 3.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 15:00:00'}, {'dt': 1764266400, 'main': {'temp': 292.08, 'feels_like': 291.54, 'temp_min': 292.08, 'temp_max': 292.08, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.52, 'deg': 288, 'gust': 3.78}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 18:00:00'}, {'dt': 1764277200, 'main': {'temp': 290.94, 'feels_like': 290.52, 'temp_min': 290.94, 'temp_max': 290.94, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.38, 'deg': 158, 'gust': 2.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 21:00:00'}, {'dt': 1764288000, 'main': {'temp': 289.9, 'feels_like': 289.56, 'temp_min': 289.9, 'temp_max': 289.9, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.03, 'deg': 121, 'gust': 3.58}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 00:00:00'}, {'dt': 1764298800, 'main': {'temp': 290, 'feels_like': 289.41, 'temp_min': 290, 'temp_max': 290, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 64, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.8, 'deg': 91, 'gust': 4.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 03:00:00'}, {'dt': 1764309600, 'main': {'temp': 289.87, 'feels_like': 289.11, 'temp_min': 289.87, 'temp_max': 289.87, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.7, 'deg': 44, 'gust': 2.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 06:00:00'}, {'dt': 1764320400, 'main': {'temp': 291.58, 'feels_like': 290.89, 'temp_min': 291.58, 'temp_max': 291.58, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 54, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.09, 'deg': 345, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 09:00:00'}, {'dt': 1764331200, 'main': {'temp': 293.32, 'feels_like': 292.72, 'temp_min': 293.32, 'temp_max': 293.32, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.33, 'deg': 270, 'gust': 1.95}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 12:00:00'}, {'dt': 1764342000, 'main': {'temp': 293.22, 'feels_like': 292.61, 'temp_min': 293.22, 'temp_max': 293.22, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.69, 'deg': 288, 'gust': 3.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 15:00:00'}, {'dt': 1764352800, 'main': {'temp': 292.82, 'feels_like': 292.2, 'temp_min': 292.82, 'temp_max': 292.82, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 52, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.82, 'deg': 314, 'gust': 4.13}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 18:00:00'}, {'dt': 1764363600, 'main': {'temp': 291.71, 'feels_like': 291.14, 'temp_min': 291.71, 'temp_max': 291.71, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.84, 'deg': 115, 'gust': 2.36}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 21:00:00'}, {'dt': 1764374400, 'main': {'temp': 290.51, 'feels_like': 290.02, 'temp_min': 290.51, 'temp_max': 290.51, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 66, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.54, 'deg': 114, 'gust': 3.39}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 00:00:00'}, {'dt': 1764385200, 'main': {'temp': 290.02, 'feels_like': 289.25, 'temp_min': 290.02, 'temp_max': 290.02, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.86, 'deg': 94, 'gust': 2.82}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 03:00:00'}, {'dt': 1764396000, 'main': {'temp': 289.22, 'feels_like': 288.42, 'temp_min': 289.22, 'temp_max': 289.22, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.64, 'deg': 4, 'gust': 1.79}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 06:00:00'}], 'city': {'id': 2561668, 'name': 'Agadir', 'coord': {'lat': 30.4202, 'lon': -9.5982}, 'country': 'MA', 'population': 698310, 'timezone': 3600, 'sunrise': 1763968323, 'sunset': 1764005913}}")