LunaQL - initial draft
Updated: Fri Jul 16 2021
Introduction
LunaQL or Luna Query, is a Query Language for MoanaDB
(a NoSQL database). It is designed to handle document reads, writes, and deletes efficiently. It also allows MoanaDB
(a NoSQL database) to be treated as relational database.
This initial draft will outline how queries are written and handled by MoanaDB
.
Overview
LunaQL is a query language designed to work on both frontends and backends.
For example, lets say your frontend application has a "feed" view, you can have something like this:
query({ from({ /** * Fetch the most recent 20 posts. * * @var post[] */ posts: { sort: "desc"; with: { /** * Get the authors of the posts. * * @var user[] */ users: { select: ["_id", "name"]; where: ["user_id", "=", "$._id"]; }; }; limit: 20; do: "fetch"; }; });});
Resuls
[ { _id: 1, user_id: 1, body: "This is a post", created_at: "2021-07-16 20:50:08", updated_at: "2021-07-16 20:50:08", users: [ { _id: 1, name: "Donald Pakkies" } ] }, ...
This query will fetch the first 20 posts with the names of the users (authors) who created those posts from the database, and will also order the posts by most recent.
Now, if we need we need to make changes to our database. We can instruct our backend to make the changes we want:
query({ from({ /** * Create a new post and assign it to user 1. */ posts: { data: { user_id: 1; body: "This is a cool post"; }; do: "put"; }; });});
Resuls
{ _id: 2, user_id: 1, body: "This is a cool post", created_at: "2021-07-16 20:51:32", updated_at: "2021-07-16 20:51:32",}
This query will insert a new document in the posts collection.
Its important to note that, you will need to have a
read-only
MoanaDB
key for your frontend and afull-access
MoanaDB
key for your backend. This will just ensure that your frontend can't make changes to your database.
Writing Queries
LunaQL queries are self-describing and self-documenting.
A simple query looks a lot like this:
query({ from({ users: { do: "fetch"; }; });});
This query tells MoanaDB
to fetch users
from the users
collection.
MoanaDB
treats do: "fetch"
as an action.
Conditions
Now that we know how to write a simple query, we can start looking at how we can write conditional queries.
LunaQL provides, the where
expression which allows us to write or provide our conditions:
query({ from({ users: { where: [ [ "age", ">", 17 ] ]; do: "fetch"; }; });});
Notice how we passed our condition inside an array. This is because we can provide multiple conditions:
query({ from({ users: { where: [ [ "name", "like", "Donald" ], [ "email", "like", "@gmail.com" ] ]; do: "fetch"; }; });});
We can also group our conditions:
query({ from({ users: { where: [ [ "name", "like", "Donald" ], [ "email", "like", "@gmail.com" ] ]; orWhere: [ [ "name", "like", "Luna" ], [ "email", "like", "@gmail.com" ] ]; do: "fetch"; }; });});
This query will fetch gmail users who have a name that contain "Donald" or "Luna".
We can also limit our results:
query({ from({ users: { where: [ [ "name", "like", "Donald" ], [ "email", "like", "@gmail.com" ] ]; limit: 1; do: "fetch"; }; });});
This query will only fetch one user.
Supported operators
The where
and orWhere
expressions support the following operators:
Equals (case insensitive / type safe):
query({ from({ users: { where: [ [ "age", "=", "20" ] ]; do: "fetch"; }; });});
Equals (case sensitive / type unsafe):
query({ from({ users: { where: [ [ "age", "===", 20 ] ]; do: "fetch"; }; });});
Not Equals (case insensitive / type safe):
query({ from({ users: { where: [ [ "age", "!=", "20" ] ]; do: "fetch"; }; });});
Not Equals (case sensitive / type unsafe):
query({ from({ users: { where: [ [ "age", "!==", "20" ] ]; do: "fetch"; }; });});
Greater Than:
query({ from({ users: { where: [ [ "age", ">", 20 ] ]; do: "fetch"; }; });});
Greater Than or Equals:
query({ from({ users: { where: [ [ "age", ">=", 20 ] ]; do: "fetch"; }; });});
Less Than:
query({ from({ users: { where: [ [ "age", "<", 20 ] ]; do: "fetch"; }; });});
Less Than or Equals:
query({ from({ users: { where: [ [ "age", "<=", 20 ] ]; do: "fetch"; }; });});
Like:
query({ from({ users: { where: [ [ "name", "like", "Luna" ] ]; do: "fetch"; }; });});
Not Like:
query({ from({ users: { where: [ [ "name", "not like", "Luna" ] ]; do: "fetch"; }; });});
In Array:
query({ from({ users: { where: [ [ "name", "in", [ "Donald, "Luna" ] ] ]; do: "fetch"; }; });});
Not In Array:
query({ from({ users: { where: [ [ "name", "not in", [ "Donald, "Luna" ] ] ]; do: "fetch"; }; });});
Contains:
query({ from({ users: { where: [ [ "name", "contains", "Luna" ] ]; do: "fetch"; }; });});
Contains:
query({ from({ users: { where: [ [ "name", "not contains", "Don" ] ]; do: "fetch"; }; });});
Between:
query({ from({ users: { where: [ [ "created_at", "between", [ 2020, 2021 ] ] ]; do: "fetch"; }; });});
Not Between:
query({ from({ users: { where: [ [ "created_at", "not between", [ 2020, 2021 ] ] ]; do: "fetch"; }; });});
Actions
Here's a list of actions we can invoke:
count
- Counts documents.delete
- Deletes documents.fetch
- Fetches documents.put
- Creates documents.update
- Updates documents.
Examples
Count documents:
query({ from({ posts: { do: "count"; }; });});
Delete documents:
query({ from({ posts: { where: [ [ "_id:", "=", 1 ] ]; do: "delete"; }; });});
Fetch documents:
query({ from({ posts: { do: "fetch"; }; });});
Create documents:
query({ from({ users: { data: { name: "Donald Pakkies; email: "donaldpakkies@gmail.com"; }; do: "put"; }; });});
Update documents:
query({ from({ users: { where: [ [ "_id", "=", 1 ] ]; data: { name: "Donald; }; do: "update"; }; });});
Relationships
LunaQL has the ability to join 2 documents from 2 different collections:
query({ from({ users: { with: { posts: { where: [ "user_id", "=", "$._id" ]; }; }; do: "fetch"; }; });})
$
represents the parent collection (users
in this query).
This query will fetch all users with their posts. This is done by using the with
expression which expects a where
condition.
Thanks for reading, i will keep updating this.