Skip to content

Commit

Permalink
✨ lazyloading of client and default to not read from cache
Browse files Browse the repository at this point in the history
  • Loading branch information
bnomei committed Jul 3, 2024
1 parent bb9fb23 commit 5102513
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 129 deletions.
77 changes: 74 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,77 @@ $pages = khulan([
]);
```

### Example: Filtering and Resolving Relations

With Kirby's content cache as a NoSQL database you can do some advanced filtering. If you would do the same with Kirby's
filtering on collection you would end up loading a lot pages and that is not as efficient. We can load the information
we need directly from the cache without the need to load the full page object.

Let's assume we have two models: `film` and `actor`. The `film` model has a field `actors` which is a pages field with
linked `actor` pages. We want to list all films with their actors.

#### Load 1000 films and their actors, total of 6429 pages accessed in 250ms

```php
$films = page('films')->children();
foreach ($films as $film) {
echo $film->title();
$actors = [];
foreach ($film->actors()->toPages() as $actor) {
$actors[] = $actor->title();
}
echo implode(', ', $actors);
}
?>
```

#### Query the cache instead to get the same information in under 100ms

```php
/** @var \MongoDB\Driver\Cursor $films */
$films = khulan()->aggregate([
[
// only get pages with template 'film'
'$match' => ['template' => 'film'],
],
[
// lookup actors by their mongodb objectId
// this will create an array of objects
'$lookup' => [
'from' => 'kirby',
'localField' => 'actors{}',
'foreignField' => '_id',
'as' => 'actor_details',
],
],
[
// only get the fields we need
// to make the query even faster
'$project' => [
'title' => 1,
'id' => 1,
'actor_details.title' => 1,
],
],
]);

/** @var \MongoDB\BSON\Document $film */
foreach ($films as $film) { ?>
<div>
<a href="<?= url($film->id) ?>"><?= html($film->title) ?></a>
<ul>
<?php foreach ($film->actor_details as $actor) { ?>
<li><?= html($actor->title) ?></li>
<?php } ?>
</ul>
</div>
<?php }
```

> [!NOTE]
>
> This example is from [my Sakila DB kit](https://github.com/bnomei/kirby-sakila-kit/tree/with-mongodb-plugin).
## MongoDB Client

You can access the underlying MongoDB client directly.
Expand Down Expand Up @@ -206,9 +277,9 @@ return [
| port | `27017` | |
| username | `null` | |
| password | `null` | |
| database | `kirby` | |
| khulan.read | `true` | read from cache |
| khulan.write | `true` | write to cache |
| database | `kirby` | you can give it any name you want and MongoDB will create it for you |
| khulan.read | `false` | read from cache is disabled by default as loading from file might be faster |
| khulan.write | `true` | write to cache for all models that use the ModelWithKhulan trait |
| khulan.patch-files-class | `true` | monkey-patch the \Kirby\CMS\Files class to use Khulan for caching it content |

## Disclaimer
Expand Down
36 changes: 20 additions & 16 deletions classes/Mongodb.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

final class Mongodb extends Cache
{
protected Client $_client;
protected ?Client $_client = null;

/**
* Sets all parameters which are needed to connect to MongoDB.
Expand Down Expand Up @@ -44,20 +44,7 @@ public function __construct(array $options = [])

parent::__construct($this->options);

if (! empty($this->options['username']) && ! empty($this->options['password'])) {
$auth = $this->options['username'].':'.$this->options['password'].'@';
} else {
$auth = '';
}

$this->_client = new Client(
'mongodb://'.$auth.$this->options['host'].':'.$this->options['port']
);
$this->_client->selectDatabase($this->options['database']);

if ($this->option('debug')) {
$this->flush();
}
// client init is done lazily see ->client()
}

/**
Expand Down Expand Up @@ -201,12 +188,29 @@ public function cache(): self

public function client(): Client
{
if (! $this->_client) {
if (! empty($this->options['username']) && ! empty($this->options['password'])) {
$auth = $this->options['username'].':'.$this->options['password'].'@';
} else {
$auth = '';
}

$this->_client = new Client(
'mongodb://'.$auth.$this->options['host'].':'.$this->options['port']
);
$this->_client->selectDatabase($this->options['database']);

if ($this->option('debug')) {
$this->flush();
}
}

return $this->_client;
}

public function collection(string $collection): Collection
{
return $this->_client->selectCollection($this->options['database'], $collection);
return $this->client()->selectCollection($this->options['database'], $collection);
}

public function cacheCollection(): Collection
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bnomei/kirby-mongodb",
"type": "kirby-plugin",
"version": "1.0.2",
"version": "1.1.0",
"description": "Khulan is a cache driver and content cache with NoSQL interface for Kirby using MongoDB",
"license": "MIT",
"authors": [
Expand Down
Loading

0 comments on commit 5102513

Please sign in to comment.