Skip to content
This repository has been archived by the owner on Aug 20, 2023. It is now read-only.

Commit

Permalink
🐣
Browse files Browse the repository at this point in the history
  • Loading branch information
jcohlmeyer committed Sep 20, 2020
0 parents commit 5205913
Show file tree
Hide file tree
Showing 11 changed files with 520 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
node_modules
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Use MeiliSearch to provide instant search

[![Statamic 3.0+](https://img.shields.io/badge/Statamic-3.0%2B-FF269E)](https://statamic.com)
[![Commercial License](https://img.shields.io/badge/License-Commercial-yellow)](#)

This Statamic v3 addon provides an easy way to integrate with [MeiliSearch](https://www.meilisearch.com/), a powerful, fast, open-source, easy to use and deploy search engine.

## Requirements

* PHP 7.2+
* Statamic v3+
* Laravel 7+

## Installation

You can install this addon via composer with the following command or from the Statamic control panel.

```bash
composer require kind-work/meili-search
```

## Configuration

### .env
Configure the addon by setting your MeiliSearch URL and API Keys in your `.env` file.

```yaml
MEILI_URL=http://localhost:7700
MEILI_MASTER_KEY=your-master-key-here
MEILI_PRIVATE_KEY=your-private-key-here
MEILI_PUBLIC_KEY=your-public-key-here
```

After you add your master key you can use the following command to get your private and public keys.

`php please meili-search:keys`

### Settings
To configure what collections you would like to index, publish the config file too `config/meili_search.php` by running the following command. Then customize the indexes section of the file.

```bash
php artisan vendor:publish --tag="meili_search-config"
```

## Changelog

Please see the [Release Notes](https://statamic.com/addons/jrc9designstudio/meili-search/release-notes) for more information what has changed recently.

## Security

If you discover any security related issues, please email [security@kind.work](mailto:security@kind.work) instead of using the issue tracker.

## License

This is commercial software. You may use the package for your sites. Each site requires its own license. You can purchase a licence from [The Statamic Marketplace](https://statamic.com/addons/jrc9designstudio/meili-search).
32 changes: 32 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "kind-work/meili-search",
"description": "Use MeiliSearch to provide instant search for Statamic V3.",
"type": "statamic-addon",
"require": {
"meilisearch/meilisearch-php": "^0.13.3"
},
"autoload": {
"psr-4": {
"KindWork\\MeiliSearch\\": "src"
}
},
"authors": [
{
"name": "Jonathan Cohlmeyer"
}
],
"support": {
"email": "help@kind.work"
},
"extra": {
"statamic": {
"name": "Statamic V3 Meili Search Addon",
"description": "Use MeiliSearch to provide instant search for Statamic V3."
},
"laravel": {
"providers": [
"KindWork\\MeiliSearch\\ServiceProvider"
]
}
}
}
73 changes: 73 additions & 0 deletions resources/config/meili_search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

return [

/*
|--------------------------------------------------------------------------
| Meili Address
|--------------------------------------------------------------------------
|
| This full URL for the Meili server, including protocol and port number IE:
| - http://localhost:7700
| - https://meili.example.com
| - https://example.com/meili
|
*/

'url' => env('MEILI_URL', 'http://localhost:7700'),

/*
|--------------------------------------------------------------------------
| Meili Search Master Key
|--------------------------------------------------------------------------
|
| This master key for your Meili Search instance. Has access to everything!
|
*/

'master_key' => env('MEILI_MASTER_KEY', false),


/*
|--------------------------------------------------------------------------
| Meili Search Private Key
|--------------------------------------------------------------------------
|
| This private key for your Meili Search instance.
| Has access to everything except can not list keys.
|
*/

'private_key' => env('MEILI_PRIVATE_KEY', false),


/*
|--------------------------------------------------------------------------
| Meili Search Public Key
|--------------------------------------------------------------------------
|
| This public key for your Meili Search instance.
| Can search and retrieve documents and get the health status of Meili.
|
*/

'public_key' => env('MEILI_PUBLIC_KEY', false),


/*
|--------------------------------------------------------------------------
| Meili Indexes
|--------------------------------------------------------------------------
|
| Define the indexes you want to use, the collections you want to index
| and the fields you want to index on those collections.
|
*/

'indexes' => [
'kindwork' => [
'collection:2fa_docs' => [ 'title', 'content' ],
'collection:pages' => [ 'title', 'content' ],
],
],
];
112 changes: 112 additions & 0 deletions src/Console/Commands/DocumentsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace KindWork\MeiliSearch\Console\Commands;

use Config;
use MeiliSearch\Client;
use \Statamic\Facades\Entry;
use Illuminate\Support\Arr;
use Illuminate\Console\Command;
use Statamic\Console\RunsInPlease;

class DocumentsCommand extends Command {
use RunsInPlease;

protected $name = 'meili-search:documents';
protected $description = 'Document convenience commands';
protected $signature = 'meili-search:documents {method=null} {document=null}';
protected $methods = ['help', 'update'];
protected $client;

public function handle() {
$this->client = new Client(Config::get('meili_search.url'), Config::get('meili_search.private_key'));
$method = $this->argument('method');

if($method == 'null') {
$this->help();
} elseif (!in_array($method, $this->methods, true)) {
$this->warn('Method not supported. Supported methods are: ' . join(', ', $this->methods));
} else {
$this->{$method}();
}
}

private function help() {
$this->info('Usage:');
$this->line('
meili-search:documents [method=help] [document=null]
');
$this->info('Methods:');
$this->line('
- help Show the help
- update Update or remove documents from the indexes
');
}

private function update() {
// Get the indexes configured
$indexes = Config::get('meili_search.indexes');

// For each index lets get the documents and update
foreach($indexes as $indexName => $indexConfigs) {
// Say we have stared indexing a specific index
$this->info('Updating index: ' . $indexName);

// For each index map over the configured collections
// Then collapse them into a 1 dimensional array
$documents = Arr::collapse(array_map(function($config, $fields) {
// Get the collection handle (Maybe get things other than collections later?)
$collectionHandle = explode(':', $config)[1];

// Query for the entries in the collection
$entries = Entry::query()
->where('collection', $collectionHandle)
->where('published', true)
->get()
->preProcessForIndex()
->toArray();

// Return the document data for indexing with a map
return array_map(function($entry) use($fields) {

// Merge in the id and fields
return array_merge(
// Add in the entry ID
[ 'id' => $entry ->id() ],

// Filter to use only the keys defined in the config
array_filter($entry->data()->toArray(), function($key) use($fields) {
return in_array($key, $fields);
}, ARRAY_FILTER_USE_KEY),

// Add in the entry URLs
[
'uri' => $entry->uri(),
'api_url' => $entry->apiUrl(),
]
);
}, $entries);
}, array_keys($indexConfigs), $indexConfigs));

// Get the index (or make it if it does not exist)
$index = $this->client->getOrCreateIndex($indexName, ['primaryKey' => 'id']);

// Find the removed document ids
$removedDocuments = array_merge(
array_diff(
array_map(function($entry) { return $entry['id']; }, $index->getDocuments()),
array_map(function($entry) { return $entry['id']; }, $documents)
)
);

// Delete all the removed documents
$index->deleteDocuments($removedDocuments);

// Add or update documents
$index->addDocuments($documents);

// Report back that we are done
$this->info('Finished updating index: ' . $indexName);
}
}
}
85 changes: 85 additions & 0 deletions src/Console/Commands/IndexCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace KindWork\MeiliSearch\Console\Commands;

use Config;
use MeiliSearch\Client;
use Illuminate\Console\Command;
use Statamic\Console\RunsInPlease;

class IndexCommand extends Command {
use RunsInPlease;

protected $name = 'meili-search:index';
protected $description = 'Index convenience commands';
protected $signature = 'meili-search:index {method=null} {index=null}';
protected $methods = ['help', 'create', 'list', 'clear', 'delete'];
protected $index;
protected $client;

public function handle() {
$this->index = $this->argument('index');
$method = $this->argument('method');
$this->client = new Client(Config::get("meili_search.url"), Config::get("meili_search.private_key"));

if ($method == 'null') {
$this->help();
} elseif (!$this->index && $method != 'index') {
$this->warn('A index name is required!');
} elseif (!in_array($method, $this->methods, true)) {
$this->warn('Method not supported. Supported methods are: ' . join(', ', $this->methods));
} else {
$this->{$method}();
}
}

private function help() {
$this->info('Usage:');
$this->line('
meili-search:index [method=help] [index=null] [primaryKey=id]
');
$this->info('Methods:');
$this->line('
- help Show the help
- create Create an search index
- list List all indexes
- clear Clear all the enteries from an index
- delete Delete a search index
');
$this->info('Arguments:');
$this->line('
- index The uid of the index to use
');
}

private function create() {
$this->client->createIndex($this->index, ['primaryKey' => 'id']);
$this->info('Index ' . $this->index . ' created');
}

private function list() {
$indexes = $this->client->getAllIndexes();
$this->info('Indexes:');
foreach($indexes as $index) {
$stats = $index->stats();
$this->line('
UID: ' . $index->getUid() . '
Primary Key: ' . $index->getPrimaryKey() . '
# of Documents: ' . $stats['numberOfDocuments'] . '
Is Indexing: ' . ($stats['isIndexing'] ? 'true' : 'false') . '
');
}
}

private function clear() {
$index = $this->client->getIndex($this->index);
$index->deleteAllDocuments();
$this->info('Index ' . $this->index . ' cleared');
}

private function delete() {
$index = $this->client->getIndex($this->index);
$index->delete();
$this->info('Index ' . $this->index . ' deleted');
}
}
28 changes: 28 additions & 0 deletions src/Console/Commands/KeysCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace KindWork\MeiliSearch\Console\Commands;

use Config;
use MeiliSearch\Client;
use Illuminate\Console\Command;
use Statamic\Console\RunsInPlease;

class KeysCommand extends Command {
use RunsInPlease;

protected $name = 'meili-search:keys';
protected $description = 'Get the master key derived private and public keys';
protected $signature = 'meili-search:keys';
protected $client;

public function handle() {
$this->client = new Client(Config::get("meili_search.url"), Config::get("meili_search.master_key"));
$keys = $this->client->getKeys();
$this->info('Keys:');
$this->line('
Master: ' . Config::get("meili_search.master_key") . '
Private: ' . $keys['private'] . '
Public: ' . $keys['public'] . '
');
}
}
Loading

0 comments on commit 5205913

Please sign in to comment.