Skip to content

Repository files navigation

ACSEOTypesenseBundle

This bundle provides integration with Typesense with Symfony.

It relies on the official TypeSense PHP package

Features include:

  • Doctrine object transformer to Typesense indexable data
  • Usefull services to search in collections
  • Listeners for Doctrine events for automatic indexing

Installation

Install the bundle using composer

composer require acseo/typesense-bundle

Enable the bundle in you Symfony project

<?php// config/bundles.phpreturn [
ACSEO\TypesenseBundle\ACSEOTypesenseBundle::class => ['all' => true],

Configuration

Configure the Bundle

# .env
TYPESENSE_URL=http://localhost:8108
TYPESENSE_KEY=123
# config/packages/acseo_typesense.ymlacseo_typesense:
# Typesense host settingstypesense:
url: '%env(resolve:TYPESENSE_URL)%'key: '%env(resolve:TYPESENSE_KEY)%'collection_prefix: 'test_'# Optional : add prefix to all collection # names in Typesense# Collection settingscollections:
books: # Typesense collection nameentity: 'App\Entity\Book'# Doctrine Entity classfields: ## Keeping Database and Typesense synchronized with ids#id: # Entity attribute namename: id # Typesense attribute nametype: primary # Attribute type## Using again id as a sortable field (int32 required)#sortable_id:
entity_attribute: id # Entity attribute name forcedname: sortable_id # Typesense field nametype: int32title: name: titletype: stringdescription: name: titletype: description author:
name: authortype: object # Object conversion with __toString()author.country:
name: author_country type: stringfacet: true # Declare field as facet (required to use "group_by" query option)entity_attribute: author.country # Equivalent of $book->getAuthor()->getCountry()genres:
name: genrestype: collection # Convert ArrayCollection to array of stringspublishedAt: name: publishedAttype: datetimeoptional: true # Declare field as optionalcover_image_url:
name: cover_image_urltype: stringoptional: trueentity_attribute: ACSEO\Service\BookConverter::getCoverImageURL # use a service converter instead of an attributeembeddings: # Since Typesense 0.25, you can generate Embeddings on the flyname: embeddings # and retrieve your documents using an vectorial searchtype: float[] # more info : https://typesense.org/docs/27.0/api/vector-search.htmlembed:
from:
- title
- descriptionmodel_config:
model_name: ts/e5-small # Typesense Cloud model (requires Typesense Cloud)# For custom embedding services (Ollama, local models, etc.):# model_name: 'openai/your-model-name'# url: 'http://your-embedding-service:port'# api_key: 'your-api-key' # Optionaldefault_sorting_field: sortable_id # Default sorting field. Must be int32 or floatsymbols_to_index: ['+'] # Optional - You can add + to this list to make the word c++ indexable verbatim.users:
entity: App\Entity\Userfields:
id:
name: idtype: primarysortable_id:
entity_attribute: idname: sortable_idtype: int32email:
name: emailtype: stringdefault_sorting_field: sortable_idtoken_separators: ['+', '-', '@', '.'] # Optional - This will cause contact+docs-example@typesense.org to be indexed as contact, docs, example, typesense and org.

You can use basic types supported by Typesense for your fields : string, int32, float, etc. You can also use specific type names, such as : primary, collection, object

Data conversion from Doctrine entity to Typesense data is managed by ACSEO\TypesenseBundle\Transformer\DoctrineToTypesenseTransformer

Usage

Create index and populate data

This bundle comes with useful commands in order to create and index your data

# Creation collections structurephp bin/console typesense:create# Import collections with Doctrine entitiesphp bin/console typesense:import

Search documents

This bundle creates dynamic generic finders services that allows you to query Typesense

The finder services are named like this : typesense.finder.collection_name

You can inject the generic finder in your Controller or into other services.

You can also create specific finder for a collection. See documentation below.

# config/services.yamlservices:
App\Controller\BookController:
arguments:
$bookFinder: '@typesense.finder.books'
<?php// src/Controller/BookController.phpuseSymfony\Bundle\FrameworkBundle\Controller\AbstractController;
useACSEO\TypesenseBundle\Finder\TypesenseQuery;
//class BookController extends AbstractController
{
private$bookFinder;
publicfunction__construct($bookFinder)
{
$this->bookFinder = $bookFinder;
}
publicfunctionsearch()
{
$query = newTypesenseQuery('Jules Vernes', 'author');
// Get Doctrine Hydrated objects$results = $this->bookFinder->query($query)->getResults();
// dump($results)// array:2 [▼// 0 => App\Entity\Book {#522 ▶}// 1 => App\Entity\Book {#525 ▶}//]// Get raw results from Typesence$rawResults = $this->bookFinder->rawQuery($query)->getResults();
// dump($rawResults)// array:2 [▼// 0 => array:3 [▼// "document" => array:4 [▼// "author" => "Jules Vernes"// "id" => "100"// "published_at" => 1443744000// "title" => "Voyage au centre de la Terre "// ]// "highlights" => array:1 [▶]// "seq_id" => 4// ]// 1 => array:3 [▼// "document" => array:4 [▶]// "highlights" => array:1 [▶]// "seq_id" => 6// ]// ]
}

Querying Typesense

The class TypesenseQuery() class takes 2 arguments :

  • The search terme (q)
  • The fields to search on (queryBy)

You can create more complex queries using all the possible Typsense search arguments

<?phpuseACSEO\TypesenseBundle\Finder\TypesenseQuery;
$simpleQuery = newTypesenseQuery('search term', 'collection field to search in');
$complexQuery = newTypesenseQuery('search term', 'collection field to search in')
->filterBy('theme: [adventure, thriller]')
->addParameter('key', 'value')
->sortBy('year:desc');

Create specific finder for a collection

You can easily create specific finders for each collection that you declare.

# config/packages/acseo_typesense.ymlacseo_typesense:
# ...# Collection settingscollections:
books: # Typesense collection name# ... # Colleciton fields definition# ...finders: # Declare your specific finderbooks_autocomplete: # Finder namefinder_parameters: # Parameters used by the finderquery_by: title #limit: 10# You can add as key / valuesspecificationsprefix: true # based on Typesense Request num_typos: 1#drop_tokens_threshold: 1#

This configuration will create a service named @typesense.specificfinder.books.books_autocomplete.
You can inject the specific finder in your Controller or into other services

# config/services.yamlservices:
App\Controller\BookController:
arguments:
$autocompleteBookFinder: '@typesense.specificfinder.books.books_autocomplete'

and then use it like this :

<?php// src/Controller/BookController.phpclass BookController extends AbstractController
{
private$autocompleteBookFinder;
publicfunction__construct($autocompleteBookFinder)
{
$this->autocompleteBookFinder = $autocompleteBookFinder;
}
publicfunctionautocomplete($term = '')
{
$results = $this->autocompleteBookFinder->search($term)->getResults();
// or if you want raw results$rawResults = $this->autocompleteBookFinder->search($term)->getRawResults();
}

Use different kind of services

This bundles creates different services that you can use in your Controllers or anywhere you want.

  • typesense.client : the basic client inherited from the official typesense-php package
  • typesense.collection_client : this service allows you to do basic actions on collections, and allows to perform search and multisearch action.
  • typesense.finder.* : this generated service allows you to perform query or rawQuery on a specific collection. Example of a generated service : typesense.finder.candidates
  • typesense.specificfinder.*.* : this generated service allows you to run pre-configured requests (declared in : config/packages/acseo_typesense.yml). Example of a generated service : typesense.specificfinder.candidates.default

Note : there a other services. You can use the debug:container command in order to see all of them.

Doctrine Listeners

Doctrine listeners will update Typesense with Entity data during the following events :

  • postPersist
  • postUpdate
  • preDelete

Perform multisearch

You can create multisearch requests and get results using the collectionClient service.

// Peform multisearch$searchRequests = [
(newTypesenseQuery('Jules'))->addParameter('collection', 'author'),
(newTypesenseQuery('Paris'))->addParameter('collection', 'library') ];
$commonParams = newTypesenseQuery()->addParameter('query_by', 'name');
$response = $this->collectionClient->multisearch($searchRequests, $commonParams);

Cookbook


Testing the Bundle

tests are written in the tests directory.

  • Unit tests doesn't require a running Typesense server
  • Functional tests require a running Typesense server

You can launch the tests with the following commands :

# Unit test
$ php ./vendor/bin/phpunit tests/Unit
# Functional test# First, start a Typesense server with Docker
$ composer run-script typesenseServer
$ php ./vendor/bin/phpunit tests/Functional

About

Typesense PHP integration for your Symfony project

Topics

Resources

Stars

74 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages