@@ -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+
2286Installation
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
83114Configuration
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+
437579Elasticsearch 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-
14551592Dynamic Indies
14561593==============
14571594In 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() `
0 commit comments