Skip to content

Commit 312955a

Browse files
committed
distinct() and groupBy() feature upgrade
1 parent 5ee70bf commit 312955a

File tree

6 files changed

+596
-109
lines changed

6 files changed

+596
-109
lines changed

README.md

Lines changed: 191 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,66 +19,97 @@ Elasticsearch in laravel as if it were native to Laravel, meaning:
1919
- No need to write your own DSL queries ([unless you want to](#raw-dsl))
2020
- [Eloquent style searching](#elasticsearching)
2121

22+
# Table of Contents
23+
24+
- [Laravel x Elasticsearch](#laravel-x-elasticsearch)
25+
- [Features](#features)
26+
- [Installation](#installation)
27+
- [Configuration](#configuration)
28+
- [Eloquent](#eloquent)
29+
- [Extending the Base Model](#extending-the-base-model)
30+
- [Querying Models](#querying-models)
31+
- [ALL](#all)
32+
- [Find](#find)
33+
- [First](#first)
34+
- [Where](#where)
35+
- [Where LIKE](#where-like)
36+
- [OR Statements](#or-statements)
37+
- [Chaining OR/AND Statements](#chaining-orand-statements)
38+
- [WhereIn](#wherein)
39+
- [WhereNotIn](#wherenotin)
40+
- [WhereNotNull](#wherenotnull)
41+
- [WhereNull](#wherenull)
42+
- [WhereBetween](#wherebetween)
43+
- [Dates](#dates)
44+
- [Aggregation](#aggregation)
45+
- [Ordering](#ordering)
46+
- [OrderBy](#orderby)
47+
- [OrderByDesc](#orderbydesc)
48+
- [Offset & Limit](#offset--limit)
49+
- [Pagination](#pagination)
50+
- [Distinct and GroupBy](#distinct-and-groupby)
51+
- [Basic Usage](#basic-usage)
52+
- [Working with Collections](#working-with-collections)
53+
- [Multiple Fields Aggregation](#multiple-fields-aggregation)
54+
- [Ordering by Aggregation Count](#ordering-by-aggregation-count)
55+
- [Returning Count with Distinct Results](#returning-count-with-distinct-results)
56+
- [Pagination Support](#pagination-support)
57+
- [Elasticsearch Specific Queries](#elasticsearch-specific-queries)
58+
- [Geo](#geo)
59+
- [Regex in Where](#regex-in-where)
60+
- [Saving Models](#saving-models)
61+
- [Save](#save)
62+
- [Create](#create)
63+
- [Update](#update)
64+
- [Mass Updating](#mass-updating)
65+
- [Fast Saves](#fast-saves)
66+
- [Deleting Models](#deleting-models)
67+
- [Delete](#delete)
68+
- [Truncate](#truncate)
69+
- [Destroy by ID](#destroy-by-id)
70+
- [Soft Deletes](#soft-deletes)
71+
- [Elasticsearching](#elasticsearching)
72+
- [The Search Query](#the-search-query)
73+
- [FuzzyTerm](#fuzzyterm)
74+
- [RegEx in Search](#regex-in-search)
75+
- [Mutators & Casting](#mutators--casting)
76+
- [Relationships](#relationships)
77+
- [Elasticsearch <-> Elasticsearch](#elasticsearch---elasticsearch)
78+
- [Elasticsearch <-> MySQL](#elasticsearch---mysql)
79+
- [Schema/Index](#schemaindex)
80+
- [Migrations](#migrations)
81+
- [Dynamic Indies](#dynamic-indies)
82+
- [RAW DSL](#raw-dsl)
83+
- [Elasticsearchisms](#elasticsearchisms)
84+
- [Unsupported Eloquent Methods](#unsupported-eloquent-methods)
85+
2286
Installation
2387
===============
2488

2589
## Elasticsearch 8.x
2690

27-
Laravel 10.x:
91+
Laravel 10.x (Latest):
2892

2993
```bash
3094
$ composer require pdphilip/elasticsearch
3195
```
3296

33-
Laravel 9.x:
34-
35-
```bash
36-
$ composer require pdphilip/elasticsearch:~2.9
37-
```
38-
39-
Laravel 8.x:
40-
41-
```bash
42-
$ composer require pdphilip/elasticsearch:~2.8
43-
```
44-
45-
Laravel 7.x:
46-
47-
```bash
48-
$ composer require pdphilip/elasticsearch:~2.7
49-
```
50-
51-
Laravel 6.x (and 5.8):
52-
53-
```bash
54-
$ composer require pdphilip/elasticsearch:~2.6
55-
```
97+
| Laravel Version | Command | Maintained |
98+
|-------------------|--------------------------------------------------|------------|
99+
| Laravel 10.x | `$ composer require pdphilip/elasticsearch` ||
100+
| Laravel 9.x | `$ composer require pdphilip/elasticsearch:~2.9` ||
101+
| Laravel 8.x | `$ composer require pdphilip/elasticsearch:~2.8` ||
102+
| Laravel 7.x | `$ composer require pdphilip/elasticsearch:~2.7` ||
103+
| Laravel 6.x (5.8) | `$ composer require pdphilip/elasticsearch:~2.6` ||
56104

57105
## Elasticsearch 7.x
58106

59-
Laravel 9.x:
60-
61-
```bash
62-
$ composer require pdphilip/elasticsearch:~1.9
63-
```
64-
65-
Laravel 8.x:
66-
67-
```bash
68-
$ composer require pdphilip/elasticsearch:~1.8
69-
```
70-
71-
Laravel 7.x:
72-
73-
```bash
74-
$ composer require pdphilip/elasticsearch:~1.7
75-
```
76-
77-
Laravel 6.x (and 5.8):
78-
79-
```bash
80-
$ composer require pdphilip/elasticsearch:~1.6
81-
```
107+
| Laravel Version | Command | Maintained |
108+
|-------------------|--------------------------------------------------|------------|
109+
| Laravel 9.x | `$ composer require pdphilip/elasticsearch:~1.9` ||
110+
| Laravel 8.x | `$ composer require pdphilip/elasticsearch:~1.8` ||
111+
| Laravel 7.x | `$ composer require pdphilip/elasticsearch:~1.7` ||
112+
| Laravel 6.x (5.8) | `$ composer require pdphilip/elasticsearch:~1.6` ||
82113

83114
Configuration
84115
===============
@@ -434,6 +465,117 @@ Pagination links (Blade)
434465
{{ $products->appends(request()->query())->links() }}
435466
```
436467

468+
Distinct and GroupBy
469+
-----------------------------
470+
This section covers the implementation of `distinct()` and `groupBy()` methods in the Elasticsearch Eloquent model.
471+
These methods are interchangeable and use term aggregation under the hood.
472+
473+
This tends to be a core use case for Elasticsearch, for example, to get all the unique user_ids of the users who have
474+
been
475+
logged in the last 30 days:
476+
477+
#### Basic Usage
478+
479+
- **Distinct**:
480+
481+
```php
482+
// Unique user_ids of users logged in the last 30 days
483+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->distinct()->get('user_id');
484+
//or:
485+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->select('user_id')->distinct()->get();
486+
```
487+
488+
- **GroupBy**:
489+
490+
```php
491+
// Equivalent to the above distinct query
492+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->groupBy('user_id')->get();
493+
494+
```
495+
496+
#### Working with Collections
497+
498+
- The results from these queries are returned as collections, allowing use of standard collection methods.
499+
- Example of loading related user data:
500+
501+
```php
502+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->distinct()->get('user_id');
503+
return $users->load('user');
504+
```
505+
506+
#### Multiple Fields Aggregation
507+
508+
- You can pass multiple fields to perform term aggregation.
509+
- Example:
510+
511+
```php
512+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->distinct()->get(['user_id', 'log_title']);
513+
/** returns:
514+
{
515+
"user_id": "1",
516+
"log_title": "LOGGED_IN"
517+
},
518+
{
519+
"user_id": "2",
520+
"log_title": "LOGGED_IN"
521+
},
522+
{
523+
"user_id": "2",
524+
"log_title": "LOGGED_OUT"
525+
},
526+
**/
527+
```
528+
529+
#### Ordering by Aggregation Count
530+
531+
- Results can be sorted based on the count of the aggregated field.
532+
- Example of ordering by the most logged users:
533+
534+
```php
535+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->distinct()->orderBy('_count')->get('user_id');
536+
```
537+
538+
- Or you can order by the distinct field, example:
539+
540+
```php
541+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->distinct()->orderBy('user_id')->get('user_id');
542+
```
543+
544+
#### Returning Count with Distinct Results
545+
546+
- To include the count of distinct values in the results, use `distinct(true)`:
547+
548+
```php
549+
$users = UserLog::where('created_at', '>=', Carbon::now()->subDays(30))->distinct(true)->orderBy('_count')->get(['user_id']);
550+
/** returns:
551+
{
552+
"user_id": "5",
553+
"user_id_count": 65
554+
},
555+
{
556+
"user_id": "1",
557+
"user_id_count": 61
558+
},
559+
{
560+
"user_id": "9",
561+
"user_id_count": 54
562+
},
563+
**/
564+
```
565+
566+
#### Pagination Support
567+
568+
- The `distinct()` and `groupBy()` methods support pagination.
569+
- Example:
570+
571+
```php
572+
573+
$users = UserLog::where('log_title', 'LOGGED_IN')->select('user_id')->distinct()->orderBy('_count')->paginate(20);
574+
//or
575+
$users = UserLog::where('log_title', 'LOGGED_IN')->groupBy('user_id')->orderBy('_count')->paginate(20);
576+
577+
```
578+
437579
Elasticsearch specific queries
438580
-----------------------------
439581

@@ -1355,7 +1497,7 @@ SiteLog::create([
13551497
'user_ip' => '0.0.0.0',
13561498
'location' => ['lat' => -7.3,'lon' => 3.1]
13571499
]);
1358-
//'location' index will be created by elasticsearch as an object
1500+
//'location' field will be indexed by elasticsearch as an object
13591501

13601502
//Trying to filter by location will fail due to the incorrect mapping
13611503
$logs = SiteLog::filterGeoBox('location',[-10,10],[10,-10])->get();
@@ -1447,11 +1589,6 @@ Product::chunkById(1000, function ($products) {
14471589
}, 'product_sku.keyword');
14481590
```
14491591

1450-
Queues
1451-
----------
1452-
_[Coming]_
1453-
1454-
14551592
Dynamic Indies
14561593
==============
14571594
In some cases you will need to split a model into different indices. There are limits to this to keep within reasonable
@@ -1598,4 +1735,4 @@ once and not update immediately or won't need to search for the record immediate
15981735

15991736
### Unsupported Eloquent methods
16001737

1601-
`upsert()`, `distinct()`, `groupBy()`, `groupByRaw()`
1738+
`upsert()`, `groupByRaw()`

composer.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,9 @@
3333
"doctrine/coding-standard": "12.0.x-dev"
3434
},
3535
"autoload-dev": {
36-
"classmap": [
37-
"tests/TestCase.php",
38-
"tests/models",
39-
"tests/seeds"
40-
]
36+
"psr-4": {
37+
"PDPhilip\\Elasticsearch\\Tests\\": "tests/"
38+
}
4139
},
4240
"autoload": {
4341
"psr-4": {

0 commit comments

Comments
 (0)