LunaQL docs are incomplete.
How To
Getting Started

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:

Terminal
lql init
💡

This 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:

Terminal
lql make:query Users/All

If we open queries/users/all.lunaql, we will see:

queries/users/all.lunaql
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:

REST
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:

REST
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:

queries/users/all.lunaql
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:

queries/users/all.lunaql
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:

queries/users/all.lunaql
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:

queries/users/all.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:

queries/users/all.lunaql
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:

queries/users/all.lunaql
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:

queries/users/all.lunaql
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:

queries/users/all.lunaql
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:

queries/users/all.lunaql
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:

queries/posts/find.lunaql
query({
	from({
		posts(_id: 349974411916529664) {
			fetchFirst!;
		};
	});
});

Using a where clause:

queries/posts/find.lunaql
query({
	from({
		posts: {
			where(["_id", "==", 349974411916529664]);
 
			fetchFirst!;
		};
	});
});

Inserting & Updating Documents

Inserts

Inserting documents is a bit different from retrieving documents:

queries/posts/create.lunaql
query({
	@prop data = {
		user_id: 336898764327514112;
		content: "This is a new post";
	};
});

You may also insert multiple documents:

queries/posts/createMany.lunaql
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:

queries/posts/update.lunaql
query({
	from({
		posts(_id: 381115106458738688) {
			update({
				content: "I updated this!"
			});
		};
	});
});

You can also mass update:

queries/users/update.lunaql
query({
	from({
		users: {
			where(["last_name", "==", "Mocheko"]);
 
			update({
				last_name: "Pakkies"
			});
		};
	});
});

Deleting Documents

To delete a document, you may call the delete method:

queries/users/delete.lunaql
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:

queries/users/delete.lunaql
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:

queries/users/delete.lunaql
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:

queries/users/index.lunaql
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:

queries/users/index.lunaql
query({
	from({
		users: {
			withTrashed!;
 
			update({
				deleted_at: null
			});
		};
	});
});