DEV Community

Cover image for Using MySQL as a NoSQL Document Store - Introduction
Prasad Saya
Prasad Saya

Posted on

Using MySQL as a NoSQL Document Store - Introduction

MySQL is mostly known to be SQL database server. To store JSON data it has table columns of JSON data type. In addition, MySQL can be used as a document store and perform "NoSQL" operations. This post is about getting an introduction to this feature.

Specialized clients can perform SQL as well as CRUD operations on the document database. These clients are MySQL Shell and the MySQL Connectors. MySQL Shell is an interactive interface using JavaScript, Python or SQL modes. Connectors are used for developing applications using programming languages like Java, NodeJS, Python, C++, etc.

The main components for using MySQL as document store are X Plugin, X Protocol and X DevAPI.

X Plugin enables MySQL Server to communicate with these clients. These clients communicate with a MySQL Server using X Protocol. X DevAPI is used to develop client applications.

In this post we will use MySQL Shell to perform some operations on document store.

To try the code samples you will require an installation of MySQL Server and MySQL Shell on your computer. I had used MySQL Server and Shell with the version 8.0.34 on a Windows 11 computer.


MySQL Server

In case you need to install MySQL newly, there are instructions from the GitHub Gist: Basic steps to install MySQL 8 Server and create database data

Verify X Plugin is installed on the server using the following command from the mysql client:

mysql> SHOW plugins;
+-------------------+----------+
| Name              | Status   | ...
+-------------------+----------+
...
| mysqlx            | ACTIVE   | ...
...
Enter fullscreen mode Exit fullscreen mode

MySQL Shell

The next step is to install MySQL Shell software.

Requirement

Make sure you have the Visual C++ Redistributable for Visual Studio 2015. You can verify if already installed from Windows Control Panel -> Programs and Features.

Download and install

Visit the MySQL website and navigate to MySQL Community Downloads -> MySQL Shell

Starting and connecting to MySQL Server

Make sure your MySQL Server is started and running. We are using the Windows command prompt.

From the MySQL Shell install directory:

command prompt>> bin\mysqlsh
MySQL Shell 8.0.34
...
mysql-js>
Enter fullscreen mode Exit fullscreen mode

By default the shell starts in JS mode.

Note the MySQL Shell is not connected to the server. The connection to MySQL Server instance is handled by a session object.

To see information about the current global session:

mysql-js> shell.status()
MySQL Shell version 8.0.34
Not Connected.
Enter fullscreen mode Exit fullscreen mode

Connect to the server

mysql-js> \connect mysqlx://user_name@localhost:33060
mysql-js> shell.status()
MySQL Shell version 8.0.34
Connection Id: 28
Server version: 8.0.34 MySQL Community Server - GPL
Current user: user@localhost
Protocol version: X protocol
...
Enter fullscreen mode Exit fullscreen mode

NOTE: You can also start mysqlsh with a session from command prompt:
command prompt>> mysqlsh mysqlx://user_name@localhost:33060

Exit the MySQL Shell.

mysql-js> \quit
Enter fullscreen mode Exit fullscreen mode

MySQL Shell Global Objects

MySQL Shell includes a number of built-in global objects that exist in both JavaScript and Python modes. Some useful ones are the session, db and shell. Their usage is shown in this post's code samples.

  • db is available when the global session was established using an X Protocol connection with a default database specified, and represents that schema.
  • session is available when a global session is established, and represents the global session.
  • shell provides access to various MySQL Shell functions.

Document Store

The main concepts are the JSON Document, Collection and CRUD (Create, Read, Update and Delete) operations.

A JSON document is a data structure composed of key-value pairs. This is the fundamental structure for using MySQL as document store. The document key-value values can be simple data types, such as integers or strings, but can also contain other documents, arrays, and lists of documents.

A JSON document is represented internally using the MySQL binary JSON object, through the MySQL JSON datatype.

Note that a document does not need to have a predefined structure. A collection can contain multiple documents with different structures.

A collection is a container that is used to store JSON documents in a MySQL database. You can perform CRUD operations against a collection of documents.

Each collection has a unique name and exists within a schema. Schema is equivalent to a database.


Collection - create, list and delete

Start MySQL Shell and connect to the server.

mysql-js> db                    /* this returns empty, as no existing schema is specified with the connection */
mysql-js> \use <existing_db>    /* specify a default schema, and it must be an existing schema */

mysql-js> \sql                  /* change the mode to sql */
                                /* check what databases exist, and can create one if needed */
                                /* in SQL mode you can use mysql SQL commands */
SQL> SHOW DATABASES;        -- list databases
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
...

SQL> CREATE DATABASE x_db;  -- create a new database
SQL> \js                    -- return to JavaScript mode

mysql-js> \use x_db;                    // make the newly created database as default database
mysql-js> db
Default schema `x_db` accessible through db.

mysql-js> db.getCollections()           // list collections in the default schema
[]
mysql-js> db.createCollection("users")  // create a new collection in the database
<Collection:users>
mysql-js> db.getCollections()
[
    <Collection:users>
]
mysql-js> db.dropCollection("users")    // delete the collection
Enter fullscreen mode Exit fullscreen mode

CRUD Operations on the Collection


CREATE

mysql-js> db.users.add({ name: "Jerry", age: 10 })  // insert a new document into the collection

mysql-js> db.users.find()                           // list all documents in the collection
{
    "_id": "000065017f9d0000000000000001",
    "age": 10,
    "name": "Jerry"
}
Enter fullscreen mode Exit fullscreen mode

Note the _id field. The _id field is a mandatory field for each document in the collection. This acts as a primary key. It is (and must be) a unique value within the collection. Its value is set by the server (when not provided) or can be provided. It is immutable (cannot be changed or deleted).

mysql-js> db.users.add({ name: "Jill", age: 12 }, { name: "Jim", age: 9 })  // insert two documents
mysql-js> db.users.add({ name: "Jack", age: 8, city: "New York" })          // insert document with different structure
mysql-js> db.users.add({ _id: "myid_99", name: "Jamie", age: 11 })          // insert a document with a provided _id

mysql-js> db.users.count()
5
Enter fullscreen mode Exit fullscreen mode

READ

db.users.find("name = 'Jack'")              // search for specific document(s) using a filter
db.users.find("name LIKE 'Ji%' AND age < 10")
Enter fullscreen mode Exit fullscreen mode

The following operators can be used to specify search conditions: OR (||), AND (&&), XOR, IS, NOT, BETWEEN, IN, LIKE, !=, <>, >, >=, <, <=, &, |, <<, >>, +, -, *, /, ~, and %.

The syntax for expressions that form a search condition is the same as that of traditional MySQL. Note that you must enclose all expressions in quotes.

db.users.find().fields([ "_id", "name" ])   // project specific field(s) in output

db.users.find().fields([ "_id" ]).limit(1)  // read only one document
db.users.find().sort("age desc")            // read documents sorted by age descending
Enter fullscreen mode Exit fullscreen mode

UPDATE

db.users.modify("name = 'Jack'").set("city", "Chicago")             // change Jack's city as Chicago
db.users.modify("name = 'Jack'").unset("city")                      // remove the city field for Jack
db.users.modify("true").set("info", { state: "NY", favs: [] })      // update all documents with a new object field

db.users.modify("name = 'Jack'").arrayAppend("$.info.favs", "Red")  // add a value to the array field for Jack
db.users.modify("name = 'Jim'").set("info.city", "LA")              // update an object field
Enter fullscreen mode Exit fullscreen mode

DELETE

db.users.remove("name = 'Jill'");   // remove documents with a condition
db.users.remove("true").limit(1);   // remove one document
db.users.remove("true");            // remove all documents in collection
Enter fullscreen mode Exit fullscreen mode

Other Notable Features

  • You can create indexes on the document field(s).

  • You can define document validation i.e., verify documents against a JSON schema. This enables that all documents in the collection have a certain predefined structure.

  • There are operations on the collections which work with one document only. These are getOne, replaceOne, addOrReplaceOne and removeOne. All these methods take the string _id value as a parameter. For example, db.users.getOne("myid_99")


Working with Result Sets

All CRUD operations discussed above return a result set object with various attributes.

The add, modify and remove operations return a Result class object. This has details about number of rows affected by the operation, auto generated document IDs, warnings, etc., depending on the operation.

The find operation returns a DocResult class object. This is the The fetched data set.

Using the MySQL Shell in JS mode:

// Add a new document, and verify the result object

var r = db.users.add({ name: "John" }).execute()
r.getAffectedItemsCount()   // 1
r.getGeneratedIds()         // [ "000064f987110000000000000004" ]
Enter fullscreen mode Exit fullscreen mode

Note the execute method. This is to be specified when using the JavaScript code in MySQL Shell. In interactive mode this is automatic.

// Get all documents and print one document at a time:

var r = db.users.find().execute()
var doc
while (doc = r.fetchOne()) {
    print(doc)
}

// Get all documents as a list object:

var r = db.users.find().execute()
var list = r.fetchAll()
print(list)
print(list.length)
for (index in list) { 
    print(list[index].name) 
}

// And, use the list as JS array:
[...list].map(e => e.name).forEach(e => print(e, " "))
Enter fullscreen mode Exit fullscreen mode

Working with Relational Tables

Perform CRUD operations on MySQL relational tables in the MySQL Shell.

First, the table(s) to work with need to be created in MySQL Shell's SQL mode or in mysql client. For example, we work with cities table in the current schema.

SQL> CREATE TABLE cities (city VARCHAR(20) NOT NULL PRIMARY KEY, state VARCHAR(2) NOT NULL);
Enter fullscreen mode Exit fullscreen mode

MySQL Shell JS mode:

db.getTables()  // list all tables in the current database

db.cities.insert([ "city", "state" ]).values("New York", "NY")
db.cities.insert().values("Los Angeles", "CA")

db.cities.select()
db.cities.select(["city"]).where("state = 'NY'").orderBy("city asc")

db.cities.update().set("city", "Buffalo").where("city = 'New York'")

db.cities.delete().where("city = 'Buffalo'")
Enter fullscreen mode Exit fullscreen mode

The above CRUD operation functions have the same results as when used with SQL.

Like the result set for the collections's find, the table's select also returns a Result object. You can use the fetchOne and fetchAll methods on it.

In addition, you can also run SQL operations using the session object. For example, session.sql("SELECT * FROM cities"). This returns a SqlResult class object.


Conclusion

The X DevAPI allows working with JSON data and relational table data within the same application program using the "NoSQL" syntax. This has advantages from the flexibility of the JSON document and the strict rules used with relational data.

You can also use the db.getCollectionAsTable("collection_name") to use the collection as table and use the table CRUD syntax on it. For example, db.getCollectionAsTable("users").select().


References


Top comments (0)