acseo / typesense-bundle
This bundle provides integration with Typesense in Symfony
Installs: 59 990
Dependents: 1
Suggesters: 0
Security: 0
Stars: 59
Watchers: 6
Forks: 38
Open Issues: 15
Type:symfony-bundle
Requires
- php: ^7.4||^8.0
- doctrine/orm: ^2.8 || ^3.2
- monolog/monolog: ^2.3|^3.0
- php-http/curl-client: ^2.2
- symfony/console: ^4.3.4|^5|^6.0|^7.0
- symfony/framework-bundle: ^4.3|^5|^6.0|^7.0
- symfony/http-client: ^5.4|^6.2|^7.0
- symfony/property-access: ^3.4|^4.3|^5|^6.0|^7.0
- typesense/typesense-php: ^4.1.0
Requires (Dev)
- dg/bypass-finals: ^1.4
- phpspec/prophecy-phpunit: ^2.0
- phpunit/phpunit: ^9.5
- symfony/phpunit-bridge: ^5.0|^6.0
- symfony/yaml: ^3.4 || ^4.4 || ^5.4 || ^6.0
README
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.php return [ ACSEO\TypesenseBundle\ACSEOTypesenseBundle::class => ['all' => true],
Configuration
Configure the Bundle
# .env
TYPESENSE_URL=http://localhost:8108
TYPESENSE_KEY=123
# config/packages/acseo_typesense.yml acseo_typesense: # Typesense host settings typesense: url: '%env(resolve:TYPESENSE_URL)%' key: '%env(resolve:TYPESENSE_KEY)%' collection_prefix: 'test_' # Optional : add prefix to all collection # names in Typesense # Collection settings collections: books: # Typesense collection name entity: 'App\Entity\Book' # Doctrine Entity class fields: # # Keeping Database and Typesense synchronized with ids # id: # Entity attribute name name: id # Typesense attribute name type: primary # Attribute type # # Using again id as a sortable field (int32 required) # sortable_id: entity_attribute: id # Entity attribute name forced name: sortable_id # Typesense field name type: int32 title: name: title type: string description: name: title type: description author: name: author type: object # Object conversion with __toString() author.country: name: author_country type: string facet: true # Declare field as facet (required to use "group_by" query option) entity_attribute: author.country # Equivalent of $book->getAuthor()->getCountry() genres: name: genres type: collection # Convert ArrayCollection to array of strings publishedAt: name: publishedAt type: datetime optional: true # Declare field as optional cover_image_url: name: cover_image_url type: string optional: true entity_attribute: ACSEO\Service\BookConverter::getCoverImageURL # use a service converter instead of an attribute embeddings: # Since Typesense 0.25, you can generate Embeddings on the fly name: embeddings # and retrieve your documents using an vectorial search type: float[] # more info : https://typesense.org/docs/27.0/api/vector-search.html embed: from: - title - description model_config: model_name: ts/e5-small default_sorting_field: sortable_id # Default sorting field. Must be int32 or float symbols_to_index: ['+'] # Optional - You can add + to this list to make the word c++ indexable verbatim. users: entity: App\Entity\User fields: id: name: id type: primary sortable_id: entity_attribute: id name: sortable_id type: int32 email: name: email type: string default_sorting_field: sortable_id token_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 structure php bin/console typesense:create # Import collections with Doctrine entities php 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.yaml services: App\Controller\BookController: arguments: $bookFinder: '@typesense.finder.books'
<?php // src/Controller/BookController.php use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use ACSEO\TypesenseBundle\Finder\TypesenseQuery; // class BookController extends AbstractController { private $bookFinder; public function __construct($bookFinder) { $this->bookFinder = $bookFinder; } public function search() { $query = new TypesenseQuery('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
<?php use ACSEO\TypesenseBundle\Finder\TypesenseQuery; $simpleQuery = new TypesenseQuery('search term', 'collection field to search in'); $complexQuery = new TypesenseQuery('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.yml acseo_typesense: # ... # Collection settings collections: books: # Typesense collection name # ... # Colleciton fields definition # ... finders: # Declare your specific finder books_autocomplete: # Finder name finder_parameters: # Parameters used by the finder query_by: title # limit: 10 # You can add as key / valuesspecifications prefix: true # based on Typesense Request num_typos: 1 # drop_tokens_threshold: 1 #
This configuration will create a service named @typesense.finder.books.books_autocomplete
.
You can inject the specific finder in your Controller or into other services
# config/services.yaml services: App\Controller\BookController: arguments: $autocompleteBookFinder: '@typesense.finder.books.books_autocomplete'
and then use it like this :
<?php // src/Controller/BookController.php class BookController extends AbstractController { private $autocompleteBookFinder; public function __construct($autocompleteBookFinder) { $this->autocompleteBookFinder = $autocompleteBookFinder; } public function autocomplete($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 officialtypesense-php
packagetypesense.collection_client
: this service allows you to do basic actions on collections, and allows to performsearch
andmultisearch
action.typesense.finder.*
: this generated service allows you to performquery
orrawQuery
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 = [ (new TypesenseQuery('Jules'))->addParameter('collection', 'author'), (new TypesenseQuery('Paris'))->addParameter('collection', 'library') ]; $commonParams = new TypesenseQuery()->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