{"id":23219120,"url":"https://github.com/b2pweb/bdf-prime-indexer","last_synced_at":"2026-03-03T04:40:13.599Z","repository":{"id":43133461,"uuid":"353003886","full_name":"b2pweb/bdf-prime-indexer","owner":"b2pweb","description":null,"archived":false,"fork":false,"pushed_at":"2025-11-26T10:05:40.000Z","size":279,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"2.0","last_synced_at":"2025-11-27T16:24:40.211Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/b2pweb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-03-30T13:05:14.000Z","updated_at":"2025-11-26T09:57:15.000Z","dependencies_parsed_at":"2024-12-02T13:31:44.690Z","dependency_job_id":"388f2837-0172-40aa-8a6f-7c1330087637","html_url":"https://github.com/b2pweb/bdf-prime-indexer","commit_stats":{"total_commits":62,"total_committers":2,"mean_commits":31.0,"dds":"0.016129032258064502","last_synced_commit":"80edf0b285394030b3c8a1d70d3ec0924adfe118"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/b2pweb/bdf-prime-indexer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b2pweb%2Fbdf-prime-indexer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b2pweb%2Fbdf-prime-indexer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b2pweb%2Fbdf-prime-indexer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b2pweb%2Fbdf-prime-indexer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/b2pweb","download_url":"https://codeload.github.com/b2pweb/bdf-prime-indexer/tar.gz/refs/heads/2.0","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b2pweb%2Fbdf-prime-indexer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30032062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T03:27:35.548Z","status":"ssl_error","status_checked_at":"2026-03-03T03:27:09.213Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-18T21:19:20.547Z","updated_at":"2026-03-03T04:40:13.585Z","avatar_url":"https://github.com/b2pweb.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Prime Indexer\n[![build](https://github.com/b2pweb/bdf-prime-indexer/actions/workflows/php.yml/badge.svg)](https://github.com/b2pweb/bdf-prime-indexer/actions/workflows/php.yml)\n[![codecov](https://codecov.io/github/b2pweb/bdf-prime-indexer/branch/2.0/graph/badge.svg?token=VOFSPEWYKX)](https://app.codecov.io/github/b2pweb/bdf-prime-indexer)\n[![Packagist Version](https://img.shields.io/packagist/v/b2pweb/bdf-prime-indexer.svg)](https://packagist.org/packages/b2pweb/bdf-prime-indexer)\n[![Total Downloads](https://img.shields.io/packagist/dt/b2pweb/bdf-prime-indexer.svg)](https://packagist.org/packages/b2pweb/bdf-prime-indexer)\n[![Type Coverage](https://shepherd.dev/github/b2pweb/bdf-prime-indexer/coverage.svg)](https://shepherd.dev/github/b2pweb/bdf-prime-indexer)\n\nIndexing entities through prime, and request from Elasticsearch index.\n\n## Installation\n\nInstall with composer :\n\n```bash\ncomposer require b2pweb/bdf-prime-indexer\n```\n\nRegister into `config/bundles.php` :\n\n```php\n\u003c?php\n\nreturn [\n    // ...\n    Bdf\\Prime\\Indexer\\Bundle\\PrimeIndexerBundle::class =\u003e ['all' =\u003e true],\n    Bdf\\PrimeBundle\\PrimeBundle::class =\u003e ['all' =\u003e true],\n];\n```\n\nConfigure indexes into `config/packages/prime_indexer.yaml` :\n\n```yaml\nprime_indexer:\n  elasticsearch:\n    # Define elasticsearch hosts\n    hosts: ['127.0.0.1:9222']\n\n  # Define indexes in form [Entity class]: [Index configuration class]\n  # This is not mandatory if autoconfiguration is enabled\n  indexes:\n    App\\Entities\\City: App\\Entities\\CityIndex\n    App\\Entities\\User: App\\Entities\\UserIndex\n```\n\n## Usage\n\n### Declaring index\n\nFor declaring an index, you should first declare the configuration :\n\n```php\n\u003c?php\nclass CityIndex implements ElasticsearchIndexConfigurationInterface\n{\n    // Declare the index name\n    public function index(): string\n    {\n        return 'test_cities';\n    }\n\n    // Get the mapped entity type\n    public function entity(): string\n    {\n        return City::class;\n    }\n\n    // Build properties\n    public function properties(PropertiesBuilder $builder): void\n    {\n        $builder\n            -\u003estring('name')\n            -\u003einteger('population')\n            -\u003estring('zipCode')\n            -\u003estring('country')-\u003enotAnalyzed()\n            -\u003eboolean('enabled')\n        ;\n    }\n\n    // The id accessor\n    public function id(): ?PropertyAccessorInterface\n    {\n        return new SimplePropertyAccessor('id');\n    }\n\n    // Declare analyzers\n    public function analyzers(): array\n    {\n        return [\n            'default' =\u003e [\n                'type'      =\u003e 'custom',\n                'tokenizer' =\u003e 'standard',\n                'filter'    =\u003e ['lowercase', 'asciifolding'],\n            ],\n        ];\n    }\n\n    // Scopes\n    public function scopes(): array\n    {\n        return [\n            // \"default\" scope is always applied to the query\n            'default' =\u003e function (ElasticsearchQuery $query) {\n                $query\n                    -\u003ewrap(\n                        (new FunctionScoreQuery())\n                            -\u003eaddFunction('field_value_factor', [\n                                'field' =\u003e 'population',\n                                'factor' =\u003e 1,\n                                'modifier' =\u003e 'log1p'\n                            ])\n                            -\u003escoreMode('multiply')\n                    )\n                    -\u003efilter('enabled', true)\n                ;\n            },\n\n            // Other scope : can be used as custom filter on query\n            // Or using $index-\u003emyScope()\n            'matchName' =\u003e function (ElasticsearchQuery $query, string $name) {\n                $query\n                    -\u003ewhere(new Match('name', $name))\n                    -\u003eorWhere(\n                        (new QueryString($name.'%'))\n                            -\u003eand()\n                            -\u003edefaultField('name')\n                            -\u003eanalyzeWildcard()\n                            -\u003euseLikeSyntax()\n                    )\n                ;\n            }\n        ];\n    }\n}\n```\n\nSome extra configuration can be added by implementing interfaces :\n\n- `CustomEntitiesConfigurationInterface` : For define the entities loading method\n- `ShouldBeIndexedConfigurationInterface` : For define predicate which check if an entity should be indexed or not\n\nAfter that, the index can be added to the \"prime_indexer.indexes\" configuration, or let the autoconfiguration do the job.\n\n### Querying the index\n\nThe query system use Prime interfaces, so usage is almost the same :\n\n```php\n\u003c?php\n// Get the City index\n$index = $container-\u003eget(\\Bdf\\Prime\\Indexer\\IndexFactory::class)-\u003efor(City::class);\n\n// Get the query\n$query = $index-\u003equery();\n\n$query\n    -\u003ewhere('country', 'FR') // Simple where works as expected\n    -\u003ewhere('name', ':like', 'P%') // \"like\" operator is supported\n    -\u003eorWhere(new QueryString('my complete query')) // Operator object can be used for more powerful filters\n;\n\n// Get all cities who match with filters\n$query-\u003eall();\n\n// First returns the first matching element, wrapped into an Optional\n$query-\u003efirst()-\u003eget();\n\n// Get the raw result of the elasticsearch query\n$query-\u003eexecute();\n\n// Use scope directly\n$index-\u003ematchName('Paris')-\u003eall();\n\n// Same as above, but with scope as filter\n$index-\u003equery()-\u003ewhere('matchName', 'Paris')-\u003eall();\n```\n\n### Updating the index\n\nUpdate operations can be done on the index manually :\n\n```php\n\u003c?php\n// Get the City index\n$index = $container-\u003eget(\\Bdf\\Prime\\Indexer\\IndexFactory::class)-\u003efor(City::class);\n\n// Create the index, and insert all cities from database\n$index-\u003ecreate(City::walk());\n\n$paris = new City([\n    'name' =\u003e 'Paris',\n    'population' =\u003e 2201578,\n    'country' =\u003e 'FR',\n    'zipCode' =\u003e '75000'\n]);\n\n// Indexing the city\n$index-\u003eadd($paris);\n\n// The \"id\" property is filled after insertion\necho $paris-\u003eid();\n\n// Make sure that index is up to date\n// !!! Do not use on production !!!\n$index-\u003erefresh();\n\n$index-\u003econtains($paris); // true\n\n// Update one attribute\n$paris-\u003esetPopulation(2201984);\n$index-\u003eupdate($paris, ['population']); \n\n// Remove the entity\n$index-\u003eremove($paris);\n$index-\u003econtains($paris); // false\n\n// Drop index\n$index-\u003edrop();\n```\n\n### With CLI\n\nCreate index, and indexing entities :\n\n```\nbin/console.php prime:indexer:create App\\Entities\\City\n```\n\nA progress bar will be displayed for follow the indexing progress.\n\n\u003e Note: The full qualified class name of the entity must be used as argument.\n\nFor manage Elasticsearch index :\n\n```\nbin/console.php elasticsearch:show\nbin/console.php elasticsearch:delete test_cities\n```\n\n### Testing\n\nBecause testing is one of more important things, an utility class is added for this :\n\nNote : The index name will be prefixed by \"test_\" to ensure that it will not impact the real index.\n\n```php\n\u003c?php\nclass MyTest extends \\Bdf\\PHPUnit\\WebTestCase\n{\n    /**\n     * @var TestingIndexer\n     */\n    private $indexTester;\n    \n    protected function setUp(): void\n    {\n        parent::setUp();\n        \n        $this-\u003eindexTester = new TestingIndexer($this-\u003eapp);\n        $this-\u003eindexTester-\u003eindex(City::class); // Declare the city index\n    }\n    \n    protected function tearDown() : void\n    {\n        parent::tearDown();\n        \n        $this-\u003eindexTester-\u003edestroy();\n    }\n    \n    public function test_city_index()\n    {\n        // Push entities to index\n        $this-\u003eindexTester-\u003epush([\n            new City(...),\n            new City(...),\n            new City(...),\n        ]);\n        \n        // Remove from index\n        $this-\u003eindexTester-\u003eremove(new City(...));\n        \n        // Querying to the index\n        $query = $this-\u003eindexTester-\u003eindex(City::class)-\u003equery();\n    }\n}\n```\n\n## Interactions and differences with Prime\n\n- Prime is not required to be registered for use index system. Some entities can be into an index, but not in database.\n- Unlike Prime, the mapping is index-oriented and not model-oriented :\n    - The PropertiesBuilder define the index properties, and maps to the model ones\n    - Computed properties are permitted (i.e. properties not stored into the entity)\n    - Query filters columns are not mapped, and use the indexed ones\n- Queries use streams (from b2pweb/bdf-collections), so first() returns an OptionalInterface, and transformation are done on the stream\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb2pweb%2Fbdf-prime-indexer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fb2pweb%2Fbdf-prime-indexer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb2pweb%2Fbdf-prime-indexer/lists"}