Skip to content
This repository was archived by the owner on Dec 12, 2021. It is now read-only.

Commit c55ed96

Browse files
- First Release.
0 parents  commit c55ed96

File tree

7 files changed

+507
-0
lines changed

7 files changed

+507
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
.tmp
3+
/composer.lock
4+
vendor

composer.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"name": "devloopsnet/laravel-typesense",
3+
"description": "Typesense laravel/scout engine",
4+
"keywords": [
5+
"laravel",
6+
"typesense",
7+
"search"
8+
],
9+
"type": "library",
10+
"homepage": "https://www.devloops.net",
11+
"license": "MIT",
12+
"authors": [
13+
{
14+
"name": "Abdullah Al-Faqeir",
15+
"email": "[email protected]",
16+
"homepage": "https://www.devloops.net",
17+
"role": "Developer"
18+
}
19+
],
20+
"minimum-stability": "stable",
21+
"autoload": {
22+
"psr-4": {
23+
"Devloops\\LaravelTypesense\\": "src/"
24+
}
25+
},
26+
"extra": {
27+
"laravel": {
28+
"providers": [
29+
"Devloops\\LaravelTypesense\\TypesenseServiceProvider"
30+
],
31+
"aliases": {
32+
"Typesense": "Devloops\\LaravelTypesense\\TypesenseFacade"
33+
}
34+
}
35+
},
36+
"require": {
37+
"php": "~7.2",
38+
"laravel/scout": "7.*|^8.0",
39+
"illuminate/bus": "~5.4|^6.0|^7.0",
40+
"illuminate/contracts": "~5.4|^6.0|^7.0",
41+
"illuminate/database": "~5.4|^6.0|^7.0",
42+
"illuminate/pagination": "~5.4|^6.0|^7.0",
43+
"illuminate/queue": "~5.4|^6.0|^7.0",
44+
"illuminate/support": "~5.4|^6.0|^7.0",
45+
"devloopsnet/typesens-php": "^1.0"
46+
},
47+
"suggest": {
48+
"devloopsnet/typesens-php": "Required to use the Typesense php client."
49+
},
50+
"require-dev": {
51+
"phpunit/phpunit": "^8.0|^9.0",
52+
"mockery/mockery": "^1.3"
53+
}
54+
}

src/Engines/TypesenseSearchEngine.php

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
<?php
2+
3+
namespace Devloops\LaravelTypesense\Engines;
4+
5+
use Laravel\Scout\Builder;
6+
use Laravel\Scout\Engines\Engine;
7+
use Illuminate\Support\Collection;
8+
use Illuminate\Database\Eloquent\Model;
9+
use Devloops\LaravelTypesense\Typesense;
10+
use GuzzleHttp\Exception\GuzzleException;
11+
use Devloops\Typesence\Exceptions\TypesenseClientError;
12+
13+
/**
14+
* Class TypesenseSearchEngine
15+
*
16+
* @package Devloops\LaravelTypesense\Engines
17+
* @date 4/5/20
18+
* @author Abdullah Al-Faqeir <[email protected]>
19+
*/
20+
class TypesenseSearchEngine extends Engine
21+
{
22+
23+
private $typesense;
24+
25+
/**
26+
* TypesenseSearchEngine constructor.
27+
*
28+
* @param $typesense
29+
*/
30+
public function __construct(Typesense $typesense)
31+
{
32+
$this->typesense = $typesense;
33+
}
34+
35+
/**
36+
* @inheritDoc
37+
*/
38+
public function update($models): void
39+
{
40+
$models->each(
41+
function (Model $model) {
42+
$array = $model->toSearchableArray();
43+
44+
$collectionIndex = $this->typesense->getCollectionIndex($model);
45+
46+
$this->typesense->upsertDocument($collectionIndex, $array);
47+
}
48+
);
49+
}
50+
51+
/**
52+
* @inheritDoc
53+
*/
54+
public function delete($models): void
55+
{
56+
$models->each(
57+
function (Model $model) {
58+
$collectionIndex = $this->typesense->getCollectionIndex($model);
59+
60+
$this->typesense->deleteDocument(
61+
$collectionIndex,
62+
$model->{$model->getKey()}
63+
);
64+
}
65+
);
66+
}
67+
68+
/**
69+
* @inheritDoc
70+
*/
71+
public function search(Builder $builder)
72+
{
73+
return $this->performSearch(
74+
$builder,
75+
array_filter(
76+
[
77+
'q' => $builder->query,
78+
'query_by' => implode(',', $builder->model->typesenseQueryBy()),
79+
'fiter_by' => $this->filters($builder),
80+
'per_page' => $builder->limit,
81+
'page' => 1,
82+
]
83+
)
84+
);
85+
}
86+
87+
/**
88+
* @inheritDoc
89+
*/
90+
public function paginate(Builder $builder, $perPage, $page)
91+
{
92+
return $this->performSearch(
93+
$builder,
94+
[
95+
'q' => $builder->query,
96+
'query_by' => implode(',', $builder->model->typesenseQueryBy()),
97+
'fiter_by' => $this->filters($builder),
98+
'per_page' => $builder->limit,
99+
'page' => 1,
100+
]
101+
);
102+
}
103+
104+
/**
105+
* @param \Laravel\Scout\Builder $builder
106+
* @param array $options
107+
*
108+
* @return array|mixed
109+
* @throws \Devloops\Typesence\Exceptions\TypesenseClientError
110+
* @throws \GuzzleHttp\Exception\GuzzleException
111+
*/
112+
protected function performSearch(Builder $builder, array $options = [])
113+
{
114+
$documents =
115+
$this->typesense->getCollectionIndex($builder->model)->getDocuments();
116+
if ($builder->callback) {
117+
return call_user_func(
118+
$builder->callback,
119+
$documents,
120+
$builder->query,
121+
$options
122+
);
123+
}
124+
return $documents->search(
125+
$options
126+
);
127+
}
128+
129+
/**
130+
* @param \Laravel\Scout\Builder $builder
131+
*
132+
* @return array
133+
*/
134+
protected function filters(Builder $builder): array
135+
{
136+
return collect($builder->wheres)->map(
137+
static function ($value, $key) {
138+
return $key . ':=' . $value;
139+
}
140+
)->values()->all();
141+
}
142+
143+
/**
144+
* @inheritDoc
145+
*/
146+
public function mapIds($results): Collection
147+
{
148+
return collect($results['hits'])->pluck('document.id')->values();
149+
}
150+
151+
/**
152+
* @inheritDoc
153+
*/
154+
public function map(Builder $builder, $results, $model)
155+
{
156+
if ((int)($results['found'] ?? 0) === 0) {
157+
return $model->newCollection();
158+
}
159+
160+
$objectIds =
161+
collect($results['hits'])->pluck('document.id')->values()->all();
162+
$objectIdPositions = array_flip($objectIds);
163+
return $model->getScoutModelsByIds(
164+
$builder,
165+
$objectIds
166+
)->filter(
167+
static function ($model) use ($objectIds) {
168+
return in_array($model->getScoutKey(), $objectIds, false);
169+
}
170+
)->sortBy(
171+
static function ($model) use ($objectIdPositions) {
172+
return $objectIdPositions[$model->getScoutKey()];
173+
}
174+
)->values();
175+
}
176+
177+
/**
178+
* @inheritDoc
179+
*/
180+
public function getTotalCount($results): int
181+
{
182+
return (int)($results['found'] ?? 0);
183+
}
184+
185+
/**
186+
* @inheritDoc
187+
*/
188+
public function flush($model): void
189+
{
190+
$collection = $this->typesense->getCollectionIndex($model);
191+
try {
192+
$collection->delete();
193+
} catch (TypesenseClientError $e) {
194+
} catch (GuzzleException $e) {
195+
}
196+
}
197+
198+
}

src/Interfaces/TypesenseSearch.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Devloops\LaravelTypesense\Interfaces;
4+
5+
/**
6+
* Interface TypesenseSearch
7+
*
8+
* @package Devloops\LaravelTypesense\Interfaces
9+
*/
10+
interface TypesenseSearch
11+
{
12+
13+
public function typesenseQueryBy(): array;
14+
15+
public function getCollectionSchema(): array;
16+
17+
}

0 commit comments

Comments
 (0)