Semantic Search in Laravel with pgvector | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Semantic Search in Laravel with pgvector and OpenAI Embeddings        On this page       1. [  Why Semantic Search Beats LIKE Queries ](#why-semantic-search-beats-like-queries)
2. [  1. Enable pgvector ](#1-enable-pgvector)
3. [  2. Migration: Add a Vector Column ](#2-migration-add-a-vector-column)
4. [  3. Generating and Storing Embeddings ](#3-generating-and-storing-embeddings)
5. [  4. Querying by Similarity ](#4-querying-by-similarity)
6. [  5. Chunking Long Documents ](#5-chunking-long-documents)
7. [  Key Takeaways ](#key-takeaways)

  ![Semantic Search in Laravel with pgvector and OpenAI Embeddings](https://cdn.msaied.com/163/c88461e4a878f0ca8939e20d5d4b12dd.png)

  #laravel   #postgresql   #pgvector   #ai   #embeddings  

 Semantic Search in Laravel with pgvector and OpenAI Embeddings 
================================================================

     14 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Semantic Search Beats LIKE Queries  ](#why-semantic-search-beats-like-queries)
2. [  02   1. Enable pgvector  ](#1-enable-pgvector)
3. [  03   2. Migration: Add a Vector Column  ](#2-migration-add-a-vector-column)
4. [  04   3. Generating and Storing Embeddings  ](#3-generating-and-storing-embeddings)
5. [  05   4. Querying by Similarity  ](#4-querying-by-similarity)
6. [  06   5. Chunking Long Documents  ](#5-chunking-long-documents)
7. [  07   Key Takeaways  ](#key-takeaways)

 Why Semantic Search Beats LIKE Queries
--------------------------------------

Full-text search handles keywords. Semantic search handles meaning. When a user types affordable accommodation near the beach, they do not want rows containing those exact words — they want conceptually relevant results. PostgreSQL's pgvector extension, combined with OpenAI embeddings, makes this achievable without leaving your existing Laravel and Postgres stack.

---

1. Enable pgvector
------------------

On a self-hosted Postgres instance run: CREATE EXTENSION IF NOT EXISTS vector;

For Laravel Forge or Ploi, SSH in and run sudo apt install postgresql-15-pgvector then the SQL above. Most managed providers such as Supabase, Neon, and Railway already ship it.

---

2. Migration: Add a Vector Column
---------------------------------

Laravel's Blueprint does not know the vector type natively, so use addColumn with the raw type name. pgvector registers it with Postgres directly. Add an HNSW index for fast approximate nearest-neighbour queries using vector\_cosine\_ops.

---

3. Generating and Storing Embeddings
------------------------------------

Create a dedicated EmbeddingService rather than scattering API calls across your codebase. Call OpenAI::embeddings()-&gt;create() with model text-embedding-3-small and store the resulting float array. pgvector expects the literal string format \[0.12,0.34,...\] so implode the array with commas and wrap it in brackets before saving.

Store the embedding inside a queued job triggered by a model observer so article saves stay fast and do not block the request cycle.

---

4. Querying by Similarity
-------------------------

Create an ArticleSearchRepository that accepts a plain-text query, converts it to an embedding via EmbeddingService, then queries the database using selectRaw with the cosine distance operator. The pgvector operator for cosine distance is the spaceship-arrow operator. Subtracting the distance from 1 gives cosine similarity where higher values mean more relevant results. Order by similarity descending and limit to the desired number of results.

---

5. Chunking Long Documents
--------------------------

OpenAI embedding models have token limits. For long articles, split the text into word chunks of around 500 words each. Store each chunk as a separate row in an article\_chunks table with its own embedding column and a foreign key back to articles. Your search query then joins back to the parent article and deduplicates results.

---

Key Takeaways
-------------

- Use addColumn with the vector type in migrations because Laravel Blueprint has no native vector type yet.
- HNSW indexes with vector\_cosine\_ops give sub-millisecond approximate search at scale. Build them after bulk-inserting embeddings.
- Normalise and truncate text before sending to the API to avoid token overflows and reduce cost.
- Wrap embedding generation in a queued job so article saves stay fast.
- Cosine distance works best for text embeddings. Use L2 distance for image or numeric vectors.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Fsemantic-search-in-laravel-with-pgvector-and-openai-embeddings&text=Semantic+Search+in+Laravel+with+pgvector+and+OpenAI+Embeddings) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Fsemantic-search-in-laravel-with-pgvector-and-openai-embeddings) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Does pgvector work with Laravel Eloquent or do I need raw queries?        You can use Eloquent with selectRaw and orderByDesc for most operations. The vector column type is not natively supported by Blueprint, so migrations need addColumn with the vector type and index creation via DB::statement. Everything else including scopes, relationships, and pagination works normally. 

      Q02  How do I keep embeddings up to date when article content changes?        Use an Eloquent observer or a model saved event to dispatch a queued job that regenerates the embedding. Avoid doing the OpenAI API call synchronously inside the request cycle because it adds 200 to 500 milliseconds of latency. 

      Q03  What is the difference between HNSW and IVFFlat indexes in pgvector?        HNSW builds the index incrementally and generally offers better recall and faster queries at the cost of more memory. IVFFlat requires a training phase and is more memory-efficient but needs a full rebuild when data grows significantly. For most Laravel applications HNSW is the safer default. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Pest Architecture Tests: Enforcing Clean Boundaries in Laravel Without a CI Nanny](https://cdn.msaied.com/166/2747407f8fcc1af6734aa1b0565dc371.png) pest laravel testing 

### Pest Architecture Tests: Enforcing Clean Boundaries in Laravel Without a CI Nanny

Pest's architecture testing API lets you encode structural rules directly in your test suite, catching layer v...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/pest-architecture-tests-enforcing-clean-boundaries-in-laravel-without-a-ci-nanny) [ ![Filament v3 Global Search: Custom Result Providers and Scoped Tenant Isolation](https://cdn.msaied.com/165/4de273593ea2005169698edc5cc53d6b.png) filament laravel multi-tenant 

### Filament v3 Global Search: Custom Result Providers and Scoped Tenant Isolation

Learn how to register custom global search providers in Filament v3, scope results to the current tenant, and...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/filament-v3-global-search-custom-result-providers-and-scoped-tenant-isolation) [ ![Filament v3 Table Column Summarizers: Totals, Averages, and Custom Aggregates](https://cdn.msaied.com/164/906904262a1af23fbdd05d06c9c83f5f.png) filament laravel php 

### Filament v3 Table Column Summarizers: Totals, Averages, and Custom Aggregates

Filament v3 ships with built-in column summarizers that render totals and averages in your table footer — here...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/filament-v3-table-column-summarizers-totals-averages-and-custom-aggregates) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
