Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Match Against Bundle Laravel Package

ciricihq/match-against-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require ciricihq/match-against-bundle
    

    Add the bundle to config/bundles.php:

    return [
        // ...
        Cirici\MatchAgainstBundle\CiriciMatchAgainstBundle::class => ['all' => true],
    ];
    
  2. Configure Database Ensure your MySQL tables have FULLTEXT indexes on the fields you want to search. Example schema:

    ALTER TABLE your_table ADD FULLTEXT(content);
    
  3. First Query Use the SearchTextIndex entity to query against indexed fields:

    use Cirici\MatchAgainstBundle\Entity\SearchTextIndex;
    
    $qb = $entityManager->getConnection()->createQueryBuilder();
    $qb->select('sti.foreignId')
        ->from(SearchTextIndex::class, 'sti')
        ->where('sti.model = :entityClass')
        ->andWhere('sti.field = :fieldName')
        ->andWhere("MATCH_AGAINST(sti.content, :text 'IN BOOLEAN MODE') > :score")
        ->setParameter('entityClass', 'App\Entity\YourEntity')
        ->setParameter('fieldName', 'title')
        ->setParameter('text', 'search term')
        ->setParameter('score', 0.1);
    

Implementation Patterns

Indexing Workflow

  1. Pre-indexing Use a Command or EventListener to populate SearchTextIndex for new/updated entities:

    // Example: Doctrine Lifecycle Event Listener
    $entityManager->getEventManager()->addEventListener(
        [OnFlush::class, PostFlush::class],
        new SearchIndexUpdater($entityManager)
    );
    
  2. Query Patterns

    • Boolean Mode: Best for precise queries (e.g., +term1 -term2).
      ->andWhere("MATCH_AGAINST(sti.content, :text 'IN BOOLEAN MODE') > 0.5")
      
    • Natural Language Mode: Best for user-friendly searches (e.g., "find me things like X").
      ->andWhere("MATCH_AGAINST(sti.content, :text 'IN NATURAL LANGUAGE MODE') > 0.3")
      
    • Combine with Other Conditions:
      ->andWhere('sti.model IN (:models)')
      ->setParameter('models', ['App\Entity\Post', 'App\Entity\Page']);
      
  3. Pagination Use LIMIT and OFFSET with ORDER BY for relevance:

    ->orderBy('MATCH_AGAINST(sti.content, :text) DESC')
    ->setMaxResults(10)
    ->setFirstResult(0);
    
  4. Dynamic Field Searching Build a flexible search across multiple fields:

    $fields = ['title', 'content', 'tags'];
    $orConditions = [];
    foreach ($fields as $field) {
        $orConditions[] = "MATCH_AGAINST(sti.content, :text 'IN BOOLEAN MODE') > 0.1 AND sti.field = :field";
    }
    $qb->andWhere(implode(' OR ', $orConditions))
       ->setParameter('field', $fields);
    

Gotchas and Tips

Pitfalls

  1. FULLTEXT Index Requirements

    • MySQL FULLTEXT indexes require a minimum word length (default: 4 characters). Configure in my.cnf:
      ft_min_word_len=3
      
    • Fields must be CHAR, VARCHAR, or TEXT types.
  2. Case Sensitivity FULLTEXT searches are case-insensitive by default, but accents may not match. Use BINARY for exact matches:

    MATCH_AGAINST(BINARY sti.content, :text) > 0
    
  3. Performance

    • Avoid searching across too many fields in a single query (use separate queries or UNION).
    • For large datasets, consider denormalizing searchable fields into a dedicated table.
  4. Boolean Mode Quirks

    • Prefix terms with + (required) or - (excluded) in boolean mode:
      ->setParameter('text', '+laravel -framework')
      
    • Wildcards (*) are inefficient; avoid leading wildcards (e.g., *term).
  5. Scoring

    • Scores are relative. Normalize thresholds based on your data (e.g., test with > 0.1 vs. > 0.5).

Debugging

  1. Raw SQL Use getSQL() to inspect queries:

    $sql = $qb->getSQL();
    $params = $qb->getParameters();
    
  2. EXPLAIN Analyze query performance:

    EXPLAIN SELECT * FROM search_text_index WHERE MATCH_AGAINST(content, 'term') > 0;
    
  3. Log Queries Enable Doctrine logging in config/packages/doctrine.yaml:

    doctrine:
        dbal:
            logging: true
            profiling: true
    

Extension Points

  1. Custom Indexing Extend SearchTextIndex or create a proxy entity to handle complex mappings:

    class CustomSearchIndex extends SearchTextIndex {
        public function getSearchContent(): string {
            return $this->title . ' ' . $this->description;
        }
    }
    
  2. Query Builder Helper Create a service to wrap repetitive logic:

    class MatchAgainstQueryBuilder {
        public function search(string $text, string $entityClass, array $fields, float $score): QueryBuilder {
            // ... implementation
        }
    }
    
  3. Event-Driven Indexing Use Symfony events to trigger indexing on entity updates:

    $dispatcher->addListener(
        KernelEvents::VIEW,
        [SearchIndexUpdater::class, 'updateIndex']
    );
    
  4. Synonyms/Stemming Configure MySQL for better search behavior:

    -- Enable stopword removal (e.g., 'the', 'and')
    SET SESSION ft_stopword_file = '/path/to/stopwords.txt';
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours