Back

NoSQL Database and MongoDB FundamentalsBlur image

This article is a starting point for anyone who wants to understand NoSQL Database — from the core concepts, the JSON data format, and the types of NoSQL, through to working with MongoDB, a popular NoSQL that stores data as documents, with command examples covering create, insert, find, update, and delete, all the way to Aggregation. Each section ends with exercises.

1. What is NoSQL?#

NoSQL (short for “Not Only SQL”) is a family of database management systems designed to handle workloads that traditional Relational Database (RDBMS) struggle with — such as massive data volumes, frequently changing data structures, and distributed scaling (horizontal scaling).

The key strength of NoSQL is a flexible schema — you don’t have to define a rigid table structure up front. Each record can have different fields, so you can add new fields without running ALTER TABLE and naturally store nested or unbounded groups of data.

NoSQL Overview

NoSQL emerged to address four main areas:

Massive Data Volumes (Big Data)#

NoSQL is designed to distribute data across many servers (sharding). As data grows, you keep adding servers instead of buying a single increasingly expensive and powerful machine as with traditional RDBMS.

Frequently Changing Structure (Flexible Schema)#

In modern applications, the data structure often changes as development progresses. NoSQL lets you add or change fields without migrating an entire table — ideal for rapid prototyping and diverse data.

High Speed (Performance)#

Many types of NoSQL sacrifice some RDBMS capabilities (such as complex JOINs and strong transactions) in exchange for very high read/write speed — for example Redis, which stores data in memory.

Diverse Data Formats (Variety)#

NoSQL supports data in many formats, divided into 4 main types. Each stores data differently and suits different workloads:

  • Document — stores data as JSON-like documents that can be nested, and each document can have different fields. Suited for document/catalog data (e.g. MongoDB)
  • Key-Value — stores simple key-value pairs; very fast read/write. Suited for cache, session (e.g. Redis)
  • Wide Column — stores tables where each row can have different columns; supports massive data volumes. Suited for time-series, log (e.g. Cassandra)
  • Graph — stores nodes (node) and relationships (edge), focusing on relationships between data. Suited for social networks, recommendation systems (e.g. Neo4j)

Choosing the type that matches the nature of your data is key — a full comparison is covered in the NoSQL types section.

Summary: NoSQL doesn’t replace RDBMS, it complements it — choose based on the workload. For document/hierarchical data choose a Document DB; for fast cache choose Key-Value.

📝 Exercises

  1. Explain in your own words why RDBMS may be unsuitable for data whose structure changes frequently.
  2. Give an example of one application where you think NoSQL is a better fit than RDBMS, with reasoning.

2. What is JSON?#

Before understanding a Document Database like MongoDB, you need to understand JSON, because MongoDB stores data in a format very similar to JSON.

JSON (JavaScript Object Notation) is a text format for storing and exchanging data that is both easy for humans to read and convenient for machines to process. Even though the name contains “JavaScript”, JSON is language-independent — almost every language (Python, Java, PHP, Go, etc.) can read and write JSON.

JSON Structure

Basic Structure of JSON#

JSON consists of key-value pairs written inside curly braces { }. Keys must be text wrapped in double quotes " ", while values can be of several types.

{
  "name": "Wireless Mouse",
  "price": 590,
  "inStock": true,
  "rating": 4.5,
  "tags": ["wireless", "ergonomic"],
  "specs": {
    "color": "black",
    "battery": "AAA"
  },
  "discount": null
}
json

💡 Tip: We recommend writing JSON in VS Code, because it checks syntax instantly — e.g. a missing comma ,, the wrong quote style (such as single quotes instead of double quotes), or an unclosed brace. VS Code flags these with a red underline, making it much easier and faster to write correct JSON (if you don’t have it yet, download it free at code.visualstudio.com).

JSON Data Types#

TypeExampleDescription
String"Wireless Mouse"Text, always in double quotes
Number590, 4.5Numbers (both integers and decimals)
Booleantrue, falseTrue/false values (no quotes)
NullnullEmpty value (no quotes)
Array["wireless", "ergonomic"]A sequence of values, in [ ]
Object{ "color": "black" }Key-value pairs, in { } (can be nested)

What makes JSON powerful is that objects and arrays can be nested to unlimited depth — for example, specs is an object nested inside the larger object, and tags is an array of strings. This property lets JSON store complex (hierarchical) structures naturally.

Why JSON Matters#

  • Data exchange between systems — REST APIs send and receive data as JSON almost universally
  • Configuration files (config) — e.g. package.json, tsconfig.json
  • The foundation of Document Databases — MongoDB stores each record as a JSON-format document (actually BSON, a binary form of JSON, explained in the next section)

Tip — common pitfall: JSON keys must always be in double quotes ("name", not name), and the last pair must not have a trailing comma, otherwise it won’t parse.

Summary: JSON is a human- and machine-readable key-value text format that supports 6 data types and nested objects/arrays — the foundation of document databases and modern data exchange.

📝 Exercises

  1. Write the JSON for a product “Notebook”, price 35, category Stationery, with tags ["school", "office"] and inStock set to true.
  2. The following JSON has 2 mistakes — fix them: { name: 'Pen', price: 20, }
  3. What data types are the values 20 and true in JSON? (Answer without quotes.)

3. SQL vs NoSQL — Which to Choose#

Neither is absolutely better; each suits different workloads.

AspectRelational (SQL)NoSQL
StructureTables (table) of rows/columns with a rigid schemaDocument/key-value/column/graph, flexible schema
LanguageSQLDepends on the system (MongoDB uses JavaScript-style commands)
RelationshipsJOINs across tables, foreign keysUsually embeds related data together (embed)
ScalingUsually scales one machine to be more powerful (vertical)Distributes well across many machines (horizontal)
TransactionsStrong (ACID)Some support, but usually prioritizes speed/volume
ExamplesMySQL, PostgreSQL, OracleMongoDB, Redis, Cassandra
Suited forClearly structured data, complex relationships (banking, ERP)Diverse/nested data, high volume, frequently changing (content, IoT, real-time)

Summary: Choose SQL when data has a rigid structure and you need strong relationships/transactions. Choose NoSQL when you need flexibility, speed, and support for massive data volumes.

📝 Exercises

  1. Should a banking/deposit-account system use SQL or NoSQL? Why?
  2. An app that stores user click logs, millions of records per day — which should it use? Why?

4. Types of NoSQL Database#

NoSQL is divided into 4 main types, based on how data is stored.

NoSQL Types

TypeData FormatExamplesSuited for
DocumentJSON-like documentsMongoDB, CouchDBDocument/hierarchical data, content, catalog
Key-ValueSimple key-value pairsRedis, DynamoDBHigh-speed cache, session, counters
Wide ColumnTables where each row can have different columnsCassandra, HBaseTime-series data, logs, large volumes
GraphNodes and relationships (node & edge)Neo4j, ArangoDBSocial networks, recommendations, fraud detection

In this article we focus on Document Databases using MongoDB, because it is the most popular and approachable NoSQL, and it stores data in the JSON format that developers already know.

Summary: Choose the type by data nature — documents → Document, fast cache → Key-Value, high-volume time-based → Wide Column, complex relationships → Graph.

📝 Exercises

Match each workload to the most appropriate NoSQL type (Document / Key-Value / Wide Column / Graph).

  1. A friend-recommendation system on a social network.
  2. Temporarily storing user sessions that must be read very fast.
  3. Storing blog articles with comments nested inside.
  4. Storing temperature sensor data every minute, millions of points per day.

5. What is MongoDB?#

MongoDB is a popular Document Database that stores data as documents in a JSON-like format. Under the hood, MongoDB actually stores BSON (Binary JSON) — a binary form of JSON that makes searching and processing faster and supports additional data types such as Date and ObjectId.

Vocabulary — Comparison with RDBMS#

RDBMSMongoDBDescription
DatabaseDatabaseThe overall container (same)
TableCollectionHolds a group of documents
RowDocumentA single data record (JSON-like)
ColumnFieldA single key-value pair in a document
Primary Key_idUniquely identifies a document (auto-generated)
SELECT * FROM tabledb.collection.find()Retrieve data

RDBMS vs MongoDB

Key difference: in an RDBMS every row in a table must have the same columns, but in MongoDB each document in the same collection can have different fields — letting you store diverse data without changing the structure.

Summary: MongoDB stores data as BSON documents in a collection, comparable to a row in an RDBMS table, but more flexible because each document can have different fields.

📝 Exercises

  1. What are the MongoDB equivalents of table, row, column in RDBMS?
  2. In RDBMS we write SELECT * FROM products. Write the equivalent command in MongoDB.
  3. Why is the statement “each document in the same collection can have different fields” considered a strength of MongoDB?

6. Tools — MongoDB Community Server and Compass#

In the RDBMS article we used MongoDB Community Server (the free, fully-featured version) and then MongoDB Compass as the GUI for managing the database, similar to phpMyAdmin.

Download and Install MongoDB Community Server#

  1. Open the download page: https://www.mongodb.com/try/download/community
  2. Choose the latest version, your operating system (Windows / macOS / Linux), and the package format (e.g. .msi for Windows, .tgz/.zip for macOS/Linux).
  3. Download and run the installer — during installation on Windows, we recommend selecting “Install MongoDB as a Service” (runs automatically as a service on every startup) and you can install MongoDB Compass at the same time.

Once installed, MongoDB runs as a background service, listening for connections on port 27017 (the default).

MongoDB Community Server

MongoDB Compass — GUI for Managing the Database#

MongoDB Compass is a GUI for managing the database, similar to phpMyAdmin (it can be installed alongside Community Server, or downloaded separately from the MongoDB website). Open Compass and paste this connection string to connect to your local server:

mongodb://localhost:27017

You’ll then see databases, collections, and documents in a graphical form. You can insert, edit, delete, and run queries through the GUI without using the command line.

MongoDB Compass

The commands in the following sections are written in mongosh (MongoDB Shell) format, which is JavaScript — you can run them in either Compass or a terminal.

MongoDB shell


7. Database, Collection, Document — Create and Insert Data#

We’ll use a shop database named shop and a collection named products, pretending to store products with nested details (specs, tags) — a strength of document databases.

Create a Database and Insert a Single Document (insertOne)#

In mongosh, use the use command to switch to a database (it’s created automatically when you first add data), then use insertOne to add a document.

use shop

db.products.insertOne({
  name: "Wireless Mouse",
  price: 590,
  category: "Accessories",
  stock: 25,
  rating: 4.5,
  tags: ["wireless", "ergonomic"],
  specs: { color: "black", battery: "AAA" }
})
javascript

Insert Document

Notice that we didn’t have to create a table or define a schema up front — you just insert a document. MongoDB auto-generates an _id (primary key) if you don’t specify one. You can check the result as shown.

MongoDB Compass DB

Insert Multiple Documents (insertMany)#

db.products.insertMany([
  { name: "Mechanical Keyboard", price: 1890, category: "Accessories", stock: 12, rating: 4.8 },
  { name: "USB-C Hub", price: 750, category: "Accessories", stock: 0, rating: 4.2 },
  { name: "4K Monitor", price: 8500, category: "Displays", stock: 8, rating: 4.7 },
  { name: "Laptop Stand", price: 690, category: "Accessories", stock: 30, rating: 4.0 },
  { name: "Webcam HD", price: 1200, category: "Electronics", stock: 15, rating: 4.3 },
  { name: "Bluetooth Speaker", price: 1450, category: "Electronics", stock: 18, rating: 4.6 },
  { name: "Desk Lamp", price: 560, category: "Accessories", stock: 0, rating: 3.9 }
])
javascript

This sample data will be used for the query examples in the following sections — notice that some products (e.g. the first Wireless Mouse) have tags and specs, while others don’t. This is the schema flexibility that RDBMS struggles with.

Summary: use <db> selects a database; db.<collection>.insertOne() / insertMany() add documents. MongoDB auto-generates _id and accepts documents with different fields in the same collection.

📝 Exercises

  1. Write an insertOne command to add a product “USB Cable”, price 199, category Accessories, stock 50, rating 4.1.
  2. If you want to add 3 products in a single command, should you use insertOne or insertMany? Why?
  3. If you don’t specify the _id field yourself, what does MongoDB do?

8. find() — Retrieve Data#

MongoDB’s find() is the equivalent of SQL’s SELECT — it retrieves documents from a collection.

Retrieve All Documents#

db.products.find()
javascript

Equivalent to SELECT * FROM products in SQL. Add .pretty() if you’re in an older shell to make the output easier to read (modern mongosh formats it automatically).

find()

Retrieve Only the Fields You Want (Projection)#

Like selecting columns in SQL: set 1 for fields you want to show and 0 for fields you want to hide.

// Show only name and price (hide _id)
db.products.find({}, { name: 1, price: 1, _id: 0 })
javascript

Result:

nameprice
Wireless Mouse590
Mechanical Keyboard1890
USB-C Hub750
4K Monitor8500
Laptop Stand690
Webcam HD1200
Bluetooth Speaker1450
Desk Lamp560

Tip: _id is always returned; if you don’t want it, explicitly set _id: 0.

💡 You can also do this via the UI: Besides typing commands in mongosh (MongoDB Shell), you can also run find through MongoDB Compass, the GUI. In Compass there are two input fields that correspond to the two arguments of find():

  • Filter — enter the filter condition (the first argument); here {} means no filter, all documents.
  • Project — enter the projection to select fields (the second argument); here { name: 1, price: 1, _id: 0 }.

Once filled in, click the Find button. The result appears in an easy-to-read table. Below is running db.products.find({}, { name: 1, price: 1, _id: 0 }) via Compass — note that we put { name: 1, price: 1, _id: 0 } in the Project field so that only the name and price fields are shown and _id is hidden.

find projection in Compass

📝 Exercises

  1. Write a find that retrieves only the name and rating fields of every product (hide _id).
  2. What SQL statement returns the same result as db.products.find({})?
  3. To hide the _id field from the result, what value do you set in the projection?

9. Query Operators — Filter with Conditions#

Filtering in MongoDB is done by putting conditions inside find() and using query operators that start with $.

Exact Match (equivalent to WHERE column = value)#

db.products.find({ category: "Accessories" })
javascript

Returns only products in the Accessories category (5 items).

find category in Compass

Compare Numbers with gt,gt, lt, gte,gte, lte#

OperatorMeaningSQL equivalent
$gtgreater than>
$gtegreater than or equal>=
$ltless than<
$lteless than or equal<=
$nenot equal!=
$inin a given setIN (...)
// Price greater than 1000
db.products.find({ price: { $gt: 1000 } })
javascript

Result:

nameprice
Mechanical Keyboard1890
4K Monitor8500
Webcam HD1200
Bluetooth Speaker1450

find price $gt in Compass

Multiple Conditions — Default Is AND#

When you put multiple fields in one condition, MongoDB treats them as all having to be true (AND) automatically.

// Category Accessories and stock > 10
db.products.find({ category: "Accessories", stock: { $gt: 10 } })
javascript

Result:

namecategorystock
Wireless MouseAccessories25
Mechanical KeyboardAccessories12
Laptop StandAccessories30

(USB-C Hub and Desk Lamp are excluded because their stock is 0)

Use $or for “or”#

db.products.find({
  $or: [
    { category: "Displays" },
    { rating: { $gte: 4.6 } }
  ]
})
javascript

Returns products that are in the Displays category or have rating ≥ 4.6 — yielding 4K Monitor (Displays), Mechanical Keyboard (4.8), Bluetooth Speaker (4.6).

Use $in for a Set of Values#

// Category is Displays or Electronics
db.products.find({ category: { $in: ["Displays", "Electronics"] } })
javascript

Summary: MongoDB filters with conditions in find(); multiple fields are implicitly AND, $or for or, $gt/$lt/... for comparisons, and $in for a set of values.

📝 Exercises

  1. Write a query to find products with rating ≥ 4.5 and stock > 0 (multiple fields in one condition).
  2. Write a query to find products in category Displays or Electronics using $in.
  3. Write a query to find products with price < 700 or rating > 4.7 using $or.
  4. Which operator would you use to query “products whose stock is not equal to 0”?

10. sort, limit, and count#

Sorting — sort()#

Use 1 for ascending (ASC) and -1 for descending (DESC), like ORDER BY in SQL.

// Sort by price descending
db.products.find().sort({ price: -1 })
javascript

Result (top portion):

nameprice
4K Monitor8500
Mechanical Keyboard1890
Bluetooth Speaker1450
Webcam HD1200

Limit the Count — limit()#

Use it with sort() to fetch the “top N”, like LIMIT in SQL.

// The 3 most expensive products
db.products.find().sort({ price: -1 }).limit(3)
javascript

Result: 4K Monitor, Mechanical Keyboard, Bluetooth Speaker

Count — countDocuments()#

// Count products in Accessories with stock > 10
db.products.countDocuments({ category: "Accessories", stock: { $gt: 10 } })
javascript

Result: 3

Summary: sort() orders results (1/-1), limit() restricts the count, and countDocuments() counts documents matching a condition.

📝 Exercises

  1. Write a query to find the 3 highest-rated products (using sort + limit).
  2. Write a command to count products with stock = 0 (what is the expected result?).
  3. To sort products by price ascending, do you use 1 or -1 in sort?

11. Aggregation — Group and Summarize Data#

MongoDB’s Aggregation Pipeline is the equivalent of SQL’s GROUP BY, including SUM, AVG, and COUNT calculations, by chaining multiple stages together.

Average Price by Category — $group#

db.products.aggregate([
  {
    $group: {
      _id: "$category",
      avgPrice: { $avg: "$price" },
      count: { $sum: 1 }
    }
  }
])
javascript

Explanation of each part:

  • $group — groups documents by a specified field
  • _id: "$category" — the field to group by (prefix the field name with $)
  • avgPrice: { $avg: "$price" } — computes the average price of each group, stored under the name avgPrice
  • count: { $sum: 1 } — counts the documents in each group (adding 1 per row)

Result:

_id (category)avgPricecount
Accessories8965
Displays85001
Electronics13252

Aggregation

Common Aggregation Operators#

OperatorMeaningSQL equivalent
$sumsum / count ($sum: 1)SUM() / COUNT()
$avgaverageAVG()
$min / $maxlowest/highest valueMIN() / MAX()
// Find the highest and lowest price in each category
db.products.aggregate([
  {
    $group: {
      _id: "$category",
      maxPrice: { $max: "$price" },
      minPrice: { $min: "$price" }
    }
  }
])
javascript

Summary: The Aggregation Pipeline groups and summarizes data across multiple stages, starting with $group and computing with $avg, $sum, $min, $max — the equivalent of SQL’s GROUP BY and aggregate functions.

📝 Exercises

  1. Write an aggregation to find the total (sum) price of products by category, naming the result totalPrice.
  2. Write an aggregation to find the average rating of all products without grouping by category (hint: use _id: null).
  3. In $group, why must field names be prefixed with $, e.g. "$price"?

12. update — Modify Data#

updateOne() modifies the first document matching a condition; updateMany() modifies every matching document. Use update operators prefixed with $.

Modify a Field with $set#

// Change the price of Wireless Mouse
db.products.updateOne(
  { name: "Wireless Mouse" },
  { $set: { price: 690 } }
)
javascript
  • First argument { name: "Wireless Mouse" } — the condition to find the document (like WHERE)
  • Second argument { $set: { price: 690 } } — what to change ($set sets a new value)

Increment a Number with $inc#

// Increase stock of all Accessories products by 5
db.products.updateMany(
  { category: "Accessories" },
  { $inc: { stock: 5 } }
)
javascript

$inc (increment) increases a numeric value by the given amount; use a negative value to decrease.

Add an Element to an Array with $push#

// Add the "sale" tag to Wireless Mouse
db.products.updateOne(
  { name: "Wireless Mouse" },
  { $push: { tags: "sale" } }
)
javascript
OperatorAction
$setSets a field (creates it if it doesn’t exist)
$incIncrements/decrements a numeric value
$pushAppends an element to an array
$unsetRemoves a field from a document

⚠️ Warning: Don’t forget the update operator ($set, $inc, etc.). If you write { price: 690 } directly, MongoDB will try to replace the entire document with { price: 690 }, which is usually not what you intend.

Summary: updateOne/updateMany modify documents — the first argument is the condition, the second uses an operator such as $set, $inc, $push.

📝 Exercises

  1. Write an updateOne to change the rating of “Desk Lamp” to 4.2 (using $set).
  2. Write an updateMany to decrease the stock of Electronics products by 3 (using $inc with a negative value).
  3. Write a command to add the tag “new” to “Webcam HD” (using $push).
  4. If you write db.products.updateOne({ name: "Desk Lamp" }, { price: 700 }) without $set, what happens?

13. delete — Remove Data#

deleteOne() removes the first document matching a condition; deleteMany() removes every matching document — like DELETE in SQL.

Delete with a Condition#

// Delete products whose stock is 0 (out of stock)
db.products.deleteMany({ stock: 0 })
javascript

This command removes USB-C Hub and Desk Lamp (both have stock: 0).

Delete a Single Document#

db.products.deleteOne({ name: "4K Monitor" })
javascript

⚠️ Important warning: If you pass an empty condition {}, it deletes all documents in the collection!

db.products.deleteMany({})  // deletes every document in products
javascript

Always double-check the condition before running deleteMany.

Summary: deleteOne/deleteMany remove documents by condition. Beware the empty condition {} which deletes everything.

📝 Exercises

  1. Write a deleteMany to remove products with stock = 0 — how many will be deleted?
  2. Write a command to delete “4K Monitor”, a single document.
  3. What does db.products.deleteMany({}) do? What should you watch out for?

14. Schema Design — Embed vs Reference#

In RDBMS we split data into many tables and connect them with foreign keys + JOINs. In MongoDB there are two main design approaches: Embed and Reference.

Best when data is always read together and is limited in quantity (one-to-few) — e.g. product reviews embedded inside the product document.

{
  name: "Wireless Mouse",
  price: 590,
  reviews: [
    { user: "Somchai", stars: 5, comment: "Very good" },
    { user: "Nida", stars: 4, comment: "Easy to use" }
  ]
}
javascript

Pros: fetch all the data in a single query, no JOIN needed, fast reads.

Reference — Store an ID That Points Across Collections#

Best when data is large, reused, or changes often (one-to-many, many-to-many) — e.g. an order that references users and products in separate collections.

// collection: orders
{
  orderNo: "ORD-001",
  userId: ObjectId("..."),       // references users
  items: [
    { productId: ObjectId("..."), qty: 2 }  // references products
  ]
}
javascript

Embed vs Reference

Which to Choose?#

CriterionRecommendation
Little data, always read together, rarely changesEmbed
Lots of data, reused across documents, changes oftenReference
One-to-few relationshipEmbed
Many-to-many relationshipReference

Summary: MongoDB lets you choose to embed or reference based on the data. Embed when read together and few in number; reference when large or reused. Good design is the heart of using NoSQL efficiently.

📝 Exercises

  1. Should a user’s “shipping address” (house number, street, subdistrict, district, province) be embedded in the user document or reference a separate collection? Why?
  2. Should “comments” on a forum where each post has thousands of comments be embedded or referenced? Why?
  3. Give one example of a one-to-few relationship suited to embedding.

15. Example Prompts for Generating Queries with Generative AI#

You can use ChatGPT or Claude to help build MongoDB queries by giving clear context.

Prompt 1 — Search and Filter:

collection products has fields name, price, category, stock, rating Write a MongoDB query to find Electronics products priced below 1500 with stock greater than 10, sorted by price ascending.

Prompt 2 — Aggregation:

From collection products with fields category and price Write a MongoDB aggregation to find the average price and number of products by category, sorted by average price descending.

Prompt 3 — Generate Sample Data:

Generate MongoDB insertMany statements for a products collection with fields name, price, category, stock, and rating, using 10 realistic sample products across categories Electronics, Accessories, and Displays.

Tip: The more clearly you specify the collection name, fields, and what you want, the more accurate the resulting query.


16. Summary#

This article covers NoSQL Database and MongoDB fundamentals, from concepts to practice:

  • NoSQL — databases with a flexible schema that support massive data volumes and diverse formats
  • JSON — a key-value text format that is the foundation of document databases
  • NoSQL types — Document, Key-Value, Wide Column, Graph, each suited to different workloads
  • MongoDB — a document database that stores data as BSON, mapping table/row to collection/document
  • Community Server & Compass — install the database locally and manage it through a GUI
  • CRUDinsertOne/insertMany, find, updateOne/updateMany, deleteOne/deleteMany
  • Query operators$gt, $lt, $or, $in for filtering
  • Aggregation$group with $avg, $sum, $max, $min for summarizing
  • Schema design — Embed vs Reference, chosen by the nature of the relationships

Each section has 📝 Exercises for practice. Once you understand these fundamentals, you’ll be able to design and manage NoSQL databases for applications that demand flexibility and speed, and build toward API development, real-time applications, and systems that handle massive data volumes.

NoSQL Database and MongoDB Fundamentals
Author กานต์ ยงศิริวิทย์ / Karn Yongsiriwit
Published at July 11, 2026

Loading comments...

Comments 0