Laravel Optimization Technology

Getting Started with Laravel Solr: A Practical Guide

  • January 8, 2025
  • 8 min read
Getting Started with Laravel Solr: A Practical Guide

Integrating Apache Solr with Laravel: A Complete Guide

Introduction

In modern web applications, implementing robust search functionality is crucial. While Laravel provides built-in search capabilities, complex applications often require more sophisticated solutions. This guide will walk you through integrating Apache Solr with Laravel using the Solarium package to create a powerful search system.


Why Choose Solr for Laravel?

Laravel’s Native Search vs Solr

Laravel’s built-in search capabilities work well for basic needs, but Solr offers several advantages:

  1. Advanced Search Features:
    • Full-text search with complex queries
    • Faceted search capabilities
    • Geospatial search
    • Suggestion/autocomplete functionality
  2. Performance Benefits:
    • Highly scalable for millions of records
    • Fast query response times
    • Efficient index management
    • Built-in caching
  3. Additional Features:
    • Custom scoring and relevance
    • Advanced text analysis
    • Multiple language support
    • Real-time indexing

Setting Up Your Environment

Prerequisites

  1. PHP 8.1 or higher
  2. Laravel 10.x
  3. Apache Solr 8.11.2
  4. Composer

Step 1: Installing Solr

# Download Solr
wget https://downloads.apache.org/lucene/solr/8.11.2/solr-8.11.2.tgz
tar xzf solr-8.11.2.tgz
cd solr-8.11.2

# Start Solr
bin/solr start

# Create a new core
bin/solr create -c articles


Step 2: Creating a New Laravel Project

composer create-project laravel/laravel laravel-solr-demo
cd laravel-solr-demo

Step 3: Installing Required Packages

composer require solarium/solarium


Implementation Guide

Step 1: Setting Up the Service Provider

Create a new service provider to manage the Solr client:

php artisan make:provider SolrServiceProvider

Add the following configuration:

namespace App\Providers;  
use Illuminate\Support\ServiceProvider; 
use Solarium\Client; 
use Solarium\Core\Client\Adapter\Curl; 
use Symfony\Component\EventDispatcher\EventDispatcher;  
class SolrServiceProvider extends ServiceProvider {  
     public function register()  {  
       $this->app->singleton(Client::class, function($app) {  
         $adapter = new Curl();  $eventDispatcher = new EventDispatcher();   
         return new Client($adapter, $eventDispatcher, 
           [  
             'endpoint' => [  'localhost' => 
             [   
               'host' => env('SOLR_HOST', '127.0.0.1'),  
               'port' => env('SOLR_PORT', '8983'),  
               'path' => env('SOLR_PATH', '/solr/'),  
                'core' => env('SOLR_CORE', 'articles')  
             ]  
           ]
       ]);  
   });  
 } 
}


Step 2: Creating the Solr Service

This service will handle all Solr operations:

namespace App\Services;

use Solarium\Client;
use App\Models\Article;

class SolrService
{
    protected $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public function indexArticle(Article $article)
    {
        $update = $this->client->createUpdate();
        $doc = $update->createDocument();
        
        $doc->id = $article->id;
        $doc->title = $article->title;
        $doc->content = $article->content;
        $doc->author = $article->author;
        $doc->category = $article->category;
        $doc->tags = $article->tags;
        $doc->published_at = $article->published_at->format('Y-m-d\TH:i:s\Z');
        
        $update->addDocument($doc);
        $update->addCommit();
        
        return $this->client->update($update);
    }

    public function search(string $query, array $filters = [], int $page = 1, int $perPage = 10)
    {
        $select = $this->client->createSelect();
        
        // Set basic query
        if (!empty($query)) {
            $select->setQuery($query);
        }

        // Add filters
        if (!empty($filters)) {
            $filterQuery = $select->createFilterQuery('filters');
            $filterQueries = [];
            
            foreach ($filters as $field => $value) {
                if (!empty($value)) {
                    $filterQueries[] = "$field:\"$value\"";
                }
            }
            
            if (!empty($filterQueries)) {
                $filterQuery->setQuery(implode(' AND ', $filterQueries));
            }
        }

        // Add faceting
        $facetSet = $select->getFacetSet();
        $facetSet->createFacetField('category')->setField('category');
        $facetSet->createFacetField('author')->setField('author');

        // Set pagination
        $select->setStart(($page - 1) * $perPage)->setRows($perPage);

        // Execute query
        $result = $this->client->select($select);
        
        return [
            'total' => $result->getNumFound(),
            'documents' => array_map(function($doc) {
                return [
                    'id' => $doc->id,
                    'title' => $doc->title,
                    'content' => $doc->content,
                    'author' => $doc->author,
                    'category' => $doc->category,
                    'published_at' => $doc->published_at
                ];
            }, $result->getDocuments()),
            'facets' => [
                'category' => $result->getFacetSet()->getFacet('category')->getValues(),
                'author' => $result->getFacetSet()->getFacet('author')->getValues(),
            ]
        ];
    }

    public function suggest(string $query)
    {
        $suggester = $this->client->createSuggester();
        $suggester->setDictionary('suggest');
        $suggester->setQuery($query);
        $suggester->setCount(5);
        
        $result = $this->client->suggester($suggester);
        
        return $result->getAllSuggestions();
    }

    public function deleteFromIndex($id)
    {
        $update = $this->client->createUpdate();
        $update->addDeleteById($id);
        $update->addCommit();
        
        return $this->client->update($update);
    }
}


Step 3: Setting Up the Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $fillable = [
        'title',
        'content',
        'author',
        'category',
        'tags',
        'published_at'
    ];

    protected $casts = [
        'published_at' => 'datetime',
        'tags' => 'array'
    ];
}


Step 4: Creating the Search Controller

namespace App\Http\Controllers;

use App\Services\SolrService;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    protected $solrService;

    public function __construct(SolrService $solrService)
    {
        $this->solrService = $solrService;
    }

    public function search(Request $request)
    {
        $validated = $request->validate([
            'q' => 'nullable|string',
            'category' => 'nullable|string',
            'author' => 'nullable|string',
            'page' => 'nullable|integer|min:1',
        ]);

        $results = $this->solrService->search(
            $validated['q'] ?? '*:*',
            array_filter([
                'category' => $validated['category'] ?? null,
                'author' => $validated['author'] ?? null,
            ]),
            $validated['page'] ?? 1
        );

        return response()->json($results);
    }

    public function suggest(Request $request)
    {
        $validated = $request->validate([
            'q' => 'required|string|min:2'
        ]);

        $suggestions = $this->solrService->suggest($validated['q']);
        
        return response()->json($suggestions);
    }
}


Key Features Implementation

1. Basic Search

public function search(string $query)
{
    $select = $this->client->createSelect();
    $select->setQuery($query);
    return $this->client->select($select);
}



2. Faceted Search

$facetSet = $select->getFacetSet();
$facetSet->createFacetField('category')->setField('category');
$facetSet->createFacetField('author')->setField('author');


3. Filtered Search

$filterQuery = $select->createFilterQuery('filters');
$filterQuery->setQuery("category:\"$category\"");


Configuration

Environment Setup

Add these variables to your .env file:

SOLR_HOST=127.0.0.1
SOLR_PORT=8983
SOLR_PATH=/solr/
SOLR_CORE=articles


Register Service Provider

Add to config/app.php:

'providers' => [
    App\Providers\SolrServiceProvider::class,
],



Best Practices

  1. Index Management
    • Implement batch indexing for large datasets
    • Use delta updates for real-time changes
    • Regularly optimize your indexes
  2. Performance Optimization
    • Cache common queries
    • Use field type optimization
    • Implement proper error handling
  3. Search Experience
    • Implement proper highlighting
    • Add spelling suggestions
    • Use boost queries for better relevance

Common Issues and Solutions

  1. Connection Issues
    • Verify Solr is running
    • Check firewall settings
    • Validate core name and path
  2. Performance Issues
    • Optimize index settings
    • Use proper field types
    • Implement caching
  3. Indexing Problems
    • Verify field mappings
    • Check data types
    • Monitor index size

Testing the Implementation

Unit Tests

public function test_basic_search()
{
    $result = $this->solrService->search('test query');
    $this->assertNotNull($result);
}



Feature Tests

public function test_search_endpoint()
{
    $response = $this->get('/api/search?q=test');
    $response->assertStatus(200);
}



Conclusion

Integrating Solr with Laravel using Solarium provides a powerful search solution for complex applications. While the setup requires more effort than using Laravel’s native search, the benefits in terms of performance, scalability, and features make it worthwhile for larger applications.


Resources

About Author

Rajat

Leave a Reply

Your email address will not be published. Required fields are marked *

RCV Technologies: Elevate your online presence with expert Digital Marketing, SEO, Web Design, and Development solutions.