SleekDB is a simple flat file NoSQL like database implemented in PHP without any third-party dependencies that store data in plain JSON files.
It is not designed to handle heavy-load IO operations, it is designed to have a simple solution where all we need a database for managing a few gigabytes of data. You can think of it as a database for low to medium operation loads.
SleekDB works great as the database engine for low to medium traffic websites. It makes simple database effortless.
SleekDB will cache all query data by default, in a way that would make any dynamic site as fast as static sites.
SleekDB is also a flat file database, but instead of a single file it stores data in multiple JSON files. That allows it to have a better concurrency support comparing to a FlatFile database system. SleekDB can be compared with other flat file database systems because the final query output will be cached and later reused from a single file instead of traversing all the available files.
SleekDB is being used with many websites and applications which are in production right now. The core team consists of two developer, they are actively contributing to the development of SleekDB Database.
Join our Discord channel. We would love to know about you and your feedbacks.
Stores data in plain-text utilizing JSON format, no binary conversion needed to store or fetch the data. Default query cache layer.
SleekDB does not require any schema, so you can insert any types of data you want.
As it supports schema free data, so you can filter and use conditions on nested properties of the JSON documents!
If you write this where clause:
where( 'post.author.role', '=', 'admin' )
SleekDB will look for data at:
{
"post": {
"author": {
"role": "admin"
}
}
}
Supports PHP 7+. Requires no third-party plugins or software.
SleekDB will serve data from cache by default and regenerate cache automatically! Query results will be cached and later reused from a single file instead of traversing all the available files.
Use multiple conditional comparisons, text search, sorting on multiple properties and nested properties. Some useful methods are:
|
|
|
SleekDB does not require any background process or network protocol in order to process data when you use it in a PHP project. All data for a query will be fetched at runtime within the same PHP process.
Runs perfectly on shared-servers or VPS too.
SleekDB provides a very simple elegant API to handle all of your data.
SleekDB use files to store information. That makes tasks like backup, import and export very easy.
SleekDB is created by @rakibtg who is using it in various types of applications which are in production right now. Our other contributor and active maintainer is Timucin who is making SleekDB much better in terms of code quality and new features.
For support and other requirements contact.
To use SleekDB make sure that you have PHP up and running in your system, and SleekDB has write and/or read permission.
To install SleekDB using composer, open a terminal, cd into your project root directory where "composer.json" file lives and run this:
composer require rakibtg/sleekdb
SleekDB should be auto loaded into your app using the composer. Find SleekDB on packagist.
Import the SleekDB.php file where you want to use SleekDB.
Example:
require_once "../SleekDB/Store.php";
To download older versions please check the releases.
We do a lot of experiments on the dev branch, which are not properly documented and the API will not be consistence in some cases. Use from dev
branch if you have any specific reason or if you want play around with it.
composer require rakibtg/sleekdb dev-dev
Getting started with SleekDB is super easy. We keep data in a "store", which is similar to MySQL "table" or MongoDB "collection". Each store contains multiple documents, which are equivalent to rows in SQL based relational database.
As an example for a blog it can be something like this: posts > documents
Here "posts" is a store and "documents" are a collection of JSON files that contains all the blog posts.
With the release of version 2.0 we have updated the internal implementation.
We introduce a new API and the old SleekDB object has to retire!
Although, we are providing downwards compatibility in this version, but will remove the old SleekDB object with version 3.0.
This documentation focus on the new API.
Each query will follow the below steps or execution life cycle.
The Store
class is the first, and in most cases also the only part you need to come in contact with.
The QueryBuilder
class is used to prepare a query. To execute it getQuery
method can be used.
At this step the Query
class contains all information needed to execute the query.
The Cache
class handles everything regarding caching. It will decide when to cache or not to cache.
To begin with, we need a valid "path" where we want to store our data.
Both absolute and relative paths are supported.
$dataDir = __DIR__ . "/mydb";
Once we have the data directory, we can initialize the store.
If the store doesn't exist it will be created automatically.
$newsStore = new \SleekDB\Store("news", $dataDir);
Optionally you can pass a configuration array as a third parameter. Read more about configurations.
Inserting your first news article using the insert
method. The article has to be an array.
$article = [
"title" => "Google Pixel XL",
"about" => "Google announced a new Pixel!",
"author" => [
"avatar" => "profile-12.jpg",
"name" => "Foo Bar"
]
];
$results = $newsStore->insert($article);
The results variable will contain all the inserted data and with their _id
property which will be unique and added automatically.
To find all news articles we will use the findAll
method.
$allNews = $newsStore->findAll();
print_r($allNews);
🎉 Congrats! You just inserted your first data into a SleekDB store and also learned how to fetch them.
Store is a simple directory where SleekDB will write all your data in JSON documents. "Store" is similar with the idea of "Table" in MySQL or "Collection" in MongoDB. You don't need to create a "store" directory manually.
To start working with a store we need to create an object.
Later, we can use that object to work with the data in that store.
Creating a store
use SleekDB\Store;
$newsStore = new Store('news', $dataDir);
Creating an additional store; assuming we are working on a community platform, where need a users store too.
$userStore = new Store('users', $dataDir);
Another store to keep all the posts shared by the user.
$postStore = new Store('posts', $dataDir);
Creating a new user
$userStore->insert([
'name' => 'Mike Doe',
'email' => 'miked@example.com',
'avatar' => [
'sm' => "/img-sm.jpg",
'lg' => "/img-lg.jpg"
]
]);
In the above example we have created a new user to understand the purpose of a store in SleekDB. In this documentation later we will see more examples on this.
To delete a store use the deleteStore()
method. It deletes a store and wipes all the data and cache it contains.
Example:
$userStore->deleteStore();
SleekDB allows a few configuration options, which are
They are store wide, which means they will be used on every query, if not changed on a query by query base.
You can pass the configurations array as a third parameter when initializing the Store object:
$configuration = [
"auto_cache" => true,
"cache_lifetime" => null,
"timeout" => 120,
"primary_key" => "_id"
];
$newsStore = new \SleekDB\Store("news", $dataDir, $configuration);
Let's get familiar with the available configuration options.
The auto_cache
is set to true
by default!
This tells SleekDB to use the build in caching system.
To disable build in caching set auto_cache
to false
in the config array.
Note that you can manually manage caching on a query by query base with methods that SleekDB provides. Available caching method's are:
QueryBuilder->regenerateCache()
QueryBuilder->useCache()
QueryBuilder->disableCache()
The cache_lifetime
is set to null
by default!
Can be an int >= 0
, that specifies the lifetime in seconds, or null
to define that there is no lifetime.
This specifies the default cache time to live store wide.
If set to null the cache files made with that store instance will have no lifetime and will be regenerated on every insert/update/delete operation.
0
means infinite lifetime.
Note that you can specify the cache lifetime on a query by query base by using the useCache method of the QueryBuilder and pass a lifetime.
QueryBuilder->useCache($lifetime)
Note: You will find more details on caching at Cache Management
Set timeout value. Default value is 120
seconds.
Set the primary key into any string you want.
SleekDB will then use that key instead of the default _id
key.
To insert data first you make a PHP array, and simply insert that array into a store.
function insert(array $data): array
One document that will be insert into the Store
Returns the inserted document as an array
including the automatically generated and unique _id
property.
// Prepare a PHP array to insert.
$user = [
'name' => 'Kazi Hasan',
'products' => [
'totalSaved' => 19,
'totalBought' => 27
],
'location' => [
'town' => 'Nagar',
'city' => 'Dhaka',
'country' => 'Bangladesh'
]
];
// Insert the data.
$user = $userStore->insert($user);
function insertMany(array $data): array
Multiple documents that will be insert into the Store
Returns the inserted documents in an array
including their automatically generated and unique _id
property.
// Prepare users data.
$users = [
[
'name' => 'Russell Newman',
'products' => [
'totalSaved' => 5,
'totalBought' => 3
],
'location' => [
'town' => 'Andreas Ave',
'city' => 'Maasdriel',
'country' => 'England'
]
],
[
'name' => 'Willard Bowman',
'products' => [
'totalSaved' => 0,
'totalBought' => 0
],
],
[
'name' => 'Tommy Mendoza',
'products' => [
'totalSaved' => 172,
'totalBought' => 54
],
],
[
'name' => 'Joshua Edwards',
'phone' => '(382)-450-8197'
]
];
// Insert all data.
$users = $userStore->insertMany($users);
To get data from the store SleekDB provides some simple yet powerful methods.
ℹ️ If you need to make more complex queries look into QueryBuilder.
function findAll(): array
Returns either an array containing all documents
of that store or an empty array
if there are no documents.
$allNews = $newsStore->findAll();
[["_id" => 12, "title" => "We love SleekDB"], ["_id" => 14, "title" => "NoSQL with just PHP"], ...]
With this method SleekDB doesn't traverse through all files. Instead, it accesses the file directly, what makes this method especially fast.
function findById(int $id): array|null
The _id of a document located in the store.
Returns one document
or null
if document could not be found.
$news = $newsStore->findById(12);
["_id" => 12, "title" => "SleekDB is the Best", ...]
function findBy(array $criteria, array $orderBy = null, int $limit = null, int $offset = null): array|null
One or multiple where clauses.
WHERE city = "london" AND age > 18
Order in which the results will be sort.
Limit the result to a specific amount.
Skip a specific amount of documents.
Returns found documents in an array
or null
if nothing is found.
$news = $newsStore->findBy(["author", "=", "John"], ["title" => "asc"], 10, 20);
// First 20 documents skipped, limited to 10 and ascending sort by title where author is John.
[ ["_id" => 12, "title" => "Best Database"], ["_id" => 4, "title" => "Why SleekDB"], ...]
function findOneBy(array $criteria): array|null
One or multiple where clauses. * [["city", "=", "london"], ["age", ">", 18]]WHERE city = "london" AND age > 18
Returns one document
or null
if nothing is found.
$news = $newsStore->findOneBy(["author", "=", "Mike"]);
// Returns one news article of the author called Mike
["_id" => 18, "title" => "SleekDB is super fast", "author" => "Mike"]
To edit a data object you can use the update()
method of the Store
class.
ℹ️ If you need to make more complex updates look into QueryBuilder and Query.
This method is especially fast because it uses the _id of the given document to update it directly instead of traversing through all documents.
function update(array $updatable): bool;
Note: This method of the Store
class updates/overrides entire document/s, not just parts.
One or multiple documents
Returns true
on success or false
if document with given _id does not exist.
$user = [
'name' => 'Willard Bowman',
'products' => [
'totalSaved' => 0,
'totalBought' => 0
],
];
//store the user
$store->insert($user); // has _id = 1
// retrieve a user
$user = $userStore->findById(1);
// update user
$user["name"] = "Luke Bowman";
$userStore->update( $user ); // updates the user by using his _id
// retrieve users
$users = $userStore->findBy(["name", "=", "Josh"]);
foreach($users as $key => $user){
// change the properties of the users
$user["name"] = "Luke Bowman";
// push changed user back to the users array
$users[$key] = $user;
}
// update all users that had the name Josh
$userStore->update( $users );
To delete documents you can use the deleteBy()
and deleteById()
methods of the Store
class.
ℹ️ If you need to make a more complex delete look into QueryBuilder and Query.
function deleteBy(array $criteria, int $returnOption = Query::DELETE_RETURN_BOOL): array|bool|int
One or multiple where conditions
WHERE name = "John" AND age > 18
Different return options provided with constants of the Query
class
Query::DELETE_RETURN_BOOL
(Default)Query::DELETE_RETURN_RESULTS
Query::DELETE_RETURN_COUNT
This method returns based on the given return option either boolean
, int
or array
.
Lets delete all user whose name is "Joshua Edwards"
$userStore->deleteBy(['name', '=', 'Joshua Edwards']);
// Returns true
Lets delete all user whose name is "Joshua Edwards" and retrieve deleted documents.
use SleekDB/Query;
$userStore->deleteBy(['name', '=', 'Joshua Edwards'], Query::DELETE_RETURN_RESULTS);
[ ["_id" => 12, "name" => "Joshua Edwards"], ["_id" => 14, "name" => "Joshua Edwards"], ... ]
This method is especially fast because SleekDB uses the _id to directly delete the document and does not traverse through all files.
function deleteById(int $id): bool
The _id of a document located in the store.
Returns true
if document does not exist or deletion was successful or false
on failure.
$userStore->deleteById(12);
// Returns true
The QueryBuilder is used to prepare more complex queries, not to execute the query!
. You can create a QueryBuilder with the createQueryBuilder()
method of the Store
class.
$userQueryBuilder = $userStore->createQueryBuilder();
With the getQuery
method of the QueryBuilder class you can retrieve the Query object to execute the query
.
function getQuery(): Query
The most important methods of the Query class to execute a query are:
Retrieve multiple documents
Retrieve first found document
Check if a document for given query exist
Delete all documents that are found with given query
Update all documents that are found with given query
For more details on query execution please visit the Query page.
To filter data we use the where() method of the QueryBuilder object.
If you provide multiple conditions they are connected with an AND
.
function where(array $criteria): QueryBuilder
One or multiple where conditions
[ [$fieldName, $condition, $value], [$fieldName, $condition, $value], ... ]
The field name argument is the property that we want to check in our data object.
As our data object is basically a JSON document so it could have nested properties.
To target nested properties we use a single dot between the property/field name.
Example: From our above users object if we want to target the "country" property of a user, then we would pass location.country
in this argument, because "location" is the parent property of the "country" property in our data object.
To apply the comparison filters we use this argument.
Allowed conditional operators are:
=
Match equal against data.!=
Match not equal against data.>
Match greater than against data.>=
Match greater equal against data.<
Match less than against data.<=
Match less equal against data.like
Match using wildcards. \
Supported wildcards:%
Represents zero or more characters \
Example: bl% finds bl, black, blue, and blob_
Represents a single character \
Example: h_t finds hot, hat, and hit[]
Represents any single character within the brackets \
Example: h[oa]t finds hot and hat, but not hit^
Represents any character not in the brackets \
Example: h[^oa]t finds hit, but not hot and hat-
Represents a range of characters \
Example: c[a-b]t finds cat and cbtData that will be checked against the property value of the JSON documents.
To only get the user whose country is equal to "England" we would query like this:
// inline
$users = $userStore
->createQueryBuilder()
->where( [ "name", "=", "Joshua Edwards" ] )
->getQuery()
->fetch();
// creating the QueryBuilder
$userQueryBuilder = $userStore->createQueryBuilder();
// preparing the query with the QueryBuilder
$userQueryBuilder->where( [ "name", "=", "Joshua Edwards" ] );
// executing the query
$users = $userQueryBuilder->getQuery()->fetch();
You can also use multiple where
conditions.
Retrieve all users that have products.totalSaved > 10 AND products.totalBought > 20
.
// inline & using where method multiple times
$users = $userQueryBuilder
->where( ["products.totalSaved", ">", 10] )
->where( ["products.totalBought", ">", 20] )
->getQuery()
->fetch();
// inline & using where method once
$users = $userQueryBuilder
->where(
[
["products.totalSaved", ">", 10],
["products.totalBought", ">", 20]
]
)
->getQuery()
->fetch();
// retrieve QueryBuilder
$userQueryBuilder = $userStore->createQueryBuilder()
// prepare query
$userQueryBuilder->where(
[
["products.totalSaved", ">", 10],
["products.totalBought", ">", 20]
]
);
// execute query
$users = $userQueryBuilder->getQuery()->fetch();
orWhere(...)
works as the OR condition of SQL. SleekDB supports multiple orWhere as object chain.
If you provide multiple conditions they are connected with an AND
.
function orWhere(array $criteria): QueryBuilder
One or multiple where conditions
[ [$fieldName, $condition, $value], [$fieldName, $condition, $value], ... ]
The field name argument is the property that we want to check in our data object.
As our data object is basically a JSON document so it could have nested properties.
To target nested properties we use a single dot between the property/field name.
Example: From our above users object if we want to target the "country" property of a user, then we would pass location.country
in this argument, because "location" is the parent property of the "country" property in our data object.
To apply the comparison filters we use this argument.
Allowed conditional operators are:
=
Match equal against data.!=
Match not equal against data.>
Match greater than against data.>=
Match greater equal against data.<
Match less than against data.<=
Match less equal against data.like
Match using wildcards. \
Supported wildcards:%
Represents zero or more characters \
Example: bl% finds bl, black, blue, and blob_
Represents a single character \
Example: h_t finds hot, hat, and hit[]
Represents any single character within the brackets \
Example: h[oa]t finds hot and hat, but not hit^
Represents any character not in the brackets \
Example: h[^oa]t finds hit, but not hot and hat-
Represents a range of characters \
Example: c[a-b]t finds cat and cbtData that will be checked against the property value of the JSON documents.
Retrieve all users that have (products.totalSaved > 10 AND products.totalBought > 20) OR products.shipped = 1
$users = $userQueryBuilder
->where(
[
["products.totalSaved", ">", 10],
["products.totalBought", ">", 20]
]
)
->orWhere( ["products.shipped", "=", 1] )
->getQuery()
->fetch();
Retrieve all users that have products.totalSaved > 10 OR (products.totalBought > 20 AND products.shipped = 1) OR totalBought = 0
$users = $userQueryBuilder
->where( ["products.totalSaved", ">", 10] )
->orWhere(
[
[ "products.totalBought", ">", 20 ],
[ "products.shipped", "=", 1 ]
]
)
->orWhere( ["products.totalBought", "=", 0] )
->getQuery()
->fetch();
With the nestedWhere(...)
method you are able to make complex nested where statements.
function nestedWhere(array $conditions): QueryBuilder
[
"OUTERMOST_OPERATION" => [
// conditions
]
];
Multiple where conditions
Small Example:
[ "OUTERMOST_OPERATION" => [ [$fieldName, $condition, $value], "OPERATION", [$fieldName, $condition, $value] ] ]
The field name argument is the property that we want to check in our data object.
As our data object is basically a JSON document so it could have nested properties.
To target nested properties we use a single dot between the property/field name.
Example: From our above users object if we want to target the "country" property of a user, then we would pass location.country
in this argument, because "location" is the parent property of the "country" property in our data object.
To apply the comparison filters we use this argument.
Allowed conditional operators are:
=
Match equal against data.!=
Match not equal against data.>
Match greater than against data.>=
Match greater equal against data.<
Match less than against data.<=
Match less equal against data.like
Match using wildcards. \
Supported wildcards:%
Represents zero or more characters \
Example: bl% finds bl, black, blue, and blob_
Represents a single character \
Example: h_t finds hot, hat, and hit[]
Represents any single character within the brackets \
Example: h[oa]t finds hot and hat, but not hit^
Represents any character not in the brackets \
Example: h[^oa]t finds hit, but not hot and hat-
Represents a range of characters \
Example: c[a-b]t finds cat and cbtData that will be checked against the property value of the JSON documents.
Can be set to AND
or OR
.
It is optional and will default to an AND
operation.
The outermost operation is optional and does specify how the given conditions are connected with other conditions, like the ones that are specified using the where
, orWhere
, in or notIn
methods.
Can be set to AND
or OR
.
The operation is used to connect multiple conditions.
Retrieve all users whos name start with "a" or "b", that have products.totalSaved > 10 and products totalBought > 10 and are between 16 and 20 or 24 and 28 years old.
WHERE
(products.totalSaved > 10 AND products.totalBought > 20)
AND // <- Outermost Operation
(
(name like 'a%' OR name like 'b%')
AND
(
(age >= 16 AND age < 20)
OR
(age >= 24 AND age < 28))
)
$users = $userQueryBuilder
->where(
[
["products.totalSaved", ">", 10],
["products.totalBought", ">", 20]
]
)
->nestedWhere(
[
"AND" => [ // <- Outermost operation
[
[ "name", "like", "a%" ], "OR", [ "name", "like", "b%" ]
],
"AND",
[
[
[ "age", ">=", 16 ], "AND", [ "age", "<", 20 ]
],
"OR",
[
[ "age", ">=", 24 ], "AND", [ "age", "<", 28 ]
]
]
]
]
)
->getQuery()
->fetch();
Retrieve all users that have the status "premium", live in "london" and are between the age of 16 and 20 or older than 30.
$users = $userQueryBuilder
->where(
[
["status", "=", "premium"]
]
)
->nestedWhere(
[
[ // <- Outermost operation is optional (Default: "AND")
[ "city", "=", "london" ]
"AND",
[
[
[ "age", ">=", 16 ], "AND", [ "age", "<", 20 ]
],
"OR",
[ "age", ">", 30 ]
]
]
]
)
->getQuery()
->fetch();
in(...) works as the IN clause of SQL. SleekDB supports multiple IN as object chain for different fields.
in(string $fieldName, array $values = []): QueryBuilder
The field name argument is the property that we want to check in our data object.
You can also provide have nested properties separated with a dot ".".
This argument takes an array to match documents within its items.
Retrieve all users that are from the country BD, CA, SE or NA.
$users = $userQueryBuilder
->in("country", ["BD", "CA", "SE", "NA"])
->getQuery()
->fetch();
Retrieve all users that are from country BD, CA, SE, NA and are at the age of 18, 20, 23 or 30.
$users = $userQueryBuilder
->in("country", ["BD", "CA", "SE", "NA"])
->in("age", [18, 20, 23, 30])
->getQuery()
->fetch();
notIn(...) works as the opposite of in() method.
It will filter out all documents that has particular data items from the given array. SleekDB supports multiple notIn as object chain for different fields.
The notIn() method takes two arguments as in() method, those are:
function notIn(string $fieldName, array $values = []): QueryBuilder
The field name argument is the property that we want to check in our data object.
You can also provide have nested properties separated with a dot ".".
This argument takes an array to match documents within its items.
Retrieve all users that are not from the coutry IN, KE or OP.
$users = $userQueryBuilder
->notIn("country", ["IN", "KE", "OP"])
->getQuery()
->fetch();
Retrieve all users that are not from the country IN, KE or OP and do not have products.totalSaved 100, 150 or 200.
$users = $userQueryBuilder
->notIn("country", ["IN", "KE", "OP"])
->notIn("products.totalSaved", [100, 150, 200])
->getQuery()
->fetch();
With select(...) you can specify specific fields to output, like after the SELECT keyword in SQL. SleekDB supports multiple select()
as object chain for multiple fields.
function select(array $fieldNames): QueryBuilder
Specify specific fields to output.
Retrieve just the name of all users.
$users = $userQueryBuilder
->select(['name'])
->getQuery()
->fetch();
// output: [["_id" => 1, "name" => "Max"], ["_id" => 2, "name" => "Hasan"]]
except(...)
works as the opposite of select()
method. Use it when you don't want a property in the result. For example, if you don't want the password field in your returned data set.
function except(array $fieldNames): QueryBuilder
Specify specific fields to exclude from the output.
Retrieve all information of an user except its _id and name.
$users = $userQueryBuilder
->except(["_id", "name"])
->getQuery()
->fetch();
// output: [["age": 28], ["age": 18]]
The distinct method is used to retrieve unique values from the store. It will remove all the duplicate documents while fetching data from a store.
distinct( array|string $fields ): QueryBuilder;
Specify one or multiple fields you want to be distinct.
Retrieve all users, but just the first user if there is another one with the same name.
// providing a string
$distinctUsers = $userQueryBuilder
->distinct("name")
->getQuery()
->fetch();
// providing an array
$distinctUsers = $userQueryBuilder
->distinct(["name"])
->getQuery()
->fetch();
Skip works as the OFFSET clause of SQL. You can use this to skip a specific amount of documents.
function skip(int $skip = 0): QueryBuilder
The value how many documents should be skipped.
Retrieve all users except the first 10 found.
$users = $userQueryBuilder
->skip(10)
->getQuery()
->fetch();
Works as the LIMIT clause of SQL. You can use this to limit the results to a specific amount.
function limit($limit = 0): QueryBuilder
Retrieve just the first ten users.
$users = $userQueryBuilder
->limit(10)
->getQuery()
->fetch();
Works as the ORDER BY clause of SQL. With this method you can sort the result. You can use this method to sort the result by one or multiple fields.
function orderBy( array $criteria): QueryBuilder
Name of the field that will be used to sort the result.
Either desc
for a descending sort or asc
for a ascending sort.
Retrieve all users sorted by their name.
$users = $userQueryBuilder
->orderBy(["name" => "asc"])
->getQuery()
->fetch();
[["_id" => 13, "name" => "Anton"], ["_id" => 2, "name" => "Berta"], ...]
Retrieve all users sorted by their name and age.
$users = $userQueryBuilder
->orderBy(["name" => "asc", "age" => "asc"])
->getQuery()
->fetch();
[
["_id" => 13, "name" => "Anton", "age" => 20],
["_id" => 4, "name" => "Aragon", "age" => 16],
["_id" => 2, "name" => "Aragon", "age" => 17],
...
]
Do a fulltext like search against one or multiple fields.
function search(string|array $fields, string $keyword): QueryBuilder
One or multiple fields that will be searched.
Find all articles that include the word "SleekDB" in their description.
$articles = $articleQueryBuilder
->search("content", "SleekDB")
->getQuery()
->fetch();
The QueryBuilder provides the useCache
, disableCache
and regenerateCache
methods to manage caching on a query by query base.
Please visit the Cache Management page for more details.
This method is used to join two or multiple stores together.
For more details please visit the Join Stores page.
function join(callable $joinedStore, string $dataPropertyName): QueryBuilder
The QueryBuilder
class does provide a couple of methods to manage caching on a query by query bases
.
By default caching is enabled store wide and does not need to be enabled on a query by query bases. For more information please look into the store configurations.
Create a QueryBuilder:
$userQueryBuilder = $userStore->createQueryBuilder();
With the useCache method you can activate caching
and define the cache lifetime
on a query by query bases.
function useCache(int $lifetime = null): QueryBuilder
The lifetime of the cache.
null
(Default)int
0
means infinite lifetime.Retrieve and cache 20 users that are active.
The results in this example will use caching untill any documents gets updated/ deleted or inserted.
// fetch and insert in cache
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache() // Use caching with lifetime = null
->getQuery()
->fetch();
// retrieve from cache
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache()
->getQuery()
->fetch();
// insert a new user (cache with no lifetime will be deleted)
$newUser = ["name" => "Max", "active" => 0];
$userStore->insert($newUser);
// fetch and insert in cache
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache()
->getQuery()
->fetch();
Retrieve and cache result for 2 minutes, 20 users that are active.
// fetch and insert in cache
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache(120) // 2 minutes = 120 seconds
->getQuery()
->fetch();
Retrieve and cache result forever, 20 users that are active.
// fetch and insert in cache
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache(0) // 0 means infinite caching
->getQuery()
->fetch();
Disable the build in caching solution on a query by query bases.
By default caching is enabled store wide. If you want to disable caching store instead of disabling it on a query by query bases visit the store configurations page.
function disableCache(): QueryBuilder
Retrieve 20 users that are active and do not use caching.
// fetch and insert in cache
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->disableCache()
->getQuery()
->fetch();
Regenerate the cache of a query regardless of its lifetime.
function regenerateCache(): QueryBuilder
// cache with infinite lifetime
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache(0)
->getQuery()
->fetch();
// returns documents not from cache and caches results for 20 seconds
$users = $userQueryBuilder
->where( ['active', '=', 1] )
->limit( 20 )
->useCache(20)
->regenerateCache()
->getQuery()
->fetch();
With SleekDB it is easy to join multiple stores. You can add more than one join
as well as nested join
methods are also supported!
Query into the "users" store to fetch all users. Each user also will get an additional property called "comments", that contains all comments of the user.
$usersWithComments = $userStore
->createQueryBuilder()
->join(function($user) use ($commentStore) {
return $commentStore->findBy(["user", "=", $user["_id"]]);
}, "comments")
->getQuery()
->fetch();
[
[
"_id" => 1,
"name" => "John",
"comments" => [
[
"_id" => 1,
"articleId" => 3
"content" => "I love SleekDB"
],
...
]
],
...
]
To join stores we use the join() method of the QueryBuilder object.
The join() method takes two arguments, those are:
function join(callable $joinFunction, string $dataPropertyName): QueryBuilder
This function has to return the result of an executed sub query
or prepares a sub query for the join and returns it as a QueryBuilder object
.
Name of the new property added to each document.
To get the users with their comments we would join like this:
use SleekDB\Store;
$userStore = new Store("users", $dataDir);
$commentStore = new Store("comments", $dataDir);
$users = $usersStore
->createQueryBuilder()
->join(function($user) use ($commentStore){
// returns result
return $commentStore->findBy([ "userId", "=", $user["_id"] ]);
}, "comments")
->getQuery()
->fetch();
// or
$users = $userStore
->createQueryBuilder()
->join(function($user) use ($commentStore){
// returns Querybuilder
return $commentStore
->createQueryBuilder()
->where([ "userId", "=", $user["_id"] ]);
}, "comments")
->getQuery()
->fetch();
Use multiple join()
.
Retrieve all users with their comments and their articles.
use SleekDB\Store;
$userStore = new Store("users", $dataDir);
$commentStore = new Store("comments", $dataDir);
$articleStore = new Store("articles", $dataDir);
$users = $userStore
->createQueryBuilder()
->join(function($user) use ($commentStore) {
// returns result
return $commentStore->findBy([ "userId", "=", $user["_id"] ]);
}, "comments")
->join(function($user) use ($articleStore) {
// returns result
return $articleStore->findBy([ "author", "=", $user["_id"] ]);
}, "articles")
->getQuery()
->fetch();
Use join()
within a join sub query.
Retrieve all users with their created articles containing the comments.
use SleekDB\Store;
$userStore = new Store("users", $dataDir);
$commentStore = new Store("comments", $dataDir);
$articleStore = new Store("articles", $dataDir);
$users = $userStore
->createQueryBuilder()
->join(function($user) use ($articleStore, $commentStore){
// returns QueryBuilder
return $articleStore
->createQueryBuilder()
->where([ "author", "=", $user["_id"] ])
->join(function($article) use ($commentStore){
// returns result
return $commentStore->findBy("articleId", "=", $article["_id"]);
}, "comments");
}, "articles")
->getQuery()
->fetch();
With the Query object you can execute a query.
To get the Query object use the getQuery
method of the QueryBuilder class:
$userQuery = $userQueryBuilder->getQuery();
Execute a query and retrieve an array containing all documents found.
function fetch(): array
An array containing all documents found or an empty array.
Retrieve all users that are located in london.
$user = $userQueryBuilder
->where([ "city", "=", "london" ])
->getQuery()
->fetch();
[
[
"_id" => 1,
"name" => "John",
"city" => "london"
],
[
"_id" => 4,
"name" => "Max",
"city" => "london"
],
...
]
It is more efficient than fetch
but one caveat is that the orderBy
will not work when using this method to get the very first item.
function first(): array
Returns the very first document discovered or an empty array if nothing found.
$user = $userQueryBuilder
->where([ "email", "=", "foo@bar.com" ])
->getQuery()
->first();
[
"_id" => 1,
"name" => "John",
"email" => "foo@bar.com"
]
It is more efficient than using fetch
to check if some data exists or not. For example, you may use exists method to check if a username or email address already exists or not.
function exists(): bool
Returns true
if a document exists and false
if no document found.
$userNameExists = $userQueryBuilder
->where([ "username", "=", "foobar" ])
->getQuery()
->exists();
Update one or multiple documents based on a given query.
function update(array $updatable): bool
An array containing the properties to update.
Returns true
on success and false
if no documents found to update.
Set the status of all users that are located in london to VIP.
$userStore
->createQueryBuilder()
->where([ "city", "=", "london" ])
->getQuery()
->update([ "status" => "VIP" ]);
Delete one or multiple documents based on a given query.
function delete(int $returnOption = Query::DELETE_RETURN_BOOL)
Different return options provided with constants of the Query
class
Query::DELETE_RETURN_BOOL
(Default)Query::DELETE_RETURN_RESULTS
Query::DELETE_RETURN_COUNT
This method returns based on the given return option either boolean
, int
or array
.
Delete all users that are not active.
$result = $userStore
->createQueryBuilder()
->where([ "active", "=", false ])
->getQuery()
->delete();
// output: true
Delete all users that are not active and retrieve the amount of deleted users.
use SleekDB\Query;
$result = $userStore
->createQueryBuilder()
->where([ "active", "=", false ])
->getQuery()
->delete(Query::DELETE_RETURN_COUNT);
// output: 14
Delete all users that are not active and retrieve the deleted users.
use SleekDB\Query;
$result = $userStore
->createQueryBuilder()
->where([ "active", "=", false ])
->getQuery()
->delete(Query::DELETE_RETURN_RESULT);
// output: [ ["_id" => 1, "name" => "Max"], ["_id" => 4, "name" => "John"], ... ]
When you use the createQueryBuilder()
method you get a new QueryBuilder object, that means your conditions are reset to default.
You can keep the QueryBuilder object and reuse it to add new conditions.
$userQueryBuilder = $userStore->createQueryBuilder();
$userQueryBuilder->where([ 'products.totalBought', '>', 0 ]);
$userQueryBuilder->where([ 'products.totalSaved', '>', 0 ]);
// fetch all users that have totalBought > 0 and totalSaved > 0
$users = $userQueryBuilder->getQuery()->fetch();
// add new condition
$userQueryBuilder->where([ 'active', '=', true ]);
// fetch all users that have totalBought > 0, totalSaved > 0 and are active
$usersActive = $userQueryBuilder->getQuery()->fetch();
If you want to keep the conditions and perform additional operations then you may want to keep the Query object.
Here is an example showing how you may fetch data and then update on the discovered documents without running an additional query:
$userQuery = $userStore->createQueryBuilder()
->where('products.totalBought', '>', 0)
->where('products.totalSaved', '>', 0)
->getQuery();
// Fetch data.
$users = $userQuery->fetch();
// Update matched documents.
$userQuery->update([
'someRandomData' => '123',
]);
// Fetch data again.
$usersUpdated = $userQuery->fetch();
With the Cache class you can control Caching in a deeper way.
That said, this class is mainly for internal use
and normally that kind of deep cache control is not needed.
⚠️️ Be careful when using the
Cache
object.
To retrieve the Cache object use the getCache
method of the Query
class.
$cache = $userQuery->getCache();
Returns the unique token for the current query, that will be used to save and retrieve a cache file.
function getToken(): string
The unique token for the current query as a string
.
Deletes the cache file for the current query.
function delete()
Delete all cache files of current store.
function deleteAll()
Delete all cache files that have no lifetime (null) of current store.
function deleteAllWithNoLifetime()
Set and cache the content for the current query / token.
function set(array $content)
The content that will be cached.
Retrieve the content of the cache file for the current query / token.
function get(): array|null
The content as an array
or null
if no cache file found.
Returns the path to the cache directory.
function getCachePath(): string
Set the lifetime for the current query / token.
function setLifetime(int|null $lifetime): Cache
int
in seconds. 0
means infinitenull
no cache lifetimeGet the lifetime for the current query / token.
function getLifetime(): int|null
Either int
>= 0 in seconds, where 0
means infinite, or null
.
This page contains a brief overview of all the classes and their methods. For more detailed information please visit the other documentation pages.
The Store class is the beginning point and handles everything regarding store configuration. It also provides all methods needed for simple queries.
Create a new Store object. (Internally it creates a new store folder if it doesn't exist)
function __construct(string $storeName, string $dataDir, array $configuration = [])
Returns a new QueryBuilder
object.
function createQueryBuilder(): QueryBuilder
Create/Insert a new document in the store.
Returns the inserted document with it's new and unique _id.
function insert(array $data): array
Create/Insert many documents in the store.
Returns the inserted documents with their new and unique _id.
function insertMany(array $data): array
Retrieve all documents of that store.
function findAll(): array
Retrieve one document by its _id. Very fast because it finds the document by its file path.
function findById(int $id): array|null
Retrieve one or multiple documents.
function findBy(array $criteria, array $orderBy = null, int $limit = null, int $offset = null): array
Retrieve one document.
function findOneBy(array $criteria): array|null
Update one or multiple documents.
function update(array $updatable): bool
Delete one or multiple documents.
function deleteBy(array $criteria, int $returnOption = Query::DELETE_RETURN_BOOL): bool|array|null
Delete one document by its _id. Very fast because it deletes the document by its file path.
function deleteById(int $id): bool
Deletes a store and wipes all the data and cache it contains.
function deleteStore(): bool
Get the name of the store.
function getStoreName(): string
Get the location (directory path) of the store.
function getDataDirectory(): string
Returns if caching is enabled store wide.
function _getUseCache(): bool
Returns the store wide default cache lifetime.
function getDefaultCacheLifetime(): null|int
Return the last created store object ID.
function getLastInsertedId(): int
Get the path to the store, including store name.
function getStorePath(): string
The QueryBuilder class handles everything regarding query creation like for example the where or join methods.
Create a new QueryBuilder object.
function __construct(Store $store)
Returns a new Query
object which can be used to execute the query build.
function getQuery(): Query
Select specific fields.
function select(string[] $fieldNames): QueryBuilder
Exclude specific fields.
function except(string[] $fieldNames): QueryBuilder
Add "where" condition to filter data. Can be used multiple times. All additional uses add an "and where" condition.
function where(array $conditions): QueryBuilder
Add or-where conditions to filter data.
function orWhere($conditions): QueryBuilder
Add "in" condition to filter data.
function in(string $fieldName, array $values = []): QueryBuilder
Add "not in" condition to filter data.
function notIn(string $fieldName, array $values = []): QueryBuilder
Set the amount of data record to skip.
function skip(int $skip = 0): QueryBuilder
Set the amount of data record to limit.
function limit(int $limit = 0): QueryBuilder
Set the sort order.
function orderBy(array $criteria): QueryBuilder
Do a fulltext like search against one or more fields.
function search(string|array $field, string $keyword): QueryBuilder
Join current store with another one. Can be used multiple times to join multiple stores.
function join(callable $joinFunction, string $dataPropertyName): QueryBuilder
Return distinct values.
function distinct(string|array $fields = []): QueryBuilder
Use caching for current query
function useCache(int $lifetime = null): QueryBuilder
Disable caching for current query.
function disableCache(): QueryBuilder
This method is used internally. It returns an array containing all properties that are used for the cache token generation.
function _getCacheTokenArray(): array
This method is used internally. It returns an array that contains all information needed to execute an query.
function _getConditionProperties(): array
Re-generate the cache for the query.
function regenerateCache(): QueryBuilder
Get the store object used to create the query builder.
function _getStore(): Store
This class handles everything regarding query execution like fetch / first / exists.
Create a new Query object. (Internally it creates a new Cache object)
function __construct(QueryBuilder $queryBuilder)
Get the Cache
object.
function getCache(): Cache
Execute Query and get Results.
function fetch(): array
Check if data is found.
function exists(): bool
Return the first document. (More efficient than fetch
but orderBy
does not work)
function first(): array
Update one or multiple documents, based on the current query.
function update(array $updatable): bool
Deletes matched documents.
function delete(int $returnOption = Query::DELETE_RETURN_BOOL): bool|array|int
This class handles everything regarding caching like for example cache deletion.
Create a new Cache object.
function __construct(Query $query, string $storePath)
Set the cache lifetime.
function setLifetime(int|null $lifetime): Cache
Get the cache lifetime.
function getLifetime(): null|int
Get the path to the cache folder.
function getCachePath(): string
Get the cache token.
function getToken(): string
Delete all cache files for current store.
function deleteAll()
Delete all cache files with no lifetime (null) in current store.
function deleteAllWithNoLifetime()
Cache content for current query
function set(array $content)
Returns cached result for current query if found, else null.
function get(): array|null
Delete cache file/s for current query.
function delete()
SleekDB 2.0 comes with so many important optimizations and other features that make it faster and more mature. This is the recommended SleekDB release version for new projects, and if you are using an older version consider upgrading as soon as possible.
Now the orderBy()
method of the QueryBuilder accepts multiple fields to sort the result.
Look at the updated orderBy section of the QueryBuilder documentation to learn more.
With the new added nestedWhere()
method you can now use much complexer where statements to filter data.
Look at the new nestedWhere section of the QueryBuilder documentation to learn more.
With the new primary_key
configuration you can now change the _id key name to everything you want. To see how to use the new configuration option visit the Configurations page.
Added better file locking support with reduced nested loops for finding documents. Added methods that can be used to easily find a document without searching for entire available JSON files of a store.
The support of PHP 5 is dropped to provide more modern features.
first()
exists()
select()
except()
distinct()
join()
findAll()
findById()
findBy()
findOneBy()
updateBy()
deleteBy()
deleteById()
We isolated most of the existing logics into different classes to make the codebase easy to understand and easy to maintain.
There was some concern among developers as it was lacking UNIT Testing, now its added!
We beliefe that downwards compatibility is very important.
That's why we always try our best to keep SleekDB as downwards compatible as possible and avoid breaking changes.
Unfortunatelly we had to refactor and rewrite the whole SleekDB project to make it future proof and keep it maintainable. As a consequence the SleekDB class is now deprecated with version 2.0 and will be removed with version 3.0.
Data caching has been improved significantly.
git checkout -b feature/my-new-feature
git commit -am 'Added some feature'
git push origin feature/my-new-feature
dev
branchPlease write details about your PR.
We would love to see how you are using the database, if you have implemented something or how it is working for you.
What changes will make you more interested.
If you want to submit a bug feel free to create a new issue or email me @ rakibtg [-at-] gmail [-dot-] com
Let us know with a tweet if you love or hate SleekDB 😄, or if you have any question mail me or tweet to my twitter handle.