Introduction
LunaQL's self describing queries make it easy to read, write and delete your data. With LunaQL you can easily group queries and call them on a single call, making your queries quick and effecient. You can also run nested queries, and go as deep as you want.
Initializing LunaQL
To get started, we will need to initialize a new LunaQL project locally:
lql initThis will not create a LunaQL database environment yet. This will only prepare your application for LunaQL. To create a LunaQL database environment, you can visit https://lunaql.com (opens in a new tab).
Generating Query Files
Now that you've initialized your LunaQL project, let's create a query file using the lql cli:
lql make:query Users/AllIf we open queries/users/all.lunaql, we will see:
query({
from({
users: {
//
};
});
});The location of your newely created Users/All query, might be different based on your LunaQL's initial setup.
Running Queries
If you've already created a database environment, you would have received an endpoint and an api token which you can use to access your database.
Retrieving Documents
When retrieving documents, you should always use a POST method:
POST https://<database-id>.<region>.lunaql.com
Authorization: Bearer <api-token>
Content-Type: application/lunaql
query({
from({
users: {
//
};
});
});Inserting Documents
When inserting documents, you should always use a PUT method and prefix your endpoint with the name of the collection you're inserting documents to:
PUT https://<database-id>.<region>.lunaql.com/<collection>
Authorization: Bearer <api-token>
Content-Type: application/lunaql
query({
@prop data = {
first_name: "Luna";
last_name: "Pakkies";
};
});Understanding LunaQL Queries
Let's take a few moments to understand our query file. We'll use the above query as an example:
query({
from({
users: {
//
};
});
});As you can see, this query open up with a query clause. This clause is a way of signaling that you will be writing query and sending it off to your database for evaluation.
Next, we have a from clause. This is where we can define the collections we want to query from. In this example, we have users inside the from clause, this means we will making a query request to the users collection.
Since we can the from clause is an object, we can pass more than 1 collection to query from:
query({
from({
users: {
//
};
posts: {
//
};
});
});Now, this query will run 2 queries in parallel and return the results at the same time.
What happens if we want to run 2 queries both on the same collections? Well for this, we can create named queries. To do this, we can use the collections clause:
query({
from({
collections({
users: {
users: {
// condition that returns users where the "is_admin" property is set to false
};
};
admins: {
users: {
// condition that returns users where the "is_admin" property is set to true
};
};
});
});
});This will group our queries under users and admins when we get our response back from our database environment.
Documenting Your Queries
You would have seen by now that we've been including // in our queries. What does this mean?
In every morden language, you can document your code. You can also do this in LunaQL:
query({
from({
// fetch users.
users: {
//
};
});
});As you can see, all lines that begin with // are comments.
Retrieving Documents
Once you've created a query file, you are ready to start retrieving documents from your database environment:
query({
from({
users: {
//
};
});
});With no extra effort from your automatically generated query file, this will return all your users.
Building Queries
Our query above gets the job done, but what if you wanted to only fetch the first 10 users and order them by recently added:
query({
from({
users: {
limit(10);
sort("desc");
};
});
});Nested Queries
You can also nest queries in your main queries. You can do this by using the hasMany clause:
query({
from({
users: {
hasMany: {
posts: {
where: ["user_id", "=", "$._id"];
};
};
};
});
});This will return all your users with their posts.
Dependable Queries
You can write queries that rely on each other for information. For example, instead of fetch all posts along with our users, we can instead, get the count of the users' posts:
query({
from({
users: {
};
posts: {
groupBy(["user_id"]);
};
});
});This will return all your users and in a seperate group, it will return the count of posts for each user as object. Each object will contain a _count property.
Selecting Specific Properties
When retrieving documents from your database environment, you may want to only select specific properties. For example, you may want to only return a user's first_name and last_name instead of the whole document. We can do this using the select clause:
query({
from({
users: {
select([
"first_name",
"last_name"
]);
};
});
});Retrieving Single Documents
In addition to retrieving all of the documents matching a given query, you may also retrieve single documents using the fetchFirst method:
query({
from({
posts(_id: 349974411916529664) {
fetchFirst!;
};
});
});Using a where clause:
query({
from({
posts: {
where(["_id", "==", 349974411916529664]);
fetchFirst!;
};
});
});Inserting & Updating Documents
Inserts
Inserting documents is a bit different from retrieving documents:
query({
@prop data = {
user_id: 336898764327514112;
content: "This is a new post";
};
});You may also insert multiple documents:
query({
@prop data = [
{
user_id: 336898764327514112;
content: "This is a new post";
};
{
user_id: 336898764327514112;
content: "This is another post";
};
];
});Updates
You can update your documents in your POST queries. To do this, you can use the update state:
query({
from({
posts(_id: 381115106458738688) {
update({
content: "I updated this!"
});
};
});
});You can also mass update:
query({
from({
users: {
where(["last_name", "==", "Mocheko"]);
update({
last_name: "Pakkies"
});
};
});
});Deleting Documents
To delete a document, you may call the delete method:
query({
from({
users(_id: 336898764327514112) {
delete!;
};
});
});Deleting Document Using Queries
Sometimes, you may want to delete all documents that match your criteria. In the example below, we will delete all users that have not logged in the past year:
query({
from({
users: {
where(["last_activity_at", "<", 2022]);
delete!;
};
});
});Soft Deleting Documents
In addition to actually removing documents from your database environment, you can also soft delete them. When documents are soft deleted, they are not actually removed from your database environment. Instead, a deleted_at property is set on the document indicating the date and time at which the document was deleted at:
query({
from({
users(_id: 336898764327514112) {
softDelete!;
};
});
});Querying Soft Deleted Documents
If you would still like to query from deleted documents, you may use the withSoftDeleted or withTrashed methods:
query({
from({
users: {
withTrashed!;
};
});
});Restoring Soft Deleted documents
Sometimes you may wish to "un-delete" soft deleted documents. To restore a soft deleted document, you may set the deleted_at property to null:
query({
from({
users: {
withTrashed!;
update({
deleted_at: null
});
};
});
});