如何用Laravel Scout Elastic实现Elasticsearch中parent与childrens索引关联?
parents and childrens Indices in Elasticsearch with Laravel Scout Elastic Hey there! Since you're new to Elasticsearch and using tamayo/laravel-scout-elastic in your Laravel project, let's walk through how to establish a relationship between your parents and childrens indices. Elasticsearch isn't a relational database, so we have two common patterns to choose from—nested documents and parent-child relationships—each with their own use cases. Let's cover both.
Option 1: Nested Documents (Best for "Always Query Together" Scenarios)
If you usually query parents along with their children and don't need to update children independently, nested documents are a great fit. This stores children as an array directly inside the parent document in Elasticsearch.
Step 1: Define the Eloquent Relationship
First, make sure your Laravel models have the correct Eloquent relationships (note: rename the parent model to avoid conflict with PHP's built-in Parent class):
// app/Models/ParentModel.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class ParentModel extends Model { use Searchable; protected $table = 'parents'; // Define relationship to children public function children() { return $this->hasMany(Children::class, 'parent_id'); } // Specify the Elasticsearch index name public function searchableAs() { return 'parents'; } // Customize data sent to Elasticsearch (include children) public function toSearchableArray() { $array = $this->toArray(); // Load the children relationship and add it to the document $array['children'] = $this->children->toArray(); return $array; } // Define Elasticsearch mapping for the nested field public function getMapping() { return [ 'properties' => [ 'id' => ['type' => 'integer'], 'name' => ['type' => 'text'], // Add other parent fields as needed 'children' => [ 'type' => 'nested', 'properties' => [ 'id' => ['type' => 'integer'], 'name' => ['type' => 'text'], // Add other children fields as needed ] ] ] ]; } }
// app/Models/Children.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Children extends Model { protected $table = 'childrens'; // Define inverse relationship to parent public function parent() { return $this->belongsTo(ParentModel::class, 'parent_id'); } }
Step 2: Create the Index Mapping
Run this Artisan command to create the index with the nested mapping (flush first if the index already exists):
# Flush existing index (if needed) php artisan scout:flush "App\Models\ParentModel" # Import parent data with nested children php artisan scout:import "App\Models\ParentModel"
Step 3: Querying Nested Documents
To search for parents with children matching specific criteria, use a nested query:
use Laravel\Scout\Builder; $parents = ParentModel::search('') ->query(function (Builder $builder) { $builder->nested('children', function ($query) { $query->match('children.name', 'John'); }); }) ->get();
Option 2: Parent-Child Relationships (Best for Independent Updates)
If you need to update children documents without reindexing the entire parent, or if you have a huge number of children per parent, use Elasticsearch's join data type to establish a parent-child relationship (both types live in a single index).
Step 1: Define the Parent Model with Join Mapping
// app/Models/ParentModel.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class ParentModel extends Model { use Searchable; protected $table = 'parents'; // Single index for both parents and children public function searchableAs() { return 'family_index'; } public function toSearchableArray() { $array = $this->toArray(); // Mark this as a parent document in the join field $array['family_relation'] = [ 'name' => 'parent', ]; return $array; } public function getMapping() { return [ 'properties' => [ 'id' => ['type' => 'integer'], 'name' => ['type' => 'text'], // Define the join relationship 'family_relation' => [ 'type' => 'join', 'relations' => [ 'parent' => 'children' // parent = parent type, children = child type ] ] ] ]; } }
Step 2: Define the Children Model with Parent Reference
// app/Models/Children.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Children extends Model { use Searchable; protected $table = 'childrens'; // Use the same index as the parent public function searchableAs() { return 'family_index'; } public function toSearchableArray() { $array = $this->toArray(); // Mark this as a child document and link to its parent $array['family_relation'] = [ 'name' => 'children', 'parent' => (string) $this->parent_id // Must be a string to match Elasticsearch's _id ]; return $array; } }
Step 3: Create the Index and Import Data
- First create the index with the join mapping by importing the parent model:
php artisan scout:import "App\Models\ParentModel"
- Then import the children documents:
php artisan scout:import "App\Models\Children"
Step 4: Querying Parent-Child Relationships
- Find parents that have matching children:
$parents = ParentModel::search('') ->query(function ($builder) { $builder->hasChild('children', function ($query) { $query->match('name', 'John'); }); }) ->get();
- Find children that belong to a matching parent:
$children = Children::search('') ->query(function ($builder) { $builder->hasParent('parent', function ($query) { $query->match('name', 'Alice'); }); }) ->get();
Key Notes
- Nested Documents: Easier to query, but updating a child requires reindexing the entire parent. Best for small, tightly coupled datasets.
- Parent-Child Relationships: More flexible for independent updates, but queries are slightly more complex and have a small performance overhead. Best for large datasets with frequent child updates.
Test both approaches with your specific use case to see which works better!
内容的提问来源于stack exchange,提问作者Jasmel Pc

