Skip to content

Commit 89f4852

Browse files
committed
v2.0.2 upgrades
agg() method, phrase() method, wherePhrasePrefix() method, toDsl() method, Error logging, Bug fix of saving _meta data,
1 parent 1f88dff commit 89f4852

File tree

8 files changed

+218
-36
lines changed

8 files changed

+218
-36
lines changed

README.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,44 @@
22

33
# Laravel-OpenSearch
44

5-
65
> **This package has been built off the back of the original [Elasticsearch version](https://github.com/pdphilip/laravel-elasticsearch) of this package**
7-
>
6+
>
87
> **The starting point of this package was forked from `v4.0.1` with over 2 years of development**
98
10-
[OpenSearch](https://opensearch.net/) is a distributed, community-driven, Apache 2.0-licensed, 100% open-source search and analytics suite used for a broad set of use cases like real-time application monitoring, log analytics, and website search.
9+
[OpenSearch](https://opensearch.net/) is a distributed, community-driven, Apache 2.0-licensed, 100% open-source search and analytics suite used for a broad set of use cases like real-time application monitoring, log analytics, and website
10+
search.
1111

1212
### An OpenSearch implementation of Laravel's Eloquent ORM
1313

1414
This package extends Laravel's Eloquent model and query builder with seamless integration of OpenSearch functionalities. Designed to feel native to Laravel, this package enables you to work with Eloquent models while leveraging the
1515
powerful search and analytics capabilities of OpenSearch.
1616

17-
Example:
17+
Examples:
18+
1819
```php
1920
$logs = UserLog::where('created_at','>=',Carbon::now()->subDays(30))->get();
2021
```
22+
2123
```php
2224
$updates = UserLog::where('status', 1)->update(['status' => 4]);
2325
```
26+
27+
```php
28+
$updates = UserLog::where('status', 1)->paginate(50);
29+
```
30+
2431
```php
2532
$profiles = UserProfile::whereIn('country_code',['US','CA'])->orderByDesc('last_login')->take(10)->get();
2633
```
34+
2735
```php
28-
$deleted = UserProfile::where('state','unsubscribed')->where('updated_at','<=',Carbon::now()->subDays(90)->delete();
36+
$deleted = UserProfile::where('state','unsubscribed')->where('updated_at','<=',Carbon::now()->subDays(90))->delete();
2937
```
38+
3039
```php
31-
$search = UserProfile::term('loves espressos')->minShouldMatch(2)->highlight()->search();
40+
$search = UserProfile::phrase('loves espressos')->highlight()->search();
3241
```
42+
3343
---
3444
> ### Read the [Documentation](https://opensearch.pdphilip.com/)
3545
---
@@ -42,8 +52,8 @@ $search = UserProfile::term('loves espressos')->minShouldMatch(2)->highlight()->
4252
composer require pdphilip/opensearch
4353
```
4454

45-
| Laravel Version | Command | Maintained |
46-
|-----------------|-----------------------------------------------|------------|
55+
| Laravel Version | Command | Maintained |
56+
|-----------------|--------------------------------------------|------------|
4757
| Laravel 10 & 11 | `composer require pdphilip/opensearch:~2 ` ||
4858
| Laravel 8 & 9 | `composer require pdphilip/opensearch:~1` ||
4959

@@ -70,6 +80,7 @@ OS_OPT_VERIFY_SSL=true
7080
OS_OPT_RETRIES=
7181
OS_OPT_SNIFF_ON_START=
7282
OS_OPT_PORT_HOST_HEADERS=
83+
OS_ERROR_INDEX=
7384
```
7485

7586
For multiple nodes, pass in as comma-separated:
@@ -106,10 +117,7 @@ OS_HOSTS="http://opensearch-node1:9200,http://opensearch-node2:9200,http://opens
106117
'sniff_on_start' => env('OS_OPT_SNIFF_ON_START'),
107118
'port_in_host_header' => env('OS_OPT_PORT_HOST_HEADERS'),
108119
],
109-
'query_log' => [
110-
'index' => false, //Or provide a name for the logging index ex: 'laravel_query_logs'
111-
'error_only' => true, //If false, then all queries are logged if the query_log index is set
112-
],
120+
'error_log_index' => env('OS_ERROR_INDEX', false),
113121
],
114122
```
115123

src/Connection.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Connection extends BaseConnection
2525
protected $portInHeaders = null;
2626
protected $rebuild = false;
2727
protected $allowIdSort = true;
28+
protected $errorLoggingIndex = false;
2829

2930
public function __construct(array $config)
3031
{
@@ -59,6 +60,13 @@ public function setOptions($config)
5960
if (isset($config['options']['port_in_host_header'])) {
6061
$this->portInHeaders = $config['options']['port_in_host_header'];
6162
}
63+
if (!empty($config['error_log_index'])) {
64+
if ($this->indexPrefix) {
65+
$this->errorLoggingIndex = $this->indexPrefix.'_'.$config['error_log_index'];
66+
} else {
67+
$this->errorLoggingIndex = $config['error_log_index'];
68+
}
69+
}
6270
}
6371

6472
public function getIndexPrefix(): string|null
@@ -89,6 +97,11 @@ public function setIndex($index): string
8997
return $this->getIndex();
9098
}
9199

100+
public function getErrorLoggingIndex(): string|bool
101+
{
102+
return $this->errorLoggingIndex;
103+
}
104+
92105
public function getSchemaGrammar()
93106
{
94107
return new Schema\Grammar($this);

src/DSL/Bridge.php

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ class Bridge
2121

2222
protected Client $client;
2323

24-
protected mixed $queryLogger = false;
24+
protected string|bool $errorLogger = false;
2525

26-
protected mixed $queryLoggerOnErrorOnly = true;
27-
28-
protected int|null $maxSize = 10; //ES default
26+
protected int|null $maxSize = 10; //OS default
2927

3028
private string|null $index;
3129

@@ -38,11 +36,7 @@ public function __construct(Connection $connection)
3836
$this->index = $this->connection->getIndex();
3937
$this->maxSize = $this->connection->getMaxSize();
4038
$this->indexPrefix = $this->connection->getIndexPrefix();
41-
42-
if (!empty(config('database.connections.elasticsearch.logging.index'))) {
43-
$this->queryLogger = config('database.connections.elasticsearch.logging.index');
44-
$this->queryLoggerOnErrorOnly = config('database.connections.elasticsearch.logging.errorOnly');
45-
}
39+
$this->errorLogger = $this->connection->getErrorLoggingIndex();
4640

4741
}
4842

@@ -191,6 +185,20 @@ public function processIndicesDsl($method, $params): Results
191185
}
192186
}
193187

188+
//----------------------------------------------------------------------
189+
// To DSL
190+
//----------------------------------------------------------------------
191+
192+
public function processToDsl($wheres, $options, $columns)
193+
{
194+
return $this->buildParams($this->index, $wheres, $options, $columns);
195+
}
196+
197+
public function processToDslForSearch($searchParams, $searchOptions, $wheres, $opts, $fields, $cols)
198+
{
199+
return $this->buildSearchParams($this->index, $searchParams, $searchOptions, $wheres, $opts, $fields, $cols);
200+
}
201+
194202

195203
//----------------------------------------------------------------------
196204
// Read Queries
@@ -314,7 +322,9 @@ public function processSave($data, $refresh): Results
314322
if (isset($data['_index'])) {
315323
unset($data['_index']);
316324
}
317-
325+
if (isset($data['_meta'])) {
326+
unset($data['_meta']);
327+
}
318328
$params = [
319329
'index' => $this->index,
320330
'body' => $data,
@@ -665,6 +675,21 @@ public function processIndexAnalyzerSettings($settings): bool
665675
// Aggregates
666676
//----------------------------------------------------------------------
667677

678+
public function processMultipleAggregate($functions, $wheres, $options, $column)
679+
{
680+
$params = $this->buildParams($this->index, $wheres, $options);
681+
try {
682+
$params['body']['aggs'] = ParameterBuilder::multipleAggregations($functions, $column);
683+
$process = $this->client->search($params);
684+
685+
return $this->_return($process['aggregations'] ?? [], $process, $params, $this->_queryTag(__FUNCTION__));
686+
687+
} catch (Exception $e) {
688+
689+
$this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
690+
}
691+
}
692+
668693
/**
669694
* Aggregate entry point
670695
*
@@ -1193,9 +1218,6 @@ private function _return($data, $meta, $params, $queryTag): Results
11931218
$results = new Results($data, $meta, $params, $queryTag);
11941219
}
11951220

1196-
if ($this->queryLogger && !$this->queryLoggerOnErrorOnly) {
1197-
$this->_logQuery($results);
1198-
}
11991221

12001222
return $results;
12011223
}
@@ -1213,9 +1235,7 @@ private function throwError(Exception $exception, $params, $queryTag): QueryExce
12131235
$this->connection->rebuildConnection();
12141236
$error = new Results([], [], $params, $queryTag);
12151237
$error->setError($errorMsg, $errorCode);
1216-
if ($this->queryLogger) {
1217-
$this->_logQuery($error);
1218-
}
1238+
12191239
$meta = $error->getMetaData();
12201240
$details = [
12211241
'error' => $meta['error']['msg'],
@@ -1226,15 +1246,22 @@ private function throwError(Exception $exception, $params, $queryTag): QueryExce
12261246
'params' => $params,
12271247
'original' => $errorMsg,
12281248
];
1249+
if ($this->errorLogger) {
1250+
$this->_logQuery($error, $details);
1251+
}
12291252
// For details catch $exception then $exception->getDetails()
12301253
throw new QueryException($meta['error']['msg'], $errorCode, new $previous, $details);
12311254
}
12321255

1233-
private function _logQuery($results)
1256+
private function _logQuery(Results $results, $details)
12341257
{
1258+
$body = $results->getLogFormattedMetaData();
1259+
if ($details) {
1260+
$body['details'] = (array)$details;
1261+
}
12351262
$params = [
1236-
'index' => $this->queryLogger,
1237-
'body' => $results->getLogFormattedMetaData(),
1263+
'index' => $this->errorLogger,
1264+
'body' => $body,
12381265
];
12391266
try {
12401267
$this->client->index($params);

src/DSL/ParameterBuilder.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,5 +140,38 @@ public static function matrixAggregation(array $fields): array
140140
];
141141
}
142142

143+
public static function multipleAggregations($aggregations, $field)
144+
{
145+
$aggs = [];
146+
foreach ($aggregations as $aggregation) {
147+
switch ($aggregation) {
148+
case 'max':
149+
$aggs['max_'.$field] = self::maxAggregation($field);
150+
break;
151+
case 'min':
152+
$aggs['min_'.$field] = self::minAggregation($field);
153+
break;
154+
case 'avg':
155+
$aggs['avg_'.$field] = self::avgAggregation($field);
156+
break;
157+
case 'sum':
158+
$aggs['sum_'.$field] = self::sumAggregation($field);
159+
break;
160+
case 'matrix':
161+
$aggs['matrix_'.$field] = self::matrixAggregation([$field]);
162+
break;
163+
case 'count':
164+
$aggs['count_'.$field] = [
165+
'value_count' => [
166+
'field' => $field,
167+
],
168+
];
169+
break;
170+
}
171+
}
172+
173+
return $aggs;
174+
175+
}
143176

144177
}

src/DSL/QueryBuilder.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ private function _parseCondition($condition, $parentField = null): array
352352
case 'phrase':
353353
$queryPart = ['match_phrase' => [$field => $operand]];
354354
break;
355+
case 'phrase_prefix':
356+
$queryPart = ['match_phrase_prefix' => [$field => ['query' => $operand]]];
357+
break;
355358
case 'exact':
356359
$keywordField = $this->parseRequiredKeywordMapping($field);
357360
if (!$keywordField) {

src/Eloquent/Builder.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Builder extends BaseEloquentBuilder
4040
'push',
4141
'raw',
4242
'sum',
43-
'toSql',
43+
'tosql',
4444
//ES only:
4545
'matrix',
4646
'query',
@@ -54,6 +54,8 @@ class Builder extends BaseEloquentBuilder
5454
'indexexists',
5555
'createindex',
5656
'search',
57+
'todsl',
58+
'agg',
5759
];
5860

5961

@@ -392,6 +394,28 @@ public function orRegEx(string $regEx, int $boostFactor = null)
392394
return $this;
393395
}
394396

397+
398+
public function phrase(string $term, int $boostFactor = null)
399+
{
400+
$this->query->searchQuery($term, $boostFactor, null, 'phrase');
401+
402+
return $this;
403+
}
404+
405+
public function andPhrase(string $term, int $boostFactor = null)
406+
{
407+
$this->query->searchQuery($term, $boostFactor, 'AND', 'phrase');
408+
409+
return $this;
410+
}
411+
412+
public function orPhrase(string $term, int $boostFactor = null)
413+
{
414+
$this->query->searchQuery($term, $boostFactor, 'OR', 'phrase');
415+
416+
return $this;
417+
}
418+
395419
/**
396420
* @param $value
397421
*

src/Eloquent/Docs/ModelDocs.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,24 @@
1212
* @method static $this regEx(string $term, $boostFactor = null)
1313
* @method $this andRegEx(string $term, $boostFactor = null)
1414
* @method $this orRegEx(string $term, $boostFactor = null)
15+
* @method static $this phrase(string $term, $boostFactor = null)
16+
* @method $this andPhrase(string $term, $boostFactor = null)
17+
* @method $this orPhrase(string $term, $boostFactor = null)
1518
* @method $this minShouldMatch(int $value)
1619
* @method $this minScore(float $value)
1720
* @method $this field(string $field, int $boostFactor = null)
1821
* @method $this fields(array $fields)
1922
* @method search(array $columns = '*')
2023
* @method query(array $columns = '*')
24+
* @method toDsl(array $columns = '*')
25+
* @method agg(array $functions, $column)
2126
*
2227
* @method $this WhereDate($column, $operator = null, $value = null, $boolean = 'and')
2328
* @method $this WhereTimestamp($column, $operator = null, $value = null, $boolean = 'and')
2429
* @method $this whereIn(string $column, array $values)
2530
* @method $this whereExact(string $column, string $value)
2631
* @method $this wherePhrase(string $column, string $value)
32+
* @method $this wherePhrasePrefix(string $column, string $value)
2733
* @method $this filterGeoBox(string $column, array $topLeftCoords, array $bottomRightCoords)
2834
* @method $this filterGeoPoint(string $column, string $distance, array $point)
2935
* @method $this whereRegex(string $column, string $regex)

0 commit comments

Comments
 (0)