Compare commits

...

649 commits

Author SHA1 Message Date
Simon Vieille 30e351b1d5 Propel provider: query_builder_method set to null 2015-05-20 11:13:43 +02:00
Simon Vieille e3abbdc700 Propel provider: default options 2015-05-20 11:12:03 +02:00
Tim Nagel 4451bd07c6 Merge pull request #860 from kayue/patch-1
Count the number of results for the query directly
2015-04-28 12:56:12 +10:00
Tim Nagel 1f8a330140 Merge pull request #870 from davidfuhr/patch-1
Fixed parameter name to kernel.environment
2015-04-28 12:55:14 +10:00
David Fuhr 8f7f24e6d3 Fixed parameter nam to kernel.environment
The parameter %kernel.env% does not exist. It throws the error message 'You have requested a non-existent parameter "kernel.env". Did you mean this: "kernel.environment"?'
2015-04-27 23:00:50 +02:00
Ka Yue Yeung e71ec4ac8a Count the number of results for the query directly 2015-04-18 00:42:02 +08:00
Lukas Kahwe Smith adf7fb21e3 Merge pull request #848 from thierrymarianne/patch-1
Fix typo
2015-04-05 23:20:26 +02:00
Thierry Marianne 1287d9f0df Fix typo 2015-04-05 22:07:41 +02:00
Tim Nagel c5728b5870 Merge branch '3.1.x' 2015-04-02 10:39:05 +11:00
Tim Nagel 7baf494c56 release 3.1.3 2015-04-02 10:34:08 +11:00
Tim Nagel ad20382e08 Merge pull request #842 from merk/fix-provider-symfony-23
Fix symfony 2.3 compatibility with 3.1
2015-04-02 10:33:05 +11:00
Tim Nagel e933a49d07 Use deprecated optionsResolver interface 2015-04-02 10:23:30 +11:00
Tim Nagel 69470d7e20 Fix the previous release. 2015-03-27 12:01:39 +11:00
Tim Nagel b6e01cd332 Fix issues with Provider's batch_size and PopulateCommand's batch_size 2015-03-27 11:45:03 +11:00
Tim Nagel 35276f469a Merge pull request #835 from TomasVotruba/patch-2
Setup.md: fix link to Elasticsearch installation
2015-03-27 08:59:18 +11:00
Tim Nagel ec9f23bd8d Merge pull request #834 from TomasVotruba/patch-1
Setup.md - composer picks last stable version
2015-03-27 08:58:33 +11:00
Tomáš Votruba ae4cfd7e04 Setup.md: fix link to Elasticsearch installation 2015-03-26 15:05:28 +01:00
Tomáš Votruba 49a0c22724 Setup.md - composer picks last stable version 2015-03-26 15:03:20 +01:00
Tim Nagel 8d8b04ead8 Fixes populate command error 2015-03-23 21:39:13 +11:00
Tim Nagel a59f2015b4 Merge branch '3.1.x' 2015-03-18 09:40:06 +11:00
Tim Nagel 447d29ab9c Release 3.1.0 2015-03-18 09:38:47 +11:00
Tim Nagel 73093beadb Merge pull request #823 from merk/more-tests
More test coverage
2015-03-18 09:37:23 +11:00
Tim Nagel 5181b02933 Resetter tests 2015-03-14 22:14:24 +11:00
Tim Nagel ac98549eb5 Fix translation of option keys for the Provider 2015-03-14 19:54:23 +11:00
Tim Nagel 9c1c771799 Mark getSlice private 2015-03-14 19:54:01 +11:00
Tim Nagel 3bb2f384ba Provider refactoring 2015-03-14 19:53:05 +11:00
Tim Nagel d4f01e8d2e Cast result from ExpressionLanguage eval to bool 2015-03-14 18:48:59 +11:00
Tim Nagel 72a9dfa267 Indexable service improvements 2015-03-14 16:08:50 +11:00
Tim Nagel 9cf0117c71 AliasProcessor 2015-03-14 00:51:07 +11:00
Tim Nagel bb4618c101 Even more QA 2015-03-13 19:34:56 +11:00
Tim Nagel 6a07f7b24e More QA 2015-03-13 19:10:24 +11:00
Tim Nagel dd3269d1ef Merge branch 'code-qa' into 3.1.x 2015-03-13 15:17:52 +11:00
Tim Nagel 84e5831a81 Fixing scrutinizer issues 2015-03-13 14:57:36 +11:00
Tim Nagel d5a9b7b235 Add missing method to HighlightableModelInterface.
This is not a BC break - the method has always been required and lacking the method would cause a fatal error.
2015-03-13 14:56:07 +11:00
Tim Nagel 4af9f442fd Move common sorting code to base Transformer class 2015-03-13 14:56:07 +11:00
Christophe Coevoet 4081c32ca0 Merge branch '3.1.x' 2015-03-12 17:54:38 +01:00
Christophe Coevoet 925410a66e Merge branch '3.0.x' into 3.1.x 2015-03-12 17:54:30 +01:00
Christophe Coevoet b6c252aac3 Update the changelog for 3.0.9 2015-03-12 17:54:08 +01:00
Christophe Coevoet 133f71b88a Merge branch '3.1.x' 2015-03-12 17:49:49 +01:00
Christophe Coevoet c013ed9657 Merge branch '3.0.x' into 3.1.x
Conflicts:
	Resources/config/orm.xml
2015-03-12 17:49:25 +01:00
Christophe Coevoet f72c51503a Fix the service definitions when the logger is not set in listener
The empty tag is parsed to an empty string, not to null. And this is not
a valid value for the service
2015-03-12 17:44:57 +01:00
Tim Nagel 4e087af50d Fix issues with CS merge 2015-03-12 22:41:48 +11:00
Tim Nagel 559b14b4a5 Merge branch '3.1.x' 2015-03-12 22:02:06 +11:00
Tim Nagel 2215d07ff8 Merge pull request #818 from merk/cs-fixes-30
php-cs-fixer for 3.0.x
2015-03-12 21:58:21 +11:00
Tim Nagel 89db88c2a0 CS fixes for 3.1 2015-03-12 21:58:02 +11:00
Tim Nagel 345b5d423d Merge cs-fixes-30 into cs-fixes-31 2015-03-12 21:57:26 +11:00
Tim Nagel a8f41fa5ef We do not provide API guarantees 2015-03-12 21:47:19 +11:00
Tim Nagel e796d6179b Elastica, Doctrine\Common and Doctrine\ORM are required for tests 2015-03-12 21:21:18 +11:00
Tim Nagel dd388e4b25 CS fixes 2015-03-12 21:20:00 +11:00
Tim Nagel 81186e40db Bump master to 3.2-dev 2015-03-12 20:36:50 +11:00
Tim Nagel 14af748840 Fix bad merge 2015-03-12 18:22:04 +11:00
Tim Nagel 19e9abaa53 Update documentation links for 3.1 and 3.0 2015-03-11 22:21:56 +11:00
Tim Nagel 5b88baeca6 Merge branch 'pr/744'
Conflicts:
	CHANGELOG-3.1.md
	Command/PopulateCommand.php
2015-03-11 22:17:14 +11:00
Tim Nagel b7c7f77383 Merge branch 'pr/725'
Conflicts:
	CHANGELOG-3.1.md
	Doctrine/AbstractProvider.php
2015-03-11 22:10:54 +11:00
Tim Nagel 181b5a0ac0 Merge pull request #815 from merk/command-tidy
Command tidy
2015-03-11 22:09:00 +11:00
Tim Nagel 0009c858a7 Fixes for errors when with the progress closure 2015-03-11 21:31:23 +11:00
Tim Nagel f834499a2c Fixes after review 2015-03-11 15:54:04 +11:00
Tim Nagel 3d69c08b5a Merge branch 'master' into pr/725
Conflicts:
	Doctrine/AbstractProvider.php
2015-03-11 15:47:03 +11:00
Tim Nagel cbb247978a Update changeling for aggregations 2015-03-11 15:37:34 +11:00
Tim Nagel a830a9b7b6 Merge pull request #726 from cassianotartari/master
Add aggregations
2015-03-11 15:34:47 +11:00
Tim Nagel 7b90c84daa Merge pull request #728 from ugomeda/master
Add missing KnpPaginator example in the usage documentation
2015-03-11 15:33:03 +11:00
Tim Nagel 034b3a9c60 Merge branch '3.0.x' 2015-03-11 15:30:09 +11:00
Evgeniy Sokolov cf9f7c6be8 fix error for empty type configuration 2015-03-11 15:29:17 +11:00
Tim Nagel 4a564401b4 Symfony <=2.5 uses the legacy progress closure 2015-03-11 15:17:06 +11:00
Tim Nagel ef2671dd36 Moved the progress helper closure building to a dedicated class 2015-03-11 15:11:42 +11:00
Tim Nagel 47785260a4 Fix an error that PopulateCommand would always ignore errors 2015-03-11 15:11:13 +11:00
Tim Nagel cb7b4c1dca Configurable ProgressBar format definition overrides 2015-03-11 15:09:48 +11:00
Tim Nagel 4c87d24fc1 Changes after review 2015-03-11 14:55:13 +11:00
Tim Nagel f6df88cc67 Merge branch 'master' into pr/744
Conflicts:
	Command/PopulateCommand.php
2015-03-11 14:11:18 +11:00
Tim Nagel 7fa14f713c Merge branch '3.0.x' 2015-03-10 22:01:24 +11:00
Tim Nagel 55bfee22e8 Cache composer 2015-03-10 22:00:09 +11:00
Tim Nagel 07995d9b75 Merge pull request #794 from merk/property-paths
Added capability to define property paths
2015-03-10 21:58:32 +11:00
Tim Nagel 72a981ab51 Attempted fix for php 5.3 2015-03-10 21:49:26 +11:00
Tim Nagel 1be1cb645c Merge pull request #809 from chtipepere/patch-1
Update usage.md
2015-03-10 21:13:21 +11:00
Marichez Pierre 70fe702ccf Update usage.md
Fix indent in yml
2015-03-07 22:06:32 +01:00
WouterJ.nl bot 3c87cc16c7 Merge branch '3.0.x' 2015-02-18 11:38:02 +01:00
Tim Nagel c5185a0307 Added capability to define property paths 2015-02-09 09:40:49 +11:00
Tim Nagel e1d5ef72d2 Merge pull request #792 from FriendsOfSymfony/progress-bar-bc
Additional change for 2.5 ProgressBar support
2015-02-07 00:03:50 +11:00
Tim Nagel 797d066286 Additional change for 2.5 ProgressBar support
getProgress() is not available and getStep() throws a deprecation warning.
2015-02-06 23:50:39 +11:00
Tim Nagel 72589f8341 Fix ProgressBar for Symfony 2.5 2015-02-06 22:07:13 +11:00
Tim Nagel 2401b1083c Bump version 2015-01-31 18:33:07 +11:00
Tim Nagel 8e627ee011 Merge pull request #788 from FriendsOfSymfony/elastica-bump
Update Elastica dependency
2015-01-31 18:31:08 +11:00
Tim Nagel 6992beeb47 Update Elastica dependency 2015-01-31 18:24:47 +11:00
Tim Nagel 7bc88494a1 Merge branch '3.0.x' 2015-01-27 08:21:57 +11:00
Tim Nagel 30d52bf0f0 Merge pull request #782 from allanbrault/patch-1
Update PopulateCommand.php
2015-01-27 08:08:31 +11:00
Allan Brault 58eed2dc7f Update PopulateCommand.php
in the case i have 110 objects, it was doing :
Populating abc/Index 90.9% (100/110)
Populating abc/index 181.8% (200/110)

now :
Populating abc/Index 90.9% (100/110)
Populating abc/index 100.0% (110/110)
2015-01-26 16:59:55 +01:00
Tim Nagel d44525f6f3 Merge pull request #780 from merk/pr/770
Continued work on ProgressBar
2015-01-25 19:54:53 +11:00
Tim Nagel 67c0b79505 Tidy up ProgressBar use, move most calculations for loggerClosure
into PopulateCommand rather than in AbstractProvider
2015-01-25 19:53:58 +11:00
Pablo Martelletti 6bb2def21e Use ProgressBar in populate commande when available. 2015-01-25 19:53:06 +11:00
Tim Nagel e5d9c3ddbb Merge branch 'pr/732' into 3.0.x 2015-01-25 19:41:57 +11:00
Tim Nagel 906e2e0749 Ability to set connectionStrategy for elastica clients 2015-01-25 19:38:01 +11:00
Tim Nagel 81f5f983c0 Fix line break 2015-01-25 19:06:41 +11:00
Tim Nagel 030b194c7b Fix tests 2015-01-22 11:49:28 +11:00
Tim Nagel 7f28be3c4e Merge branch '3.0.x' 2015-01-22 11:26:07 +11:00
Tim Nagel 79e263d7a7 Merge pull request #779 from merk/index-name-doc
Clarified what index_name does. Closes #731
2015-01-22 11:25:47 +11:00
Tim Nagel e772ca6450 Fix php 5.3 compatibility 2015-01-22 11:23:51 +11:00
Tim Nagel 55abe132c6 Update changelog 2015-01-22 09:36:33 +11:00
Tim Nagel aef5940578 Merge branch 'pr/760' into 3.0.x 2015-01-22 09:34:10 +11:00
Tim Nagel b9b0c1b961 Move TypeConfig creation to its own method 2015-01-22 09:33:46 +11:00
Michaël Perrin 64fa26e3d9 Fix PHP notice when using indexes without defined types 2015-01-22 09:24:59 +11:00
Tim Nagel c4a2858265 Merge branch 'pr/772' into 3.0.x 2015-01-22 09:23:24 +11:00
CedCannes 7471c13d75 Update manual-provider.md
Typo in class path
2015-01-22 09:22:34 +11:00
Tim Nagel c901d60552 Clarified what index_name does. Closes #731 2015-01-22 09:15:38 +11:00
Tim Nagel 401446e1c4 Add integration testing around _parent mapping 2015-01-22 09:06:21 +11:00
Christophe Coevoet 1cea135dc5 Merge branch '3.0.x' 2015-01-21 18:11:18 +01:00
Christophe Coevoet 32d190f554 Bump the changelog for 3.0.7 2015-01-21 18:10:33 +01:00
Christophe Coevoet 5060fa4d4a Move the test file to a better location
The test of the DI extension is not a functional test.
2015-01-21 17:51:56 +01:00
Christophe Coevoet b4c01f3641 Merge branch 'formapro-forks/fixing-bug-with-parent' into 3.0.x 2015-01-21 17:43:36 +01:00
Tim Nagel 1bf59d5b66 Merge pull request #773 from merk/transformer-event
Dispatch an event when transforming objects
2015-01-21 13:29:23 +11:00
Tim Nagel 9188566dfe Dispatch an event when transforming objects 2015-01-20 14:41:11 +11:00
Tim Nagel d7e9d9b8a6 Merge pull request #718 from WouterJ/patch-1
Applied standard installation template
2015-01-12 10:48:42 +11:00
Wouter J a28b9d3069 Applied standard installation template 2015-01-11 17:48:13 +01:00
Tim Nagel c2c87a53e4 Merge branch '3.0.x' 2015-01-09 08:56:41 +11:00
Tim Nagel 2ce2d7e610 Add test for multi_field 2015-01-09 08:55:57 +11:00
Vladimir Kartaviy 905265ea0e "multi_field" type fields are not normalized
Fix for #764
2015-01-09 08:47:15 +11:00
Tim Nagel ee9f7e5297 Merge pull request #768 from vkartaviy/patch-1
Fix ODM listener service arguments
2015-01-09 08:44:44 +11:00
Vladimir Kartaviy a0f11ff36f Fix ODM listener service arguments
It became broken after d731443aa5 commit
2015-01-07 16:45:18 +02:00
Oleg Andreyev 7c90660e02 attempt to make scrutinizer happy, about duplicate code in test 2015-01-04 20:57:08 +02:00
Oleg Andreyev 7efcdad97c adding ResetEvent (pre/post index and pre/post type reset), injected EventDispatcher into Resetter 2015-01-04 14:51:21 +02:00
Oleg Andreyev afbe1e03a1 adding unit test for PopulateListener 2015-01-04 14:51:21 +02:00
Oleg Andreyev 303af508b2 adding populate events 2015-01-04 14:17:25 +02:00
Tim Nagel c39b86c6c5 Merge branch 'detection-config' 2015-01-04 22:12:21 +11:00
Tim Nagel b6d46dba4a Merge tag 'v3.0.6' 2015-01-04 22:10:18 +11:00
Tim Nagel 9f5ce217dc Release 3.0.6 2015-01-04 22:08:20 +11:00
Tim Nagel 6ef6092f3f Update 3.0 changeling 2015-01-04 22:03:19 +11:00
Tim Nagel 92aab4bcf6 Add PR# to changelog 2015-01-04 22:02:54 +11:00
Tim Nagel e361b7c53b Implement additional configuration options for types. 2015-01-04 21:58:53 +11:00
Tim Nagel 7fac93ff8b Fix mongodb doctrine listener 2015-01-04 20:16:32 +11:00
Dmitry Korotovsky d731443aa5 Avoid Doctrine\Listener::getSubscribedEvents() call on each page where doctrine is active 2015-01-04 20:05:47 +11:00
Evan Owens 156884527c Update example
FOS\ElasticaBundle\Client has been deprecated.
2015-01-04 20:01:04 +11:00
DjangoFR 5eaff9e61b removed unused image (being base64 encoded - see #742) 2015-01-04 19:49:30 +11:00
Danijel Brkic 3975ed3d5b Built-in templates use a base64 encoded image for the toolbar 2015-01-04 19:49:25 +11:00
Tim Nagel d33e064801 Merge commit '1f7acc5' into 3.0.x 2014-12-24 09:10:41 +11:00
Tim Nagel 1f7acc563a Ignore strings starting with @ 2014-12-24 09:09:45 +11:00
Christophe Coevoet eaa32cbf22 Fix the BC layer for indexable callbacks
Using services was not based on a @ prefix in the string in the old API
but based on the existence of the class.
2014-12-23 15:21:25 +01:00
Christophe Coevoet f3e31e613e Merge branch '3.0.x'
Conflicts:
	.travis.yml

Duplicate testing on Symfony 2.6 is removed (it is already done by the
normal testing as it is the latest stable), and testing on 2.4 is
removed again because it is EOLed.
2014-12-17 14:25:15 +01:00
Christophe Coevoet 97848ca0d0 Removed the testing on Symfony dev-master
dev-master is Symfony 3.0, and we are not yet marking it as compatible
2014-12-17 14:23:00 +01:00
Tim Nagel 88e9f5aac6 Merge pull request #748 from NinjDS/totalhits
Genuine ES behaviour for getTotalHits()
2014-12-01 22:04:25 +11:00
Tim Nagel ca57c42489 Merge pull request #738 from FriendsOfSymfony/adjust-build-matrix
adjust to symfony 3.0 development having started
2014-12-01 22:02:10 +11:00
David Buchmann 2664fec35e adjust to symfony 3.0 development having started. whenever there is a new 2.x branch in symfony, we should add it to the matrix 2014-12-01 09:21:43 +01:00
Nikolai Zujev c45dcd955d Clean filtered objects if the entire batch was filtered away, to prevent memory allocation issue. 2014-12-01 09:47:32 +11:00
Tim Nagel 7dc2f833c4 Merge branch '3.0.x' 2014-12-01 09:46:49 +11:00
Tim Nagel e211f31658 Merge pull request #743 from KingCrunch/patch-1
Update custom-repositories.md to use code highlighting
2014-12-01 09:36:45 +11:00
Tim Nagel 00df6c586f Merge pull request #737 from jaymecd/clean_filtered
Doctrine provider: memory allocation caused by "entire batch was filtered away"
2014-12-01 09:34:21 +11:00
Tim Nagel 921377d5ed Merge pull request #750 from javiereguiluz/update_installation_instructions
Updated installation instructions
2014-12-01 08:52:01 +11:00
Javier Eguiluz c4ee9fa83e Updated installation instructions 2014-11-29 17:17:31 +01:00
Adrien Morel b09bf3cf10 RawPaginatorAdapter::getTotalHits() can retrieve the genuine total hits returned by elasticsearch 2014-11-26 16:25:23 +01:00
Sebastian Krebs 1e2da2d84f Update custom-repositories.md to use code highlighting
Looks better this way
2014-11-21 14:21:26 +01:00
David Buchmann 2d8903a330 Merge pull request #736 from piotrantosik/patch-2
Fix changelog header
2014-11-14 15:59:43 +01:00
Nikolai Zujev e6d50c584c Clean filtered objects if the entire batch was filtered away, to prevent memory allocation issue. 2014-11-13 17:34:21 +02:00
Piotr Antosik 2a7459f327 Fix changelog header 2014-11-10 14:05:56 +01:00
Gnucki c80b4efd3e Merge branch master 2014-11-02 14:28:44 +01:00
Gnucki 1369a01dd7 Add slice fetching abstraction 2014-11-02 14:24:28 +01:00
Gnucki 901ea49a32 Factorize manager call 2014-11-02 14:22:26 +01:00
Ugo Méda 1b01aef46f Add missing KnpPaginator example in the usage documentation 2014-10-16 15:46:40 +02:00
Gnucki be75b387a5 Add slice fetching abstraction 2014-10-13 15:41:29 +02:00
Gnucki 445f2f93f6 Add slice fetching abstraction 2014-10-13 15:40:31 +02:00
Cassiano e7634d8ba2 Update RawPaginatorAdapter.php 2014-10-08 17:15:57 -03:00
Cassiano Tartari 419bf2ccf6 Merge remote-tracking branch 'upstream/master'
Conflicts:
	Paginator/RawPaginatorAdapter.php
2014-10-08 17:08:13 -03:00
Cassiano 6f4e389dfd Update RawPaginatorAdapter.php 2014-10-08 15:19:48 -03:00
Cassiano 196aed6630 Update PaginateElasticaQuerySubscriber.php 2014-10-08 14:33:15 -03:00
Cassiano 7c6fe4eaab Update FantaPaginatorAdapter.php 2014-10-08 14:32:24 -03:00
Cassiano d2de7ba6e8 Update PaginatorAdapterInterface.php 2014-10-08 14:31:39 -03:00
Cassiano 197bb3ebad Update PartialResultsInterface.php 2014-10-08 14:31:19 -03:00
Cassiano f9ce1dcd4e Update RawPaginatorAdapter.php 2014-10-08 14:30:03 -03:00
Cassiano 4c4e9ffe36 Update RawPartialResults.php 2014-10-08 14:27:50 -03:00
Gnucki 7fa7e44bee Fix mongodb populate falling down performances for big collections 2014-10-08 10:44:49 +02:00
Tim Nagel 67ae044309 #724 Fix debug_logging option on the provider 2014-10-08 08:59:45 +11:00
Tim Nagel 25d56d0a0f 3.1.x requires doctrine 2.4+ 2014-10-03 08:21:02 +10:00
Tim Nagel a88e4e38f8 Merge branch 'cs-fixes-31' 2014-09-21 21:09:39 +10:00
Tim Nagel 5cdeac9b45 Merge branch '3.0.x' 2014-09-21 21:09:33 +10:00
Michael Schramm b3f87e414f move classes to parametes 2014-09-21 21:08:41 +10:00
Tim Nagel 71a86cada5 BC BREAK: Add handlesObject method to ObjectPersisterInterface 2014-09-21 20:25:32 +10:00
Tim Nagel d0ce82ac2a Adjust DoctrineListener to remove unnecessary method getDoctrineObject 2014-09-21 20:25:25 +10:00
Tim Nagel 428a1014ca Move query logging into its own method 2014-09-21 20:12:30 +10:00
Tim Nagel 1d5fe44ca4 Fix CS from scrutiniser-ci 2014-09-21 20:12:30 +10:00
Tim Nagel cf586a4ef4 Merge branch '3.0.x' 2014-09-04 09:39:54 +10:00
Tim Nagel c4210a5c6d Fix previous merge 2014-09-04 09:37:27 +10:00
Tim Nagel 029ebb153a Merge remote-tracking branch 'upstream/pr/705' into 3.0.x
Conflicts:
	Index/AliasProcessor.php
2014-09-04 09:26:53 +10:00
Tim Nagel 736163551c Merge remote-tracking branch 'upstream/pr/704' into 3.0.x 2014-09-04 09:17:34 +10:00
Patrick McAndrew 76dcd2f62e fix warning if no aliases
PHP Warning:  array_keys() expects parameter 1 to be array, AliasProcessor.php on line 128
2014-08-29 15:52:46 +01:00
Patrick McAndrew 2958833012 Ability to delete an index if expecting an alias 2014-08-28 17:59:58 +01:00
Tim Nagel 6bea3c2154 Add code quality badge 2014-08-21 23:23:20 +10:00
Tim Nagel 001daeeac0 Merge branch '3.0.x' 2014-08-21 23:17:42 +10:00
Tim Nagel a7a23b92cb Merge pull request #698 from merk/new-testing
Update testing
2014-08-21 23:15:38 +10:00
Tim Nagel 598a59927e Update travis testing 2014-08-21 22:54:12 +10:00
Patrick McAndrew 0425379420 add back fos_elastica.client tag that was removed in e78950ddb7 2014-08-21 22:18:53 +10:00
Tim Nagel 380727afbe Merge pull request #695 from notFloran/fix-search-annotation
Use the new Search annotation
2014-08-21 21:54:23 +10:00
Floran Brutel 69c2214bc5 Use the new Search annotation
Use "FOS\ElasticaBundle\Annotation\Search" instead of "FOS\ElasticaBundle\Configuration\Search" in the repository manager.
Update the cookbook
2014-08-18 13:30:45 +02:00
Tim Nagel af5fb7e97e Merge pull request #694 from notFloran/update-version-setup
Update version in setup.md
2014-08-17 21:13:11 +10:00
Floran Brutel 22a2a223cc Update version in setup.md
Use "~3.0" instead of "~3.0.2" to get version 3.0.3 and future minor
versions
2014-08-17 12:16:15 +02:00
Tim Nagel c08d86124a Merge branch '3.0.x' 2014-08-11 08:44:22 +10:00
Luis Cordova 33ee047f83 fix dependency on elastic extension
i had a weird error in which just installing the bundle with "friendsofsymfony/elastica-bundle":     "~3.0.2",
game me a version back even before it had a composer! 😊
i checked and found out composer gets really confused or glitchy with packages not following semver.
In any case 1.3.0 is out, and i remove the 4th digit which i think composer ignores totally or gets confused about.
2014-08-11 08:44:15 +10:00
Tim Nagel 49c5b2ee63 Merge branch 'pr/685' 2014-08-08 21:37:20 +10:00
Oleg Andreyev 27385046ca changing AliasProcessor::setRootName, so to use "Y-m-d-His" instead of random string 2014-08-08 21:36:49 +10:00
Tim Nagel 517d6e679c Merge branch '3.0.x' 2014-08-08 21:31:14 +10:00
Tim Nagel 420135dc85 Merge branch 'travis-fixing' into 3.0.x 2014-08-08 10:00:55 +10:00
Tim Nagel 20033709cf Fix empty mappings for old ES versions 2014-08-08 09:59:24 +10:00
Tim Nagel 229d4cb982 Merge branch '3.0.x' 2014-08-08 08:36:36 +10:00
Tim Nagel 84cf6c79c2 Merge remote-tracking branch 'upstream/pr/684' into 3.0.x 2014-08-08 08:33:59 +10:00
Tim Nagel 9d45de8b64 Merge pull request #684 from cygnusb2b/deep-merging-config
Configuration Bug: Restored noDeepMerging
2014-08-08 08:33:09 +10:00
Josh Worden f5987a48b9 BC Break: Restored noDeepMerging to Configuration
When performNoDeepMerging is not used, Symfony environment-specific server configurations no longer work.
2014-08-07 16:33:14 -05:00
Tim Nagel dafe8abe0e Output ES version 2014-08-07 09:32:30 +10:00
Tim Nagel 0d22c20d37 Merge remote-tracking branch 'upstream/pr/682' into 3.0.x 2014-08-07 09:26:11 +10:00
Tim Nagel f9eb6577d1 Add tests to cover no mappings for serializer enabled type 2014-08-07 09:25:49 +10:00
Tim Nagel c44f676224 Test mappings key being null still causes appropriate configuration changes 2014-08-07 09:25:09 +10:00
Luis Cordova 9296534d30 Update setup.md 2014-08-06 17:25:05 -05:00
Tim Nagel f6e018f011 Merge remote-tracking branch 'upstream/pr/679' into 3.0.x 2014-08-04 08:53:14 +10:00
Lukasz Cybula 9a5b80e723 Ignore missing Doctrine results during hybridTransform() 2014-07-31 14:13:01 +02:00
Tim Nagel eaa6c2e085 Merge branch '3.0.x' 2014-07-31 15:55:37 +10:00
Tim Nagel 64c5c19831 Merge branch 'pr/675' into 3.0.x
Closes #675
2014-07-31 15:54:53 +10:00
Pablo 9befa90f41 Return repository in Manager Class MongoDB 2014-07-31 15:54:22 +10:00
esodin 659468ae3a Issue: Parent is missing in the fields list that causes RoutingMissingException on flushing - test 2014-07-24 17:36:45 +03:00
esodin 001b38cf59 Issue: Parent is missing in the fields list that causes RoutingMissingException on flushing - test 2014-07-24 17:07:22 +03:00
esodin 1ef55b1239 Issue: Parent is missing in the fields list that causes RoutingMissingException on flushing - test 2014-07-24 17:03:26 +03:00
Tim Nagel c748ec64e9 Merge pull request #672 from merk/gh663
Fix indexable callbacks being overwritten by another index
2014-07-23 23:10:24 +10:00
Tobias Nyholm 11ee25cfea Added PHP 5.6 2014-07-23 21:47:10 +10:00
Tim Nagel e5410a5b65 Fix indexable callbacks being overwritten by another index
closes #663
2014-07-23 21:38:46 +10:00
Tim Nagel 9fbc622929 Merge pull request #671 from merk/completion-type-fix
Fix completion type
2014-07-23 20:06:33 +10:00
Tim Nagel fad481d822 Fix completion type 2014-07-23 20:00:14 +10:00
esodin 714502fa1f Issue: Parent is missing in the fields list that causes RoutingMissingException on flushing 2014-07-17 16:01:10 +03:00
Tim Nagel 7cedc5ba5a Merge pull request #653 from notFloran/fix-doc
Fix "Faceted Searching" doc
2014-07-08 09:35:40 +10:00
Floran Brutel d797af5b80 Fix "Faceted Searching" doc 2014-07-07 18:35:23 +02:00
Tim Nagel cdaf7105e0 Bump dev version to 3.1 2014-07-05 15:14:29 +10:00
Tim Nagel d88d96bf55 Fix invalid service reference in production 2014-07-04 22:10:24 +10:00
Tim Nagel bf38e59e49 Documentation on aliased repopulation 2014-07-04 13:07:35 +10:00
Tim Nagel 90a554f627 Merge branch 'pr/622' 2014-07-04 12:49:13 +10:00
tamirvs d57d430ab3 Ignore iterator keys when converting to array 2014-07-04 12:48:27 +10:00
Tim Nagel be4af0d1af Merge pull request #644 from merk/elastica-connections
Rename servers to connections
2014-07-04 11:28:44 +10:00
Tim Nagel 425aa3d3e1 Merge pull request #645 from merk/stopwatch
Add stopwatch support for Elastica\Client
2014-07-04 11:25:37 +10:00
Tim Nagel 21e5d906a7 Simplify *One methods in the persister 2014-07-03 23:37:44 +10:00
Tim Nagel 815b836cdc Add stopwatch support for Elastica\Client 2014-07-03 23:20:18 +10:00
Tim Nagel 55dcf6859a Rename servers to connections 2014-07-03 21:58:54 +10:00
Tim Nagel c200e8fdfd Add tests for attachment type 2014-07-03 20:21:01 +10:00
Maksim Kotlyar 965b319d82 [transformers] tell container that first argument is collection. 2014-07-01 14:40:03 +03:00
Tim Nagel 7fcbb64a15 Fix edge case for dynamic templates 2014-07-01 18:02:30 +10:00
Tim Nagel 5d65676659 Add tests and normalisation to support old dynamic_templates format 2014-07-01 17:59:22 +10:00
Tobias Schultze c9a24436f3 dynamic templates are a list of hashes and have a mapping key (not properties) 2014-06-30 16:19:10 +02:00
Tim Nagel 9f85db9876 Merge pull request #639 from RonXS/master
[FEATURE] Use static instantiation with max depth check
2014-06-30 08:41:50 +10:00
Ron van der Molen 78db0b9b63 [FEATURE] Use static instantiation with max depth check 2014-06-29 22:14:32 +02:00
Tim Nagel ad37a28356 Fix populating command setting alias before population 2014-06-27 15:08:56 +10:00
Tim Nagel 474cbfa979 Added test coverage for ES store 2014-06-26 19:51:16 +10:00
Tim Nagel 77f2b99a3e Fix Indexable tests 2014-06-26 18:01:34 +10:00
Tim Nagel 5cc8c2978f Merge pull request #635 from merk/fix-array-callable
Fix array format for indexable_callback
2014-06-26 17:47:14 +10:00
Tim Nagel 96dc613c71 Fix array format for indexable_callback 2014-06-26 17:46:29 +10:00
Tim Nagel 4eacb5f4c8 Merge pull request #634 from merk/more-mapping-tests
Fix ClassCastException when no settings are present
2014-06-26 17:32:01 +10:00
Tim Nagel a879d3c1c9 Fix ClassCastException when no settings are present 2014-06-26 17:31:20 +10:00
Tim Nagel ffa73db1d2 Merge pull request #621 from gcds/patch-1
Added elapsed item to toolbar and menu
2014-06-25 13:27:53 +10:00
Tim Nagel 4d52c327aa Add back missing KnpPaginatorSubscriber service 2014-06-25 13:24:02 +10:00
Tim Nagel 524474fdc6 Merge branch 'propel-test' 2014-06-25 13:18:15 +10:00
Tim Nagel ae03b3f3cf Fix propel service definition 2014-06-25 13:18:02 +10:00
Tim Nagel f6264f4149 Fix AbstractProvider tests 2014-06-24 10:30:31 +10:00
Tim Nagel 2437a098ba Fix anonymous service error 2014-06-24 10:20:15 +10:00
Tim Nagel ae3605828e Fix AbstractProvider test 2014-06-24 10:19:26 +10:00
Tim Nagel 2a20bb623c Merge branch 'typed-config'
Conflicts:
	composer.json
2014-06-24 09:09:29 +10:00
Tim Nagel 9a62187329 Fix undefined index_name notice 2014-06-23 23:50:52 +10:00
Tim Nagel ca6991d494 Fix serializer 2014-06-23 23:05:57 +10:00
Tim Nagel 95e445bd0d Merge pull request #623 from merk/provider-fix
Fix provider bailing if the indexable service filters an entire batch of objects
2014-06-19 12:18:57 +10:00
Tim Nagel c97f0f1ddf Fix provider bailing if the indexable service filters an entire batch of objects 2014-06-19 11:14:13 +10:00
Tim Nagel 4e990e0cee Fixed mapping issues 2014-06-19 00:14:41 +10:00
Aurimas Niekis b0749afaf1 Added elapsed item to toolbar and menu
Kind similar to doctrine toolbar item
2014-06-18 16:12:16 +03:00
Tim Nagel 3ae382c933 Add tests to make sure KnpPaginator plays nice 2014-06-18 20:02:05 +10:00
Tim Nagel 949ea6963f Revert "Make the class of fos_elastica.paginator.subscriber service configurable"
This reverts commit fe19df365a.
2014-06-18 19:55:33 +10:00
Tim Nagel b155f304e4 Move IndexManager's resolution to tagged index services 2014-06-18 16:49:57 +10:00
Tim Nagel afbaf875b9 Cache creation of indexes and types in Elastica to avoid recreation 2014-06-18 16:48:00 +10:00
Tim Nagel 8905b4248c Rename Manager to ConfigManager 2014-06-18 16:47:01 +10:00
Tim Nagel 5cbb8ce1b6 Merge pull request #591 from OskarStark/patch-1
use $this->container instead of $container in usage.md
2014-06-18 09:55:16 +10:00
Tim Nagel b3c0d4fd44 Merge pull request #600 from gido/patch-1
Make the class of `fos_elastica.paginator.subscriber` service configurable
2014-06-18 09:54:13 +10:00
Tim Nagel ec5c05e8be dev 2014-06-17 23:22:58 +10:00
Tim Nagel e78950ddb7 Merge branch 'master' into typed-config
Conflicts:
	CHANGELOG-3.0.md
	Client.php
	DependencyInjection/Configuration.php
	DependencyInjection/FOSElasticaExtension.php
	DynamicIndex.php
	Resources/config/config.xml
	Resources/config/mongodb.xml
	Resources/config/orm.xml
	Tests/DependencyInjection/ConfigurationTest.php
	composer.json
2014-06-17 22:42:15 +10:00
Tim Nagel 5f335c37ab Merge pull request #613 from merk/composer-update
Update composer.json
2014-06-17 22:38:56 +10:00
Tim Nagel 1e07d3c7fe Update composer.json 2014-06-17 22:19:19 +10:00
Tim Nagel 089e7f0d2e Add unstable badge
[skip ci]
2014-06-17 12:12:54 +10:00
Tim Nagel aafb8c8e89 Fix indexable callbacks with alias based indexes 2014-06-17 12:03:38 +10:00
Tim Nagel e225d841ed Wrong service. Whoops. 2014-06-17 10:54:09 +10:00
Tim Nagel b49437529c Fix incorrect provider configuration 2014-06-17 10:41:11 +10:00
Tim Nagel 94568d9554 Merge pull request #608 from merk/populate-indexable
Indexable callback refactoring, implemented callback in Provider
2014-06-17 10:11:46 +10:00
Tim Nagel 2e23f4a1bf Added documentation changes for indexable_callback 2014-06-17 10:07:10 +10:00
Tim Nagel 02d864f7e2 Merge pull request #593 from merk/gh-592
Fix nested property configuration
2014-06-17 09:48:22 +10:00
Tim Nagel 12797c60fa Merge pull request #609 from PeerJ/FixLoggerWithNullTransport
make sure headers is set prior to accessing
2014-06-17 09:44:43 +10:00
Patrick McAndrew 934c6af8b8 make PSR2 compliant 2014-06-16 14:30:03 +01:00
Tim Nagel 0383811834 Fix test, add test for failing mappings 2014-06-16 23:28:53 +10:00
Tim Nagel ada3942576 Config Source providers 2014-06-16 23:23:49 +10:00
Tim Nagel 813a4a5d26 Fix previous commits 2014-06-16 22:43:16 +10:00
Tim Nagel 7682d5a80a Update composer.json 2014-06-16 22:33:31 +10:00
Tim Nagel 64be10447d Move Search annotation 2014-06-16 22:33:04 +10:00
Patrick McAndrew 629ca0df2e make sure headers is set prior to accessing 2014-06-16 12:59:09 +01:00
Tim Nagel 391e18dcbf Update changelog 2014-06-16 16:20:17 +10:00
Tim Nagel e54cc3c243 Implement callback checking in the provider 2014-06-16 16:17:04 +10:00
Tim Nagel 66d2410999 Move Indexable callback calculations to a new service 2014-06-16 15:57:42 +10:00
Tim Nagel f5932a8e47 Merge remote-tracking branch 'origin/refactor' into typed-config
Conflicts:
	Resetter.php
2014-06-14 11:11:31 +10:00
Gilles Doge fe19df365a Make the class of fos_elastica.paginator.subscriber service configurable 2014-06-10 18:20:02 +02:00
Tim Nagel 14083496d7 Merge pull request #598 from merk/mapping-tests
Adds initial functional tests
2014-06-08 22:45:40 +10:00
Tim Nagel 5009673b6a Functional Tests v1 2014-06-08 22:35:38 +10:00
Tim Nagel 833feee207 Merge branch 'transport-option' of https://github.com/milan/FOSElasticaBundle into transport-option
Conflicts:
	DependencyInjection/Configuration.php
2014-06-08 18:00:17 +10:00
Tim Nagel f6b9e57a9c Merge pull request #595 from PeerJ/FixResetterException
fix method call
2014-06-08 17:56:15 +10:00
Patrick McAndrew 366fb39606 fix method call 2014-06-04 17:26:25 +01:00
Tim Nagel 8540f13bbf Fix nested property configuration
Fixes #592
2014-06-02 21:35:51 +10:00
Tim Nagel fa65784b47 Merge pull request #583 from merk/rename-mappings
Rename mappings to properties maintaining BC
2014-06-02 00:41:54 +10:00
Tim Nagel 1dc6856ef9 Configuration rework 2014-06-02 00:40:03 +10:00
Oskar Stark 6a822504bc use $this->container instead of $container 2014-06-01 15:57:29 +02:00
Tim Nagel c52c32fb56 Merge pull request #590 from Tornaldo/patch-1
Update usage.md
2014-06-01 16:38:37 +10:00
Tornaldo 2e0aa064a2 Update usage.md 2014-06-01 01:27:20 +02:00
Tim Nagel 12b724dd20 Merge pull request #585 from leberknecht/patch-2
fixing missing flush event handler
2014-05-26 10:17:11 +10:00
Delf Tonder 8060d3dcd7 fixing missing flush event handler
In [commit](843c76b6ca (diff-850942b3ba24ab03a40aaa81b6152852)) the configuration-definition for the flush listener was accidentally removed. 
As the flush listener is no longer set to be enabled in the extensions getDoctrineEvents method, the flush listener is not set. 
This results in a situation were we are only able to have the modified objects on the list for index-update, but never actually sending the update to the ES host.
2014-05-25 18:51:14 +02:00
Tim Nagel dad15d0b38 Deprecate top level classes 2014-05-25 20:51:46 +10:00
Tim Nagel 53180e2810 Bring tidy in line with property renaming 2014-05-25 20:14:51 +10:00
Tim Nagel 8e88505a3f Merge branch 'rename-mappings' into configuration-tidy 2014-05-25 20:13:21 +10:00
Tim Nagel a79fa0242e Simplified Configuration.php 2014-05-25 20:08:01 +10:00
Tim Nagel c38dc107e7 Rename mappings to properties maintaining BC
Fixes #407
2014-05-25 17:56:57 +10:00
Tim Nagel be89ccf825 Merge pull request #582 from tobiassjosten/urllessconfig
Don't default url
2014-05-25 14:56:42 +10:00
Tobias Sjösten f97e66712a Don't default url 2014-05-25 00:31:40 +01:00
Tim Nagel 5a84d55129 Merge pull request #580 from leberknecht/patch-1
update setup.md - immediate is an listener option
2014-05-24 23:10:44 +10:00
Delf Tonder f9745c8d21 update setup.md - immediate is an listener option
fixed yml config example (having immediate as an persistence option will result in parsing error)
2014-05-24 12:49:12 +02:00
Tim Nagel a9ea78443f Support Elastica proxy option 2014-05-24 00:17:59 +10:00
Tim Nagel 458b53240b Merge branch 'configuration-fixes' 2014-05-23 23:21:29 +10:00
Tim Nagel e77aa0c180 Test on php 5.5 2014-05-23 23:20:52 +10:00
Tim Nagel f20392d78b Fix test failures for DoctrineProvider 2014-05-23 23:19:55 +10:00
Tim Nagel f8a445b46c Fix disabling of logger in DoctrineProvider 2014-05-23 23:11:45 +10:00
Tim Nagel d532e6b1e3 Merge pull request #579 from merk/client-overwrite
Surpress server errors cookbook update
2014-05-23 23:01:16 +10:00
Tim Nagel 3addfffc91 Added logging of server errors to example 2014-05-23 23:00:34 +10:00
Tim Nagel 18143449cc Merge branch 'patch-2' of https://github.com/stloyd/FOSElasticaBundle into client-overwrite 2014-05-23 22:52:58 +10:00
Tim Nagel b09c7fb50e Merge branch 'master' of https://github.com/edast/FOSElasticaBundle 2014-05-23 22:51:11 +10:00
Tim Nagel 352e3b68ac Add configuration tests 2014-05-23 22:49:03 +10:00
Tim Nagel 41c4d77b20 Move serializer node to its own method, add serializer to type_prototype 2014-05-23 22:49:03 +10:00
Tim Nagel 843c76b6ca Move persistence node to its own method 2014-05-23 22:49:02 +10:00
Tim Nagel 6d2b7a8367 Combine client normalisation into a single method 2014-05-23 22:48:19 +10:00
Tim Nagel 57fbc70015 Merge pull request #522 from merk/doctrine-provider-speedup
Doctrine provider speedup
2014-05-23 22:46:36 +10:00
Tim Nagel 2029aba76a Ability for FOSElasticaBundle to disable persistence backend logging for population
Update documentation and changelog
2014-05-23 22:46:14 +10:00
Tim Nagel 6253d3f8df Merge pull request #553 from benniekrijger/issue-552-geoshape-mapping
Added GeoShape mapping options
2014-05-23 22:03:58 +10:00
Darius Staisiunas 28d0ee925d added support for geohash 2014-05-23 12:55:33 +03:00
Milan Magudia 2c208a4f10 Allow other transport options to be used i.e. Http, Https, Guzzle etc... 2014-05-22 16:18:08 +01:00
Tim Nagel 70ad5c9b37 Merge pull request #576 from milan/#534-cluster-logger-default
Fix for Issue #543 Client has a dependency on a non-existent service "%kernel.debug%"
2014-05-21 20:58:04 +10:00
Milan Magudia e1bbb87cfe Fix for Issue #543 Client has a dependency on a non-existent service "%kernel.debug%" 2014-05-21 10:24:44 +01:00
Tim Nagel 6748c9c623 Merge pull request #571 from tPl0ch/feature-flush-event
Make it possible to disable flush event through configuration
2014-05-20 08:50:50 +10:00
Tim Nagel 72e7b77dae Merge pull request #562 from evillemez/config
stop config from adding empty arrays into type mappings
2014-05-20 08:49:26 +10:00
Tim Nagel 1c5339ac40 Merge pull request #560 from cassianotartari/master
Update Configuration.php
2014-05-20 08:48:51 +10:00
Thomas Ploch e5754ef5fc Make it possible to disable flush event through configuration 2014-05-13 13:13:06 +02:00
Tim Nagel 6bbe61f319 Merge pull request #567 from caponica/ToModelTransformer_andWhere
Changed QueryBuilder method from ->where() to ->andWhere() ...
2014-05-08 09:43:16 +10:00
caponica b0841c18ec Changed QueryBuilder method from ->where() to ->andWhere() so it works with customised QueryBuilders which have an existing where clause (instead of over-writing any existing DQL 'where' part) 2014-05-08 00:26:30 +01:00
Tim Nagel 62f6cf0f8a Merge pull request #565 from FriendsOfSymfony/elasticsearch-1.1
Elasticsearch 1.*
2014-05-06 16:39:45 +10:00
Lea Haensenberger 5da8ee1a16 still supporting 0.9 versions of elastica 2014-05-06 08:18:37 +02:00
Lea Haensenberger b1d64e358d Also cleanup fields in properties of objects 2014-05-05 13:39:36 +02:00
Lea Haensenberger eaa9f83997 remove empty fields arrays from mapping, this is not ignored anymore by elasticsearch 1.* 2014-05-05 13:39:36 +02:00
Lea Haensenberger ae02364e7c use elastica library 1.1 and higher 2014-05-05 13:39:36 +02:00
Tim Nagel ca50617776 Merge pull request #563 from PeerJ/UpdateElasticaDependency
Update Elastica Dependency
2014-05-03 11:28:52 +10:00
Patrick McAndrew d9f3fa1a59 commit 1dcaadbe6f
Persister/ObjectPersister.php
line 112: $this->type->updateDocuments($documents);
introduces a dependency on Elastica 0.9.10.0
2014-05-02 15:07:15 +01:00
Evan Villemez c93bbb9081 stop config from adding empty arrays into type mappings 2014-05-01 11:05:16 -04:00
Cassiano 39f1033a34 Update Configuration.php
Adding the option to set index_analyzer and search_analyzer to _all field.
2014-04-24 09:39:22 -03:00
Tim Nagel 90abc44968 Merge pull request #556 from ahmedmhmd/master
Revert declaring PaginateElasticaQuerySubscriber as a parameter, so not to break Knp compiler pass
2014-04-20 16:01:38 +10:00
Ahmed Mohamed c9fd1cc5d9 Revert declaring PaginateElasticaQuerySubscriber as a paremeter, so not to break Knp compiler pass 2014-04-20 06:11:20 +02:00
Tim Nagel 1748e9af18 Merge pull request #549 from ahmedmhmd/master
Ensure non-empty properties under fields mappings in case of auto-mapped objects exists along with manual ones
2014-04-20 11:52:03 +10:00
Ahmed Mohamed 806452813a Add test multiple objects mapping of at least one auto-mapped and at least one manually mapped 2014-04-20 03:41:41 +02:00
Tim Nagel 35700cdd78 Merge pull request #551 from piotrantosik/patch-1
Fix immediate doc
2014-04-20 10:20:47 +10:00
Tim Nagel dd7b8d3210 Merge pull request #555 from RuslanZavacky/master
More classes exposed as parameters in config.xml
2014-04-20 10:20:32 +10:00
Ruslan Zavacky 0dcb77d749 More classes exposed as parameters. 2014-04-19 18:31:56 +03:00
ben 2bd6aba7ef Added GeoShape mapping options 2014-04-18 13:57:08 +02:00
Piotr Antosik d33f2e547f Fix immediate doc 2014-04-17 10:14:53 +02:00
Tim Nagel b11d48fe89 Merge pull request #546 from bobvandevijver/master
Fixes multiple updates on multiple flush executions
2014-04-17 07:27:37 +10:00
Tim Nagel 8387667167 Merge pull request #534 from dylanschoenmakers/patch-1
Added example to usage.md
2014-04-16 18:28:11 +10:00
Ahmed Mohamed 4e638a0492 Ensure non-empty properties under fields mappings 2014-04-16 05:27:23 +02:00
Tim Nagel 165696a5a0 Bump Elastica requirement 2014-04-15 08:43:11 +10:00
Bob van de Vijver 449c33aea3 Fixes multiple updates on multiple flush executions 2014-04-12 13:30:52 +02:00
Tim Nagel f93a7d278f Merge pull request #532 from solocommand/master
Added support for getting documents in unified Doctrine Listener
2014-04-11 08:54:03 +10:00
Joshua Worden 4cfe24ae02 Update CS 2014-04-10 07:38:01 -05:00
Dylan Schoenmakers 088452cf88 Added example to usage.md
Added example for using a custom query builder method to usage.md.
2014-04-10 09:25:40 +02:00
Tim Nagel 1bc085141b Fix logger option for listeners 2014-04-10 13:14:03 +10:00
Tim Nagel a97ed80aee Merge branch 'nurikabe-master'
Conflicts:
	Resources/doc/types.md
2014-04-10 13:06:22 +10:00
Tim Nagel b3fdf7b256 Logger for a listener is false by default 2014-04-10 13:05:23 +10:00
nurikabe f07e55417d Re-throw exception if no logger defined 2014-04-07 16:16:37 -04:00
Joshua Worden 22e5a1d4ab CS fix 2014-04-07 10:58:11 -05:00
Tim Nagel d7302847e4 Merge pull request #542 from hugohenrique/patch-1
fix link to documentation of custom repositories
2014-04-07 09:11:12 +10:00
Hugo Henrique 998c69bfc3 Update usage.md
This link to documentation custom repositories is broken
2014-04-06 15:17:02 -03:00
nurikabe a483471694 Set listener loggers to fos_elastica.logger if null 2014-04-04 22:56:47 -04:00
Tim Nagel ab3587980c Merge pull request #539 from nurikabe/patch-2
Fix documentation about `immediate`
2014-04-05 10:34:09 +11:00
Tim Nagel 10fcbfc135 Merge pull request #538 from nurikabe/patch-1
Remove rogue paren
2014-04-05 10:30:36 +11:00
Evan Owens 53332eb057 Allow for catching/logging persistance errors per listener 2014-04-04 18:32:48 -04:00
Evan Owens 377b2843ba Fix documentation about immediate
Unless I misimplemented this, "immediate" means persist to ElasticSearch "immediately"; before flushing.  Default behavior is false; persist to ElasticSearch postFlush.
2014-04-05 07:12:16 +10:00
Evan Owens 10f6149f8c Remove rogue paren 2014-04-05 07:04:52 +10:00
Evan Owens 6f444f1ce8 Bulk upsert 2014-04-04 15:51:55 -04:00
nurikabe 49284a4963 Merge remote-tracking branch 'upstream/master' 2014-04-04 15:29:06 -04:00
Josh Worden b6e2583455 Added getDoctrineObject method to retrieve an entity or document from a LifecycleEventArgs instance in the unified Listener class 2014-04-04 10:47:50 -05:00
Tim Nagel d67d525b66 Merge pull request #485 from piotrantosik/elastica-knp-paginator-sort
Added knp paginator sort functionality to PaginateElasticaQuerySubscribe
2014-04-04 08:46:40 +11:00
Piotr Antosik 1bc148569b Added knp paginator sort functionality to PaginateElasticaQuerySubscriber 2014-04-03 10:44:18 +02:00
Patrick Zahnd 13c2d10e39 Added knp paginator sort functionality to PaginateElasticaQuerySubscriber 2014-04-03 00:02:43 +02:00
Tim Nagel e74acb1e4f Revert "Avoid index reset error in case of unexistant index"
This reverts commit a121a77774.
2014-04-03 08:00:52 +11:00
Josh Worden 1628413e65 Added support for getting documents in unified Doctrine Listener 2014-04-02 10:06:24 -05:00
nurikabe a89856be50 Reset / create new index even if the index doesn't exist 2014-04-02 07:18:46 -04:00
Tim Nagel ff95945819 Merge pull request #527 from baggachipz/master
Upsert-type functionality for existing ORM Entities
2014-04-02 08:47:47 +11:00
Tim Nagel ec7f04a261 Merge pull request #515 from nurikabe/master
Clone entities on delete to preserve ids
2014-04-01 20:22:22 +11:00
Tim Nagel 5855ac6fbd Merge pull request #525 from jvandesande/MultiField
Unset nested "fields" for deeper nested configs too
2014-04-01 09:11:31 +11:00
baggachipz 20810fa415 Upsert-type functionality for existing ORM Entities
This is an attempt to fix the issue: https://github.com/FriendsOfSymfony/FOSElasticaBundle/issues/526

It will cause a significant slowdown in a large batch, but it appears to be the only way to prevent an exception from bubbling up during a normal use case.
2014-03-31 15:16:32 -04:00
Joris van de Sande 588c4e2d02 Unset nested "fields" for deeper nested configs too 2014-03-31 12:52:29 +02:00
Joseph Bielawski f15ca02859 Fix documentation for client overwriting. 2014-03-31 11:59:37 +02:00
nurikabe 0de48d2190 Use identifiers for bulk delete rather than cloning objects 2014-03-29 18:36:06 -04:00
Tim Nagel 40e79b2abe Merge pull request #521 from morticue/master
Corrected bundle class name in install instructions
2014-03-28 08:52:27 +11:00
Joakim Friberg a81a630520 Corrected bundle class name in install instructions 2014-03-27 16:33:20 +01:00
Tim Nagel 8c146134da Merge pull request #519 from merk/logger-debug
Change logging to only be enabled in debug mode
2014-03-26 12:42:34 +11:00
Tim Nagel 38b4074745 Update logger documentation based on configuration change 2014-03-26 12:40:27 +11:00
Tim Nagel 9f27c1dade Update changelog 2014-03-26 12:38:20 +11:00
Tim Nagel e25a5420a5 Logger enabled with debugging 2014-03-26 12:38:20 +11:00
Tim Nagel 37db040096 Merge pull request #435 from merk/documentation
Refactored documentation
2014-03-26 10:09:17 +11:00
Tim Nagel eb7758605e Documentation updates 2014-03-26 10:07:50 +11:00
Tim Nagel 49f077cc30 Merge pull request #510 from peterjmit/master
Make fetchSlice compatible with custom repo method
2014-03-26 10:01:18 +11:00
Tim Nagel befb0907cc Merge branch 'master' into documentation
Conflicts:
	DependencyInjection/Configuration.php
	README.md
2014-03-25 16:04:32 +11:00
Tim Nagel e83ea0b6c8 Merge pull request #505 from piotrantosik/fix_docblocks
Fix some block comment
2014-03-25 15:48:18 +11:00
Tim Nagel 1e44051442 Merge pull request #514 from jvandesande/MultiField
Unset fields if no nested fields are defined
2014-03-25 15:47:06 +11:00
nurikabe 361d80a720 Prevent division by zero error 2014-03-24 11:18:55 -04:00
nurikabe 720917f609 Clone entities on delete to preserve ids 2014-03-24 11:16:31 -04:00
Joris van de Sande 455ff9e0f7 Unset fields if no nested fields are defined 2014-03-24 15:06:24 +01:00
Peter Mitchell 2009f88109 Make fetchSlice compatible with custom repo method
If a user configures query_builder_method for the doctrine provider, and
does not alias the root entity to equal Provider::ENTITY_ALIAS then a
DQL error will occur during index population.

> [Semantical Error] [...] near 'a.id ASC': Error: 'a' is not defined.

Provider::countObjects already handles this by dynamically fetching the
alias with QueryBuilder::getRootAliases, and Provider::fetchSlice should
be doing the same.

nb. "Provider" refers to FOS\ElasticaBundle\Doctrine\ORM\Provider
2014-03-19 08:40:58 -04:00
Tim Nagel 514e63f26f Merge pull request #496 from rayrigam/master
Add support for clients requiring basic HTTP authentication
2014-03-19 12:32:48 +11:00
Ray 4dc08cd006 Added notice about added support for HTTP headers. 2014-03-18 20:07:30 -04:30
Ray 41b347dfe4 Merge remote-tracking branch 'upstream/master'
Conflicts:
	Logger/ElasticaLogger.php
	README.md
2014-03-18 19:59:27 -04:30
rayrigam de70e78b53 Update Configuration.php
Updated to support any HTTP request header type in the "headers" section.
2014-03-18 18:14:03 -04:30
Tim Nagel babe20800e Updated changelog 2014-03-18 09:12:11 +11:00
Tim Nagel 7d13823488 Bump symfony requirement 2014-03-18 09:10:44 +11:00
Tim Nagel dc01b189ed Update CHANGELOG-3.0.md 2014-03-17 09:26:16 +11:00
Tim Nagel 5292a2adad Merge pull request #463 from FriendsOfSymfony/add-hotswapping-aliased-indexes-on-populate
Add support for using aliases to allow hot swapping of indexes.
2014-03-17 09:20:12 +11:00
Tim Nagel 49521e9fc4 Merge branch 'master' into add-hotswapping-aliased-indexes-on-populate
Conflicts:
	Resetter.php
2014-03-17 09:18:57 +11:00
Tim Nagel 857c1c8e48 Merge pull request #415 from nurikabe/master
Refactoring to update ElasticSearch index in postFush
2014-03-17 09:11:43 +11:00
Piotr Antosik 6a26c63b2c fix some block comment 2014-03-14 18:41:49 +01:00
Tim Nagel cdd6e3af45 Bump elastica version 2014-03-14 08:59:59 +11:00
Tim Nagel 6cd69ad131 Merge pull request #361 from xphere-forks/master
Add support for include_in_parent and include_in_root options
2014-03-14 08:53:42 +11:00
Tim Nagel 7cf8cd446f Merge pull request #501 from FriendsOfSymfony/reset-type-ignore-type-missing-exception
Ignore TypeMissingException when resetting a single type. This allows to...
2014-03-14 08:48:59 +11:00
Tim Nagel 6beafd23f1 Merge pull request #494 from bastnic/patch-1
[doc] inform about the override of Elastica logger
2014-03-14 08:44:06 +11:00
Tim Nagel da0abca38c Merge pull request #487 from XWB/logger-cleanup
ElasticaLogger cleanup
2014-03-14 08:43:42 +11:00
Tim Nagel 48edc195cd Merge pull request #484 from bits4breakfast/containeraware-callback
Container is now available in Callback
2014-03-14 08:41:15 +11:00
Lea Haensenberger 2f9896c893 Check for TypeMissingException anywhere in exception message 2014-03-13 10:45:57 +01:00
Lea Haensenberger 726892c586 Ignore TypeMissingException when resetting a single type. This allows to create new types without having to recreate the whole index. 2014-03-13 10:43:35 +01:00
Lukas Kahwe Smith 33f85bec22 Merge pull request #499 from mvrhov/patch-2
Fix wrong annotation
2014-03-11 15:18:36 +01:00
Miha Vrhovnik fe871c5ac4 Fix wrong annotation 2014-03-11 14:50:00 +01:00
Ray 0116a6ac4f Add support for clients requiring basic HTTP authentication 2014-03-06 12:38:23 -04:30
Berny Cantos 7f53badad5 Add support for include_in_{parent,root} for nested and objects 2014-03-06 12:31:50 +01:00
Bastien Jaillot a1f7449efc [doc] inform about the override of Elastica logger
Add a documentation related to this change: https://github.com/FriendsOfSymfony/FOSElasticaBundle/pull/395#issuecomment-27729759
2014-03-05 16:13:51 +01:00
Tim Nagel 245732781b Merge pull request #493 from tgallice/url_parameter_fix
Rework configuration validation to fix #461
2014-03-05 09:12:44 +11:00
tgallice 418b9d72ce Rework configuration validation to fix #461 2014-03-04 17:58:10 +01:00
Karel Souffriau 2b04f6cf34 ElasticaLogger cleanup 2014-02-25 11:05:26 +01:00
Matteo Galli eecdd3474a Fixes #459 2014-02-21 16:47:42 +01:00
Tim Nagel c8c72f5f0b Merge pull request #481 from XWB/abstract-provider-cleanup
AbstractProvider cleanup
2014-02-21 09:23:45 +11:00
Tim Nagel 6f8b3a5a0f Fix populate command option 2014-02-21 08:58:43 +11:00
Karel Souffriau 1c74f61b4e AbstractProvider cleanup 2014-02-20 12:57:10 +01:00
Tim Nagel b7d664c665 Merge pull request #480 from XWB/populate-command-hotfix
Fixed bug made in #479
2014-02-19 23:02:13 +11:00
Karel Souffriau 891dd51abe Fixed bug made in #479 2014-02-19 12:50:48 +01:00
Tim Nagel dd718c9a63 Merge pull request #479 from XWB/populate-command-fix
Tweak: use hasOption() in PopulateCommand instead of checking for booleans
2014-02-19 22:45:37 +11:00
Karel Souffriau 63cca11a0b Tweak: use hasOption() in PopulateCommand instead of checking for booleans 2014-02-19 12:43:44 +01:00
Tim Nagel 20b16d4f8c Merge pull request #478 from XWB/fix-tests
Fixes in "stop on error" feature
2014-02-19 22:40:47 +11:00
Karel Souffriau 44180793fc Add changelog file for 3.0 2014-02-19 12:36:39 +01:00
Karel Souffriau 41bf07ec59 Fixed issues in #426 2014-02-19 12:19:34 +01:00
Tim Nagel 5fff46cc60 Merge pull request #477 from XWB/cs-fix
CS fix in AbstractProvider
2014-02-19 20:13:26 +11:00
Karel Souffriau b8cc6d758c CS fix in AbstractProvider 2014-02-19 09:31:22 +01:00
Tim Nagel d03d494d30 Merge remote-tracking branch 'tgabi/stop-on-error' 2014-02-19 13:50:33 +11:00
Tim Nagel efc1794d3f Merge pull request #447 from astina-forks/populate_interactive_fix
Populate command: use isInteractive() instead of option
2014-02-19 13:46:09 +11:00
Tim Nagel e6f1d9ffe8 Merge pull request #454 from elfafa/master
Add RAM (current & peak) in populate evolution information
2014-02-19 13:45:24 +11:00
Tim Nagel 92b7fd981b Merge pull request #458 from digitalkaoz/patch-1
fixes ignore_missing option and unknown index
2014-02-19 13:44:42 +11:00
Tim Nagel cb2eeba318 Merge pull request #461 from tgallice/url_parameter
Force slash at the end of the url parameter
2014-02-19 13:40:18 +11:00
Tim Nagel 13e81fd7bb Merge pull request #462 from oohnoitz/master
added support for setting search options
2014-02-19 13:40:03 +11:00
Tim Nagel aa9d74a8a0 Merge remote-tracking branch 'PeerJ/AddServerTimeout'
Conflicts:
	DependencyInjection/Configuration.php
2014-02-19 13:39:03 +11:00
Tim Nagel 5a36d8f769 Merge pull request #468 from FriendsOfSymfony/fix-reset-index-type
Keep all special mapping fields for types when resetting a single type. ...
2014-02-19 13:31:29 +11:00
Hung Tran 7d2776e27a fixed phpdoc 2014-02-18 10:12:31 -06:00
Patrick McAndrew 3065c96a2c Add ability to specify server timeout 2014-02-14 17:32:22 +00:00
Lea Haensenberger 765e400278 handle _parent separately 2014-02-13 10:06:10 +01:00
Lea Haensenberger ead3c95c88 Keep dynamic templates 2014-02-13 09:45:27 +01:00
Lea Haensenberger 1402bdc9e6 Keep all special mapping fields for types when resetting a single type. Field list is according to the current documentation of elasticsearch 0.90 2014-02-13 09:13:36 +01:00
Richard Miller 03bf793b65 Make real index name use uniqid so can be reset within a second 2014-02-10 10:55:13 +00:00
Richard Miller 3b1a756e6f Add support for using aliases to allow hot swapping of indexes when populating 2014-02-06 21:11:12 +00:00
Hung Tran 5be3a8c22e added query string to collector and updated tests 2014-02-05 11:58:13 -06:00
Hung Tran d04a403f71 added support for setting search options 2014-02-04 19:41:38 -06:00
tgallice 04390b37d1 Force slash at the end of the url parameter 2014-02-04 10:07:58 +01:00
Fabien Somnier 48b785e876 New method getMemoryUsage to get RAM information message 2014-02-03 18:12:20 +01:00
Fabien Somnier 0ae84d56c7 Merge remote-tracking branch 'upstream/master' 2014-02-03 17:51:23 +01:00
Fabien Somnier a121a77774 Avoid index reset error in case of unexistant index 2014-02-03 17:50:35 +01:00
Robert Schönthal f348ebd026 fixes ignore_missing option and unknown index 2014-02-03 09:47:16 +01:00
nurikabe 559d7c13f2 Update tests 2014-02-01 03:37:47 +00:00
nurikabe 93c6085fa5 Merge remote-tracking branch 'upstream/master' 2014-02-01 02:15:52 +00:00
nurikabe 1dcaadbe6f Bulk delete 2014-02-01 02:14:21 +00:00
Tim Nagel 82e9809ceb Merge pull request #453 from pentarim/master
adding ttl for documents
2014-01-30 13:42:09 -08:00
Fabien Somnier 5e54dcd955 Add RAM (current & peak) in populate evolution information 2014-01-30 13:01:13 +01:00
Laszlo Horvath 5480e037e3 adding ttl for documents 2014-01-30 12:39:48 +01:00
Tim Nagel d0457549c6 Merge pull request #452 from piotrantosik/master
Services classes as parameters
2014-01-29 13:57:48 -08:00
Piotr Antosik 076d51ac40 Services classes as parameters 2014-01-29 17:50:42 +01:00
Tim Nagel 1f9c111193 Merge pull request #450 from antistatique/master
[doc] repository methode has to return the query
2014-01-28 02:54:11 -08:00
Simon Perdrisat 1c7da33526 [doc] repository methode has to return the query 2014-01-28 11:34:54 +01:00
pkraeutli 62dc3aaca0 Populate command: use isInteractive() instead of option 2014-01-24 14:20:10 +01:00
nurikabe b6d010a9d7 Bulk update. Still working on bulk delete for indexes and types in Elastica. 2014-01-23 16:20:11 +00:00
Tóth Gábor bbacad4bab no-stop-on-error option added to populate command 2014-01-21 18:38:51 +01:00
Tim Nagel ef872dd22d Merge pull request #439 from FriendsOfSymfony/type-timestamp
Adding support for enabling timestamps
2014-01-20 12:57:02 -08:00
Tim Nagel f0f6fc8129 Merge pull request #441 from klmatom/master
Add similarity as a valid field mapping.  http://www.elasticsearch.org/g...
2014-01-20 12:56:47 -08:00
Tim Nagel b6d0627c2a Merge pull request #440 from vermi0ffh/master
A simple change in conf def for easier allowing new type through prependExtensionConfig()
2014-01-20 12:52:56 -08:00
Tom A 4287a91d50 Add similarity as a valid field mapping. http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-core-types.html#similarity 2014-01-16 23:05:44 -05:00
nurikabe 4a4716a55b Merge remote-tracking branch 'upstream/master' 2014-01-11 16:38:29 +00:00
nurikabe 7481376835 Use bulk insert. Still working on bulk update/delete which is not yet suppored in Elastica. 2014-01-11 16:28:15 +00:00
Vermi 73ee750515 Removing "->performNoDeepMerging()"in indexes configuration, allowing custom Bundle defining new types in an existing index.
Configuration is injected in the new Bundle using prependExtensionConfig()
2014-01-10 16:25:45 +01:00
Lea Haensenberger 274fc00991 Add store and index options to timestamp 2014-01-06 10:19:11 +01:00
Lea Haensenberger 4ee81dc010 Adding support for enabling timestamps 2014-01-06 09:20:44 +01:00
Tim Nagel d33c5d0ece Fix tests 2013-12-17 21:38:08 +11:00
Tim Nagel 21f688e014 Merge pull request #433 from FriendsOfSymfony/fix-reset-command
Fix reset command
2013-12-17 02:34:00 -08:00
Tim Nagel a2d4070fdc Merge pull request #429 from craigmarvelley/improve-code-coverage
Improve test coverage
2013-12-17 02:33:29 -08:00
Tim Nagel 5f8b8003d1 Refactor documentation 2013-12-17 21:17:28 +11:00
Lea Haensenberger e906d780ad Adding tests 2013-12-16 12:06:53 +01:00
Lea Haensenberger 05ee300ddb Fixing reset command to allow resetting of just one type, starting to write test 2013-12-16 11:58:58 +01:00
Tim Nagel 90022b0d0a Move type configuration into independent method 2013-12-15 18:33:45 +11:00
Tim Nagel e2e21b1e0c Allow bundle to be used without clients or indexes defined. 2013-12-15 18:33:31 +11:00
nurikabe 3bd9155f46 Use constants of corresponding events classes rather than making assumption about string values. 2013-12-13 17:51:15 +00:00
Tim Nagel f565ec5638 Merge pull request #432 from hardchor/duplicate-code-removal
Removed duplicate methods
2013-12-12 15:07:28 -08:00
Craig Marvelley 292af9f039 Better variable names 2013-12-12 22:57:02 +00:00
Burkhard Reffeling e0ef8dff23 removed duplicate methods 2013-12-12 22:41:20 +00:00
Craig Marvelley 5d16ffb1bf Remove unused method 2013-12-12 20:50:15 +00:00
Craig Marvelley 11a87c5ce3 Add test for ElasticaToModelTransformerCollection::hybridTransform 2013-12-12 20:48:15 +00:00
Craig Marvelley 090f02b05d Improve test coverage 2013-12-12 11:11:34 +00:00
Tim Nagel 6c4740b292 Merge pull request #423 from FriendsOfSymfony/dynamic-templates
Apply same mapping logic to dynamic templates...
2013-12-11 02:17:52 -08:00
Lea Haensenberger a59385af7b Do not set default value to *, might conflict when using e.g. path_match and match_pattern regex together 2013-12-10 15:37:41 +01:00
Lea Haensenberger 0aa98d2295 Fixing copy&paste error 2013-12-10 15:21:46 +01:00
Lea Haensenberger e55c7e8632 Set match to '*' by default 2013-12-10 15:21:46 +01:00
Lea Haensenberger 09031457cd Allow more matching methods 2013-12-10 15:21:46 +01:00
Lea Haensenberger bad1769c84 Fixing tests 2013-12-10 15:21:46 +01:00
Lea Haensenberger eaf52fa907 Allow a more dynamic mapping for dynamic templates 2013-12-10 15:21:46 +01:00
leahaense ab792952c1 Merge pull request #425 from cs-daniel/patch-1
Fix client configuration missing logger
2013-12-10 05:12:18 -08:00
Császár Dániel e16ece821d Fix client configuration missing logger 2013-12-10 13:54:50 +01:00
Tim Nagel 70e6cb2e7e Fix issue with logger not having logQuery method 2013-12-10 21:25:38 +11:00
Tim Nagel da44ee65f6 Merge pull request #395 from pierredup/logger
Add option to enable logger and define logger class
2013-12-10 02:17:10 -08:00
Tim Nagel e1ed7bdc67 Merge pull request #420 from FriendsOfSymfony/all-field-config
Add support to disable the _all field for a type
2013-12-10 02:16:23 -08:00
Tim Nagel 8668537740 Merge pull request #400 from cordoval/cleanups
clean ups
2013-12-10 02:15:53 -08:00
Jeremy Mikola 21474b844b Merge pull request #403 from cordoval/patch-1
typo
2013-12-10 00:25:55 -08:00
Tim Nagel fc64078575 Merge pull request #413 from damienalexandre/serializer-integration
Serializer support in providers and mapping configuration fixes
2013-12-09 12:35:26 -08:00
Tim Nagel 4addb0d66f Merge pull request #421 from damienalexandre/profiler-curl
[Feature] Allow to display a run-able cURL query in the web profiler
2013-12-09 12:33:02 -08:00
Damien Alexandre 1ddd7c0e0c Store connection infos for all transports, display infos in debug 2013-12-09 14:46:06 +01:00
Damien Alexandre ca507a5e34 Add full_host in logger for HTTP(s) queries 2013-12-09 10:40:47 +01:00
Damien Alexandre c97a4abceb Add display cURL on the web debug queries shower 2013-12-09 10:09:36 +01:00
Lea Haensenberger 8f1b52328f Merge remote-tracking branch 'origin/master' into all-field-config
Conflicts:
	DependencyInjection/FOSElasticaExtension.php
2013-12-06 08:25:02 +01:00
Lea Haensenberger 08193ec2fd Add support to disable the _all field for a type 2013-12-05 16:35:30 +01:00
Pierre du Plessis 759950aff6 Add option to disable logger or specify different logger service 2013-12-04 22:09:27 +02:00
nurikabe 3a279f8edb Remove debug. 2013-12-04 10:46:05 +00:00
Richard Miller a227a3f076 Merge branch '2.1.x' 2013-12-04 08:03:04 +00:00
nurikabe ed21e60869 Remove upgrade doc. Merk will add manually. 2013-12-03 23:40:00 +00:00
nurikabe 22a5d67d05 pre/postFlush configuration. Update documentation. 2013-12-03 20:41:26 +00:00
nurikabe af2827df01 Re-add renamed Listener. 2013-12-03 14:01:21 +00:00
Damien Alexandre 8a4848a16d Add UPGRADE-3.0.md file with descriptions of new features / changes 2013-12-02 22:35:50 +01:00
Damien Alexandre 25e59a311b Fix Persister constructor and undefined property 2013-12-02 22:26:30 +01:00
Damien Alexandre cf3e35e892 Remove extra line-break 2013-12-02 22:15:11 +01:00
nurikabe 1d700261ab Refactor to a single Listener class. Update tests. 2013-12-02 12:42:04 +00:00
nurikabe 5ec652063d Don't need postRemove. 2013-12-01 21:48:41 +00:00
nurikabe f258c9ddc0 Exploratory development for https://github.com/FriendsOfSymfony/FOSElasticaBundle/issues/410 2013-12-01 21:17:55 +00:00
Damien Alexandre 3027c687e2 Ref #341, fix the insertMany method to use Bulk indexing
To make it works, I inject the serializer defined for the Type
into the fos_elastica.object_serializer_persister service.

This is the SAME service injected in the setSerializer of Type.

We deport the handling of serialization outside Elastica,
this is not so good but we need to build our own Documents to
get the ID's correctly.
2013-11-29 15:35:59 +01:00
Damien Alexandre 2862259d8e Allow empty or null or no mappings: key under type configuration
refs #300. This commit allow to define types without having to
set any mapping as Elasticsearch build his own.

The minimal config become:

    indexes:
        toto:
            client: default
            types:
                Article:
                    mappings: ~
                    ...
2013-11-29 14:59:56 +01:00
Damien Alexandre b149ac235b Replace deprecated calls Document::add to Document::set 2013-11-29 13:53:25 +01:00
Damien Alexandre e180f1fd61 Merge remote-tracking branch 'peerj/ObjectWithoutProperties' into serializer-integration
Conflicts:
	Transformer/ModelToElasticaAutoTransformer.php
2013-11-29 13:51:58 +01:00
Damien Alexandre d546b4d3f3 Merge remote-tracking branch 'origin/serializer-transformer' into serializer-integration
Conflicts:
	DependencyInjection/FOSElasticaExtension.php
	Resources/config/config.xml
2013-11-29 10:07:13 +01:00
Luis Cordova cacb40286c clean ups 2013-11-20 08:11:15 -05:00
Luis Cordova c82006e8ac fix typos 2013-11-20 07:48:00 -05:00
Tim Nagel 40d3df9f92 Merge branch '2.1.x'
Conflicts:
	Finder/TransformedFinder.php
2013-11-19 08:29:02 +11:00
Tim Nagel 3c26f157aa Merge branch '2.1.x'
Conflicts:
	Tests/ResetterTest.php
2013-11-11 13:30:29 +11:00
Tim Nagel 08697a4d84 Merge pull request #397 from igorRovenki/master
Update README.md
2013-11-06 13:03:10 -08:00
igorRovenki be4c99a0d3 Update README.md
Should be "listener: ~" on the line 595, otherwise listener does not working
2013-11-06 11:01:53 +02:00
Tim Nagel 8a14fbd73b Merge pull request #394 from pierredup/master
Add Symfony ExpressionLanguage support for indexable callback
2013-11-04 01:59:40 -08:00
Pierre du Plessis f972b4af59 Fixed CS 2013-11-04 08:50:41 +02:00
Pierre du Plessis 97c98a0243 Add Symfony ExpressionLanguage support for indexable callback 2013-11-04 00:09:21 +02:00
Tim Nagel 28641427d5 Merge tag 'v2.1.3-PL1' 2013-11-01 09:17:17 +11:00
Jeremy Mikola 467ccbf753 Tag client services in DI extension class
These tags were originally introduced in 16ecd7cca3. #339 removed the fos_elastica.client definition from config.xml, so this tag needs to be added via the DI extension class now.
2013-10-30 17:15:09 -04:00
Jeremy Mikola 68092eb4db Merge branch '2.1.x'
Conflicts:
	Resources/config/config.xml
2013-10-30 17:14:20 -04:00
Tim Nagel ca57b0d72e Merge pull request #379 from smatyas/populate-command-provider-options
New options to fos:elastica:populate command: offset, sleep, batch-size
2013-10-29 17:10:46 -07:00
Matyas Somfai 43e026500c added new options to fos:elastica:populate command and options array parameter to ProviderInterface
removed assume-yes option as the no-interaction option has the same purpose
2013-10-30 00:02:34 +01:00
Tim Nagel 85c4dc92f9 Merge tag 'v2.1.3'
Conflicts:
	DependencyInjection/Configuration.php
	README.md
2013-10-30 09:06:48 +11:00
Tim Nagel c6b01a2c50 Merge pull request #382 from thierrymarianne/master
Fix type hinting in "Manual provider" example
2013-10-17 15:05:42 -07:00
Thierry Marianne 85a10613ba Fix type hinting in "Manual provider" example 2013-10-17 22:40:28 +02:00
Lukas Kahwe Smith 2cbf5f797b Merge pull request #367 from peterkokot/patch-2
LICENSE file removed in favor of Resources/meta/LICENSE file
2013-10-13 06:15:13 -07:00
Tim Nagel 7b1fe89627 Merge pull request #378 from dbu/patch-1
add a note how to use the parent mapping
2013-10-08 16:09:20 -07:00
David Buchmann ac56775791 add a note how to use the parent mapping 2013-09-26 10:02:48 +02:00
Peter Kokot 2b942a6edf LICENSE file removed in favor of Resources/meta/LICENSE file 2013-09-16 19:47:05 +02:00
Lukas Kahwe Smith 9fca3516b2 Merge pull request #365 from peterkokot/patch-1
Update LICENSE
2013-09-15 02:24:59 -07:00
Peter Kokot 6551028753 Update LICENSE
Updated license year range
2013-09-15 09:17:13 +02:00
leahaense 23382c895e Merge pull request #322 from ajgarlag/parentmapping
Fix _parent mapping.
2013-09-10 23:53:15 -07:00
Jeremy Mikola 2bfb637a00 Merge pull request #358 2013-08-19 12:02:28 -04:00
Robert Plant 41e132140e Added ability to access facets with paginated results 2013-08-19 12:01:32 -04:00
Jeremy Mikola b40149cbdc Code and documentation formatting in Propel transformer class 2013-08-19 12:00:56 -04:00
cedric lombardot 3c01ebb775 Fix ElasticaToModelTransformer the transform closure use Result instead of Document 2013-08-19 12:00:56 -04:00
Richard Fullmer e97b60fc7e Ignore failed deletions in ObjectPersister
This probably isn't the best way to solve my problem,
but the issue is this.

Step 1:  Create a new doctrine entity for which it's `is_indexable_callback`
returns false.  When doctrine flushes this entity to the database,
elastia will not index it with elastic search.  (Correct)

Step 2:  Update your doctrine entity and change some fields so
that `is_indexable_callback` _still_ returns false.  Persist and flush
to the database.

At this point, the postUpdate listener on ElastiaBundle is called
and since the `is_indexable_callback` returns false, it believes
it needs to remove it from the elastic search index and queues it
for deletion.  The deletion of course fails because it was never there
in the first place.

This solution simply ignores failures from deletions in the index.

Perhaps a better solution would be to have a smarter listener
that could determine if the entity was previously present in the
elastic search index or not, but that would require significant
refactoring.

Addresses issues discuseed in #284

Credit to @bbeaulant for simple solution.  Opening a PR to
discuss more generally.
2013-08-19 12:00:56 -04:00
Jeremy Mikola 0b3c1ebbc2 Merge pull request #350 2013-08-09 13:57:16 -04:00
Jeremy Mikola c15caf3096 Code and documentation formatting in Propel transformer class 2013-08-09 13:56:51 -04:00
cedric lombardot e2b6177a33 Fix ElasticaToModelTransformer the transform closure use Result instead of Document 2013-08-09 13:42:12 -04:00
Jeremy Mikola b922315172 Merge pull request #312 from opensoft/issue-284
Ignore failed deletions in ObjectPersister
2013-08-07 13:57:23 -07:00
NicolasBadey 617b9d3f4b Recalculate pagination Query size if a limit is already set 2013-07-18 14:06:32 -04:00
Lea Haensenberger 37cfdb0df7 refactoring some code 2013-07-18 10:56:25 +02:00
Lea Haensenberger 77156b35aa some formatting and cleaning up 2013-07-18 10:54:11 +02:00
Lea Haensenberger 300d189a9d renaming services to avoid potential conflicts 2013-07-18 10:51:51 +02:00
Jeremy Mikola dcd3cbb944 Merge branch '2.1.x'
Conflicts:
	Tests/Transformer/ModelToElasticaAutoTransformerTest.php
2013-07-17 15:57:24 -04:00
Lea Haensenberger d10e8f56c8 Tests for new provider and new transformer 2013-07-17 10:10:07 +02:00
Lea Haensenberger 5b6a1f7bd6 Adding new persister and new transformer to make use of the serializer support of elastica when persisting doctrine objects 2013-07-17 08:58:15 +02:00
Jeremy Mikola 00f37835fc Rewrite filtered query example in README
Cleaning up the description paragraph and removing extra fields and pagination from the example, since we already have pagination examples elsewhere in the README.
2013-07-16 13:35:27 -04:00
RobertPlant cce1ffb06c Add filtered query example to README.md 2013-07-16 13:35:27 -04:00
Jeremy Mikola 1af85115df Merge pull request #323 from ajgarlag/orm-provider-order-by
An orderBy DQL  part is required to avoid feching the same row twice
2013-07-08 09:08:11 -07:00
Jeremy Mikola deb29233ba Merge pull request #330 from tgabi333/patch-1
Remove unused parameter from ModelToElasticaAutoTransformer.php
2013-07-04 13:11:03 -07:00
Gábor Tóth 9beb277745 Remove unused parameter from ModelToElasticaAutoTransformer.php 2013-07-04 18:47:58 +02:00
Antonio J. García Lagar 170864a30d Removes sort call 2013-07-04 08:07:00 +02:00
Jeremy Mikola 5bee33eec9 Merge pull request #321 from ajgarlag/fix-output-color
Fix output colors for single type commands
2013-07-02 13:48:40 -07:00
Lukas Kahwe Smith 8912e52980 Merge pull request #326 from Tobion/patch-1
Allow elastica 0.90. Elasticsearch made the jump from 0.20 to 0.90
2013-06-24 08:52:03 -07:00
Tobias Schultze 8b785c57c3 Allow elastica 0.90. Elasticsearch made the jump from 0.20 to 0.90 2013-06-24 18:44:46 +03:00
Antonio J. García Lagar 7f3cfa49fb Make the property param optional 2013-06-19 13:57:15 +02:00
Antonio J. García Lagar b11b4299ef An orderBy DQL part is required to avoid feching the same row twice 2013-06-19 12:36:59 +02:00
Antonio J. García Lagar 0bf6e0b09a Fix output colors 2013-06-18 09:52:44 +02:00
Antonio J. García Lagar a386ffefe3 Fix parent mapping 2013-06-17 21:19:11 +02:00
Lukas Kahwe Smith 9d5a0c2ed9 Merge pull request #318 from RobertPlant/patch-1
Update README.md
2013-06-13 03:15:46 -07:00
RobertPlant fd5ef8005e Update README.md 2013-06-13 11:28:49 +02:00
Jeremy Mikola 7824678e70 Merge pull request #316 2013-06-12 10:48:29 -04:00
RobertPlant c4cc199fbe Document object mapping implications for Doctrine ORM 2013-06-12 10:47:59 -04:00
Jeremy Mikola a9c44b491f Merge pull request #292 2013-06-10 10:37:11 -04:00
Jeremy Mikola b9da709f22 Add documentation for client tagging 2013-06-10 10:37:02 -04:00
Patrick McAndrew 16ecd7cca3 Tag client with fos_elastica.client 2013-06-10 10:30:31 -04:00
Jeremy Mikola 5a64420779 Merge branch '2.1.x'
Conflicts:
	composer.json
2013-06-06 11:21:15 -04:00
Richard Fullmer 1fc94b2213 Ignore failed deletions in ObjectPersister
This probably isn't the best way to solve my problem,
but the issue is this.

Step 1:  Create a new doctrine entity for which it's `is_indexable_callback`
returns false.  When doctrine flushes this entity to the database,
elastia will not index it with elastic search.  (Correct)

Step 2:  Update your doctrine entity and change some fields so
that `is_indexable_callback` _still_ returns false.  Persist and flush
to the database.

At this point, the postUpdate listener on ElastiaBundle is called
and since the `is_indexable_callback` returns false, it believes
it needs to remove it from the elastic search index and queues it
for deletion.  The deletion of course fails because it was never there
in the first place.

This solution simply ignores failures from deletions in the index.

Perhaps a better solution would be to have a smarter listener
that could determine if the entity was previously present in the
elastic search index or not, but that would require significant
refactoring.

Addresses issues discuseed in #284

Credit to @bbeaulant for simple solution.  Opening a PR to
discuss more generally.
2013-06-04 11:45:45 -07:00
Jeremy Mikola 94ab3b1d3c Add PSR Log dependency (see: #304) 2013-05-31 13:16:49 -04:00
Jeremy Mikola 00fde6847a Merge pull request #304 from ceednee/master
Fixes Undefined property: FOS\ElasticaBundle\Client::$logger
2013-05-30 13:18:02 -07:00
ceednee 929c1bfc0d Fixes Undefined property: FOS\ElasticaBundle\Client:: 2013-05-30 21:27:31 +02:00
Lukas Kahwe Smith 860a1f8108 Merge pull request #303 from instaclick/logger-patch
update to PSR3 LoggerInterface per change in ruflin/elastica
2013-05-30 09:54:10 -07:00
Anthon Pang 55edceadcb remove redundant property/method 2013-05-30 16:43:10 +00:00
Anthon Pang f374dbbaa2 update to PSR3 LoggerInterface per change in ruflin/elastica 2013-05-30 16:26:16 +00:00
Patrick McAndrew fab42fa0ce Properities should not be required for object as elastica can automap 2013-05-24 12:27:37 +01:00
Lukas Kahwe Smith 43d1531cd4 cs fix 2013-05-23 00:25:38 +03:00
Lukas Kahwe Smith 16edbfa03b Merge pull request #285 from Appstrakt/master
Updated documentation
2013-05-22 12:56:17 -07:00
Lukas Kahwe Smith b7eff37f63 Merge pull request #290 from hacfi/readme_fix
Correct serializer configuration in README
2013-05-22 12:54:32 -07:00
Lukas Kahwe Smith be065735c1 master is 3.0.x 2013-05-22 21:52:48 +02:00
Lukas Kahwe Smith 79327d281c Merge pull request #293 from FriendsOfSymfony/add-path-support
added path support
2013-05-17 00:09:06 -07:00
Lukas Kahwe Smith 50730cca3d added path support, see http://www.elasticsearch.org/guide/reference/mapping/id-field/ 2013-05-17 00:22:46 +02:00
Jeremy Mikola 814460dbf2 Merge branch '2.1.x' 2013-05-15 10:58:47 -05:00
Philipp Wahala d6e8134189 Correct serializer configuration in README 2013-05-15 15:56:21 +02:00
Arne Stockmans 23dfd3f595 Renamed is_indexable_callback_class back to is_indexable_callback 2013-05-08 14:29:22 +02:00
Lukas Kahwe Smith b8a4a47b38 renamed PurgeCommand to ResetCommand 2013-05-08 09:10:40 +02:00
Lukas Kahwe Smith b5392df9fd Merge pull request #280 from FriendsOfSymfony/purge_command
added purge command
2013-05-07 04:54:08 -07:00
Lukas Kahwe Smith f48ae85cc8 added missing use statement 2013-05-06 10:32:50 +02:00
Tim Nagel 4ffc499c66 Saner version constraints 2013-05-03 11:20:44 +10:00
Jeremy Mikola ee6f3cb04d Expect a more specific PropertyAccess exception
RuntimeException is the nearest common ancestor of the PropertyAccessDeniedException (removed in 2.3) and NoSuchPropertyException.

See: symfony/symfony@2a666cb7c3
2013-05-02 11:24:27 -04:00
Jeremy Mikola 64d27c37fa Merge pull request #282 from tommygnr/symfony2.3
Allow symfony >= 2.3
2013-05-02 11:17:50 -04:00
Tom Corrigan b204f5bf5f Allow symfony >= 2.3
This required a small tweak to the tests as
Symfony\Component\PropertyAccess\Exception\PropertyAccessDeniedException
 has been removed in 2.3
2013-05-03 01:01:29 +10:00
Jeremy Mikola be3c2779ef Merge pull request #277 from FriendsOfSymfony/namespace_compat
Compatibility with Elastica 0.20.x (incl. Serializer support)
2013-04-26 16:12:30 -07:00
Lukas Kahwe Smith 813f57e3d0 added purge command 2013-04-26 19:46:15 +02:00
Lukas Kahwe Smith e898deb6df cleanups 2013-04-26 14:12:28 +02:00
Lukas Kahwe Smith c35cb1b25f fixed Elastica dependency 2013-04-26 14:10:06 +02:00
Lukas Kahwe Smith 0af8f6ce19 updated for Elastica master changes 2013-04-26 14:10:06 +02:00
Lukas Kahwe Smith 800e38f8aa tweaked the callback handling 2013-04-26 14:10:06 +02:00
Lukas Kahwe Smith 8a9a9686ba various tweaks 2013-04-26 14:10:05 +02:00
Lukas Kahwe Smith 965ee39c5a typo fix 2013-04-26 14:10:05 +02:00
Lukas Kahwe Smith aafb6e53fb made the bundle compatible with Elastica 0.20.x 2013-04-26 14:10:05 +02:00
Lukas Kahwe Smith d8f836d179 CS fixes and minor tweaks in the Callback class 2013-04-26 14:10:05 +02:00
Lea Haensenberger 1abe1f48dd Having a parameter name defining a class for the serializer callback, because directly putting service id or parameter doesn't work because these values are not available at bundle configuration time 2013-04-26 14:10:05 +02:00
Lea Haensenberger bcf564d09c Adapting readme 2013-04-26 14:09:42 +02:00
Lea Haensenberger a139d18b22 Adding default callback for serialization if serialization is turned on 2013-04-26 14:09:42 +02:00
Lea Haensenberger c99eee9c4b Adding some documentation 2013-04-26 14:06:50 +02:00
Lea Haensenberger 73fd4fe6b0 Creating a serializer callable for every type in an index and passing it to elastica 2013-04-26 14:06:49 +02:00
Lea Haensenberger c5ee26099b Adding support to pass a serializer to elastica 2013-04-26 14:06:48 +02:00
168 changed files with 9394 additions and 2825 deletions

5
.scrutinizer.yml Normal file
View file

@ -0,0 +1,5 @@
imports:
- php
tools:
external_code_coverage: true

View file

@ -1,9 +1,36 @@
language: php
cache:
directories:
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
matrix:
include:
- php: 5.5
env: SYMFONY_VERSION='2.3.*'
- php: 5.5
env: SYMFONY_VERSION='2.5.*'
before_script:
- echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- composer install --dev
- /usr/share/elasticsearch/bin/elasticsearch -v
- sudo /usr/share/elasticsearch/bin/plugin -install elasticsearch/elasticsearch-mapper-attachments/2.0.0
- sudo service elasticsearch restart
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
- composer install --dev --prefer-source
script:
- vendor/bin/phpunit --coverage-clover=coverage.clover
services:
- elasticsearch
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover

16
Annotation/Search.php Normal file
View file

@ -0,0 +1,16 @@
<?php
namespace FOS\ElasticaBundle\Annotation;
/**
* Annotation class for setting search repository.
*
* @author Richard Miller <info@limethinking.co.uk>
* @Annotation
* @Target("CLASS")
*/
class Search
{
/** @var string */
public $repositoryClass;
}

76
CHANGELOG-3.0.md Normal file
View file

@ -0,0 +1,76 @@
CHANGELOG for 3.0.x
===================
This changelog references the relevant changes (bug and security fixes) done
in 3.0 minor versions.
To get the diff for a specific change, go to
https://github.com/FriendsOfSymfony/FOSElasticaBundle/commit/XXX where XXX is
the commit hash. To get the diff between two versions, go to
https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.0...v3.0.1
To generate a changelog summary since the last version, run
`git log --no-merges --oneline v3.0.0...3.0.x`
* 3.0.9 (2015-03-12)
* Fix a bug in the BC layer of the type configuration for empty configs
* Fix the service definition for the Doctrine listener when the logger is not enabled
* 3.0.8 (2014-01-31)
* Fixed handling of empty indexes #760
* Added support for `connectionStrategy` Elastica configuration #732
* Allow Elastica 1.4
* 3.0.7 (2015-01-21)
* Fixed the indexing of parent/child relations, broken since 3.0 #774
* Fixed multi_field properties not being normalised #769
* 3.0.6 (2015-01-04)
* Removed unused public image asset for the web development toolbar #742
* Fixed is_indexable_callback BC code to support array notation #761
* Fixed debug_logger for type providers #724
* Clean the OM if we filter away the entire batch #737
* 3.0.0-ALPHA6
* Moved `is_indexable_callback` from the listener properties to a type property called
`indexable_callback` which is run when both populating and listening for object
changes.
* AbstractProvider constructor change: Second argument is now an `IndexableInterface`
instance.
* Annotation @Search moved to FOS\ElasticaBundle\Annotation\Search with FOS\ElasticaBundle\Configuration\Search deprecated
* Deprecated FOS\ElasticaBundle\Client in favour of FOS\ElasticaBundle\Elastica\Client
* Deprecated FOS\ElasticaBundle\DynamicIndex in favour of FOS\ElasticaBundle\Elastica\Index
* Deprecated FOS\ElasticaBundle\IndexManager in favour of FOS\ElasticaBundle\Index\IndexManager
* Deprecated FOS\ElasticaBundle\Resetter in favour of FOS\ElasticaBundle\Index\Resetter
* 3.0.0-ALPHA5 (2014-05-23)
* Doctrine Provider speed up by disabling persistence logging while populating documents
* 3.0.0-ALPHA4 (2014-04-10)
* Indexes are now capable of logging errors with Elastica
* Fixed deferred indexing of deleted documents
* Resetting an index will now create it even if it doesn't exist
* Bulk upserting of documents is now supported when populating
* 3.0.0-ALPHA3 (2014-04-01)
* a9c4c93: Logger is now only enabled in debug mode by default
* #463: allowing hot swappable reindexing
* #415: BC BREAK: document indexing occurs in postFlush rather than the pre* events previously.
* 7d13823: Dropped (broken) support for Symfony <2.3
* #496: Added support for HTTP headers
* #528: FOSElasticaBundle will disable Doctrine logging when populating for a large increase in speed
* 3.0.0-ALPHA2 (2014-03-17)
* 41bf07e: Renamed the `no-stop-on-error` option in PopulateCommand to `ignore-errors`
* 418b9d7: Fixed validation of url configuration
* 726892c: Ignore TypeMissingException when resetting a single type. This allows to create new types without having to recreate the whole index.
* 7f53bad Add support for include_in_{parent,root} for nested and objects

61
CHANGELOG-3.1.md Normal file
View file

@ -0,0 +1,61 @@
CHANGELOG for 3.1.x
===================
This changelog references the relevant changes (bug and security fixes) done
in 3.1 versions.
To get the diff for a specific change, go to
https://github.com/FriendsOfSymfony/FOSElasticaBundle/commit/XXX where XXX is
the commit hash. To get the diff between two versions, go to
https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.4...v3.1.0
* 3.1.3 (2015-04-02)
* Fix Symfony 2.3 compatibility
* 3.1.2 (2015-03-27)
* Fix the previous release
* 3.1.1 (2015-03-27)
* Fix PopulateCommand trying to set formats for ProgressBar in Symfony < 2.5
* Fix Provider implementations that depend on a batch size from going into
infinite loops
* 3.1.0 (2015-03-18)
* BC BREAK: `Doctrine\Listener#scheduleForDeletion` access changed to private.
* BC BREAK: `ObjectPersisterInterface` gains the method `handlesObject` that
returns a boolean value if it will handle a given object or not.
* BC BREAK: Removed `Doctrine\Listener#getSubscribedEvents`. The container
configuration now configures tags with the methods to call to avoid loading
this class on every request where doctrine is active. #729
* BC BREAK: Added methods for retrieving aggregations when paginating results.
The `PaginationAdapterInterface` has a new method, `getAggregations`. #726
* Added ability to configure `date_detection`, `numeric_detection` and
`dynamic_date_formats` for types. #753
* New event `POST_TRANSFORM` which allows developers to add custom properties to
Elastica Documents for indexing.
* When available, the `fos:elastica:populate` command will now use the
ProgressBar helper instead of outputting strings. You can use verbosity
controls on the command to output additional information like memory
usage, runtime and estimated time.
* Added new option `property_path` to a type property definition to allow
customisation of the property path used to retrieve data from objects.
Setting `property_path` to `false` will configure the Transformer to ignore
that property while transforming. Combined with the above POST_TRANSFORM event
developers can now create calculated dynamic properties on Elastica documents
for indexing. #794
* Fixed a case where ProgressCommand would always ignore errors regardless of
--ignore-errors being passed or not.
* Added a `SliceFetcher` abstraction for Doctrine providers that get more
information about the previous slice allowing for optimising queries during
population. #725
* New events `PRE_INDEX_POPULATE`, `POST_INDEX_POPULATE`, `PRE_TYPE_POPULATE` and
`POST_TYPE_POPULATE` allow for monitoring when an index is about to be or has
just been populated. #744
* New events `PRE_INDEX_RESET`, `POST_INDEX_RESET`, `PRE_TYPE_RESET` and
`POST_TYPE_RESET` are run before and after operations that will reset an
index. #744
* Added indexable callback support for the __invoke method of a service. #823

View file

@ -2,34 +2,11 @@
namespace FOS\ElasticaBundle;
use Elastica_Client;
use FOS\ElasticaBundle\Logger\ElasticaLogger;
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
/**
* @author Gordon Franke <info@nevalon.de>
* @deprecated Use \FOS\ElasticaBundle\Elastica\LoggingClient
*/
class Client extends Elastica_Client
class Client extends BaseClient
{
/**
* @var ElasticaLogger
*/
protected $logger;
public function setLogger(ElasticaLogger $logger)
{
$this->logger = $logger;
}
public function request($path, $method, $data = array(), array $query = array())
{
$start = microtime(true);
$response = parent::request($path, $method, $data, $query);
if (null !== $this->logger) {
$time = microtime(true) - $start;
$this->logger->logQuery($path, $method, $data, $time);
}
return $response;
}
}

109
Command/PopulateCommand.php Executable file → Normal file
View file

@ -2,28 +2,38 @@
namespace FOS\ElasticaBundle\Command;
use FOS\ElasticaBundle\Event\IndexPopulateEvent;
use FOS\ElasticaBundle\Event\TypePopulateEvent;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\Output;
use FOS\ElasticaBundle\IndexManager;
use FOS\ElasticaBundle\Provider\ProviderRegistry;
use FOS\ElasticaBundle\Resetter;
use FOS\ElasticaBundle\Provider\ProviderInterface;
use Symfony\Component\Console\Helper\ProgressBar;
/**
* Populate the search index
* Populate the search index.
*/
class PopulateCommand extends ContainerAwareCommand
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $dispatcher;
/**
* @var IndexManager
*/
private $indexManager;
/**
* @var ProgressClosureBuilder
*/
private $progressClosureBuilder;
/**
* @var ProviderRegistry
*/
@ -47,32 +57,49 @@ class PopulateCommand extends ContainerAwareCommand
->addOption('offset', null, InputOption::VALUE_REQUIRED, 'Start indexing at offset', 0)
->addOption('sleep', null, InputOption::VALUE_REQUIRED, 'Sleep time between persisting iterations (microseconds)', 0)
->addOption('batch-size', null, InputOption::VALUE_REQUIRED, 'Index packet size (overrides provider config option)')
->addOption('ignore-errors', null, InputOption::VALUE_NONE, 'Do not stop on errors')
->addOption('no-overwrite-format', null, InputOption::VALUE_NONE, 'Prevent this command from overwriting ProgressBar\'s formats')
->setDescription('Populates search indexes from providers')
;
}
/**
* @see Symfony\Component\Console\Command\Command::initialize()
* {@inheritDoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->dispatcher = $this->getContainer()->get('event_dispatcher');
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
$this->resetter = $this->getContainer()->get('fos_elastica.resetter');
$this->progressClosureBuilder = new ProgressClosureBuilder();
if (!$input->getOption('no-overwrite-format') && class_exists('Symfony\\Component\\Console\\Helper\\ProgressBar')) {
ProgressBar::setFormatDefinition('normal', " %current%/%max% [%bar%] %percent:3s%%\n%message%");
ProgressBar::setFormatDefinition('verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%\n%message%");
ProgressBar::setFormatDefinition('very_verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%\n%message%");
ProgressBar::setFormatDefinition('debug', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%\n%message%");
}
}
/**
* @see Symfony\Component\Console\Command\Command::execute()
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$index = $input->getOption('index');
$type = $input->getOption('type');
$reset = $input->getOption('no-reset') ? false : true;
$noInteraction = $input->getOption('no-interaction');
$options = $input->getOptions();
$index = $input->getOption('index');
$type = $input->getOption('type');
$reset = !$input->getOption('no-reset');
$options = array(
'ignore_errors' => $input->getOption('ignore-errors'),
'offset' => $input->getOption('offset'),
'sleep' => $input->getOption('sleep')
);
if ($input->getOption('batch-size')) {
$options['batch_size'] = (int) $input->getOption('batch-size');
}
if (!$noInteraction && $reset && $input->getOption('offset')) {
if ($input->isInteractive() && $reset && $input->getOption('offset')) {
/** @var DialogHelper $dialog */
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog->askConfirmation($output, '<question>You chose to reset the index and start indexing with an offset. Do you really want to do that?</question>', true)) {
@ -109,24 +136,22 @@ class PopulateCommand extends ContainerAwareCommand
*/
private function populateIndex(OutputInterface $output, $index, $reset, $options)
{
if ($reset) {
$event = new IndexPopulateEvent($index, $reset, $options);
$this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);
if ($event->isReset()) {
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
$this->resetter->resetIndex($index);
$this->resetter->resetIndex($index, true);
}
/** @var $providers ProviderInterface[] */
$providers = $this->providerRegistry->getIndexProviders($index);
foreach ($providers as $type => $provider) {
$loggerClosure = function($message) use ($output, $index, $type) {
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
};
$provider->populate($loggerClosure, $options);
$types = array_keys($this->providerRegistry->getIndexProviders($index));
foreach ($types as $type) {
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
}
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
$this->indexManager->getIndex($index)->refresh();
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
$this->refreshIndex($output, $index);
}
/**
@ -140,19 +165,37 @@ class PopulateCommand extends ContainerAwareCommand
*/
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
{
if ($reset) {
$output->writeln(sprintf('Resetting: %s/%s', $index, $type));
$event = new TypePopulateEvent($index, $type, $reset, $options);
$this->dispatcher->dispatch(TypePopulateEvent::PRE_TYPE_POPULATE, $event);
if ($event->isReset()) {
$output->writeln(sprintf('<info>Resetting</info> <comment>%s/%s</comment>', $index, $type));
$this->resetter->resetIndexType($index, $type);
}
$loggerClosure = function($message) use ($output, $index, $type) {
$output->writeln(sprintf('Populating: %s/%s, %s', $index, $type, $message));
};
$provider = $this->providerRegistry->getProvider($index, $type);
$provider->populate($loggerClosure, $options);
$loggerClosure = $this->progressClosureBuilder->build($output, 'Populating', $index, $type);
$provider->populate($loggerClosure, $event->getOptions());
$output->writeln(sprintf('Refreshing: %s', $index));
$this->dispatcher->dispatch(TypePopulateEvent::POST_TYPE_POPULATE, $event);
$this->refreshIndex($output, $index, false);
}
/**
* Refreshes an index.
*
* @param OutputInterface $output
* @param string $index
* @param bool $postPopulate
*/
private function refreshIndex(OutputInterface $output, $index, $postPopulate = true)
{
if ($postPopulate) {
$this->resetter->postPopulate($index);
}
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
$this->indexManager->getIndex($index)->refresh();
}
}

View file

@ -0,0 +1,103 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;
class ProgressClosureBuilder
{
/**
* Builds a loggerClosure to be called from inside the Provider to update the command
* line.
*
* @param OutputInterface $output
* @param string $action
* @param string $index
* @param string $type
*
* @return callable
*/
public function build(OutputInterface $output, $action, $index, $type)
{
if (!class_exists('Symfony\Component\Console\Helper\ProgressBar') ||
!is_callable(array('Symfony\Component\Console\Helper\ProgressBar', 'getProgress'))) {
return $this->buildLegacy($output, $action, $index, $type);
}
$progress = null;
return function ($increment, $totalObjects, $message = null) use (&$progress, $output, $action, $index, $type) {
if (null === $progress) {
$progress = new ProgressBar($output, $totalObjects);
$progress->start();
}
if (null !== $message) {
$progress->clear();
$output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
$progress->display();
}
$progress->setMessage(sprintf('<info>%s</info> <comment>%s/%s</comment>', $action, $index, $type));
$progress->advance($increment);
};
}
/**
* Builds a legacy closure that outputs lines for each step. Used in cases
* where the ProgressBar component doesnt exist or does not have the correct
* methods to support what we need.
*
* @param OutputInterface $output
* @param string $action
* @param string $index
* @param string $type
*
* @return callable
*/
private function buildLegacy(OutputInterface $output, $action, $index, $type)
{
$lastStep = null;
$current = 0;
return function ($increment, $totalObjects, $message = null) use ($output, $action, $index, $type, &$lastStep, &$current) {
if ($current + $increment > $totalObjects) {
$increment = $totalObjects - $current;
}
if (null !== $message) {
$output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
}
$currentTime = microtime(true);
$timeDifference = $currentTime - $lastStep;
$objectsPerSecond = $lastStep ? ($increment / $timeDifference) : $increment;
$lastStep = $currentTime;
$current += $increment;
$percent = 100 * $current / $totalObjects;
$output->writeln(sprintf(
'<info>%s</info> <comment>%s/%s</comment> %0.1f%% (%d/%d), %d objects/s (RAM: current=%uMo peak=%uMo)',
$action,
$index,
$type,
$percent,
$current,
$totalObjects,
$objectsPerSecond,
round(memory_get_usage() / (1024 * 1024)),
round(memory_get_peak_usage() / (1024 * 1024))
));
};
}
}

78
Command/ResetCommand.php Executable file
View file

@ -0,0 +1,78 @@
<?php
namespace FOS\ElasticaBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use FOS\ElasticaBundle\IndexManager;
use FOS\ElasticaBundle\Resetter;
/**
* Reset search indexes.
*/
class ResetCommand extends ContainerAwareCommand
{
/**
* @var IndexManager
*/
private $indexManager;
/**
* @var Resetter
*/
private $resetter;
/**
* @see Symfony\Component\Console\Command\Command::configure()
*/
protected function configure()
{
$this
->setName('fos:elastica:reset')
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index to reset')
->addOption('type', null, InputOption::VALUE_OPTIONAL, 'The type to reset')
->addOption('force', null, InputOption::VALUE_NONE, 'Force index deletion if same name as alias')
->setDescription('Reset search indexes')
;
}
/**
* @see Symfony\Component\Console\Command\Command::initialize()
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
$this->resetter = $this->getContainer()->get('fos_elastica.resetter');
}
/**
* @see Symfony\Component\Console\Command\Command::execute()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$index = $input->getOption('index');
$type = $input->getOption('type');
$force = (bool) $input->getOption('force');
if (null === $index && null !== $type) {
throw new \InvalidArgumentException('Cannot specify type option without an index.');
}
if (null !== $type) {
$output->writeln(sprintf('<info>Resetting</info> <comment>%s/%s</comment>', $index, $type));
$this->resetter->resetIndexType($index, $type);
} else {
$indexes = null === $index
? array_keys($this->indexManager->getAllIndexes())
: array($index)
;
foreach ($indexes as $index) {
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
$this->resetter->resetIndex($index, false, $force);
}
}
}
}

View file

@ -7,12 +7,11 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\Output;
use Elastica_Query;
use Elastica_Result;
use Elastica\Query;
use Elastica\Result;
/**
* Searches a type
* Searches a type.
*/
class SearchCommand extends ContainerAwareCommand
{
@ -41,11 +40,11 @@ class SearchCommand extends ContainerAwareCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
$indexName = $input->getOption('index');
/** @var $index \Elastica_Index */
/** @var $index \Elastica\Index */
$index = $this->getContainer()->get('fos_elastica.index_manager')->getIndex($indexName ? $indexName : null);
$type = $index->getType($input->getArgument('type'));
$query = Elastica_Query::create($input->getArgument('query'));
$query->setLimit($input->getOption('limit'));
$query = Query::create($input->getArgument('query'));
$query->setSize($input->getOption('limit'));
if ($input->getOption('explain')) {
$query->setExplain(true);
}
@ -58,7 +57,7 @@ class SearchCommand extends ContainerAwareCommand
}
}
protected function formatResult(Elastica_Result $result, $showField, $showSource, $showId, $explain)
protected function formatResult(Result $result, $showField, $showSource, $showId, $explain)
{
$source = $result->getSource();
if ($showField) {

View file

@ -0,0 +1,64 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration;
/**
* Central manager for index and type configuration.
*/
class ConfigManager implements ManagerInterface
{
/**
* @var IndexConfig[]
*/
private $indexes = array();
/**
* @param Source\SourceInterface[] $sources
*/
public function __construct(array $sources)
{
foreach ($sources as $source) {
$this->indexes = array_merge($source->getConfiguration(), $this->indexes);
}
}
public function getIndexConfiguration($indexName)
{
if (!$this->hasIndexConfiguration($indexName)) {
throw new \InvalidArgumentException(sprintf('Index with name "%s" is not configured.', $indexName));
}
return $this->indexes[$indexName];
}
public function getIndexNames()
{
return array_keys($this->indexes);
}
public function getTypeConfiguration($indexName, $typeName)
{
$index = $this->getIndexConfiguration($indexName);
$type = $index->getType($typeName);
if (!$type) {
throw new \InvalidArgumentException(sprintf('Type with name "%s" on index "%s" is not configured', $typeName, $indexName));
}
return $type;
}
public function hasIndexConfiguration($indexName)
{
return isset($this->indexes[$indexName]);
}
}

View file

@ -0,0 +1,124 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration;
class IndexConfig
{
/**
* The name of the index for ElasticSearch.
*
* @var string
*/
private $elasticSearchName;
/**
* The internal name of the index. May not be the same as the name used in ElasticSearch,
* especially if aliases are enabled.
*
* @var string
*/
private $name;
/**
* An array of settings sent to ElasticSearch when creating the index.
*
* @var array
*/
private $settings;
/**
* All types that belong to this index.
*
* @var TypeConfig[]
*/
private $types;
/**
* Indicates if the index should use an alias, allowing an index repopulation to occur
* without overwriting the current index.
*
* @var bool
*/
private $useAlias = false;
/**
* Constructor expects an array as generated by the Container Configuration builder.
*
* @param string $name
* @param TypeConfig[] $types
* @param array $config
*/
public function __construct($name, array $types, array $config)
{
$this->elasticSearchName = isset($config['elasticSearchName']) ? $config['elasticSearchName'] : $name;
$this->name = $name;
$this->settings = isset($config['settings']) ? $config['settings'] : array();
$this->types = $types;
$this->useAlias = isset($config['useAlias']) ? $config['useAlias'] : false;
}
/**
* @return string
*/
public function getElasticSearchName()
{
return $this->elasticSearchName;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getSettings()
{
return $this->settings;
}
/**
* @param string $typeName
*
* @return TypeConfig
*
* @throws \InvalidArgumentException
*/
public function getType($typeName)
{
if (!array_key_exists($typeName, $this->types)) {
throw new \InvalidArgumentException(sprintf('Type "%s" does not exist on index "%s"', $typeName, $this->name));
}
return $this->types[$typeName];
}
/**
* @return \FOS\ElasticaBundle\Configuration\TypeConfig[]
*/
public function getTypes()
{
return $this->types;
}
/**
* @return boolean
*/
public function isUseAlias()
{
return $this->useAlias;
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration;
/**
* Central manager for index and type configuration.
*/
interface ManagerInterface
{
/**
* Returns configuration for an index.
*
* @param $index
*
* @return IndexConfig
*/
public function getIndexConfiguration($index);
/**
* Returns an array of known index names.
*
* @return array
*/
public function getIndexNames();
/**
* Returns a type configuration.
*
* @param string $index
* @param string $type
*
* @return TypeConfig
*/
public function getTypeConfiguration($index, $type);
}

View file

@ -1,18 +1,26 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration;
use FOS\ElasticaBundle\Annotation\Search as BaseSearch;
/**
* Annotation class for setting search repository.
*
* @author Richard Miller <info@limethinking.co.uk>
* @Annotation
*
* @deprecated Use FOS\ElasticaBundle\Annotation\Search instead
* @Target("CLASS")
*/
class Search
class Search extends BaseSearch
{
/** @var string */
public $repositoryClass;
}

View file

@ -0,0 +1,80 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration\Source;
use FOS\ElasticaBundle\Configuration\IndexConfig;
use FOS\ElasticaBundle\Configuration\TypeConfig;
/**
* Returns index and type configuration from the container.
*/
class ContainerSource implements SourceInterface
{
/**
* The internal container representation of information.
*
* @var array
*/
private $configArray;
public function __construct(array $configArray)
{
$this->configArray = $configArray;
}
/**
* Should return all configuration available from the data source.
*
* @return IndexConfig[]
*/
public function getConfiguration()
{
$indexes = array();
foreach ($this->configArray as $config) {
$types = $this->getTypes($config);
$index = new IndexConfig($config['name'], $types, array(
'elasticSearchName' => $config['elasticsearch_name'],
'settings' => $config['settings'],
'useAlias' => $config['use_alias'],
));
$indexes[$config['name']] = $index;
}
return $indexes;
}
/**
* Builds TypeConfig objects for each type.
*
* @param array $config
*
* @return array
*/
protected function getTypes($config)
{
$types = array();
if (isset($config['types'])) {
foreach ($config['types'] as $typeConfig) {
$types[$typeConfig['name']] = new TypeConfig(
$typeConfig['name'],
$typeConfig['mapping'],
$typeConfig['config']
);
// TODO: handle prototypes..
}
}
return $types;
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration\Source;
/**
* Represents a source of index and type information (ie, the Container configuration or
* annotations).
*/
interface SourceInterface
{
/**
* Should return all configuration available from the data source.
*
* @return \FOS\ElasticaBundle\Configuration\IndexConfig[]
*/
public function getConfiguration();
}

View file

@ -0,0 +1,113 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Configuration;
class TypeConfig
{
/**
* @var array
*/
private $config;
/**
* @var array
*/
private $mapping;
/**
* @var string
*/
private $name;
public function __construct($name, array $mapping, array $config = array())
{
$this->config = $config;
$this->mapping = $mapping;
$this->name = $name;
}
/**
* @return bool|null
*/
public function getDateDetection()
{
return $this->getConfig('date_detection');
}
/**
* @return array
*/
public function getDynamicDateFormats()
{
return $this->getConfig('dynamic_date_formats');
}
/**
* @return string|null
*/
public function getIndexAnalyzer()
{
return $this->getConfig('index_analyzer');
}
/**
* @return array
*/
public function getMapping()
{
return $this->mapping;
}
/**
* @return string|null
*/
public function getModel()
{
return isset($this->config['persistence']['model']) ?
$this->config['persistence']['model'] :
null;
}
/**
* @return bool|null
*/
public function getNumericDetection()
{
return $this->getConfig('numeric_detection');
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string|null
*/
public function getSearchAnalyzer()
{
return $this->getConfig('search_analyzer');
}
/**
* @param string $key
*/
private function getConfig($key)
{
return isset($this->config[$key]) ?
$this->config[$key] :
null;
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class ConfigSourcePass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('fos_elastica.config_manager')) {
return;
}
$sources = array();
foreach (array_keys($container->findTaggedServiceIds('fos_elastica.config_source')) as $id) {
$sources[] = new Reference($id);
}
$container->getDefinition('fos_elastica.config_manager')->replaceArgument(0, $sources);
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class IndexPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('fos_elastica.index_manager')) {
return;
}
$indexes = array();
foreach ($container->findTaggedServiceIds('fos_elastica.index') as $id => $tags) {
foreach ($tags as $tag) {
$indexes[$tag['name']] = new Reference($id);
}
}
$container->getDefinition('fos_elastica.index_manager')->replaceArgument(0, $indexes);
}
}

View file

@ -55,6 +55,7 @@ class RegisterProvidersPass implements CompilerPassInterface
* Returns whether the class implements ProviderInterface.
*
* @param string $class
*
* @return boolean
*/
private function isProviderImplementation($class)

View file

@ -31,7 +31,7 @@ class TransformerPass implements CompilerPassInterface
throw new InvalidArgumentException('The Transformer must have both a type and an index defined.');
}
$transformers[$tag['index']][$tag['type']]= new Reference($id);
$transformers[$tag['index']][$tag['type']] = new Reference($id);
}
}

View file

@ -8,18 +8,29 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
/**
* Stores supported database drivers.
*
* @var array
*/
private $supportedDrivers = array('orm', 'mongodb', 'propel');
private $configArray = array();
/**
* If the kernel is running in debug mode.
*
* @var bool
*/
private $debug;
public function __construct($configArray){
$this->configArray = $configArray;
public function __construct($debug)
{
$this->debug = $debug;
}
/**
* Generates the configuration tree.
*
* @return \Symfony\Component\Config\Definition\NodeInterface
* @return TreeBuilder
*/
public function getConfigTreeBuilder()
{
@ -31,9 +42,20 @@ class Configuration implements ConfigurationInterface
$rootNode
->children()
->scalarNode('default_client')->end()
->scalarNode('default_index')->end()
->scalarNode('default_client')
->info('Defaults to the first client defined')
->end()
->scalarNode('default_index')
->info('Defaults to the first index defined')
->end()
->scalarNode('default_manager')->defaultValue('orm')->end()
->arrayNode('serializer')
->treatNullLike(array())
->children()
->scalarNode('callback_class')->defaultValue('FOS\ElasticaBundle\Serializer\Callback')->end()
->scalarNode('serializer')->defaultValue('serializer')->end()
->end()
->end()
->end()
;
@ -41,17 +63,7 @@ class Configuration implements ConfigurationInterface
}
/**
* Generates the configuration tree.
*
* @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface
*/
public function getConfigTree()
{
return $this->getConfigTreeBuilder()->buildTree();
}
/**
* Adds the configuration for the "clients" key
* Adds the configuration for the "clients" key.
*/
private function addClientsSection(ArrayNodeDefinition $rootNode)
{
@ -62,43 +74,67 @@ class Configuration implements ConfigurationInterface
->useAttributeAsKey('id')
->prototype('array')
->performNoDeepMerging()
// BC - Renaming 'servers' node to 'connections'
->beforeNormalization()
->ifTrue(function($v) { return isset($v['host']) && isset($v['port']); })
->then(function($v) {
return array(
'servers' => array(
array(
'host' => $v['host'],
'port' => $v['port'],
)
)
);
})
->ifTrue(function ($v) { return isset($v['servers']); })
->then(function ($v) {
$v['connections'] = $v['servers'];
unset($v['servers']);
return $v;
})
->end()
// Elastica names its properties with camel case, support both
->beforeNormalization()
->ifTrue(function($v) { return isset($v['url']); })
->then(function($v) {
return array(
'servers' => array(
array(
'url' => $v['url'],
)
)
);
})
->ifTrue(function ($v) { return isset($v['connection_strategy']); })
->then(function ($v) {
$v['connectionStrategy'] = $v['connection_strategy'];
unset($v['connection_strategy']);
return $v;
})
->end()
// If there is no connections array key defined, assume a single connection.
->beforeNormalization()
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); })
->then(function ($v) {
return array(
'connections' => array($v),
);
})
->end()
->children()
->arrayNode('servers')
->arrayNode('connections')
->requiresAtLeastOneElement()
->prototype('array')
->fixXmlConfig('header')
->children()
->scalarNode('url')->end()
->scalarNode('url')
->validate()
->ifTrue(function ($url) { return $url && substr($url, -1) !== '/'; })
->then(function ($url) { return $url.'/'; })
->end()
->end()
->scalarNode('host')->end()
->scalarNode('port')->end()
->scalarNode('proxy')->end()
->scalarNode('logger')
->defaultValue($this->debug ? 'fos_elastica.logger' : false)
->treatNullLike('fos_elastica.logger')
->treatTrueLike('fos_elastica.logger')
->end()
->arrayNode('headers')
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->scalarNode('transport')->end()
->scalarNode('timeout')->end()
->end()
->end()
->end()
->scalarNode('timeout')->end()
->scalarNode('headers')->end()
->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
->end()
->end()
->end()
@ -107,7 +143,7 @@ class Configuration implements ConfigurationInterface
}
/**
* Adds the configuration for the "indexes" key
* Adds the configuration for the "indexes" key.
*/
private function addIndexesSection(ArrayNodeDefinition $rootNode)
{
@ -117,9 +153,11 @@ class Configuration implements ConfigurationInterface
->arrayNode('indexes')
->useAttributeAsKey('name')
->prototype('array')
->performNoDeepMerging()
->children()
->scalarNode('index_name')->end()
->scalarNode('index_name')
->info('Defaults to the name of the index, but can be modified if the index name is different in ElasticSearch')
->end()
->booleanNode('use_alias')->defaultValue(false)->end()
->scalarNode('client')->end()
->scalarNode('finder')
->treatNullLike(true)
@ -129,60 +167,8 @@ class Configuration implements ConfigurationInterface
->children()
->scalarNode('index_analyzer')->end()
->scalarNode('search_analyzer')->end()
->arrayNode('persistence')
->validate()
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
->thenInvalid('Propel doesn\'t support listeners')
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
->thenInvalid('Propel doesn\'t support the "repository" parameter')
->end()
->children()
->scalarNode('driver')
->validate()
->ifNotInArray($this->supportedDrivers)
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
->end()
->end()
->scalarNode('identifier')->defaultValue('id')->end()
->arrayNode('provider')
->children()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('batch_size')->defaultValue(100)->end()
->scalarNode('clear_object_manager')->defaultTrue()->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('listener')
->children()
->scalarNode('insert')->defaultTrue()->end()
->scalarNode('update')->defaultTrue()->end()
->scalarNode('delete')->defaultTrue()->end()
->scalarNode('service')->end()
->variableNode('is_indexable_callback')->defaultNull()->end()
->end()
->end()
->arrayNode('finder')
->children()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('elastica_to_model_transformer')
->addDefaultsIfNotSet()
->children()
->scalarNode('hydrate')->defaultTrue()->end()
->scalarNode('ignore_missing')->defaultFalse()->end()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('model_to_elastica_transformer')
->addDefaultsIfNotSet()
->children()
->scalarNode('service')->end()
->end()
->end()
->end()
->end()
->append($this->getPersistenceNode())
->append($this->getSerializerNode())
->end()
->end()
->variableNode('settings')->defaultValue(array())->end()
@ -206,71 +192,83 @@ class Configuration implements ConfigurationInterface
->useAttributeAsKey('name')
->prototype('array')
->treatNullLike(array())
->children()
->scalarNode('index_analyzer')->end()
->scalarNode('search_analyzer')->end()
->arrayNode('persistence')
->validate()
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
->thenInvalid('Propel doesn\'t support listeners')
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
->thenInvalid('Propel doesn\'t support the "repository" parameter')
->end()
->children()
->scalarNode('driver')
->validate()
->ifNotInArray($this->supportedDrivers)
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
->end()
->end()
->scalarNode('model')->end()
->scalarNode('repository')->end()
->scalarNode('identifier')->defaultValue('id')->end()
->arrayNode('provider')
->children()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('batch_size')->defaultValue(100)->end()
->scalarNode('clear_object_manager')->defaultTrue()->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('listener')
->children()
->scalarNode('insert')->defaultTrue()->end()
->scalarNode('update')->defaultTrue()->end()
->scalarNode('delete')->defaultTrue()->end()
->scalarNode('service')->end()
->variableNode('is_indexable_callback')->defaultNull()->end()
->end()
->end()
->arrayNode('finder')
->children()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('elastica_to_model_transformer')
->addDefaultsIfNotSet()
->children()
->scalarNode('hydrate')->defaultTrue()->end()
->scalarNode('ignore_missing')->defaultFalse()->end()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('model_to_elastica_transformer')
->addDefaultsIfNotSet()
->children()
->scalarNode('service')->end()
->end()
->end()
->end()
->end()
->beforeNormalization()
->ifNull()
->thenEmptyArray()
->end()
->append($this->getMappingsNode())
// BC - Renaming 'mappings' node to 'properties'
->beforeNormalization()
->ifTrue(function ($v) { return array_key_exists('mappings', $v); })
->then(function ($v) {
$v['properties'] = $v['mappings'];
unset($v['mappings']);
return $v;
})
->end()
// BC - Support the old is_indexable_callback property
->beforeNormalization()
->ifTrue(function ($v) {
return isset($v['persistence']) &&
isset($v['persistence']['listener']) &&
isset($v['persistence']['listener']['is_indexable_callback']);
})
->then(function ($v) {
$callback = $v['persistence']['listener']['is_indexable_callback'];
if (is_array($callback)) {
list($class) = $callback + array(null);
if ($class[0] !== '@' && is_string($class) && !class_exists($class)) {
$callback[0] = '@'.$class;
}
}
$v['indexable_callback'] = $callback;
unset($v['persistence']['listener']['is_indexable_callback']);
return $v;
})
->end()
// Support multiple dynamic_template formats to match the old bundle style
// and the way ElasticSearch expects them
->beforeNormalization()
->ifTrue(function ($v) { return isset($v['dynamic_templates']); })
->then(function ($v) {
$dt = array();
foreach ($v['dynamic_templates'] as $key => $type) {
if (is_int($key)) {
$dt[] = $type;
} else {
$dt[][$key] = $type;
}
}
$v['dynamic_templates'] = $dt;
return $v;
})
->end()
->children()
->booleanNode('date_detection')->end()
->arrayNode('dynamic_date_formats')->prototype('scalar')->end()->end()
->scalarNode('index_analyzer')->end()
->booleanNode('numeric_detection')->end()
->scalarNode('search_analyzer')->end()
->variableNode('indexable_callback')->end()
->append($this->getPersistenceNode())
->append($this->getSerializerNode())
->end()
->append($this->getIdNode())
->append($this->getPropertiesNode())
->append($this->getDynamicTemplateNode())
->append($this->getSourceNode())
->append($this->getBoostNode())
->append($this->getRoutingNode())
->append($this->getParentNode())
->append($this->getAllNode())
->append($this->getTimestampNode())
->append($this->getTtlNode())
->end()
;
@ -278,23 +276,17 @@ class Configuration implements ConfigurationInterface
}
/**
* Returns the array node used for "mappings".
* Returns the array node used for "properties".
*/
protected function getMappingsNode()
protected function getPropertiesNode()
{
$builder = new TreeBuilder();
$node = $builder->root('mappings');
$node = $builder->root('properties');
$nestings = $this->getNestings();
$childrenNode = $node
$node
->useAttributeAsKey('name')
->prototype('array')
->treatNullLike(array())
->addDefaultsIfNotSet()
->children();
$this->addFieldConfig($childrenNode, $nestings);
->prototype('variable')
->treatNullLike(array());
return $node;
}
@ -308,18 +300,18 @@ class Configuration implements ConfigurationInterface
$node = $builder->root('dynamic_templates');
$node
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('match')->isRequired()->end()
->scalarNode('match_mapping_type')->end()
->arrayNode('mapping')
->isRequired()
->children()
->scalarNode('type')->end()
->scalarNode('index')->end()
->arrayNode('fields')
->children()
->prototype('array')
->children()
->scalarNode('match')->end()
->scalarNode('unmatch')->end()
->scalarNode('match_mapping_type')->end()
->scalarNode('path_match')->end()
->scalarNode('path_unmatch')->end()
->scalarNode('match_pattern')->end()
->arrayNode('mapping')
->prototype('variable')
->treatNullLike(array())
->end()
->end()
->end()
@ -331,127 +323,20 @@ class Configuration implements ConfigurationInterface
}
/**
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the field config to
* @param array $nestings the nested mappings for the current field level
* Returns the array node used for "_id".
*/
protected function addFieldConfig($node, $nestings)
protected function getIdNode()
{
$builder = new TreeBuilder();
$node = $builder->root('_id');
$node
->scalarNode('type')->defaultValue('string')->end()
->scalarNode('boost')->end()
->scalarNode('store')->end()
->scalarNode('index')->end()
->scalarNode('index_analyzer')->end()
->scalarNode('search_analyzer')->end()
->scalarNode('analyzer')->end()
->scalarNode('term_vector')->end()
->scalarNode('null_value')->end()
->booleanNode('include_in_all')->defaultValue(true)->end()
->booleanNode('enabled')->defaultValue(true)->end()
->scalarNode('lat_lon')->end()
->scalarNode('index_name')->end()
->booleanNode('omit_norms')->end()
->scalarNode('index_options')->end()
->scalarNode('ignore_above')->end()
->scalarNode('position_offset_gap')->end()
->arrayNode('_parent')
->treatNullLike(array())
->children()
->scalarNode('type')->end()
->scalarNode('identifier')->defaultValue('id')->end()
->end()
->end()
->scalarNode('format')->end();
if (isset($nestings['fields'])) {
$this->addNestedFieldConfig($node, $nestings, 'fields');
}
if (isset($nestings['properties'])) {
$this->addNestedFieldConfig($node, $nestings, 'properties');
}
}
/**
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the nested config to
* @param array $nestings The nestings for the current field level
* @param string $property the name of the nested property ('fields' or 'properties')
*/
protected function addNestedFieldConfig($node, $nestings, $property)
{
$childrenNode = $node
->arrayNode($property)
->useAttributeAsKey('name')
->prototype('array')
->treatNullLike(array())
->addDefaultsIfNotSet()
->children();
$this->addFieldConfig($childrenNode, $nestings[$property]);
$childrenNode
->end()
->end()
->children()
->scalarNode('path')->end()
->end()
;
}
/**
* @return array The unique nested mappings for all types
*/
protected function getNestings()
{
if (!isset($this->configArray[0]['indexes'])) {
return array();
}
$nestings = array();
foreach ($this->configArray[0]['indexes'] as $index) {
if (empty($index['types'])) {
continue;
}
foreach ($index['types'] as $type) {
$nestings = array_merge_recursive($nestings, $this->getNestingsForType($type['mappings'], $nestings));
}
}
return $nestings;
}
/**
* @param array $mappings The mappings for the current type
* @return array The nested mappings defined for this type
*/
protected function getNestingsForType(array $mappings = null)
{
if ($mappings === null) {
return array();
}
$nestings = array();
foreach ($mappings as $field) {
if (isset($field['fields'])) {
$this->addPropertyNesting($field, $nestings, 'fields');
} else if (isset($field['properties'])) {
$this->addPropertyNesting($field, $nestings, 'properties');
}
}
return $nestings;
}
/**
* @param array $field The field mapping definition
* @param array $nestings The nestings array
* @param string $property The nested property name ('fields' or 'properties')
*/
protected function addPropertyNesting($field, &$nestings, $property)
{
if (!isset($nestings[$property])) {
$nestings[$property] = array();
}
$nestings[$property] = array_merge_recursive($nestings[$property], $this->getNestingsForType($field[$property]));
return $node;
}
/**
@ -474,7 +359,7 @@ class Configuration implements ConfigurationInterface
->end()
->scalarNode('compress')->end()
->scalarNode('compress_threshold')->end()
->scalarNode('enabled')->end()
->scalarNode('enabled')->defaultTrue()->end()
->end()
;
@ -516,4 +401,181 @@ class Configuration implements ConfigurationInterface
return $node;
}
/**
* Returns the array node used for "_parent".
*/
protected function getParentNode()
{
$builder = new TreeBuilder();
$node = $builder->root('_parent');
$node
->children()
->scalarNode('type')->end()
->scalarNode('property')->defaultValue(null)->end()
->scalarNode('identifier')->defaultValue('id')->end()
->end()
;
return $node;
}
/**
* Returns the array node used for "_all".
*/
protected function getAllNode()
{
$builder = new TreeBuilder();
$node = $builder->root('_all');
$node
->children()
->scalarNode('enabled')->defaultValue(true)->end()
->scalarNode('index_analyzer')->end()
->scalarNode('search_analyzer')->end()
->end()
;
return $node;
}
/**
* Returns the array node used for "_timestamp".
*/
protected function getTimestampNode()
{
$builder = new TreeBuilder();
$node = $builder->root('_timestamp');
$node
->children()
->scalarNode('enabled')->defaultValue(true)->end()
->scalarNode('path')->end()
->scalarNode('format')->end()
->scalarNode('store')->end()
->scalarNode('index')->end()
->end()
;
return $node;
}
/**
* Returns the array node used for "_ttl".
*/
protected function getTtlNode()
{
$builder = new TreeBuilder();
$node = $builder->root('_ttl');
$node
->children()
->scalarNode('enabled')->defaultValue(true)->end()
->scalarNode('default')->end()
->scalarNode('store')->end()
->scalarNode('index')->end()
->end()
;
return $node;
}
/**
* @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
*/
protected function getPersistenceNode()
{
$builder = new TreeBuilder();
$node = $builder->root('persistence');
$node
->validate()
->ifTrue(function ($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
->thenInvalid('Propel doesn\'t support listeners')
->ifTrue(function ($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
->thenInvalid('Propel doesn\'t support the "repository" parameter')
->end()
->children()
->scalarNode('driver')
->validate()
->ifNotInArray($this->supportedDrivers)
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
->end()
->end()
->scalarNode('model')->end()
->scalarNode('repository')->end()
->scalarNode('identifier')->defaultValue('id')->end()
->arrayNode('provider')
->children()
->scalarNode('batch_size')->defaultValue(100)->end()
->scalarNode('clear_object_manager')->defaultTrue()->end()
->scalarNode('debug_logging')
->defaultValue($this->debug)
->treatNullLike(true)
->end()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('listener')
->children()
->scalarNode('insert')->defaultTrue()->end()
->scalarNode('update')->defaultTrue()->end()
->scalarNode('delete')->defaultTrue()->end()
->scalarNode('flush')->defaultTrue()->end()
->booleanNode('immediate')->defaultFalse()->end()
->scalarNode('logger')
->defaultFalse()
->treatNullLike('fos_elastica.logger')
->treatTrueLike('fos_elastica.logger')
->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('finder')
->children()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('elastica_to_model_transformer')
->addDefaultsIfNotSet()
->children()
->scalarNode('hydrate')->defaultTrue()->end()
->scalarNode('ignore_missing')->defaultFalse()->end()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('service')->end()
->end()
->end()
->arrayNode('model_to_elastica_transformer')
->addDefaultsIfNotSet()
->children()
->scalarNode('service')->end()
->end()
->end()
->end();
return $node;
}
/**
* @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
*/
protected function getSerializerNode()
{
$builder = new TreeBuilder();
$node = $builder->root('serializer');
$node
->addDefaultsIfNotSet()
->children()
->arrayNode('groups')
->treatNullLike(array())
->prototype('scalar')->end()
->end()
->scalarNode('version')->end()
->end();
return $node;
}
}

View file

@ -5,7 +5,6 @@ namespace FOS\ElasticaBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
@ -13,20 +12,42 @@ use InvalidArgumentException;
class FOSElasticaExtension extends Extension
{
protected $indexConfigs = array();
protected $typeFields = array();
protected $loadedDrivers = array();
/**
* Definition of elastica clients as configured by this extension.
*
* @var array
*/
private $clients = array();
/**
* An array of indexes as configured by the extension.
*
* @var array
*/
private $indexConfigs = array();
/**
* If we've encountered a type mapped to a specific persistence driver, it will be loaded
* here.
*
* @var array
*/
private $loadedDrivers = array();
public function load(array $configs, ContainerBuilder $container)
{
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$config = $this->processConfiguration($configuration, $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('config.xml');
if (empty($config['clients']) || empty($config['indexes'])) {
throw new InvalidArgumentException('You must define at least one client and one index');
// No Clients or indexes are defined
return;
}
foreach (array('config', 'index', 'persister', 'provider', 'source', 'transformer') as $basename) {
$loader->load(sprintf('%s.xml', $basename));
}
if (empty($config['default_client'])) {
@ -39,109 +60,129 @@ class FOSElasticaExtension extends Extension
$config['default_index'] = reset($keys);
}
$clientIdsByName = $this->loadClients($config['clients'], $container);
$indexIdsByName = $this->loadIndexes($config['indexes'], $container, $clientIdsByName, $config['default_client']);
$indexRefsByName = array_map(function($id) {
return new Reference($id);
}, $indexIdsByName);
if (isset($config['serializer'])) {
$loader->load('serializer.xml');
$this->loadIndexManager($indexRefsByName, $container);
$this->loadResetter($this->indexConfigs, $container);
$this->loadSerializer($config['serializer'], $container);
}
$this->loadClients($config['clients'], $container);
$container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
$this->loadIndexes($config['indexes'], $container);
$container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']));
$container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
$this->loadIndexManager($container);
$this->createDefaultManagerAlias($config['default_manager'], $container);
}
/**
* @param array $config
* @param ContainerBuilder $container
*
* @return Configuration
*/
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($config);
return new Configuration($container->getParameter('kernel.debug'));
}
/**
* Loads the configured clients.
*
* @param array $clients An array of clients configurations
* @param array $clients An array of clients configurations
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @return array
*/
protected function loadClients(array $clients, ContainerBuilder $container)
private function loadClients(array $clients, ContainerBuilder $container)
{
$clientIds = array();
foreach ($clients as $name => $clientConfig) {
$clientId = sprintf('fos_elastica.client.%s', $name);
$clientDef = new Definition('%fos_elastica.client.class%', array($clientConfig));
$clientDef->addMethodCall('setLogger', array(new Reference('fos_elastica.logger')));
$clientDef = new DefinitionDecorator('fos_elastica.client_prototype');
$clientDef->replaceArgument(0, $clientConfig);
$logger = $clientConfig['connections'][0]['logger'];
if (false !== $logger) {
$clientDef->addMethodCall('setLogger', array(new Reference($logger)));
}
$clientDef->addTag('fos_elastica.client');
$container->setDefinition($clientId, $clientDef);
$clientIds[$name] = $clientId;
$this->clients[$name] = array(
'id' => $clientId,
'reference' => new Reference($clientId),
);
}
return $clientIds;
}
/**
* Loads the configured indexes.
*
* @param array $indexes An array of indexes configurations
* @param array $indexes An array of indexes configurations
* @param ContainerBuilder $container A ContainerBuilder instance
* @param array $clientIdsByName
* @param $defaultClientName
*
* @throws \InvalidArgumentException
*
* @return array
*/
protected function loadIndexes(array $indexes, ContainerBuilder $container, array $clientIdsByName, $defaultClientName)
private function loadIndexes(array $indexes, ContainerBuilder $container)
{
$indexIds = array();
foreach ($indexes as $name => $index) {
if (isset($index['client'])) {
$clientName = $index['client'];
if (!isset($clientIdsByName[$clientName])) {
throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
}
} else {
$clientName = $defaultClientName;
}
$indexableCallbacks = array();
$clientId = $clientIdsByName[$clientName];
foreach ($indexes as $name => $index) {
$indexId = sprintf('fos_elastica.index.%s', $name);
$indexName = isset($index['index_name']) ? $index['index_name'] : $name;
$indexDefArgs = array($indexName);
$indexDef = new Definition('%fos_elastica.index.class%', $indexDefArgs);
$indexDef->setFactoryService($clientId);
$indexDef->setFactoryMethod('getIndex');
$indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
$indexDef->replaceArgument(0, $indexName);
$indexDef->addTag('fos_elastica.index', array(
'name' => $name,
));
if (isset($index['client'])) {
$client = $this->getClient($index['client']);
$indexDef->setFactoryService($client);
}
$container->setDefinition($indexId, $indexDef);
$typePrototypeConfig = isset($index['type_prototype']) ? $index['type_prototype'] : array();
$indexIds[$name] = $indexId;
$reference = new Reference($indexId);
$this->indexConfigs[$name] = array(
'index' => new Reference($indexId),
'config' => array(
'mappings' => array()
)
'elasticsearch_name' => $indexName,
'reference' => $reference,
'name' => $name,
'settings' => $index['settings'],
'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : array(),
'use_alias' => $index['use_alias'],
);
if ($index['finder']) {
$this->loadIndexFinder($container, $name, $indexId);
$this->loadIndexFinder($container, $name, $reference);
}
if (!empty($index['settings'])) {
$this->indexConfigs[$name]['config']['settings'] = $index['settings'];
}
$this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig);
$this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
}
return $indexIds;
$indexable = $container->getDefinition('fos_elastica.indexable');
$indexable->replaceArgument(0, $indexableCallbacks);
}
/**
* Loads the configured index finders.
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param string $name The index name
* @param string $indexId The index service identifier
* @param string $name The index name
* @param Reference $index Reference to the related index
*
* @return string
*/
protected function loadIndexFinder(ContainerBuilder $container, $name, $indexId)
private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
{
/* Note: transformer services may conflict with "collection.index", if
* an index and type names were "collection" and an index, respectively.
@ -152,122 +193,141 @@ class FOSElasticaExtension extends Extension
$finderId = sprintf('fos_elastica.finder.%s', $name);
$finderDef = new DefinitionDecorator('fos_elastica.finder');
$finderDef->replaceArgument(0, new Reference($indexId));
$finderDef->replaceArgument(0, $index);
$finderDef->replaceArgument(1, new Reference($transformerId));
$container->setDefinition($finderId, $finderDef);
return $finderId;
}
/**
* Loads the configured types.
*
* @param array $types An array of types configurations
* @param ContainerBuilder $container A ContainerBuilder instance
* @param $indexName
* @param $indexId
* @param array $typePrototypeConfig
* @param array $types
* @param ContainerBuilder $container
* @param array $indexConfig
* @param array $indexableCallbacks
*/
protected function loadTypes(array $types, ContainerBuilder $container, $indexName, $indexId, array $typePrototypeConfig)
private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
{
foreach ($types as $name => $type) {
$type = self::deepArrayUnion($typePrototypeConfig, $type);
$typeId = sprintf('%s.%s', $indexId, $name);
$typeDefArgs = array($name);
$typeDef = new Definition('%fos_elastica.type.class%', $typeDefArgs);
$typeDef->setFactoryService($indexId);
$typeDef->setFactoryMethod('getType');
$indexName = $indexConfig['name'];
$typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
$typeDef = new DefinitionDecorator('fos_elastica.type_prototype');
$typeDef->replaceArgument(0, $name);
$typeDef->setFactoryService($indexConfig['reference']);
$container->setDefinition($typeId, $typeDef);
if (isset($type['_source'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_source'] = $type['_source'];
}
if (isset($type['_boost'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_boost'] = $type['_boost'];
}
if (isset($type['_routing'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_routing'] = $type['_routing'];
}
if (isset($type['mappings'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['properties'] = $type['mappings'];
$typeName = sprintf('%s/%s', $indexName, $name);
$this->typeFields[$typeName] = $type['mappings'];
}
if (isset($type['persistence'])) {
$this->loadTypePersistenceIntegration($type['persistence'], $container, $typeDef, $indexName, $name);
}
if (isset($type['index_analyzer'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index_analyzer'] = $type['index_analyzer'];
}
if (isset($type['search_analyzer'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['search_analyzer'] = $type['search_analyzer'];
}
if (isset($type['index'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index'] = $type['index'];
}
if (!empty($type['dynamic_templates'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'] = array();
foreach ($type['dynamic_templates'] as $templateName => $templateData) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'][] = array($templateName => $templateData);
$typeConfig = array(
'name' => $name,
'mapping' => array(), // An array containing anything that gets sent directly to ElasticSearch
'config' => array(),
);
foreach (array(
'dynamic_templates',
'properties',
'_all',
'_boost',
'_id',
'_parent',
'_routing',
'_source',
'_timestamp',
'_ttl',
) as $field) {
if (isset($type[$field])) {
$typeConfig['mapping'][$field] = $type[$field];
}
}
}
}
/**
* Merges two arrays without reindexing numeric keys.
*
* @param array $array1 An array to merge
* @param array $array2 An array to merge
*
* @return array The merged array
*/
static protected function deepArrayUnion($array1, $array2)
{
foreach ($array2 as $key => $value) {
if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) {
$array1[$key] = self::deepArrayUnion($array1[$key], $value);
} else {
$array1[$key] = $value;
foreach (array(
'persistence',
'serializer',
'index_analyzer',
'search_analyzer',
'date_detection',
'dynamic_date_formats',
'numeric_detection',
) as $field) {
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
$type[$field] :
null;
}
$this->indexConfigs[$indexName]['types'][$name] = $typeConfig;
if (isset($type['persistence'])) {
$this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
$typeConfig['persistence'] = $type['persistence'];
}
if (isset($type['indexable_callback'])) {
$indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $type['indexable_callback'];
}
if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
$typeSerializerId = sprintf('%s.serializer.callback', $typeId);
$typeSerializerDef = new DefinitionDecorator('fos_elastica.serializer_callback_prototype');
if (isset($type['serializer']['groups'])) {
$typeSerializerDef->addMethodCall('setGroups', array($type['serializer']['groups']));
}
if (isset($type['serializer']['version'])) {
$typeSerializerDef->addMethodCall('setVersion', array($type['serializer']['version']));
}
$typeDef->addMethodCall('setSerializer', array(array(new Reference($typeSerializerId), 'serialize')));
$container->setDefinition($typeSerializerId, $typeSerializerDef);
}
}
return $array1;
}
/**
* Loads the optional provider and finder for a type
* Loads the optional provider and finder for a type.
*
* @param array $typeConfig
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param \Symfony\Component\DependencyInjection\Definition $typeDef
* @param $indexName
* @param $typeName
* @param array $typeConfig
* @param ContainerBuilder $container
* @param Reference $typeRef
* @param string $indexName
* @param string $typeName
*/
protected function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Definition $typeDef, $indexName, $typeName)
private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
{
$this->loadDriver($container, $typeConfig['driver']);
$elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
$modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
$objectPersisterId = $this->loadObjectPersister($typeConfig, $typeDef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
$objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
if (isset($typeConfig['provider'])) {
$this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName);
$this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
}
if (isset($typeConfig['finder'])) {
$this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeDef, $indexName, $typeName);
$this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
}
if (isset($typeConfig['listener'])) {
$this->loadTypeListener($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName);
$this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
}
}
protected function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
/**
* Creates and loads an ElasticaToModelTransformer.
*
* @param array $typeConfig
* @param ContainerBuilder $container
* @param string $indexName
* @param string $typeName
*
* @return string
*/
private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
{
if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
return $typeConfig['elastica_to_model_transformer']['service'];
}
/* Note: transformer services may conflict with "prototype.driver", if
* the index and type names were "prototype" and a driver, respectively.
*/
@ -280,71 +340,141 @@ class FOSElasticaExtension extends Extension
$argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
$serviceDef->replaceArgument($argPos, $typeConfig['model']);
$serviceDef->replaceArgument($argPos + 1, array(
'hydrate' => $typeConfig['elastica_to_model_transformer']['hydrate'],
'identifier' => $typeConfig['identifier'],
'ignore_missing' => $typeConfig['elastica_to_model_transformer']['ignore_missing'],
'query_builder_method' => $typeConfig['elastica_to_model_transformer']['query_builder_method']
));
$serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], array(
'identifier' => $typeConfig['identifier'],
)));
$container->setDefinition($serviceId, $serviceDef);
return $serviceId;
}
protected function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
/**
* Creates and loads a ModelToElasticaTransformer for an index/type.
*
* @param array $typeConfig
* @param ContainerBuilder $container
* @param string $indexName
* @param string $typeName
*
* @return string
*/
private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
{
if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
return $typeConfig['model_to_elastica_transformer']['service'];
}
$abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
'fos_elastica.model_to_elastica_identifier_transformer' :
'fos_elastica.model_to_elastica_transformer';
$serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
$serviceDef = new DefinitionDecorator('fos_elastica.model_to_elastica_transformer');
$serviceDef = new DefinitionDecorator($abstractId);
$serviceDef->replaceArgument(0, array(
'identifier' => $typeConfig['identifier']
'identifier' => $typeConfig['identifier'],
));
$container->setDefinition($serviceId, $serviceDef);
return $serviceId;
}
protected function loadObjectPersister(array $typeConfig, Definition $typeDef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
/**
* Creates and loads an object persister for a type.
*
* @param array $typeConfig
* @param Reference $typeRef
* @param ContainerBuilder $container
* @param string $indexName
* @param string $typeName
* @param string $transformerId
*
* @return string
*/
private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
{
$arguments = array(
$typeRef,
new Reference($transformerId),
$typeConfig['model'],
);
if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
$abstractId = 'fos_elastica.object_serializer_persister';
$callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
$arguments[] = array(new Reference($callbackId), 'serialize');
} else {
$abstractId = 'fos_elastica.object_persister';
$mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
$argument = $mapping['properties'];
if (isset($mapping['_parent'])) {
$argument['_parent'] = $mapping['_parent'];
}
$arguments[] = $argument;
}
$serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
$serviceDef = new DefinitionDecorator('fos_elastica.object_persister');
$serviceDef->replaceArgument(0, $typeDef);
$serviceDef->replaceArgument(1, new Reference($transformerId));
$serviceDef->replaceArgument(2, $typeConfig['model']);
$serviceDef->replaceArgument(3, $this->typeFields[sprintf('%s/%s', $indexName, $typeName)]);
$serviceDef = new DefinitionDecorator($abstractId);
foreach ($arguments as $i => $argument) {
$serviceDef->replaceArgument($i, $argument);
}
$container->setDefinition($serviceId, $serviceDef);
return $serviceId;
}
protected function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $typeDef, $indexName, $typeName)
/**
* Loads a provider for a type.
*
* @param array $typeConfig
* @param ContainerBuilder $container
* @param string $objectPersisterId
* @param string $indexName
* @param string $typeName
*
* @return string
*/
private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
{
if (isset($typeConfig['provider']['service'])) {
return $typeConfig['provider']['service'];
}
/* Note: provider services may conflict with "prototype.driver", if the
* index and type names were "prototype" and a driver, respectively.
*/
$providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.' . $typeConfig['driver']);
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.'.$typeConfig['driver']);
$providerDef->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
$providerDef->replaceArgument(1, $typeConfig['model']);
$providerDef->replaceArgument(2, $typeConfig['model']);
// Propel provider can simply ignore Doctrine-specific options
$providerDef->replaceArgument(2, array_diff_key($typeConfig['provider'], array('service' => 1)));
$providerDef->replaceArgument(3, array_merge(array_diff_key($typeConfig['provider'], array('service' => 1)), array(
'indexName' => $indexName,
'typeName' => $typeName,
)));
$container->setDefinition($providerId, $providerDef);
return $providerId;
}
protected function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $typeDef, $indexName, $typeName)
/**
* Loads doctrine listeners to handle indexing of new or updated objects.
*
* @param array $typeConfig
* @param ContainerBuilder $container
* @param string $objectPersisterId
* @param string $indexName
* @param string $typeName
*
* @return string
*/
private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
{
if (isset($typeConfig['listener']['service'])) {
return $typeConfig['listener']['service'];
}
/* Note: listener services may conflict with "prototype.driver", if the
* index and type names were "prototype" and a driver, respectively.
*/
@ -352,37 +482,59 @@ class FOSElasticaExtension extends Extension
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
$listenerDef = new DefinitionDecorator($abstractListenerId);
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
$listenerDef->replaceArgument(1, $typeConfig['model']);
$listenerDef->replaceArgument(3, $typeConfig['identifier']);
$listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig));
$listenerDef->replaceArgument(2, array(
'identifier' => $typeConfig['identifier'],
'indexName' => $indexName,
'typeName' => $typeName,
));
$listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
new Reference($typeConfig['listener']['logger']) :
null
);
$tagName = null;
switch ($typeConfig['driver']) {
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
case 'orm':
$tagName = 'doctrine.event_listener';
break;
case 'mongodb':
$tagName = 'doctrine_mongodb.odm.event_listener';
break;
}
if (isset($typeConfig['listener']['is_indexable_callback'])) {
$callback = $typeConfig['listener']['is_indexable_callback'];
if (is_array($callback)) {
list($class) = $callback + array(null);
if (is_string($class) && !class_exists($class)) {
$callback[0] = new Reference($class);
}
if (null !== $tagName) {
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
$listenerDef->addTag($tagName, array('event' => $event));
}
$listenerDef->addMethodCall('setIsIndexableCallback', array($callback));
}
$container->setDefinition($listenerId, $listenerDef);
return $listenerId;
}
/**
* Map Elastica to Doctrine events for the current driver.
*/
private function getDoctrineEvents(array $typeConfig)
{
switch ($typeConfig['driver']) {
case 'orm':
$eventsClass = '\Doctrine\ORM\Events';
break;
case 'mongodb':
$eventsClass = '\Doctrine\ODM\MongoDB\Events';
break;
default:
throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
}
$events = array();
$eventMapping = array(
'insert' => array('postPersist'),
'update' => array('postUpdate'),
'delete' => array('postRemove', 'preRemove')
'insert' => array(constant($eventsClass.'::postPersist')),
'update' => array(constant($eventsClass.'::postUpdate')),
'delete' => array(constant($eventsClass.'::preRemove')),
'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush')),
);
foreach ($eventMapping as $event => $doctrineEvents) {
@ -394,14 +546,26 @@ class FOSElasticaExtension extends Extension
return $events;
}
protected function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, $typeDef, $indexName, $typeName)
/**
* Loads a Type specific Finder.
*
* @param array $typeConfig
* @param ContainerBuilder $container
* @param string $elasticaToModelId
* @param Reference $typeRef
* @param string $indexName
* @param string $typeName
*
* @return string
*/
private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
{
if (isset($typeConfig['finder']['service'])) {
$finderId = $typeConfig['finder']['service'];
} else {
$finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
$finderDef = new DefinitionDecorator('fos_elastica.finder');
$finderDef->replaceArgument(0, $typeDef);
$finderDef->replaceArgument(0, $typeRef);
$finderDef->replaceArgument(1, new Reference($elasticaToModelId));
$container->setDefinition($finderId, $finderDef);
}
@ -418,41 +582,63 @@ class FOSElasticaExtension extends Extension
}
/**
* Loads the index manager
* Loads the index manager.
*
* @param array $indexRefsByName
* @param ContainerBuilder $container
**/
protected function loadIndexManager(array $indexRefsByName, ContainerBuilder $container)
private function loadIndexManager(ContainerBuilder $container)
{
$indexRefs = array_map(function ($index) {
return $index['reference'];
}, $this->indexConfigs);
$managerDef = $container->getDefinition('fos_elastica.index_manager');
$managerDef->replaceArgument(0, $indexRefsByName);
$managerDef->replaceArgument(1, new Reference('fos_elastica.index'));
$managerDef->replaceArgument(0, $indexRefs);
}
/**
* Loads the resetter
* Makes sure a specific driver has been loaded.
*
* @param array $indexConfigs
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param ContainerBuilder $container
* @param string $driver
*/
protected function loadResetter(array $indexConfigs, ContainerBuilder $container)
{
$resetterDef = $container->getDefinition('fos_elastica.resetter');
$resetterDef->replaceArgument(0, $indexConfigs);
}
protected function loadDriver(ContainerBuilder $container, $driver)
private function loadDriver(ContainerBuilder $container, $driver)
{
if (in_array($driver, $this->loadedDrivers)) {
return;
}
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load($driver.'.xml');
$this->loadedDrivers[] = $driver;
}
protected function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
/**
* Loads and configures the serializer prototype.
*
* @param array $config
* @param ContainerBuilder $container
*/
private function loadSerializer($config, ContainerBuilder $container)
{
$container->setAlias('fos_elastica.serializer', $config['serializer']);
$serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
$serializer->setClass($config['callback_class']);
$callbackClassImplementedInterfaces = class_implements($config['callback_class']);
if (isset($callbackClassImplementedInterfaces['Symfony\Component\DependencyInjection\ContainerAwareInterface'])) {
$serializer->addMethodCall('setContainer', array(new Reference('service_container')));
}
}
/**
* Creates a default manager alias for defined default manager or the first loaded driver.
*
* @param string $defaultManager
* @param ContainerBuilder $container
*/
private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
{
if (0 == count($this->loadedDrivers)) {
return;
@ -469,4 +655,21 @@ class FOSElasticaExtension extends Extension
$container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
}
/**
* Returns a reference to a client given its configured name.
*
* @param string $clientName
*
* @return Reference
*
* @throws \InvalidArgumentException
*/
private function getClient($clientName)
{
if (!array_key_exists($clientName, $this->clients)) {
throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
}
return $this->clients[$clientName]['reference'];
}
}

View file

@ -2,32 +2,34 @@
namespace FOS\ElasticaBundle\Doctrine;
use Doctrine\Common\Persistence\ManagerRegistry;
use FOS\ElasticaBundle\HybridResult;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* Maps Elastica documents with Doctrine objects
* This mapper assumes an exact match between
* elastica documents ids and doctrine object ids
* elastica documents ids and doctrine object ids.
*/
abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTransformerInterface
abstract class AbstractElasticaToModelTransformer extends BaseTransformer
{
/**
* Manager registry
* Manager registry.
*
* @var ManagerRegistry
*/
protected $registry = null;
/**
* Class of the model to map to the elastica documents
* Class of the model to map to the elastica documents.
*
* @var string
*/
protected $objectClass = null;
/**
* Optional parameters
* Optional parameters.
*
* @var array
*/
@ -39,20 +41,13 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
);
/**
* PropertyAccessor instance
* Instantiates a new Mapper.
*
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
/**
* Instantiates a new Mapper
*
* @param object $registry
* @param ManagerRegistry $registry
* @param string $objectClass
* @param array $options
* @param array $options
*/
public function __construct($registry, $objectClass, array $options = array())
public function __construct(ManagerRegistry $registry, $objectClass, array $options = array())
{
$this->registry = $registry;
$this->objectClass = $objectClass;
@ -69,22 +64,14 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
return $this->objectClass;
}
/**
* Set the PropertyAccessor
*
* @param PropertyAccessorInterface $propertyAccessor
*/
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
{
$this->propertyAccessor = $propertyAccessor;
}
/**
* Transforms an array of elastica objects into an array of
* model objects fetched from the doctrine repository
* model objects fetched from the doctrine repository.
*
* @param array $elasticaObjects of elastica objects
*
* @throws \RuntimeException
*
* @return array
**/
public function transform(array $elasticaObjects)
@ -109,29 +96,31 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
// sort objects in the order of ids
$idPos = array_flip($ids);
$identifier = $this->options['identifier'];
$propertyAccessor = $this->propertyAccessor;
usort($objects, function($a, $b) use ($idPos, $identifier, $propertyAccessor)
{
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
});
usort($objects, $this->getSortingClosure($idPos, $identifier));
return $objects;
}
public function hybridTransform(array $elasticaObjects)
{
$indexedElasticaResults = array();
foreach ($elasticaObjects as $elasticaObject) {
$indexedElasticaResults[$elasticaObject->getId()] = $elasticaObject;
}
$objects = $this->transform($elasticaObjects);
$result = array();
for ($i = 0; $i < count($elasticaObjects); $i++) {
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
foreach ($objects as $object) {
$id = $this->propertyAccessor->getValue($object, $this->options['identifier']);
$result[] = new HybridResult($indexedElasticaResults[$id], $object);
}
return $result;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getIdentifierField()
{
@ -139,11 +128,12 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
}
/**
* Fetches objects by theses identifier values
* Fetches objects by theses identifier values.
*
* @param array $identifierValues ids values
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
*
* @param array $identifierValues ids values
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
* @return array of objects or arrays
*/
protected abstract function findByIdentifiers(array $identifierValues, $hydrate);
abstract protected function findByIdentifiers(array $identifierValues, $hydrate);
}

View file

@ -1,158 +0,0 @@
<?php
namespace FOS\ElasticaBundle\Doctrine;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\ObjectManager;
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
use FOS\ElasticaBundle\Persister\ObjectPersister;
abstract class AbstractListener implements EventSubscriber
{
/**
* Object persister
*
* @var ObjectPersister
*/
protected $objectPersister;
/**
* Class of the domain model
*
* @var string
*/
protected $objectClass;
/**
* List of subscribed events
*
* @var array
*/
protected $events;
/**
* Name of domain model field used as the ES identifier
*
* @var string
*/
protected $esIdentifierField;
/**
* Callback for determining if an object should be indexed
*
* @var mixed
*/
protected $isIndexableCallback;
/**
* Objects scheduled for removal
*
* @var array
*/
private $scheduledForRemoval = array();
/**
* Constructor.
*
* @param ObjectPersisterInterface $objectPersister
* @param string $objectClass
* @param array $events
* @param string $esIdentifierField
*/
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id')
{
$this->objectPersister = $objectPersister;
$this->objectClass = $objectClass;
$this->events = $events;
$this->esIdentifierField = $esIdentifierField;
}
/**
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
*/
public function getSubscribedEvents()
{
return $this->events;
}
/**
* Set the callback for determining object index eligibility.
*
* If callback is a string, it must be public method on the object class
* that expects no arguments and returns a boolean. Otherwise, the callback
* should expect the object for consideration as its only argument and
* return a boolean.
*
* @param callback $callback
* @throws \RuntimeException if the callback is not callable
*/
public function setIsIndexableCallback($callback)
{
if (is_string($callback)) {
if (!is_callable(array($this->objectClass, $callback))) {
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $this->objectClass, $callback));
}
} elseif (!is_callable($callback)) {
if (is_array($callback)) {
list($class, $method) = $callback + array(null, null);
if (is_object($class)) {
$class = get_class($class);
}
if ($class && $method) {
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $class, $method));
}
}
throw new \RuntimeException('Indexable callback is not callable.');
}
$this->isIndexableCallback = $callback;
}
/**
* Return whether the object is indexable with respect to the callback.
*
* @param object $object
* @return boolean
*/
protected function isObjectIndexable($object)
{
if (!$this->isIndexableCallback) {
return true;
}
return is_string($this->isIndexableCallback)
? call_user_func(array($object, $this->isIndexableCallback))
: call_user_func($this->isIndexableCallback, $object);
}
/**
* Schedules the object for removal.
*
* This is usually called during the pre-remove event.
*
* @param object $object
* @param ObjectManager $objectManager
*/
protected function scheduleForRemoval($object, ObjectManager $objectManager)
{
$metadata = $objectManager->getClassMetadata($this->objectClass);
$esId = $metadata->getFieldValue($object, $this->esIdentifierField);
$this->scheduledForRemoval[spl_object_hash($object)] = $esId;
}
/**
* Removes the object if it was scheduled for removal.
*
* This is usually called during the post-remove event.
*
* @param object $object
*/
protected function removeIfScheduled($object)
{
$objectHash = spl_object_hash($object);
if (isset($this->scheduledForRemoval[$objectHash])) {
$this->objectPersister->deleteById($this->scheduledForRemoval[$objectHash]);
unset($this->scheduledForRemoval[$objectHash]);
}
}
}

View file

@ -3,73 +3,65 @@
namespace FOS\ElasticaBundle\Doctrine;
use Doctrine\Common\Persistence\ManagerRegistry;
use Elastica\Exception\Bulk\ResponseException as BulkResponseException;
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
use FOS\ElasticaBundle\Provider\IndexableInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
abstract class AbstractProvider extends BaseAbstractProvider
{
/**
* @var SliceFetcherInterface
*/
private $sliceFetcher;
/**
* @var ManagerRegistry
*/
protected $managerRegistry;
/**
* Constructor.
*
* @param ObjectPersisterInterface $objectPersister
* @param IndexableInterface $indexable
* @param string $objectClass
* @param array $options
* @param array $baseOptions
* @param ManagerRegistry $managerRegistry
* @param SliceFetcherInterface $sliceFetcher
*/
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options, $managerRegistry)
{
parent::__construct($objectPersister, $objectClass, array_merge(array(
'clear_object_manager' => true,
'query_builder_method' => 'createQueryBuilder',
), $options));
public function __construct(
ObjectPersisterInterface $objectPersister,
IndexableInterface $indexable,
$objectClass,
array $baseOptions,
ManagerRegistry $managerRegistry,
SliceFetcherInterface $sliceFetcher = null
) {
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
$this->managerRegistry = $managerRegistry;
}
/**
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
$queryBuilder = $this->createQueryBuilder();
$nbObjects = $this->countObjects($queryBuilder);
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
for (; $offset < $nbObjects; $offset += $batchSize) {
if ($loggerClosure) {
$stepStartTime = microtime(true);
}
$objects = $this->fetchSlice($queryBuilder, $batchSize, $offset);
$this->objectPersister->insertMany($objects);
if ($this->options['clear_object_manager']) {
$this->managerRegistry->getManagerForClass($this->objectClass)->clear();
}
usleep($sleep);
if ($loggerClosure) {
$stepNbObjects = count($objects);
$stepCount = $stepNbObjects + $offset;
$percentComplete = 100 * $stepCount / $nbObjects;
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond));
}
}
$this->sliceFetcher = $sliceFetcher;
}
/**
* Counts objects that would be indexed using the query builder.
*
* @param object $queryBuilder
*
* @return integer
*/
protected abstract function countObjects($queryBuilder);
abstract protected function countObjects($queryBuilder);
/**
* Creates the query builder, which will be used to fetch objects to index.
*
* @param string $method
*
* @return object
*/
abstract protected function createQueryBuilder($method);
/**
* Fetches a slice of objects using the query builder.
@ -77,14 +69,102 @@ abstract class AbstractProvider extends BaseAbstractProvider
* @param object $queryBuilder
* @param integer $limit
* @param integer $offset
*
* @return array
*/
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
abstract protected function fetchSlice($queryBuilder, $limit, $offset);
/**
* Creates the query builder, which will be used to fetch objects to index.
*
* @return object
* {@inheritDoc}
*/
protected abstract function createQueryBuilder();
protected function doPopulate($options, \Closure $loggerClosure = null)
{
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
$queryBuilder = $this->createQueryBuilder($options['query_builder_method']);
$nbObjects = $this->countObjects($queryBuilder);
$offset = $options['offset'];
$objects = array();
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
try {
$objects = $this->getSlice($queryBuilder, $options['batch_size'], $offset, $objects);
$objects = $this->filterObjects($options, $objects);
if (!empty($objects)) {
$this->objectPersister->insertMany($objects);
}
} catch (BulkResponseException $e) {
if (!$options['ignore_errors']) {
throw $e;
}
if (null !== $loggerClosure) {
$loggerClosure(
$options['batch_size'],
$nbObjects,
sprintf('<error>%s</error>', $e->getMessage())
);
}
}
if ($options['clear_object_manager']) {
$manager->clear();
}
usleep($options['sleep']);
if (null !== $loggerClosure) {
$loggerClosure($options['batch_size'], $nbObjects);
}
}
}
/**
* {@inheritDoc}
*/
protected function configureOptions()
{
parent::configureOptions();
$this->resolver->setDefaults(array(
'clear_object_manager' => true,
'debug_logging' => false,
'ignore_errors' => false,
'offset' => 0,
'query_builder_method' => 'createQueryBuilder',
'sleep' => 0
));
}
/**
* If this Provider has a SliceFetcher defined, we use it instead of falling back to
* the fetchSlice methods defined in the ORM/MongoDB subclasses.
*
* @param $queryBuilder
* @param int $limit
* @param int $offset
* @param array $lastSlice
*
* @return array
*/
private function getSlice($queryBuilder, $limit, $offset, $lastSlice)
{
if (!$this->sliceFetcher) {
return $this->fetchSlice($queryBuilder, $limit, $offset);
}
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
$identifierFieldNames = $manager
->getClassMetadata($this->objectClass)
->getIdentifierFieldNames();
return $this->sliceFetcher->fetch(
$queryBuilder,
$limit,
$offset,
$lastSlice,
$identifierFieldNames
);
}
}

212
Doctrine/Listener.php Normal file
View file

@ -0,0 +1,212 @@
<?php
namespace FOS\ElasticaBundle\Doctrine;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use FOS\ElasticaBundle\Persister\ObjectPersister;
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
use FOS\ElasticaBundle\Provider\IndexableInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* Automatically update ElasticSearch based on changes to the Doctrine source
* data. One listener is generated for each Doctrine entity / ElasticSearch type.
*/
class Listener
{
/**
* Object persister.
*
* @var ObjectPersisterInterface
*/
protected $objectPersister;
/**
* Configuration for the listener.
*
* @var array
*/
private $config;
/**
* Objects scheduled for insertion.
*
* @var array
*/
public $scheduledForInsertion = array();
/**
* Objects scheduled to be updated or removed.
*
* @var array
*/
public $scheduledForUpdate = array();
/**
* IDs of objects scheduled for removal.
*
* @var array
*/
public $scheduledForDeletion = array();
/**
* PropertyAccessor instance.
*
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
/**
* @var IndexableInterface
*/
private $indexable;
/**
* Constructor.
*
* @param ObjectPersisterInterface $objectPersister
* @param IndexableInterface $indexable
* @param array $config
* @param LoggerInterface $logger
*/
public function __construct(
ObjectPersisterInterface $objectPersister,
IndexableInterface $indexable,
array $config = array(),
LoggerInterface $logger = null
) {
$this->config = array_merge(array(
'identifier' => 'id',
), $config);
$this->indexable = $indexable;
$this->objectPersister = $objectPersister;
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
if ($logger && $this->objectPersister instanceof ObjectPersister) {
$this->objectPersister->setLogger($logger);
}
}
/**
* Looks for new objects that should be indexed.
*
* @param LifecycleEventArgs $eventArgs
*/
public function postPersist(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getObject();
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
$this->scheduledForInsertion[] = $entity;
}
}
/**
* Looks for objects being updated that should be indexed or removed from the index.
*
* @param LifecycleEventArgs $eventArgs
*/
public function postUpdate(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getObject();
if ($this->objectPersister->handlesObject($entity)) {
if ($this->isObjectIndexable($entity)) {
$this->scheduledForUpdate[] = $entity;
} else {
// Delete if no longer indexable
$this->scheduleForDeletion($entity);
}
}
}
/**
* Delete objects preRemove instead of postRemove so that we have access to the id. Because this is called
* preRemove, first check that the entity is managed by Doctrine.
*
* @param LifecycleEventArgs $eventArgs
*/
public function preRemove(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getObject();
if ($this->objectPersister->handlesObject($entity)) {
$this->scheduleForDeletion($entity);
}
}
/**
* Persist scheduled objects to ElasticSearch
* After persisting, clear the scheduled queue to prevent multiple data updates when using multiple flush calls.
*/
private function persistScheduled()
{
if (count($this->scheduledForInsertion)) {
$this->objectPersister->insertMany($this->scheduledForInsertion);
$this->scheduledForInsertion = array();
}
if (count($this->scheduledForUpdate)) {
$this->objectPersister->replaceMany($this->scheduledForUpdate);
$this->scheduledForUpdate = array();
}
if (count($this->scheduledForDeletion)) {
$this->objectPersister->deleteManyByIdentifiers($this->scheduledForDeletion);
$this->scheduledForDeletion = array();
}
}
/**
* Iterate through scheduled actions before flushing to emulate 2.x behavior.
* Note that the ElasticSearch index will fall out of sync with the source
* data in the event of a crash during flush.
*
* This method is only called in legacy configurations of the listener.
*
* @deprecated This method should only be called in applications that depend
* on the behaviour that entities are indexed regardless of if a
* flush is successful.
*/
public function preFlush()
{
$this->persistScheduled();
}
/**
* Iterating through scheduled actions *after* flushing ensures that the
* ElasticSearch index will be affected only if the query is successful.
*/
public function postFlush()
{
$this->persistScheduled();
}
/**
* Record the specified identifier to delete. Do not need to entire object.
*
* @param object $object
*/
private function scheduleForDeletion($object)
{
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
$this->scheduledForDeletion[] = $identifierValue;
}
}
/**
* Checks if the object is indexable or not.
*
* @param object $object
*
* @return bool
*/
private function isObjectIndexable($object)
{
return $this->indexable->isObjectIndexable(
$this->config['indexName'],
$this->config['typeName'],
$object
);
}
}

View file

@ -7,21 +7,23 @@ use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
/**
* Maps Elastica documents with Doctrine objects
* This mapper assumes an exact match between
* elastica documents ids and doctrine object ids
* elastica documents ids and doctrine object ids.
*/
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
{
/**
* Fetch objects for theses identifier values
* Fetch objects for theses identifier values.
*
* @param array $identifierValues ids values
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
*
* @param array $identifierValues ids values
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
* @return array of objects or arrays
*/
protected function findByIdentifiers(array $identifierValues, $hydrate)
{
return $this->registry
->getManagerForClass($this->objectClass)
->getRepository($this->objectClass)
->{$this->options['query_builder_method']}($this->objectClass)
->field($this->options['identifier'])->in($identifierValues)
->hydrate($hydrate)

View file

@ -1,50 +0,0 @@
<?php
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use FOS\ElasticaBundle\Doctrine\AbstractListener;
class Listener extends AbstractListener
{
public function postPersist(LifecycleEventArgs $eventArgs)
{
$document = $eventArgs->getDocument();
if ($document instanceof $this->objectClass && $this->isObjectIndexable($document)) {
$this->objectPersister->insertOne($document);
}
}
public function postUpdate(LifecycleEventArgs $eventArgs)
{
$document = $eventArgs->getDocument();
if ($document instanceof $this->objectClass) {
if ($this->isObjectIndexable($document)) {
$this->objectPersister->replaceOne($document);
} else {
$this->scheduleForRemoval($document, $eventArgs->getDocumentManager());
$this->removeIfScheduled($document);
}
}
}
public function preRemove(LifecycleEventArgs $eventArgs)
{
$document = $eventArgs->getDocument();
if ($document instanceof $this->objectClass) {
$this->scheduleForRemoval($document, $eventArgs->getDocumentManager());
}
}
public function postRemove(LifecycleEventArgs $eventArgs)
{
$document = $eventArgs->getDocument();
if ($document instanceof $this->objectClass) {
$this->removeIfScheduled($document);
}
}
}

View file

@ -9,7 +9,42 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
class Provider extends AbstractProvider
{
/**
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
* Disables logging and returns the logger that was previously set.
*
* @return mixed
*/
protected function disableLogging()
{
$configuration = $this->managerRegistry
->getManagerForClass($this->objectClass)
->getConnection()
->getConfiguration();
$logger = $configuration->getLoggerCallable();
$configuration->setLoggerCallable(null);
return $logger;
}
/**
* Reenables the logger with the previously returned logger from disableLogging();.
*
* @param mixed $logger
*
* @return mixed
*/
protected function enableLogging($logger)
{
$configuration = $this->managerRegistry
->getManagerForClass($this->objectClass)
->getConnection()
->getConfiguration();
$configuration->setLoggerCallable($logger);
}
/**
* {@inheritDoc}
*/
protected function countObjects($queryBuilder)
{
@ -23,7 +58,7 @@ class Provider extends AbstractProvider
}
/**
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
* {@inheritDoc}
*/
protected function fetchSlice($queryBuilder, $limit, $offset)
{
@ -32,21 +67,21 @@ class Provider extends AbstractProvider
}
return $queryBuilder
->limit($limit)
->skip($offset)
->limit($limit)
->getQuery()
->execute()
->toArray();
}
/**
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
* {@inheritDoc}
*/
protected function createQueryBuilder()
protected function createQueryBuilder($method)
{
return $this->managerRegistry
->getManagerForClass($this->objectClass)
->getRepository($this->objectClass)
->{$this->options['query_builder_method']}();
->{$method}();
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
use Doctrine\ODM\MongoDB\Query\Builder;
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
use FOS\ElasticaBundle\Doctrine\SliceFetcherInterface;
/**
* Fetches a slice of objects.
*
* @author Thomas Prelot <tprelot@gmail.com>
*/
class SliceFetcher implements SliceFetcherInterface
{
/**
* {@inheritdoc}
*/
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames)
{
if (!$queryBuilder instanceof Builder) {
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
}
$lastObject = array_pop($previousSlice);
if ($lastObject) {
$queryBuilder
->field('_id')->gt($lastObject->getId())
->skip(0)
;
} else {
$queryBuilder->skip($offset);
}
return $queryBuilder
->limit($limit)
->sort(array('_id' => 'asc'))
->getQuery()
->execute()
->toArray()
;
}
}

View file

@ -8,17 +8,18 @@ use Doctrine\ORM\Query;
/**
* Maps Elastica documents with Doctrine objects
* This mapper assumes an exact match between
* elastica documents ids and doctrine object ids
* elastica documents ids and doctrine object ids.
*/
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
{
const ENTITY_ALIAS = 'o';
/**
* Fetch objects for theses identifier values
* Fetch objects for theses identifier values.
*
* @param array $identifierValues ids values
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
*
* @param array $identifierValues ids values
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
* @return array of objects or arrays
*/
protected function findByIdentifiers(array $identifierValues, $hydrate)
@ -29,14 +30,14 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
$hydrationMode = $hydrate ? Query::HYDRATE_OBJECT : Query::HYDRATE_ARRAY;
$qb = $this->getEntityQueryBuilder();
$qb->where($qb->expr()->in(static::ENTITY_ALIAS.'.'.$this->options['identifier'], ':values'))
$qb->andWhere($qb->expr()->in(static::ENTITY_ALIAS.'.'.$this->options['identifier'], ':values'))
->setParameter('values', $identifierValues);
return $qb->getQuery()->setHydrationMode($hydrationMode)->execute();
}
/**
* Retrieves a query builder to be used for querying by identifiers
* Retrieves a query builder to be used for querying by identifiers.
*
* @return \Doctrine\ORM\QueryBuilder
*/

View file

@ -1,50 +0,0 @@
<?php
namespace FOS\ElasticaBundle\Doctrine\ORM;
use Doctrine\ORM\Event\LifecycleEventArgs;
use FOS\ElasticaBundle\Doctrine\AbstractListener;
class Listener extends AbstractListener
{
public function postPersist(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getEntity();
if ($entity instanceof $this->objectClass && $this->isObjectIndexable($entity)) {
$this->objectPersister->insertOne($entity);
}
}
public function postUpdate(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getEntity();
if ($entity instanceof $this->objectClass) {
if ($this->isObjectIndexable($entity)) {
$this->objectPersister->replaceOne($entity);
} else {
$this->scheduleForRemoval($entity, $eventArgs->getEntityManager());
$this->removeIfScheduled($entity);
}
}
}
public function preRemove(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getEntity();
if ($entity instanceof $this->objectClass) {
$this->scheduleForRemoval($entity, $eventArgs->getEntityManager());
}
}
public function postRemove(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getEntity();
if ($entity instanceof $this->objectClass) {
$this->removeIfScheduled($entity);
}
}
}

View file

@ -8,8 +8,45 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
class Provider extends AbstractProvider
{
const ENTITY_ALIAS = 'a';
/**
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
* Disables logging and returns the logger that was previously set.
*
* @return mixed
*/
protected function disableLogging()
{
$configuration = $this->managerRegistry
->getManagerForClass($this->objectClass)
->getConnection()
->getConfiguration();
$logger = $configuration->getSQLLogger();
$configuration->setSQLLogger(null);
return $logger;
}
/**
* Reenables the logger with the previously returned logger from disableLogging();.
*
* @param mixed $logger
*
* @return mixed
*/
protected function enableLogging($logger)
{
$configuration = $this->managerRegistry
->getManagerForClass($this->objectClass)
->getConnection()
->getConfiguration();
$configuration->setSQLLogger($logger);
}
/**
* {@inheritDoc}
*/
protected function countObjects($queryBuilder)
{
@ -32,7 +69,9 @@ class Provider extends AbstractProvider
}
/**
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
* This method should remain in sync with SliceFetcher::fetch until it is deprecated and removed.
*
* {@inheritDoc}
*/
protected function fetchSlice($queryBuilder, $limit, $offset)
{
@ -40,6 +79,24 @@ class Provider extends AbstractProvider
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
}
/*
* An orderBy DQL part is required to avoid fetching the same row twice.
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
* @see http://www.sqlite.org/lang_select.html#orderby
*/
$orderBy = $queryBuilder->getDQLPart('orderBy');
if (empty($orderBy)) {
$rootAliases = $queryBuilder->getRootAliases();
$identifierFieldNames = $this->managerRegistry
->getManagerForClass($this->objectClass)
->getClassMetadata($this->objectClass)
->getIdentifierFieldNames();
foreach ($identifierFieldNames as $fieldName) {
$queryBuilder->addOrderBy($rootAliases[0].'.'.$fieldName);
}
}
return $queryBuilder
->setFirstResult($offset)
->setMaxResults($limit)
@ -48,14 +105,14 @@ class Provider extends AbstractProvider
}
/**
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
* {@inheritDoc}
*/
protected function createQueryBuilder()
protected function createQueryBuilder($method)
{
return $this->managerRegistry
->getManagerForClass($this->objectClass)
->getRepository($this->objectClass)
// ORM query builders require an alias argument
->{$this->options['query_builder_method']}('a');
->{$method}(static::ENTITY_ALIAS);
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace FOS\ElasticaBundle\Doctrine\ORM;
use Doctrine\ORM\QueryBuilder;
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
use FOS\ElasticaBundle\Doctrine\SliceFetcherInterface;
/**
* Fetches a slice of objects.
*
* @author Thomas Prelot <tprelot@gmail.com>
*/
class SliceFetcher implements SliceFetcherInterface
{
/**
* This method should remain in sync with Provider::fetchSlice until that method is deprecated and
* removed.
*
* {@inheritdoc}
*/
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames)
{
if (!$queryBuilder instanceof QueryBuilder) {
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
}
/*
* An orderBy DQL part is required to avoid feching the same row twice.
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
* @see http://www.sqlite.org/lang_select.html#orderby
*/
$orderBy = $queryBuilder->getDQLPart('orderBy');
if (empty($orderBy)) {
$rootAliases = $queryBuilder->getRootAliases();
foreach ($identifierFieldNames as $fieldName) {
$queryBuilder->addOrderBy($rootAliases[0].'.'.$fieldName);
}
}
return $queryBuilder
->setFirstResult($offset)
->setMaxResults($limit)
->getQuery()
->getResult()
;
}
}

View file

@ -25,20 +25,19 @@ class RepositoryManager extends BaseManager
}
/**
* Return repository for entity
* Return repository for entity.
*
* Returns custom repository if one specified otherwise
* returns a basic respository.
* returns a basic repository.
*/
public function getRepository($entityName)
{
$realEntityName = $entityName;
if (strpos($entityName, ':') !== false) {
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias).'\\'.$simpleClassName;
}
return parent::getRepository($realEntityName);
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace FOS\ElasticaBundle\Doctrine;
/**
* Fetches a slice of objects.
*
* @author Thomas Prelot <tprelot@gmail.com>
*/
interface SliceFetcherInterface
{
/**
* Fetches a slice of objects using the query builder.
*
* @param object $queryBuilder
* @param integer $limit
* @param integer $offset
* @param array $previousSlice
* @param array $identifierFieldNames
*
* @return array
*/
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames);
}

12
DynamicIndex.php Normal file
View file

@ -0,0 +1,12 @@
<?php
namespace FOS\ElasticaBundle;
use FOS\ElasticaBundle\Elastica\Index;
/**
* @deprecated Use \FOS\ElasticaBundle\Elastica\TransformingIndex
*/
class DynamicIndex extends Index
{
}

104
Elastica/Client.php Normal file
View file

@ -0,0 +1,104 @@
<?php
namespace FOS\ElasticaBundle\Elastica;
use Elastica\Client as BaseClient;
use Elastica\Request;
use FOS\ElasticaBundle\Logger\ElasticaLogger;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* Extends the default Elastica client to provide logging for errors that occur
* during communication with ElasticSearch.
*
* @author Gordon Franke <info@nevalon.de>
*/
class Client extends BaseClient
{
/**
* Stores created indexes to avoid recreation.
*
* @var array
*/
private $indexCache = array();
/**
* Symfony's debugging Stopwatch.
*
* @var Stopwatch|null
*/
private $stopwatch;
/**
* @param string $path
* @param string $method
* @param array $data
* @param array $query
*
* @return \Elastica\Response
*/
public function request($path, $method = Request::GET, $data = array(), array $query = array())
{
if ($this->stopwatch) {
$this->stopwatch->start('es_request', 'fos_elastica');
}
$start = microtime(true);
$response = parent::request($path, $method, $data, $query);
$this->logQuery($path, $method, $data, $query, $start);
if ($this->stopwatch) {
$this->stopwatch->stop('es_request');
}
return $response;
}
public function getIndex($name)
{
if (isset($this->indexCache[$name])) {
return $this->indexCache[$name];
}
return $this->indexCache[$name] = new Index($this, $name);
}
/**
* Sets a stopwatch instance for debugging purposes.
*
* @param Stopwatch $stopwatch
*/
public function setStopwatch(Stopwatch $stopwatch = null)
{
$this->stopwatch = $stopwatch;
}
/**
* Log the query if we have an instance of ElasticaLogger.
*
* @param string $path
* @param string $method
* @param array $data
* @param array $query
* @param int $start
*/
private function logQuery($path, $method, $data, array $query, $start)
{
if (!$this->_logger or !$this->_logger instanceof ElasticaLogger) {
return;
}
$time = microtime(true) - $start;
$connection = $this->getLastRequest()->getConnection();
$connection_array = array(
'host' => $connection->getHost(),
'port' => $connection->getPort(),
'transport' => $connection->getTransport(),
'headers' => $connection->hasConfig('headers') ? $connection->getConfig('headers') : array(),
);
$this->_logger->logQuery($path, $method, $data, $time, $connection_array, $query);
}
}

59
Elastica/Index.php Normal file
View file

@ -0,0 +1,59 @@
<?php
namespace FOS\ElasticaBundle\Elastica;
use Elastica\Index as BaseIndex;
/**
* Overridden Elastica Index class that provides dynamic index name changes.
*
* @author Konstantin Tjuterev <kostik.lv@gmail.com>
*/
class Index extends BaseIndex
{
private $originalName;
/**
* Stores created types to avoid recreation.
*
* @var array
*/
private $typeCache = array();
/**
* Returns the original name of the index if the index has been renamed for reindexing
* or realiasing purposes.
*
* @return string
*/
public function getOriginalName()
{
return $this->originalName ?: $this->_name;
}
/**
* @param string $type
*/
public function getType($type)
{
if (isset($this->typeCache[$type])) {
return $this->typeCache[$type];
}
return $this->typeCache[$type] = parent::getType($type);
}
/**
* Reassign index name.
*
* While it's technically a regular setter for name property, it's specifically named overrideName, but not setName
* since it's used for a very specific case and normally should not be used
*
* @param string $name Index name
*/
public function overrideName($name)
{
$this->originalName = $this->_name;
$this->_name = $name;
}
}

38
Event/IndexEvent.php Normal file
View file

@ -0,0 +1,38 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Tim Nagel <tim@nagel.com.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Event;
use Symfony\Component\EventDispatcher\Event;
class IndexEvent extends Event
{
/**
* @var string
*/
private $index;
/**
* @param string $index
*/
public function __construct($index)
{
$this->index = $index;
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
}

View file

@ -0,0 +1,70 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Event;
/**
* Index Populate Event.
*
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
*/
class IndexPopulateEvent extends IndexEvent
{
const PRE_INDEX_POPULATE = 'elastica.index.index_pre_populate';
const POST_INDEX_POPULATE = 'elastica.index.index_post_populate';
/**
* @var bool
*/
private $reset;
/**
* @var array
*/
private $options;
/**
* @param string $index
* @param boolean $reset
* @param array $options
*/
public function __construct($index, $reset, $options)
{
parent::__construct($index);
$this->reset = $reset;
$this->options = $options;
}
/**
* @return boolean
*/
public function isReset()
{
return $this->reset;
}
/**
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* @param boolean $reset
*/
public function setReset($reset)
{
$this->reset = $reset;
}
}

70
Event/IndexResetEvent.php Normal file
View file

@ -0,0 +1,70 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Event;
/**
* Index ResetEvent.
*
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
*/
class IndexResetEvent extends IndexEvent
{
const PRE_INDEX_RESET = 'elastica.index.pre_reset';
const POST_INDEX_RESET = 'elastica.index.post_reset';
/**
* @var bool
*/
private $force;
/**
* @var bool
*/
private $populating;
/**
* @param string $index
* @param bool $populating
* @param bool $force
*/
public function __construct($index, $populating, $force)
{
parent::__construct($index);
$this->force = $force;
$this->populating = $populating;
}
/**
* @return boolean
*/
public function isForce()
{
return $this->force;
}
/**
* @return boolean
*/
public function isPopulating()
{
return $this->populating;
}
/**
* @param boolean $force
*/
public function setForce($force)
{
$this->force = $force;
}
}

78
Event/TransformEvent.php Normal file
View file

@ -0,0 +1,78 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Event;
use Symfony\Component\EventDispatcher\Event;
class TransformEvent extends Event
{
const POST_TRANSFORM = 'fos_elastica.post_transform';
/**
* @var mixed
*/
private $document;
/**
* @var array
*/
private $fields;
/**
* @var mixed
*/
private $object;
/**
* @param mixed $document
* @param array $fields
* @param mixed $object
*/
public function __construct($document, array $fields, $object)
{
$this->document = $document;
$this->fields = $fields;
$this->object = $object;
}
/**
* @return mixed
*/
public function getDocument()
{
return $this->document;
}
/**
* @return array
*/
public function getFields()
{
return $this->fields;
}
/**
* @return mixed
*/
public function getObject()
{
return $this->object;
}
/**
* @param mixed $document
*/
public function setDocument($document)
{
$this->document = $document;
}
}

View file

@ -0,0 +1,51 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Event;
use Symfony\Component\EventDispatcher\Event;
/**
* Type Populate Event.
*
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
*/
class TypePopulateEvent extends IndexPopulateEvent
{
const PRE_TYPE_POPULATE = 'elastica.index.type_pre_populate';
const POST_TYPE_POPULATE = 'elastica.index.type_post_populate';
/**
* @var string
*/
private $type;
/**
* @param string $index
* @param string $type
* @param bool $reset
* @param array $options
*/
public function __construct($index, $type, $reset, $options)
{
parent::__construct($index, $reset, $options);
$this->type = $type;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
}

49
Event/TypeResetEvent.php Normal file
View file

@ -0,0 +1,49 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Event;
use Symfony\Component\EventDispatcher\Event;
/**
* Type ResetEvent.
*
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
*/
class TypeResetEvent extends IndexEvent
{
const PRE_TYPE_RESET = 'elastica.index.type_pre_reset';
const POST_TYPE_RESET = 'elastica.index.type_post_reset';
/**
* @var string
*/
private $type;
/**
* @param string $index
* @param string $type
*/
public function __construct($index, $type)
{
parent::__construct($index);
$this->type = $type;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace FOS\ElasticaBundle\Exception;
class AliasIsIndexException extends \Exception
{
public function __construct($indexName)
{
parent::__construct(sprintf('Expected %s to be an alias but it is an index.', $indexName));
}
}

View file

@ -2,21 +2,28 @@
namespace FOS\ElasticaBundle;
use FOS\ElasticaBundle\DependencyInjection\Compiler\ConfigSourcePass;
use FOS\ElasticaBundle\DependencyInjection\Compiler\IndexPass;
use FOS\ElasticaBundle\DependencyInjection\Compiler\RegisterProvidersPass;
use FOS\ElasticaBundle\DependencyInjection\Compiler\TransformerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Bundle.
*/
class FOSElasticaBundle extends Bundle
{
/**
* @see Symfony\Component\HttpKernel\Bundle\Bundle::build()
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new ConfigSourcePass());
$container->addCompilerPass(new IndexPass());
$container->addCompilerPass(new RegisterProvidersPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new TransformerPass());
}

View file

@ -5,11 +5,13 @@ namespace FOS\ElasticaBundle\Finder;
interface FinderInterface
{
/**
* Searches for query results within a given limit
* Searches for query results within a given limit.
*
* @param mixed $query Can be a string, an array or an \Elastica\Query object
* @param int $limit How many results to get
* @param array $options
*
* @param mixed $query Can be a string, an array or an Elastica_Query object
* @param int $limit How many results to get
* @return array results
*/
function find($query, $limit = null);
public function find($query, $limit = null, $options = array());
}

View file

@ -4,23 +4,27 @@ namespace FOS\ElasticaBundle\Finder;
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
use Pagerfanta\Pagerfanta;
use Elastica_Query;
use Elastica\Query;
interface PaginatedFinderInterface extends FinderInterface
{
/**
* Searches for query results and returns them wrapped in a paginator
* Searches for query results and returns them wrapped in a paginator.
*
* @param mixed $query Can be a string, an array or an \Elastica\Query object
* @param array $options
*
* @param mixed $query Can be a string, an array or an Elastica_Query object
* @return Pagerfanta paginated results
*/
function findPaginated($query);
public function findPaginated($query, $options = array());
/**
* Creates a paginator adapter for this query
* Creates a paginator adapter for this query.
*
* @param mixed $query
* @param array $options
*
* @return PaginatorAdapterInterface
*/
function createPaginatorAdapter($query);
public function createPaginatorAdapter($query, $options = array());
}

View file

@ -2,46 +2,47 @@
namespace FOS\ElasticaBundle\Finder;
use FOS\ElasticaBundle\Finder\PaginatedFinderInterface;
use Elastica\Document;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
use FOS\ElasticaBundle\Paginator\TransformedPaginatorAdapter;
use FOS\ElasticaBundle\Paginator\FantaPaginatorAdapter;
use Pagerfanta\Pagerfanta;
use Elastica_Document;
use Elastica_Searchable;
use Elastica_Query;
use Elastica\SearchableInterface;
use Elastica\Query;
/**
* Finds elastica documents and map them to persisted objects
* Finds elastica documents and map them to persisted objects.
*/
class TransformedFinder implements PaginatedFinderInterface
{
protected $searchable;
protected $transformer;
public function __construct(Elastica_Searchable $searchable, ElasticaToModelTransformerInterface $transformer)
public function __construct(SearchableInterface $searchable, ElasticaToModelTransformerInterface $transformer)
{
$this->searchable = $searchable;
$this->transformer = $transformer;
}
/**
* Search for a query string
* Search for a query string.
*
* @param string $query
* @param string $query
* @param integer $limit
* @param array $options
*
* @return array of model objects
**/
public function find($query, $limit = null)
public function find($query, $limit = null, $options = array())
{
$results = $this->search($query, $limit);
$results = $this->search($query, $limit, $options);
return $this->transformer->transform($results);
}
public function findHybrid($query, $limit = null)
public function findHybrid($query, $limit = null, $options = array())
{
$results = $this->search($query, $limit);
$results = $this->search($query, $limit, $options);
return $this->transformer->hybridTransform($results);
}
@ -50,13 +51,14 @@ class TransformedFinder implements PaginatedFinderInterface
* Find documents similar to one with passed id.
*
* @param integer $id
* @param array $params
* @param array $query
* @param array $params
* @param array $query
*
* @return array of model objects
**/
public function moreLikeThis($id, $params = array(), $query = array())
{
$doc = new Elastica_Document($id);
$doc = new Document($id);
$results = $this->searchable->moreLikeThis($doc, $params, $query)->getResults();
return $this->transformer->transform($results);
@ -65,29 +67,33 @@ class TransformedFinder implements PaginatedFinderInterface
/**
* @param $query
* @param null|int $limit
* @param array $options
*
* @return array
*/
protected function search($query, $limit = null)
protected function search($query, $limit = null, $options = array())
{
$queryObject = Elastica_Query::create($query);
$queryObject = Query::create($query);
if (null !== $limit) {
$queryObject->setLimit($limit);
$queryObject->setSize($limit);
}
$results = $this->searchable->search($queryObject)->getResults();
$results = $this->searchable->search($queryObject, $options)->getResults();
return $results;
}
/**
* Gets a paginator wrapping the result of a search
* Gets a paginator wrapping the result of a search.
*
* @param string $query
* @param array $options
*
* @return Pagerfanta
*/
public function findPaginated($query)
public function findPaginated($query, $options = array())
{
$queryObject = Elastica_Query::create($query);
$paginatorAdapter = $this->createPaginatorAdapter($queryObject);
$queryObject = Query::create($query);
$paginatorAdapter = $this->createPaginatorAdapter($queryObject, $options);
return new Pagerfanta(new FantaPaginatorAdapter($paginatorAdapter));
}
@ -95,9 +101,10 @@ class TransformedFinder implements PaginatedFinderInterface
/**
* {@inheritdoc}
*/
public function createPaginatorAdapter($query)
public function createPaginatorAdapter($query, $options = array())
{
$query = Elastica_Query::create($query);
return new TransformedPaginatorAdapter($this->searchable, $query, $this->transformer);
$query = Query::create($query);
return new TransformedPaginatorAdapter($this->searchable, $query, $options, $this->transformer);
}
}

View file

@ -2,14 +2,14 @@
namespace FOS\ElasticaBundle;
use Elastica_Result;
use Elastica\Result;
class HybridResult
{
protected $result;
protected $transformed;
public function __construct(Elastica_Result $result, $transformed = null)
public function __construct(Result $result, $transformed = null)
{
$this->result = $result;
$this->transformed = $transformed;
@ -24,4 +24,4 @@ class HybridResult
{
return $this->result;
}
}
}

197
Index/AliasProcessor.php Normal file
View file

@ -0,0 +1,197 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Index;
use Elastica\Client;
use Elastica\Exception\ExceptionInterface;
use Elastica\Request;
use FOS\ElasticaBundle\Configuration\IndexConfig;
use FOS\ElasticaBundle\Elastica\Index;
use FOS\ElasticaBundle\Exception\AliasIsIndexException;
class AliasProcessor
{
/**
* Sets the randomised root name for an index.
*
* @param IndexConfig $indexConfig
* @param Index $index
*/
public function setRootName(IndexConfig $indexConfig, Index $index)
{
$index->overrideName(
sprintf('%s_%s',
$indexConfig->getElasticSearchName(),
date('Y-m-d-His')
)
);
}
/**
* Switches an index to become the new target for an alias. Only applies for
* indexes that are set to use aliases.
*
* $force will delete an index encountered where an alias is expected.
*
* @param IndexConfig $indexConfig
* @param Index $index
* @param bool $force
*
* @throws AliasIsIndexException
* @throws \RuntimeException
*/
public function switchIndexAlias(IndexConfig $indexConfig, Index $index, $force = false)
{
$client = $index->getClient();
$aliasName = $indexConfig->getElasticSearchName();
$oldIndexName = null;
$newIndexName = $index->getName();
try {
$oldIndexName = $this->getAliasedIndex($client, $aliasName);
} catch (AliasIsIndexException $e) {
if (!$force) {
throw $e;
}
$this->deleteIndex($client, $aliasName);
}
try {
$aliasUpdateRequest = $this->buildAliasUpdateRequest($oldIndexName, $aliasName, $newIndexName);
$client->request('_aliases', 'POST', $aliasUpdateRequest);
} catch (ExceptionInterface $e) {
$this->cleanupRenameFailure($client, $newIndexName, $e);
}
// Delete the old index after the alias has been switched
if (null !== $oldIndexName) {
$this->deleteIndex($client, $oldIndexName);
}
}
/**
* Builds an ElasticSearch request to rename or create an alias.
*
* @param string|null $aliasedIndex
* @param string $aliasName
* @param string $newIndexName
* @return array
*/
private function buildAliasUpdateRequest($aliasedIndex, $aliasName, $newIndexName)
{
$aliasUpdateRequest = array('actions' => array());
if (null !== $aliasedIndex) {
// if the alias is set - add an action to remove it
$aliasUpdateRequest['actions'][] = array(
'remove' => array('index' => $aliasedIndex, 'alias' => $aliasName),
);
}
// add an action to point the alias to the new index
$aliasUpdateRequest['actions'][] = array(
'add' => array('index' => $newIndexName, 'alias' => $aliasName),
);
return $aliasUpdateRequest;
}
/**
* Cleans up an index when we encounter a failure to rename the alias.
*
* @param Client $client
* @param string $indexName
* @param \Exception $renameAliasException
*/
private function cleanupRenameFailure(Client $client, $indexName, \Exception $renameAliasException)
{
$additionalError = '';
try {
$this->deleteIndex($client, $indexName);
} catch (ExceptionInterface $deleteNewIndexException) {
$additionalError = sprintf(
'Tried to delete newly built index %s, but also failed: %s',
$indexName,
$deleteNewIndexException->getMessage()
);
}
throw new \RuntimeException(sprintf(
'Failed to updated index alias: %s. %s',
$renameAliasException->getMessage(),
$additionalError ?: sprintf('Newly built index %s was deleted', $indexName)
), 0, $renameAliasException);
}
/**
* Delete an index.
*
* @param Client $client
* @param string $indexName Index name to delete
*/
private function deleteIndex(Client $client, $indexName)
{
try {
$path = sprintf("%s", $indexName);
$client->request($path, Request::DELETE);
} catch (ExceptionInterface $deleteOldIndexException) {
throw new \RuntimeException(sprintf(
'Failed to delete index %s with message: %s',
$indexName,
$deleteOldIndexException->getMessage()
), 0, $deleteOldIndexException);
}
}
/**
* Returns the name of a single index that an alias points to or throws
* an exception if there is more than one.
*
* @param Client $client
* @param string $aliasName Alias name
*
* @return string|null
*
* @throws AliasIsIndexException
*/
private function getAliasedIndex(Client $client, $aliasName)
{
$aliasesInfo = $client->request('_aliases', 'GET')->getData();
$aliasedIndexes = array();
foreach ($aliasesInfo as $indexName => $indexInfo) {
if ($indexName === $aliasName) {
throw new AliasIsIndexException($indexName);
}
if (!isset($indexInfo['aliases'])) {
continue;
}
$aliases = array_keys($indexInfo['aliases']);
if (in_array($aliasName, $aliases)) {
$aliasedIndexes[] = $indexName;
}
}
if (count($aliasedIndexes) > 1) {
throw new \RuntimeException(sprintf(
'Alias %s is used for multiple indexes: [%s]. Make sure it\'s'.
'either not used or is assigned to one index only',
$aliasName,
implode(', ', $aliasedIndexes)
));
}
return array_shift($aliasedIndexes);
}
}

70
Index/IndexManager.php Normal file
View file

@ -0,0 +1,70 @@
<?php
namespace FOS\ElasticaBundle\Index;
use FOS\ElasticaBundle\Elastica\Index;
class IndexManager
{
/**
* @var Index
*/
private $defaultIndex;
/**
* @var array
*/
private $indexes;
/**
* @param array $indexes
* @param Index $defaultIndex
*/
public function __construct(array $indexes, Index $defaultIndex)
{
$this->defaultIndex = $defaultIndex;
$this->indexes = $indexes;
}
/**
* Gets all registered indexes.
*
* @return array
*/
public function getAllIndexes()
{
return $this->indexes;
}
/**
* Gets an index by its name.
*
* @param string $name Index to return, or the default index if null
*
* @return Index
*
* @throws \InvalidArgumentException if no index exists for the given name
*/
public function getIndex($name = null)
{
if (null === $name) {
return $this->defaultIndex;
}
if (!isset($this->indexes[$name])) {
throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name));
}
return $this->indexes[$name];
}
/**
* Gets the default index.
*
* @return Index
*/
public function getDefaultIndex()
{
return $this->defaultIndex;
}
}

134
Index/MappingBuilder.php Normal file
View file

@ -0,0 +1,134 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Index;
use FOS\ElasticaBundle\Configuration\IndexConfig;
use FOS\ElasticaBundle\Configuration\TypeConfig;
class MappingBuilder
{
/**
* Skip adding default information to certain fields.
*
* @var array
*/
private $skipTypes = array('completion');
/**
* Builds mappings for an entire index.
*
* @param IndexConfig $indexConfig
*
* @return array
*/
public function buildIndexMapping(IndexConfig $indexConfig)
{
$typeMappings = array();
foreach ($indexConfig->getTypes() as $typeConfig) {
$typeMappings[$typeConfig->getName()] = $this->buildTypeMapping($typeConfig);
}
$mapping = array();
if (!empty($typeMappings)) {
$mapping['mappings'] = $typeMappings;
}
// 'warmers' => $indexConfig->getWarmers(),
$settings = $indexConfig->getSettings();
if (!empty($settings)) {
$mapping['settings'] = $settings;
}
return $mapping;
}
/**
* Builds mappings for a single type.
*
* @param TypeConfig $typeConfig
*
* @return array
*/
public function buildTypeMapping(TypeConfig $typeConfig)
{
$mapping = $typeConfig->getMapping();
if (null !== $typeConfig->getDynamicDateFormats()) {
$mapping['dynamic_date_formats'] = $typeConfig->getDynamicDateFormats();
}
if (null !== $typeConfig->getDateDetection()) {
$mapping['date_detection'] = $typeConfig->getDateDetection();
}
if (null !== $typeConfig->getNumericDetection()) {
$mapping['numeric_detection'] = $typeConfig->getNumericDetection();
}
if ($typeConfig->getIndexAnalyzer()) {
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
}
if ($typeConfig->getSearchAnalyzer()) {
$mapping['search_analyzer'] = $typeConfig->getSearchAnalyzer();
}
if (isset($mapping['dynamic_templates']) and empty($mapping['dynamic_templates'])) {
unset($mapping['dynamic_templates']);
}
$this->fixProperties($mapping['properties']);
if (!$mapping['properties']) {
unset($mapping['properties']);
}
if ($typeConfig->getModel()) {
$mapping['_meta']['model'] = $typeConfig->getModel();
}
if (empty($mapping)) {
// Empty mapping, we want it encoded as a {} instead of a []
$mapping = new \stdClass();
}
return $mapping;
}
/**
* Fixes any properties and applies basic defaults for any field that does not have
* required options.
*
* @param $properties
*/
private function fixProperties(&$properties)
{
foreach ($properties as $name => &$property) {
unset($property['property_path']);
if (!isset($property['type'])) {
$property['type'] = 'string';
}
if ($property['type'] == 'multi_field' && isset($property['fields'])) {
$this->fixProperties($property['fields']);
}
if (isset($property['properties'])) {
$this->fixProperties($property['properties']);
}
if (in_array($property['type'], $this->skipTypes)) {
continue;
}
if (!isset($property['store'])) {
$property['store'] = true;
}
}
}
}

158
Index/Resetter.php Normal file
View file

@ -0,0 +1,158 @@
<?php
namespace FOS\ElasticaBundle\Index;
use Elastica\Index;
use Elastica\Exception\ResponseException;
use Elastica\Type\Mapping;
use FOS\ElasticaBundle\Configuration\ConfigManager;
use FOS\ElasticaBundle\Event\IndexResetEvent;
use FOS\ElasticaBundle\Event\TypeResetEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Deletes and recreates indexes.
*/
class Resetter
{
/**
* @var AliasProcessor
*/
private $aliasProcessor;
/***
* @var ConfigManager
*/
private $configManager;
/**
* @var EventDispatcherInterface
*/
private $dispatcher;
/**
* @var IndexManager
*/
private $indexManager;
/**
* @var MappingBuilder
*/
private $mappingBuilder;
/**
* @param ConfigManager $configManager
* @param IndexManager $indexManager
* @param AliasProcessor $aliasProcessor
* @param MappingBuilder $mappingBuilder
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(
ConfigManager $configManager,
IndexManager $indexManager,
AliasProcessor $aliasProcessor,
MappingBuilder $mappingBuilder,
EventDispatcherInterface $eventDispatcher
) {
$this->aliasProcessor = $aliasProcessor;
$this->configManager = $configManager;
$this->dispatcher = $eventDispatcher;
$this->indexManager = $indexManager;
$this->mappingBuilder = $mappingBuilder;
}
/**
* Deletes and recreates all indexes.
*
* @param bool $populating
* @param bool $force
*/
public function resetAllIndexes($populating = false, $force = false)
{
foreach ($this->configManager->getIndexNames() as $name) {
$this->resetIndex($name, $populating, $force);
}
}
/**
* Deletes and recreates the named index. If populating, creates a new index
* with a randomised name for an alias to be set after population.
*
* @param string $indexName
* @param bool $populating
* @param bool $force If index exists with same name as alias, remove it
*
* @throws \InvalidArgumentException if no index exists for the given name
*/
public function resetIndex($indexName, $populating = false, $force = false)
{
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
$index = $this->indexManager->getIndex($indexName);
$event = new IndexResetEvent($indexName, $populating, $force);
$this->dispatcher->dispatch(IndexResetEvent::PRE_INDEX_RESET, $event);
if ($indexConfig->isUseAlias()) {
$this->aliasProcessor->setRootName($indexConfig, $index);
}
$mapping = $this->mappingBuilder->buildIndexMapping($indexConfig);
$index->create($mapping, true);
if (!$populating and $indexConfig->isUseAlias()) {
$this->aliasProcessor->switchIndexAlias($indexConfig, $index, $force);
}
$this->dispatcher->dispatch(IndexResetEvent::POST_INDEX_RESET, $event);
}
/**
* Deletes and recreates a mapping type for the named index.
*
* @param string $indexName
* @param string $typeName
*
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
* @throws ResponseException
*/
public function resetIndexType($indexName, $typeName)
{
$typeConfig = $this->configManager->getTypeConfiguration($indexName, $typeName);
$type = $this->indexManager->getIndex($indexName)->getType($typeName);
$event = new TypeResetEvent($indexName, $typeName);
$this->dispatcher->dispatch(TypeResetEvent::PRE_TYPE_RESET, $event);
try {
$type->delete();
} catch (ResponseException $e) {
if (strpos($e->getMessage(), 'TypeMissingException') === false) {
throw $e;
}
}
$mapping = new Mapping();
foreach ($this->mappingBuilder->buildTypeMapping($typeConfig) as $name => $field) {
$mapping->setParam($name, $field);
}
$type->setMapping($mapping);
$this->dispatcher->dispatch(TypeResetEvent::POST_TYPE_RESET, $event);
}
/**
* A command run when a population has finished.
*
* @param string $indexName
*/
public function postPopulate($indexName)
{
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
if ($indexConfig->isUseAlias()) {
$index = $this->indexManager->getIndex($indexName);
$this->aliasProcessor->switchIndexAlias($indexConfig, $index);
}
}
}

View file

@ -2,60 +2,11 @@
namespace FOS\ElasticaBundle;
class IndexManager
use FOS\ElasticaBundle\Index\IndexManager as BaseIndexManager;
/**
* @deprecated Use \FOS\ElasticaBundle\Index\IndexManager
*/
class IndexManager extends BaseIndexManager
{
protected $indexesByName;
protected $defaultIndexName;
/**
* Constructor.
*
* @param array $indexesByName
* @param \Elastica_Index $defaultIndex
*/
public function __construct(array $indexesByName, \Elastica_Index $defaultIndex)
{
$this->indexesByName = $indexesByName;
$this->defaultIndexName = $defaultIndex->getName();
}
/**
* Gets all registered indexes
*
* @return array
*/
public function getAllIndexes()
{
return $this->indexesByName;
}
/**
* Gets an index by its name
*
* @param string $name Index to return, or the default index if null
* @return \Elastica_Index
* @throws \InvalidArgumentException if no index exists for the given name
*/
public function getIndex($name = null)
{
if (null === $name) {
$name = $this->defaultIndexName;
}
if (!isset($this->indexesByName[$name])) {
throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name));
}
return $this->indexesByName[$name];
}
/**
* Gets the default index
*
* @return \Elastica_Index
*/
public function getDefaultIndex()
{
return $this->getIndex($this->defaultIndexName);
}
}

18
LICENSE
View file

@ -1,18 +0,0 @@
Copyright (c) 2012 Exercise.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -2,7 +2,7 @@
namespace FOS\ElasticaBundle\Logger;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Psr\Log\LoggerInterface;
/**
* Logger for the Elastica.
@ -12,41 +12,55 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
*
* @author Gordon Franke <info@nevalon.de>
*/
class ElasticaLogger
class ElasticaLogger implements LoggerInterface
{
/**
* @var LoggerInterface
*/
protected $logger;
protected $queries;
/**
* @var array
*/
protected $queries = array();
/**
* @var boolean
*/
protected $debug;
/**
* Constructor.
*
* @param LoggerInterface|null $logger The Symfony logger
* @param bool $debug
* @param boolean $debug
*/
public function __construct(LoggerInterface $logger = null, $debug = false)
{
$this->logger = $logger;
$this->queries = array();
$this->debug = $debug;
}
/**
* Logs a query.
*
* @param string $path Path to call
* @param string $method Rest method to use (GET, POST, DELETE, PUT)
* @param array $data arguments
* @param float $time execution time
* @param string $path Path to call
* @param string $method Rest method to use (GET, POST, DELETE, PUT)
* @param array $data Arguments
* @param float $time Execution time
* @param array $connection Host, port, transport, and headers of the query
* @param array $query Arguments
*/
public function logQuery($path, $method, $data, $time)
public function logQuery($path, $method, $data, $time, $connection = array(), $query = array())
{
if ($this->debug) {
$this->queries[] = array(
'path' => $path,
'method' => $method,
'data' => $data,
'executionMS' => $time
'executionMS' => $time,
'connection' => $connection,
'queryString' => $query,
);
}
@ -75,4 +89,76 @@ class ElasticaLogger
{
return $this->queries;
}
/**
* {@inheritdoc}
*/
public function emergency($message, array $context = array())
{
return $this->logger->emergency($message, $context);
}
/**
* {@inheritdoc}
*/
public function alert($message, array $context = array())
{
return $this->logger->alert($message, $context);
}
/**
* {@inheritdoc}
*/
public function critical($message, array $context = array())
{
return $this->logger->critical($message, $context);
}
/**
* {@inheritdoc}
*/
public function error($message, array $context = array())
{
return $this->logger->error($message, $context);
}
/**
* {@inheritdoc}
*/
public function warning($message, array $context = array())
{
return $this->logger->warning($message, $context);
}
/**
* {@inheritdoc}
*/
public function notice($message, array $context = array())
{
return $this->logger->notice($message, $context);
}
/**
* {@inheritdoc}
*/
public function info($message, array $context = array())
{
return $this->logger->info($message, $context);
}
/**
* {@inheritdoc}
*/
public function debug($message, array $context = array())
{
return $this->logger->debug($message, $context);
}
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array())
{
return $this->logger->log($level, $message, $context);
}
}

View file

@ -5,6 +5,7 @@ namespace FOS\ElasticaBundle\Manager;
use Doctrine\Common\Annotations\Reader;
use FOS\ElasticaBundle\Finder\FinderInterface;
use RuntimeException;
/**
* @author Richard Miller <info@limethinking.co.uk>
*
@ -24,16 +25,16 @@ class RepositoryManager implements RepositoryManagerInterface
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
{
$this->entities[$entityName]= array();
$this->entities[$entityName] = array();
$this->entities[$entityName]['finder'] = $finder;
$this->entities[$entityName]['repositoryName'] = $repositoryName;
}
/**
* Return repository for entity
* Return repository for entity.
*
* Returns custom repository if one specified otherwise
* returns a basic respository.
* returns a basic repository.
*/
public function getRepository($entityName)
{
@ -58,23 +59,26 @@ class RepositoryManager implements RepositoryManagerInterface
}
$refClass = new \ReflectionClass($entityName);
$annotation = $this->reader->getClassAnnotation($refClass, 'FOS\\ElasticaBundle\\Configuration\\Search');
$annotation = $this->reader->getClassAnnotation($refClass, 'FOS\\ElasticaBundle\\Annotation\\Search');
if ($annotation) {
$this->entities[$entityName]['repositoryName']
= $annotation->repositoryClass;
return $annotation->repositoryClass;
}
return 'FOS\ElasticaBundle\Repository';
}
/**
* @param string $entityName
*/
private function createRepository($entityName)
{
$repositoryName = $this->getRepositoryName($entityName);
if (!class_exists($repositoryName)) {
if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) {
throw new RuntimeException(sprintf('%s repository for %s does not exist', $repositoryName, $entityName));
}
return new $repositoryName($this->entities[$entityName]['finder']);
}
}

View file

@ -12,7 +12,6 @@ use FOS\ElasticaBundle\Finder\FinderInterface;
*/
interface RepositoryManagerInterface
{
/**
* Adds entity name and its finder.
* Custom repository class name can also be added.
@ -24,7 +23,7 @@ interface RepositoryManagerInterface
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
/**
* Return repository for entity
* Return repository for entity.
*
* Returns custom repository if one specified otherwise
* returns a basic repository.
@ -32,5 +31,4 @@ interface RepositoryManagerInterface
* @param string $entityName
*/
public function getRepository($entityName);
}

View file

@ -3,14 +3,13 @@
namespace FOS\ElasticaBundle\Paginator;
use Pagerfanta\Adapter\AdapterInterface;
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
class FantaPaginatorAdapter implements AdapterInterface
{
private $adapter;
/**
* @param PaginatorAdapterInterface $adapter
* @param \FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface $adapter
*/
public function __construct(PaginatorAdapterInterface $adapter)
{
@ -21,8 +20,6 @@ class FantaPaginatorAdapter implements AdapterInterface
* Returns the number of results.
*
* @return integer The number of results.
*
* @api
*/
public function getNbResults()
{
@ -30,14 +27,34 @@ class FantaPaginatorAdapter implements AdapterInterface
}
/**
* Returns an slice of the results.
* Returns Facets.
*
* @return mixed
*/
public function getFacets()
{
return $this->adapter->getFacets();
}
/**
* Returns Aggregations.
*
* @return mixed
*
* @api
*/
public function getAggregations()
{
return $this->adapter->getAggregations();
}
/**
* Returns a slice of the results.
*
* @param integer $offset The offset.
* @param integer $length The length.
*
* @return array|\Traversable The slice.
*
* @api
*/
public function getSlice($offset, $length)
{

View file

@ -2,18 +2,14 @@
namespace FOS\ElasticaBundle\Paginator;
use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
interface PaginatorAdapterInterface
{
/**
* Returns the number of results.
*
* @return integer The number of results.
*
* @api
*/
function getTotalHits();
public function getTotalHits();
/**
* Returns an slice of the results.
@ -22,8 +18,20 @@ interface PaginatorAdapterInterface
* @param integer $length The length.
*
* @return PartialResultsInterface
*
* @api
*/
function getResults($offset, $length);
public function getResults($offset, $length);
/**
* Returns Facets.
*
* @return mixed
*/
public function getFacets();
/**
* Returns Aggregations.
*
* @return mixed
*/
public function getAggregations();
}

View file

@ -8,24 +8,27 @@ interface PartialResultsInterface
* Returns the paginated results.
*
* @return array
*
* @api
*/
function toArray();
public function toArray();
/**
* Returns the number of results.
*
* @return integer The number of results.
*
* @api
*/
function getTotalHits();
public function getTotalHits();
/**
* Returns the facets
* Returns the facets.
*
* @return array
*/
function getFacets();
}
public function getFacets();
/**
* Returns the aggregations.
*
* @return array
*/
public function getAggregations();
}

View file

@ -2,54 +2,96 @@
namespace FOS\ElasticaBundle\Paginator;
use Elastica_Searchable;
use Elastica_Query;
use Elastica_ResultSet;
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
use FOS\ElasticaBundle\Paginator\RawPartialResults;
use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
use Elastica\SearchableInterface;
use Elastica\Query;
use Elastica\ResultSet;
use InvalidArgumentException;
/**
* Allows pagination of Elastica_Query. Does not map results
* Allows pagination of Elastica\Query. Does not map results.
*/
class RawPaginatorAdapter implements PaginatorAdapterInterface
{
/**
* @var Elastica_Searchable the object to search in
* @var SearchableInterface the object to search in
*/
private $searchable = null;
private $searchable;
/**
* @var Elastica_Query the query to search
* @var Query the query to search
*/
private $query = null;
private $query;
/**
* @var array search options
*/
private $options;
/**
* @var integer the number of hits
*/
private $totalHits;
/**
* @var array for the facets
*/
private $facets;
/**
* @var array for the aggregations
*/
private $aggregations;
/**
* @see PaginatorAdapterInterface::__construct
*
* @param Elastica_Searchable $searchable the object to search in
* @param Elastica_Query $query the query to search
* @param SearchableInterface $searchable the object to search in
* @param Query $query the query to search
* @param array $options
*/
public function __construct(Elastica_Searchable $searchable, Elastica_Query $query)
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
{
$this->searchable = $searchable;
$this->query = $query;
$this->options = $options;
}
/**
* Returns the paginated results.
*
* @param $offset
* @param $itemCountPerPage
* @return Elastica_ResultSet
* @param integer $offset
* @param integer $itemCountPerPage
*
* @throws \InvalidArgumentException
*
* @return ResultSet
*/
protected function getElasticaResults($offset, $itemCountPerPage)
{
$offset = (integer) $offset;
$itemCountPerPage = (integer) $itemCountPerPage;
$size = $this->query->hasParam('size')
? (integer) $this->query->getParam('size')
: null;
if (null !== $size && $size < $offset + $itemCountPerPage) {
$itemCountPerPage = $size - $offset;
}
if ($itemCountPerPage < 1) {
throw new InvalidArgumentException('$itemCountPerPage must be greater than zero');
}
$query = clone $this->query;
$query->setFrom($offset);
$query->setLimit($itemCountPerPage);
$query->setSize($itemCountPerPage);
return $this->searchable->search($query);
$resultSet = $this->searchable->search($query, $this->options);
$this->totalHits = $resultSet->getTotalHits();
$this->facets = $resultSet->getFacets();
$this->aggregations = $resultSet->getAggregations();
return $resultSet;
}
/**
@ -57,6 +99,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
*
* @param int $offset
* @param int $itemCountPerPage
*
* @return PartialResultsInterface
*/
public function getResults($offset, $itemCountPerPage)
@ -67,10 +110,60 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
/**
* Returns the number of results.
*
* If genuineTotal is provided as true, total hits is returned from the
* hits.total value from the search results instead of just returning
* the requested size.
*
* @param boolean $genuineTotal
*
* @return integer The number of results.
*/
public function getTotalHits()
public function getTotalHits($genuineTotal = false)
{
return $this->searchable->search($this->query)->getTotalHits();
if (! isset($this->totalHits)) {
$this->totalHits = $this->searchable->count($this->query);
}
return $this->query->hasParam('size') && !$genuineTotal
? min($this->totalHits, (integer) $this->query->getParam('size'))
: $this->totalHits;
}
/**
* Returns Facets.
*
* @return mixed
*/
public function getFacets()
{
if (! isset($this->facets)) {
$this->facets = $this->searchable->search($this->query)->getFacets();
}
return $this->facets;
}
/**
* Returns Aggregations.
*
* @return mixed
*/
public function getAggregations()
{
if (!isset($this->aggregations)) {
$this->aggregations = $this->searchable->search($this->query)->getAggregations();
}
return $this->aggregations;
}
/**
* Returns the Query.
*
* @return Query the search query
*/
public function getQuery()
{
return $this->query;
}
}

View file

@ -2,21 +2,20 @@
namespace FOS\ElasticaBundle\Paginator;
use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
use Elastica_ResultSet;
use Elastica_Result;
use Elastica\ResultSet;
use Elastica\Result;
/**
* Raw partial results transforms to a simple array
* Raw partial results transforms to a simple array.
*/
class RawPartialResults implements PartialResultsInterface
{
protected $resultSet;
/**
* @param \Elastica_ResultSet $resultSet
* @param ResultSet $resultSet
*/
public function __construct(Elastica_ResultSet $resultSet)
public function __construct(ResultSet $resultSet)
{
$this->resultSet = $resultSet;
}
@ -26,7 +25,7 @@ class RawPartialResults implements PartialResultsInterface
*/
public function toArray()
{
return array_map(function(Elastica_Result $result) {
return array_map(function (Result $result) {
return $result->getSource();
}, $this->resultSet->getResults());
}
@ -48,6 +47,18 @@ class RawPartialResults implements PartialResultsInterface
return $this->resultSet->getFacets();
}
return null;
return;
}
}
/**
* {@inheritDoc}
*/
public function getAggregations()
{
if ($this->resultSet->hasAggregations()) {
return $this->resultSet->getAggregations();
}
return;
}
}

View file

@ -3,25 +3,25 @@
namespace FOS\ElasticaBundle\Paginator;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
use FOS\ElasticaBundle\Paginator\TransformedPartialResults;
use Elastica_Searchable;
use Elastica_Query;
use Elastica\SearchableInterface;
use Elastica\Query;
/**
* Allows pagination of Elastica_Query
* Allows pagination of \Elastica\Query.
*/
class TransformedPaginatorAdapter extends RawPaginatorAdapter
{
private $transformer;
/**
* @param Elastica_Searchable $searchable the object to search in
* @param Elastica_Query $query the query to search
* @param SearchableInterface $searchable the object to search in
* @param Query $query the query to search
* @param array $options
* @param ElasticaToModelTransformerInterface $transformer the transformer for fetching the results
*/
public function __construct(Elastica_Searchable $searchable, Elastica_Query $query, ElasticaToModelTransformerInterface $transformer)
public function __construct(SearchableInterface $searchable, Query $query, array $options = array(), ElasticaToModelTransformerInterface $transformer)
{
parent::__construct($searchable, $query);
parent::__construct($searchable, $query, $options);
$this->transformer = $transformer;
}

View file

@ -3,21 +3,20 @@
namespace FOS\ElasticaBundle\Paginator;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
use FOS\ElasticaBundle\Paginator\RawPartialResults;
use Elastica_ResultSet;
use Elastica\ResultSet;
/**
* Partial transformed result set
* Partial transformed result set.
*/
class TransformedPartialResults extends RawPartialResults
{
protected $transformer;
/**
* @param \Elastica_ResultSet $resultSet
* @param ResultSet $resultSet
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
*/
public function __construct(Elastica_ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
{
parent::__construct($resultSet);
@ -31,4 +30,4 @@ class TransformedPartialResults extends RawPartialResults
{
return $this->transformer->transform($this->resultSet->getResults());
}
}
}

View file

@ -2,13 +2,15 @@
namespace FOS\ElasticaBundle\Persister;
use Psr\Log\LoggerInterface;
use Elastica\Exception\BulkException;
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
use Elastica_Type;
use Elastica_Document;
use Elastica\Type;
use Elastica\Document;
/**
* Inserts, replaces and deletes single documents in an elastica type
* Accepts domain model objects and converts them to elastica documents
* Accepts domain model objects and converts them to elastica documents.
*
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
*/
@ -18,8 +20,9 @@ class ObjectPersister implements ObjectPersisterInterface
protected $transformer;
protected $objectClass;
protected $fields;
protected $logger;
public function __construct(Elastica_Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, array $fields)
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, array $fields)
{
$this->type = $type;
$this->transformer = $transformer;
@ -27,75 +30,161 @@ class ObjectPersister implements ObjectPersisterInterface
$this->fields = $fields;
}
/**
* If the ObjectPersister handles a given object.
*
* @param object $object
*
* @return bool
*/
public function handlesObject($object)
{
return $object instanceof $this->objectClass;
}
/**
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Log exception if logger defined for persister belonging to the current listener, otherwise re-throw.
*
* @param BulkException $e
*
* @throws BulkException
*/
private function log(BulkException $e)
{
if (! $this->logger) {
throw $e;
}
$this->logger->error($e);
}
/**
* Insert one object into the type
* The object will be transformed to an elastica document
* The object will be transformed to an elastica document.
*
* @param object $object
*/
public function insertOne($object)
{
$document = $this->transformToElasticaDocument($object);
$this->type->addDocument($document);
$this->insertMany(array($object));
}
/**
* Replaces one object in the type
* Replaces one object in the type.
*
* @param object $object
* @return null
**/
public function replaceOne($object)
{
$document = $this->transformToElasticaDocument($object);
$this->type->deleteById($document->getId());
$this->type->addDocument($document);
$this->replaceMany(array($object));
}
/**
* Deletes one object in the type
* Deletes one object in the type.
*
* @param object $object
* @return null
**/
public function deleteOne($object)
{
$document = $this->transformToElasticaDocument($object);
$this->type->deleteById($document->getId());
$this->deleteMany(array($object));
}
/**
* Deletes one object in the type by id
* Deletes one object in the type by id.
*
* @param mixed $id
*
* @return null
**/
public function deleteById($id)
{
$this->type->deleteById($id);
$this->deleteManyByIdentifiers(array($id));
}
/**
* Inserts an array of objects in the type
* Bulk insert an array of objects in the type for the given method.
*
* @param array $objects array of domain model objects
**/
* @param string Method to call
*/
public function insertMany(array $objects)
{
$documents = array();
foreach ($objects as $object) {
$documents[] = $this->transformToElasticaDocument($object);
}
$this->type->addDocuments($documents);
try {
$this->type->addDocuments($documents);
} catch (BulkException $e) {
$this->log($e);
}
}
/**
* Transforms an object to an elastica document
* Bulk update an array of objects in the type. Create document if it does not already exist.
*
* @param array $objects array of domain model objects
*/
public function replaceMany(array $objects)
{
$documents = array();
foreach ($objects as $object) {
$document = $this->transformToElasticaDocument($object);
$document->setDocAsUpsert(true);
$documents[] = $document;
}
try {
$this->type->updateDocuments($documents);
} catch (BulkException $e) {
$this->log($e);
}
}
/**
* Bulk deletes an array of objects in the type.
*
* @param array $objects array of domain model objects
*/
public function deleteMany(array $objects)
{
$documents = array();
foreach ($objects as $object) {
$documents[] = $this->transformToElasticaDocument($object);
}
try {
$this->type->deleteDocuments($documents);
} catch (BulkException $e) {
$this->log($e);
}
}
/**
* Bulk deletes records from an array of identifiers.
*
* @param array $identifiers array of domain model object identifiers
*/
public function deleteManyByIdentifiers(array $identifiers)
{
try {
$this->type->getIndex()->getClient()->deleteIds($identifiers, $this->type->getIndex(), $this->type);
} catch (BulkException $e) {
$this->log($e);
}
}
/**
* Transforms an object to an elastica document.
*
* @param object $object
* @return Elastica_Document the elastica document
*
* @return Document the elastica document
*/
public function transformToElasticaDocument($object)
{

View file

@ -4,47 +4,75 @@ namespace FOS\ElasticaBundle\Persister;
/**
* Inserts, replaces and deletes single documents in an elastica type
* Accepts domain model objects and converts them to elastica documents
* Accepts domain model objects and converts them to elastica documents.
*
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
*/
interface ObjectPersisterInterface
{
/**
* Checks if this persister can handle the given object or not.
*
* @param mixed $object
*
* @return boolean
*/
public function handlesObject($object);
/**
* Insert one object into the type
* The object will be transformed to an elastica document
* The object will be transformed to an elastica document.
*
* @param object $object
*/
function insertOne($object);
public function insertOne($object);
/**
* Replaces one object in the type
* Replaces one object in the type.
*
* @param object $object
**/
function replaceOne($object);
public function replaceOne($object);
/**
* Deletes one object in the type
* Deletes one object in the type.
*
* @param object $object
**/
function deleteOne($object);
public function deleteOne($object);
/**
* Deletes one object in the type by id
* Deletes one object in the type by id.
*
* @param mixed $id
*
* @return null
**/
function deleteById($id);
*/
public function deleteById($id);
/**
* Inserts an array of objects in the type
* Bulk inserts an array of objects in the type.
*
* @param array $objects array of domain model objects
**/
function insertMany(array $objects);
*/
public function insertMany(array $objects);
/**
* Bulk updates an array of objects in the type.
*
* @param array $objects array of domain model objects
*/
public function replaceMany(array $objects);
/**
* Bulk deletes an array of objects in the type.
*
* @param array $objects array of domain model objects
*/
public function deleteMany(array $objects);
/**
* Bulk deletes records from an array of identifiers.
*
* @param array $identifiers array of domain model object identifiers
*/
public function deleteManyByIdentifiers(array $identifiers);
}

View file

@ -0,0 +1,50 @@
<?php
namespace FOS\ElasticaBundle\Persister;
use Elastica\Document;
use Elastica\Type;
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
/**
* Inserts, replaces and deletes single objects in an elastica type, making use
* of elastica's serializer support to convert objects in to elastica documents.
* Accepts domain model objects and passes them directly to elastica.
*
* @author Lea Haensenberber <lea.haensenberger@gmail.com>
*/
class ObjectSerializerPersister extends ObjectPersister
{
protected $serializer;
/**
* @param Type $type
* @param ModelToElasticaTransformerInterface $transformer
* @param string $objectClass
* @param callable $serializer
*/
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer)
{
parent::__construct($type, $transformer, $objectClass, array());
$this->serializer = $serializer;
}
/**
* Transforms an object to an elastica document
* with just the identifier set.
*
* @param object $object
*
* @return Document the elastica document
*/
public function transformToElasticaDocument($object)
{
$document = $this->transformer->transform($object, array());
$data = call_user_func($this->serializer, $object);
$document->setData($data);
return $document;
}
}

View file

@ -3,91 +3,75 @@
namespace FOS\ElasticaBundle\Propel;
use FOS\ElasticaBundle\HybridResult;
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* Maps Elastica documents with Propel objects
* This mapper assumes an exact match between
* elastica documents ids and propel object ids
* Maps Elastica documents with Propel objects.
*
* This mapper assumes an exact match between Elastica document IDs and Propel
* entity IDs.
*
* @author William Durand <william.durand1@gmail.com>
*/
class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
{
/**
* Class of the model to map to the elastica documents
* Propel model class to map to Elastica documents.
*
* @var string
*/
protected $objectClass = null;
/**
* Optional parameters
* Transformer options.
*
* @var array
*/
protected $options = array(
'hydrate' => true,
'identifier' => 'id'
'identifier' => 'id',
);
/**
* PropertyAccessor instance
*
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
/**
* Instantiates a new Mapper
* Constructor.
*
* @param string $objectClass
* @param array $options
* @param array $options
*/
public function __construct($objectClass, array $options = array())
{
$this->objectClass = $objectClass;
$this->options = array_merge($this->options, $options);
$this->options = array_merge($this->options, $options);
}
/**
* Set the PropertyAccessor
* Transforms an array of Elastica document into an array of Propel entities
* fetched from the database.
*
* @param PropertyAccessorInterface $propertyAccessor
*/
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
{
$this->propertyAccessor = $propertyAccessor;
}
/**
* Transforms an array of elastica objects into an array of
* model objects fetched from the propel repository
* @param array $elasticaObjects
*
* @param \Elastica_Document[] $elasticaObjects array of elastica objects
* @return array
* @return array|\ArrayObject
*/
public function transform(array $elasticaObjects)
{
$ids = array_map(function(\Elastica_Document $elasticaObject) {
return $elasticaObject->getId();
}, $elasticaObjects);
$ids = array();
foreach ($elasticaObjects as $elasticaObject) {
$ids[] = $elasticaObject->getId();
}
$objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
// sort objects in the order of ids
// Sort objects in the order of their IDs
$idPos = array_flip($ids);
$identifier = $this->options['identifier'];
$propertyAccessor = $this->propertyAccessor;
$sortCallback = $this->getSortingClosure($idPos, $identifier);
if (is_object($objects)) {
$objects->uasort(function($a, $b) use ($idPos, $identifier, $propertyAccessor) {
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
});
$objects->uasort($sortCallback);
} else {
usort($objects, function($a, $b) use ($idPos, $identifier, $propertyAccessor) {
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
});
usort($objects, $sortCallback);
}
return $objects;
@ -101,7 +85,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
$objects = $this->transform($elasticaObjects);
$result = array();
for ($i = 0; $i < count($elasticaObjects); $i++) {
for ($i = 0, $j = count($elasticaObjects); $i < $j; $i++) {
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
}
@ -125,11 +109,15 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
}
/**
* Fetch objects for theses identifier values
* Fetch Propel entities for the given identifier values.
*
* @param array $identifierValues ids values
* @param boolean $hydrate whether or not to hydrate the objects, false returns arrays
* @return array of objects or arrays
* If $hydrate is false, the returned array elements will be arrays.
* Otherwise, the results will be hydrated to instances of the model class.
*
* @param array $identifierValues Identifier values
* @param boolean $hydrate Whether or not to hydrate the results
*
* @return array
*/
protected function findByIdentifiers(array $identifierValues, $hydrate)
{
@ -139,7 +127,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
if (!$hydrate) {
if (! $hydrate) {
return $query->toArray();
}
@ -149,9 +137,10 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
/**
* Create a query to use in the findByIdentifiers() method.
*
* @param string $class the model class
* @param string $identifierField like 'id'
* @param array $identifierValues ids values
* @param string $class Propel model class
* @param string $identifierField Identifier field name (e.g. "id")
* @param array $identifierValues Identifier values
*
* @return \ModelCriteria
*/
protected function createQuery($class, $identifierField, array $identifierValues)
@ -159,13 +148,13 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
$queryClass = $class.'Query';
$filterMethod = 'filterBy'.$this->camelize($identifierField);
return $queryClass::create()
->$filterMethod($identifierValues)
;
return $queryClass::create()->$filterMethod($identifierValues);
}
/**
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
*
* @param string $str
*/
private function camelize($str)
{

View file

@ -5,44 +5,69 @@ namespace FOS\ElasticaBundle\Propel;
use FOS\ElasticaBundle\Provider\AbstractProvider;
/**
* Propel provider
* Propel provider.
*
* @author William Durand <william.durand1@gmail.com>
*/
class Provider extends AbstractProvider
{
/**
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
* {@inheritDoc}
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
public function doPopulate($options, \Closure $loggerClosure = null)
{
$queryClass = $this->objectClass . 'Query';
$queryClass = $this->objectClass.'Query';
$nbObjects = $queryClass::create()->count();
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
for (; $offset < $nbObjects; $offset += $batchSize) {
if ($loggerClosure) {
$stepStartTime = microtime(true);
$offset = $options['offset'];
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
$objects = $queryClass::create()
->limit($options['batch_size'])
->offset($offset)
->find()
->getArrayCopy();
$objects = $this->filterObjects($options, $objects);
if (!empty($objects)) {
$this->objectPersister->insertMany($objects);
}
$objects = $queryClass::create()
->limit($batchSize)
->offset($offset)
->find();
$this->objectPersister->insertMany($objects->getArrayCopy());
usleep($sleep);
usleep($options['sleep']);
if ($loggerClosure) {
$stepNbObjects = count($objects);
$stepCount = $stepNbObjects + $offset;
$percentComplete = 100 * $stepCount / $nbObjects;
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond));
$loggerClosure($options['batch_size'], $nbObjects);
}
}
}
/**
* {@inheritDoc}
*/
protected function configureOptions()
{
parent::configureOptions();
$this->resolver->setDefaults(array(
'clear_object_manager' => true,
'debug_logging' => false,
'ignore_errors' => false,
'offset' => 0,
'query_builder_method' => null,
'sleep' => 0
));
}
/**
* {@inheritDoc}
*/
protected function disableLogging()
{
}
/**
* {@inheritDoc}
*/
protected function enableLogging($logger)
{
}
}

View file

@ -3,28 +3,193 @@
namespace FOS\ElasticaBundle\Provider;
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
use FOS\ElasticaBundle\Provider\ProviderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* AbstractProvider.
*/
abstract class AbstractProvider implements ProviderInterface
{
/**
* @var array
*/
protected $baseOptions;
/**
* @var string
*/
protected $objectClass;
/**
* @var ObjectPersisterInterface
*/
protected $objectPersister;
protected $options;
/**
* @var OptionsResolver
*/
protected $resolver;
/**
* @var IndexableInterface
*/
private $indexable;
/**
* Constructor.
*
* @param ObjectPersisterInterface $objectPersister
* @param IndexableInterface $indexable
* @param string $objectClass
* @param array $options
* @param array $baseOptions
*/
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
{
$this->objectPersister = $objectPersister;
public function __construct(
ObjectPersisterInterface $objectPersister,
IndexableInterface $indexable,
$objectClass,
array $baseOptions = array()
) {
$this->baseOptions = $baseOptions;
$this->indexable = $indexable;
$this->objectClass = $objectClass;
$this->objectPersister = $objectPersister;
$this->resolver = new OptionsResolver();
$this->configureOptions();
}
$this->options = array_merge(array(
/**
* {@inheritDoc}
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
$options = $this->resolveOptions($options);
$logger = !$options['debug_logging'] ?
$this->disableLogging() :
null;
$this->doPopulate($options, $loggerClosure);
if (null !== $logger) {
$this->enableLogging($logger);
}
}
/**
* Disables logging and returns the logger that was previously set.
*
* @return mixed
*/
abstract protected function disableLogging();
/**
* Perform actual population.
*
* @param array $options
* @param \Closure $loggerClosure
*/
abstract protected function doPopulate($options, \Closure $loggerClosure = null);
/**
* Reenables the logger with the previously returned logger from disableLogging();.
*
* @param mixed $logger
*
* @return mixed
*/
abstract protected function enableLogging($logger);
/**
* Configures the option resolver.
*/
protected function configureOptions()
{
$this->resolver->setDefaults(array(
'batch_size' => 100,
), $options);
'skip_indexable_check' => false,
));
$this->resolver->setAllowedTypes(array(
'batch_size' => 'int'
));
$this->resolver->setRequired(array(
'indexName',
'typeName',
));
}
/**
* Filters objects away if they are not indexable.
*
* @param array $options
* @param array $objects
* @return array
*/
protected function filterObjects(array $options, array $objects)
{
if ($options['skip_indexable_check']) {
return $objects;
}
$index = $options['indexName'];
$type = $options['typeName'];
$return = array();
foreach ($objects as $object) {
if (!$this->indexable->isObjectIndexable($index, $type, $object)) {
continue;
}
$return[] = $object;
}
return $return;
}
/**
* Checks if a given object should be indexed or not.
*
* @deprecated To be removed in 4.0
*
* @param object $object
*
* @return bool
*/
protected function isObjectIndexable($object)
{
return $this->indexable->isObjectIndexable(
$this->baseOptions['indexName'],
$this->baseOptions['typeName'],
$object
);
}
/**
* Get string with RAM usage information (current and peak).
*
* @deprecated To be removed in 4.0
*
* @return string
*/
protected function getMemoryUsage()
{
$memory = round(memory_get_usage() / (1024 * 1024)); // to get usage in Mo
$memoryMax = round(memory_get_peak_usage() / (1024 * 1024)); // to get max usage in Mo
return sprintf('(RAM : current=%uMo peak=%uMo)', $memory, $memoryMax);
}
/**
* Merges the base options provided by the class with options passed to the populate
* method and runs them through the resolver.
*
* @param array $options
*
* @return array
*/
protected function resolveOptions(array $options)
{
return $this->resolver->resolve(array_merge($this->baseOptions, $options));
}
}

240
Provider/Indexable.php Normal file
View file

@ -0,0 +1,240 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Provider;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\SyntaxError;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
class Indexable implements IndexableInterface
{
/**
* An array of raw configured callbacks for all types.
*
* @var array
*/
private $callbacks = array();
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
/**
* An instance of ExpressionLanguage.
*
* @var ExpressionLanguage
*/
private $expressionLanguage;
/**
* An array of initialised callbacks.
*
* @var array
*/
private $initialisedCallbacks = array();
/**
* PropertyAccessor instance.
*
* @var PropertyAccessorInterface
*/
private $propertyAccessor;
/**
* @param array $callbacks
* @param ContainerInterface $container
*/
public function __construct(array $callbacks, ContainerInterface $container)
{
$this->callbacks = $callbacks;
$this->container = $container;
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}
/**
* Return whether the object is indexable with respect to the callback.
*
* @param string $indexName
* @param string $typeName
* @param mixed $object
*
* @return bool
*/
public function isObjectIndexable($indexName, $typeName, $object)
{
$type = sprintf('%s/%s', $indexName, $typeName);
$callback = $this->getCallback($type, $object);
if (!$callback) {
return true;
}
if ($callback instanceof Expression) {
return (bool) $this->getExpressionLanguage()->evaluate($callback, array(
'object' => $object,
$this->getExpressionVar($object) => $object,
));
}
return is_string($callback)
? call_user_func(array($object, $callback))
: call_user_func($callback, $object);
}
/**
* Builds and initialises a callback.
*
* @param string $type
* @param object $object
*
* @return mixed
*/
private function buildCallback($type, $object)
{
if (!array_key_exists($type, $this->callbacks)) {
return;
}
$callback = $this->callbacks[$type];
if (is_callable($callback) or is_callable(array($object, $callback))) {
return $callback;
}
if (is_array($callback) && !is_object($callback[0])) {
return $this->processArrayToCallback($type, $callback);
}
if (is_string($callback)) {
return $this->buildExpressionCallback($type, $object, $callback);
}
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
}
/**
* Processes a string expression into an Expression.
*
* @param string $type
* @param mixed $object
* @param string $callback
*
* @return Expression
*/
private function buildExpressionCallback($type, $object, $callback)
{
$expression = $this->getExpressionLanguage();
if (!$expression) {
throw new \RuntimeException('Unable to process an expression without the ExpressionLanguage component.');
}
try {
$callback = new Expression($callback);
$expression->compile($callback, array(
'object', $this->getExpressionVar($object)
));
return $callback;
} catch (SyntaxError $e) {
throw new \InvalidArgumentException(sprintf(
'Callback for type "%s" is an invalid expression',
$type
), $e->getCode(), $e);
}
}
/**
* Retreives a cached callback, or creates a new callback if one is not found.
*
* @param string $type
* @param object $object
*
* @return mixed
*/
private function getCallback($type, $object)
{
if (!array_key_exists($type, $this->initialisedCallbacks)) {
$this->initialisedCallbacks[$type] = $this->buildCallback($type, $object);
}
return $this->initialisedCallbacks[$type];
}
/**
* Returns the ExpressionLanguage class if it is available.
*
* @return ExpressionLanguage|null
*/
private function getExpressionLanguage()
{
if (null === $this->expressionLanguage && class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
$this->expressionLanguage = new ExpressionLanguage();
}
return $this->expressionLanguage;
}
/**
* Returns the variable name to be used to access the object when using the ExpressionLanguage
* component to parse and evaluate an expression.
*
* @param mixed $object
*
* @return string
*/
private function getExpressionVar($object = null)
{
if (!is_object($object)) {
return 'object';
}
$ref = new \ReflectionClass($object);
return strtolower($ref->getShortName());
}
/**
* Processes an array into a callback. Replaces the first element with a service if
* it begins with an @.
*
* @param string $type
* @param array $callback
* @return array
*/
private function processArrayToCallback($type, array $callback)
{
list($class, $method) = $callback + array(null, '__invoke');
if (strpos($class, '@') === 0) {
$service = $this->container->get(substr($class, 1));
$callback = array($service, $method);
if (!is_callable($callback)) {
throw new \InvalidArgumentException(sprintf(
'Method "%s" on service "%s" is not callable.',
$method,
substr($class, 1)
));
}
return $callback;
}
throw new \InvalidArgumentException(sprintf(
'Unable to parse callback array for type "%s"',
$type
));
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* This file is part of the FOSElasticaBundle project.
*
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\ElasticaBundle\Provider;
interface IndexableInterface
{
/**
* Checks if an object passed should be indexable or not.
*
* @param string $indexName
* @param string $typeName
* @param mixed $object
*
* @return bool
*/
public function isObjectIndexable($indexName, $typeName, $object);
}

View file

@ -3,7 +3,7 @@
namespace FOS\ElasticaBundle\Provider;
/**
* Insert application domain objects into elastica types
* Insert application domain objects into elastica types.
*
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
*/
@ -12,9 +12,15 @@ interface ProviderInterface
/**
* Persists all domain objects to ElasticSearch for this provider.
*
* The closure can expect 2 or 3 arguments:
* * The step size
* * The total number of objects
* * A message to output in error conditions (not normally provided)
*
* @param \Closure $loggerClosure
* @param array $options
*
* @return
*/
function populate(\Closure $loggerClosure = null, array $options = array());
public function populate(\Closure $loggerClosure = null, array $options = array());
}

View file

@ -2,8 +2,6 @@
namespace FOS\ElasticaBundle\Provider;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -59,8 +57,10 @@ class ProviderRegistry implements ContainerAwareInterface
*
* Providers will be indexed by "type" strings in the returned array.
*
* @param string $index
* @return array of ProviderInterface instances
* @param string $index
*
* @return ProviderInterface[]
*
* @throws \InvalidArgumentException if no providers were registered for the index
*/
public function getIndexProviders($index)
@ -83,7 +83,9 @@ class ProviderRegistry implements ContainerAwareInterface
*
* @param string $index
* @param string $type
*
* @return ProviderInterface
*
* @throws \InvalidArgumentException if no provider was registered for the index and type
*/
public function getProvider($index, $type)

726
README.md
View file

@ -1,717 +1,35 @@
[Elastica](https://github.com/ruflin/Elastica) integration in Symfony2
FOSElasticaBundle
=================
### Installation
This bundle provides integration with [ElasticSearch](http://www.elasticsearch.org) and [Elastica](https://github.com/ruflin/Elastica) with
Symfony2. Features include:
#### Bundle and Dependencies
- Integrates the Elastica library into a Symfony2 environment
- Automatically generate mappings using a serializer
- Listeners for Doctrine events for automatic indexing
For Symfony 2.0.x projects, you must use a 1.x release of this bundle. Please
check the bundle
[tags](https://github.com/FriendsOfSymfony/FOSElasticaBundle/tags) or the
[Packagist](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
page for information on Symfony and Elastica compatibility.
> **Note** Propel support is limited and contributions fixing issues are welcome!
Add FOSElasticaBundle to your application's `composer.json` file:
[![Build Status](https://secure.travis-ci.org/FriendsOfSymfony/FOSElasticaBundle.png?branch=master)](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [![Total Downloads](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/downloads.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Stable Version](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/v/stable.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Unstable Version](https://poser.pugx.org/friendsofsymfony/elastica-bundle/v/unstable.svg)](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/?branch=master)
```json
{
"require": {
"friendsofsymfony/elastica-bundle": "~2.0"
}
}
```
Documentation
-------------
Install the bundle and its dependencies with the following command:
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
```bash
$ php composer.phar update friendsofsymfony/elastica-bundle
```
[Read the documentation for 3.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
You may rely on Composer to fetch the appropriate version of Elastica. Lastly,
enable the bundle in your application kernel:
[Read the documentation for 3.0.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/3.0.x/Resources/doc/index.md)
```php
// app/AppKernel.php
Installation
------------
public function registerBundles()
{
$bundles = array(
// ...
new FOS\ElasticaBundle\FOSElasticaBundle(),
);
}
```
Installation instructions can be found in the [documentation](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md)
#### Elasticsearch
License
-------
Instructions for installing and deploying Elasticsearch may be found
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
This bundle is under the MIT license. See the complete license in the bundle:
### Basic configuration
#### Declare a client
Elasticsearch client is comparable to a database connection.
Most of the time, you will need only one.
#app/config/config.yml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
#### Declare an index
Elasticsearch index is comparable to Doctrine entity manager.
Most of the time, you will need only one.
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
Here we created a "website" index, that uses our "default" client.
Our index is now available as a service: `fos_elastica.index.website`. It is an instance of `Elastica_Index`.
If you need to have different index name from the service name, for example,
in order to have different indexes for different environments then you can
use the ```index_name``` key to change the index name. The service name will
remain the same across the environments:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
index_name: website_qa
The service id will be `fos_elastica.index.website` but the underlying index name is website_qa.
#### Declare a type
Elasticsearch type is comparable to Doctrine entity repository.
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
user:
mappings:
username: { boost: 5 }
firstName: { boost: 3 }
lastName: { boost: 3 }
aboutMe: ~
Our type is now available as a service: `fos_elastica.index.website.user`. It is an instance of `Elastica_Type`.
### Declaring parent field
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
comment:
mappings:
post: {_parent: { type: "post", identifier: "id" } }
date: { boost: 5 }
content: ~
### Declaring `nested` or `object`
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
post:
mappings:
date: { boost: 5 }
title: { boost: 3 }
content: ~
comments:
type: "nested"
properties:
date: { boost: 5 }
content: ~
### Populate the types
php app/console fos:elastica:populate
This command deletes and creates the declared indexes and types.
It applies the configured mappings to the types.
This command needs providers to insert new documents in the elasticsearch types.
There are 2 ways to create providers.
If your elasticsearch type matches a Doctrine repository or a Propel query, go for the persistence automatic provider.
Or, for complete flexibility, go for manual provider.
#### Persistence automatic provider
If we want to index the entities from a Doctrine repository or a Propel query,
some configuration will let ElasticaBundle do it for us.
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
user:
mappings:
username: { boost: 5 }
firstName: { boost: 3 }
# more mappings...
persistence:
driver: orm # orm, mongodb, propel are available
model: Application\UserBundle\Entity\User
provider: ~
Three drivers are actually supported: orm, mongodb, and propel.
##### Use a custom Doctrine query builder
You can control which entities will be indexed by specifying a custom query builder method.
persistence:
driver: orm
model: Application\UserBundle\Entity\User
provider:
query_builder_method: createIsActiveQueryBuilder
Your repository must implement this method and return a Doctrine query builder.
> **Propel** doesn't support this feature yet.
##### Change the batch size
By default, ElasticaBundle will index documents by packets of 100.
You can change this value in the provider configuration.
persistence:
driver: orm
model: Application\UserBundle\Entity\User
provider:
batch_size: 100
##### Change the document identifier field
By default, ElasticaBundle will use the `id` field of your entities as the elasticsearch document identifier.
You can change this value in the persistence configuration.
persistence:
driver: orm
model: Application\UserBundle\Entity\User
identifier: id
#### Manual provider
Create a service with the tag "fos_elastica.provider" and attributes for the
index and type for which the service will provide.
<service id="acme.search_provider.user" class="Acme\UserBundle\Search\UserProvider">
<tag name="fos_elastica.provider" index="website" type="user" />
<argument type="service" id="fos_elastica.index.website.user" />
</service>
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
<?php
namespace Acme\UserBundle\Provider;
use FOS\ElasticaBundle\Provider\ProviderInterface;
use Elastica_Type;
class UserProvider implements ProviderInterface
{
protected $userType;
public function __construct(Elastica_Type $userType)
{
$this->userType = $userType;
}
/**
* Insert the repository objects in the type index
*
* @param \Closure $loggerClosure
* @param array $options
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
if ($loggerClosure) {
$loggerClosure('Indexing users');
}
$document = new \Elastica_Document();
$document->setData(array('username' => 'Bob'));
$this->userType->addDocuments(array($document));
}
}
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
### Search
You can just use the index and type Elastica objects, provided as services, to perform searches.
/** var Elastica_Type */
$userType = $this->container->get('fos_elastica.index.website.user');
/** var Elastica_ResultSet */
$resultSet = $userType->search('bob');
#### Doctrine/Propel finder
If your elasticsearch type is bound to a Doctrine entity repository or a Propel query,
you can get your entities instead of Elastica results when you perform a search.
Declare that you want a Doctrine/Propel finder in your configuration:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
user:
mappings:
# your mappings
persistence:
driver: orm
model: Application\UserBundle\Entity\User
provider: ~
finder: ~
You can now use the `fos_elastica.finder.website.user` service:
/** var FOS\ElasticaBundle\Finder\TransformedFinder */
$finder = $container->get('fos_elastica.finder.website.user');
/** var array of Acme\UserBundle\Entity\User */
$users = $finder->find('bob');
/** var array of Acme\UserBundle\Entity\User limited to 10 results */
$users = $finder->find('bob', 10);
You can even get paginated results!
Pagerfanta:
/** var Pagerfanta\Pagerfanta */
$userPaginator = $finder->findPaginated('bob');
Knp paginator:
$paginator = $this->get('knp_paginator');
$userPaginator = $paginator->paginate($finder->createPaginatorAdapter('bob'));
You can also get both the Elastica results and the entities together from the finder.
You can then access the score, highlights etc. from the Elastica_Result whilst
still also getting the entity.
/** var array of FOS\ElasticaBundle\HybridResult */
$hybridResults = $finder->findHybrid('bob');
foreach ($hybridResults as $hybridResult) {
/** var Acme\UserBundle\Entity\User */
$user = $hybridResult->getTransformed();
/** var Elastica_Result */
$result = $hybridResult->getResult();
}
##### Index wide finder
You can also define a finder that will work on the entire index. Adjust your index
configuration as per below:
fos_elastica:
indexes:
website:
client: default
finder: ~
You can now use the index wide finder service `fos_elastica.finder.website`:
/** var FOS\ElasticaBundle\Finder\MappedFinder */
$finder = $container->get('fos_elastica.finder.website');
// Returns a mixed array of any objects mapped
$results = $finder->find('bob');
#### Repositories
As well as using the finder service for a particular Doctrine/Propel entity you
can use a manager service for each driver and get a repository for an entity to search
against. This allows you to use the same service rather than the particular finder. For
example:
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('fos_elastica.manager.orm');
/** var FOS\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');
/** var array of Acme\UserBundle\Entity\User */
$users = $repository->find('bob');
You can also specify the full name of the entity instead of the shortcut syntax:
/** var FOS\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('Application\UserBundle\Entity\User');
> The **2.0** branch doesn't support using `UserBundle:User` style syntax and you must use the full name of the entity. .
##### Default Manager
If you are only using one driver then its manager service is automatically aliased
to `fos_elastica.manager`. So the above example could be simplified to:
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('fos_elastica.manager');
/** var FOS\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');
/** var array of Acme\UserBundle\Entity\User */
$users = $repository->find('bob');
If you use multiple drivers then you can choose which one is aliased to `fos_elastica.manager`
using the `default_manager` parameter:
fos_elastica:
default_manager: mongodb #defauults to orm
clients:
default: { host: localhost, port: 9200 }
#--
##### Custom Repositories
As well as the default repository you can create a custom repository for an entity and add
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
access to the finder:
```
<?php
namespace Acme\ElasticaBundle\SearchRepository;
use FOS\ElasticaBundle\Repository;
class UserRepository extends Repository
{
public function findWithCustomQuery($searchText)
{
// build $query with Elastica objects
$this->find($query);
}
}
```
To use the custom repository specify it in the mapping for the entity:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
user:
mappings:
# your mappings
persistence:
driver: orm
model: Application\UserBundle\Entity\User
provider: ~
finder: ~
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
Then the custom queries will be available when using the repository returned from the manager:
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('fos_elastica.manager');
/** var FOS\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');
/** var array of Acme\UserBundle\Entity\User */
$users = $repository->findWithCustomQuery('bob');
Alternatively you can specify the custom repository using an annotation in the entity:
```
<?php
namespace Application\UserBundle\Entity;
use FOS\ElasticaBundle\Configuration\Search;
/**
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
*/
class User
{
//---
}
```
### Realtime, selective index update
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically
when an object is added, updated or removed. It uses Doctrine lifecycle events.
Declare that you want to update the index in real time:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
user:
mappings:
# your mappings
persistence:
driver: orm
model: Application\UserBundle\Entity\User
listener: # by default, listens to "insert", "update" and "delete"
Now the index is automatically updated each time the state of the bound Doctrine repository changes.
No need to repopulate the whole "user" index when a new `User` is created.
You can also choose to only listen for some of the events:
persistence:
listener:
insert: true
update: false
delete: true
> **Propel** doesn't support this feature yet.
### Checking an entity method for listener
If you use listeners to update your index, you may need to validate your
entities before you index them (e.g. only index "public" entities). Typically,
you'll want the listener to be consistent with the provider's query criteria.
This may be achieved by using the `is_indexable_callback` config parameter:
persistence:
listener:
is_indexable_callback: "isPublic"
If `is_indexable_callback` is a string and the entity has a method with the
specified name, the listener will only index entities for which the method
returns `true`. Additionally, you may provide a service and method name pair:
persistence:
listener:
is_indexable_callback: [ "%custom_service_id%", "isIndexable" ]
In this case, the callback will be the `isIndexable()` method on the specified
service and the object being considered for indexing will be passed as the only
argument. This allows you to do more complex validation (e.g. ACL checks).
As you might expect, new entities will only be indexed if the callback returns
`true`. Additionally, modified entities will be updated or removed from the
index depending on whether the callback returns `true` or `false`, respectively.
The delete listener disregards the callback.
> **Propel** doesn't support this feature yet.
### Ignoring missing index results
By default, FOSElasticaBundle will throw an exception if the results returned from
Elasticsearch are different from the results it finds from the chosen persistence
provider. This may pose problems for a large index where updates do not occur instantly
or another process has removed the results from your persistence provider without
updating Elasticsearch.
The error you're likely to see is something like:
'Cannot find corresponding Doctrine objects for all Elastica results.'
To solve this issue, each mapped object can be configured to ignore the missing results:
persistence:
elastica_to_model_transformer:
ignore_missing: true
### Advanced elasticsearch configuration
Any setting can be specified when declaring a type. For example, to enable a custom analyzer, you could write:
fos_elastica:
indexes:
doc:
settings:
index:
analysis:
analyzer:
my_analyzer:
type: custom
tokenizer: lowercase
filter : [my_ngram]
filter:
my_ngram:
type: "nGram"
min_gram: 3
max_gram: 5
types:
blog:
mappings:
title: { boost: 8, analyzer: my_analyzer }
### Overriding the Client class to suppress exceptions
By default, exceptions from the Elastica client library will propagate through
the bundle's Client class. For instance, if the elasticsearch server is offline,
issuing a request will result in an `Elastica_Exception_Client` being thrown.
Depending on your needs, it may be desirable to suppress these exceptions and
allow searches to fail silently.
One way to achieve this is to override the `fos_elastica.client.class` service
container parameter with a custom class. In the following example, we override
the `Client::request()` method and return the equivalent of an empty search
response if an exception occurred.
```
<?php
namespace Acme\ElasticaBundle;
use FOS\ElasticaBundle\Client as BaseClient;
class Client extends BaseClient
{
public function request($path, $method, $data = array())
{
try {
return parent::request($path, $method, $data);
} catch (\Elastica_Exception_Abstract $e) {
return new \Elastica_Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
}
}
}
```
### Example of Advanced Query
If you would like to perform more advanced queries, here is one example using
the snowball stemming algorithm.
It searches for Article entities using `title`, `tags`, and `categoryIds`.
Results must match at least one specified `categoryIds`, and should match the
`title` or `tags` criteria. Additionally, we define a snowball analyzer to
apply to queries against the `title` field.
```php
$finder = $this->container->get('fos_elastica.finder.website.article');
$boolQuery = new \Elastica_Query_Bool();
$fieldQuery = new \Elastica_Query_Text();
$fieldQuery->setFieldQuery('title', 'I am a title string');
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
$boolQuery->addShould($fieldQuery);
$tagsQuery = new \Elastica_Query_Terms();
$tagsQuery->setTerms('tags', array('tag1', 'tag2'));
$boolQuery->addShould($tagsQuery);
$categoryQuery = new \Elastica_Query_Terms();
$categoryQuery->setTerms('categoryIds', array('1', '2', '3'));
$boolQuery->addMust($categoryQuery);
$data = $finder->find($boolQuery);
```
Configuration:
```yaml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
site:
settings:
index:
analysis:
analyzer:
my_analyzer:
type: snowball
language: English
types:
article:
mappings:
title: { boost: 10, analyzer: my_analyzer }
tags:
categoryIds:
persistence:
driver: orm
model: Acme\DemoBundle\Entity\Article
provider:
finder:
```
#### Date format example
If you want to specify a [date format](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html):
```yaml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
site:
types:
user:
mappings:
username: { type: string }
lastlogin: { type: date, format: basic_date_time }
birthday: { type: date, format: "yyyy-MM-dd" }
```
#### Dynamic templates
Dynamic templates allow to define mapping templates that will be
applied when dynamic introduction of fields / objects happens.
[Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates)
```yaml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
site:
types:
user:
dynamic_templates:
my_template_1:
match: apples_*
mapping:
type: float
my_template_2:
match: *
match_mapping_type: string
mapping:
type: string
index: not_analyzed
mappings:
username: { type: string }
```
Resources/meta/LICENSE

View file

@ -7,7 +7,7 @@ use FOS\ElasticaBundle\Finder\PaginatedFinderInterface;
/**
* @author Richard Miller <info@limethinking.co.uk>
*
* Basic respoitory to be extended to hold custom queries to be run
* Basic repository to be extended to hold custom queries to be run
* in the finder.
*/
class Repository
@ -19,23 +19,49 @@ class Repository
$this->finder = $finder;
}
public function find($query, $limit=null)
/**
* @param mixed $query
* @param integer $limit
* @param array $options
*
* @return array
*/
public function find($query, $limit = null, $options = array())
{
return $this->finder->find($query, $limit);
return $this->finder->find($query, $limit, $options);
}
public function findHybrid($query, $limit=null)
/**
* @param mixed $query
* @param integer $limit
* @param array $options
*
* @return mixed
*/
public function findHybrid($query, $limit = null, $options = array())
{
return $this->finder->findHybrid($query, $limit);
return $this->finder->findHybrid($query, $limit, $options);
}
public function findPaginated($query)
/**
* @param mixed $query
* @param array $options
*
* @return \Pagerfanta\Pagerfanta
*/
public function findPaginated($query, $options = array())
{
return $this->finder->findPaginated($query);
return $this->finder->findPaginated($query, $options);
}
public function createPaginatorAdapter($query)
/**
* @param string $query
* @param array $options
*
* @return Paginator\PaginatorAdapterInterface
*/
public function createPaginatorAdapter($query, $options = array())
{
return $this->finder->createPaginatorAdapter($query);
return $this->finder->createPaginatorAdapter($query, $options);
}
}

View file

@ -2,104 +2,11 @@
namespace FOS\ElasticaBundle;
use FOS\ElasticaBundle\Index\Resetter as BaseResetter;
/**
* Deletes and recreates indexes
* @deprecated Use \FOS\ElasticaBundle\Index\Resetter
*/
class Resetter
class Resetter extends BaseResetter
{
protected $indexConfigsByName;
/**
* Constructor.
*
* @param array $indexConfigsByName
*/
public function __construct(array $indexConfigsByName)
{
$this->indexConfigsByName = $indexConfigsByName;
}
/**
* Deletes and recreates all indexes
*/
public function resetAllIndexes()
{
foreach ($this->indexConfigsByName as $indexConfig) {
$indexConfig['index']->create($indexConfig['config'], true);
}
}
/**
* Deletes and recreates the named index
*
* @param string $indexName
* @throws \InvalidArgumentException if no index exists for the given name
*/
public function resetIndex($indexName)
{
$indexConfig = $this->getIndexConfig($indexName);
$indexConfig['index']->create($indexConfig['config'], true);
}
/**
* Deletes and recreates a mapping type for the named index
*
* @param string $indexName
* @param string $typeName
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
*/
public function resetIndexType($indexName, $typeName)
{
$indexConfig = $this->getIndexConfig($indexName);
if (!isset($indexConfig['config']['mappings'][$typeName]['properties'])) {
throw new \InvalidArgumentException(sprintf('The mapping for index "%s" and type "%s" does not exist.', $indexName, $typeName));
}
$type = $indexConfig['index']->getType($typeName);
$type->delete();
$mapping = $this->createMapping($indexConfig['config']['mappings'][$typeName]);
$type->setMapping($mapping);
}
/**
* create type mapping object
*
* @param array $indexConfig
* @return \Elastica_Type_Mapping
*/
protected function createMapping($indexConfig)
{
$mapping = \Elastica_Type_Mapping::create($indexConfig['properties']);
foreach($indexConfig['properties'] as $type) {
if (!empty($type['_parent']) && $type['_parent'] !== '~') {
$mapping->setParam('_parent', array('type' => $type['_parent']['type']));
}
}
if (isset($indexConfig['dynamic_templates'])) {
$mapping->setParam('dynamic_templates', $indexConfig['dynamic_templates']);
}
return $mapping;
}
/**
* Gets an index config by its name
*
* @param string $indexName Index name
*
* @param $indexName
* @return array
* @throws \InvalidArgumentException if no index config exists for the given name
*/
protected function getIndexConfig($indexName)
{
if (!isset($this->indexConfigsByName[$indexName])) {
throw new \InvalidArgumentException(sprintf('The configuration for index "%s" does not exist.', $indexName));
}
return $this->indexConfigsByName[$indexName];
}
}

View file

@ -1,77 +1,51 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_elastica.client.class">FOS\ElasticaBundle\Client</parameter>
<parameter key="fos_elastica.index.class">Elastica_Index</parameter>
<parameter key="fos_elastica.type.class">Elastica_Type</parameter>
<parameter key="fos_elastica.client.class">FOS\ElasticaBundle\Elastica\Client</parameter>
<parameter key="fos_elastica.logger.class">FOS\ElasticaBundle\Logger\ElasticaLogger</parameter>
<parameter key="fos_elastica.data_collector.class">FOS\ElasticaBundle\DataCollector\ElasticaDataCollector</parameter>
<parameter key="fos_elastica.manager.class">FOS\ElasticaBundle\Manager\RepositoryManager</parameter>
<parameter key="fos_elastica.elastica_to_model_transformer.collection.class">FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection</parameter>
<parameter key="fos_elastica.provider_registry.class">FOS\ElasticaBundle\Provider\ProviderRegistry</parameter>
<parameter key="fos_elastica.mapping_builder.class">FOS\ElasticaBundle\Index\MappingBuilder</parameter>
<parameter key="fos_elastica.property_accessor.class">Symfony\Component\PropertyAccess\PropertyAccessor</parameter>
</parameters>
<services>
<service id="fos_elastica.client_prototype" class="%fos_elastica.client.class%" abstract="true">
<argument type="collection" /> <!-- configuration -->
<argument /> <!-- callback -->
<call method="setStopwatch">
<argument type="service" id="debug.stopwatch" on-invalid="null" />
</call>
</service>
<service id="fos_elastica.config_manager" class="FOS\ElasticaBundle\Configuration\ConfigManager">
<argument type="collection" /> <!-- collection of SourceInterface services -->
</service>
<service id="fos_elastica.data_collector" class="%fos_elastica.data_collector.class%">
<tag name="data_collector" template="FOSElasticaBundle:Collector:elastica" id="elastica" />
<argument type="service" id="fos_elastica.logger" />
</service>
<service id="fos_elastica.paginator.subscriber" class="FOS\ElasticaBundle\Subscriber\PaginateElasticaQuerySubscriber">
<call method="setRequest">
<argument type="service" id="request" on-invalid="null" strict="false" />
</call>
<tag name="knp_paginator.subscriber" />
</service>
<service id="fos_elastica.logger" class="%fos_elastica.logger.class%">
<argument type="service" id="logger" on-invalid="null" />
<argument>%kernel.debug%</argument>
<tag name="monolog.logger" channel="elastica" />
</service>
<service id="fos_elastica.data_collector" class="%fos_elastica.data_collector.class%" public="true">
<tag name="data_collector" template="FOSElasticaBundle:Collector:elastica" id="elastica" />
<argument type="service" id="fos_elastica.logger" />
</service>
<service id="fos_elastica.index_manager" class="FOS\ElasticaBundle\IndexManager">
<argument /> <!-- indexes -->
<argument /> <!-- default index -->
</service>
<service id="fos_elastica.mapping_builder" class="%fos_elastica.mapping_builder.class%" />
<service id="fos_elastica.resetter" class="FOS\ElasticaBundle\Resetter">
<argument /> <!-- index configs -->
</service>
<service id="fos_elastica.object_persister" class="FOS\ElasticaBundle\Persister\ObjectPersister" abstract="true">
<argument /> <!-- type -->
<argument /> <!-- model to elastica transformer -->
<argument /> <!-- model -->
<argument /> <!-- properties mapping -->
</service>
<service id="fos_elastica.finder" class="FOS\ElasticaBundle\Finder\TransformedFinder" public="true" abstract="true">
<argument /> <!-- searchable -->
<argument /> <!-- transformer -->
</service>
<service id="fos_elastica.model_to_elastica_transformer" class="FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer" public="false" abstract="true">
<argument /> <!-- options -->
<call method="setPropertyAccessor">
<argument type="service" id="fos_elastica.property_accessor" />
</call>
</service>
<service id="fos_elastica.elastica_to_model_transformer.collection" class="%fos_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
<argument type="collection" /> <!-- transformers -->
</service>
<service id="fos_elastica.provider_registry" class="%fos_elastica.provider_registry.class%">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
<service id="fos_elastica.paginator.subscriber" class="FOS\ElasticaBundle\Subscriber\PaginateElasticaQuerySubscriber">
<tag name="knp_paginator.subscriber" />
</service>
<service id="fos_elastica.property_accessor" class="%fos_elastica.property_accessor.class%">
</service>
<service id="fos_elastica.property_accessor" class="%fos_elastica.property_accessor.class%" />
</services>
</container>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_elastica.alias_processor.class">FOS\ElasticaBundle\Index\AliasProcessor</parameter>
<parameter key="fos_elastica.finder.class">FOS\ElasticaBundle\Finder\TransformedFinder</parameter>
<parameter key="fos_elastica.index.class">FOS\ElasticaBundle\Elastica\Index</parameter>
<parameter key="fos_elastica.indexable.class">FOS\ElasticaBundle\Provider\Indexable</parameter>
<parameter key="fos_elastica.index_manager.class">FOS\ElasticaBundle\Index\IndexManager</parameter>
<parameter key="fos_elastica.resetter.class">FOS\ElasticaBundle\Index\Resetter</parameter>
<parameter key="fos_elastica.type.class">Elastica\Type</parameter>
</parameters>
<services>
<service id="fos_elastica.alias_processor" class="%fos_elastica.alias_processor.class%" />
<service id="fos_elastica.indexable" class="%fos_elastica.indexable.class%">
<argument type="collection" /> <!-- array of indexable callbacks keyed by type name -->
<argument type="service" id="service_container" />
</service>
<service id="fos_elastica.index_prototype" class="%fos_elastica.index.class%" factory-service="fos_elastica.client" factory-method="getIndex" abstract="true">
<argument /> <!-- index name -->
<!-- tagged with fos_elastica.index in the Extension -->
</service>
<service id="fos_elastica.type_prototype" class="%fos_elastica.type.class%" factory-method="getType" abstract="true">
<argument /> <!-- type name -->
</service>
<service id="fos_elastica.index_manager" class="%fos_elastica.index_manager.class%">
<argument /> <!-- indexes -->
<argument type="service" id="fos_elastica.index" /> <!-- default index -->
</service>
<service id="fos_elastica.resetter" class="%fos_elastica.resetter.class%">
<argument type="service" id="fos_elastica.config_manager" />
<argument type="service" id="fos_elastica.index_manager" />
<argument type="service" id="fos_elastica.alias_processor" />
<argument type="service" id="fos_elastica.mapping_builder" />
<argument type="service" id="event_dispatcher"/>
</service>
<!-- Abstract definition for all finders. -->
<service id="fos_elastica.finder" class="%fos_elastica.finder.class%" public="true" abstract="true">
<argument /> <!-- searchable -->
<argument /> <!-- transformer -->
</service>
</services>
</container>

View file

@ -4,23 +4,35 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<parameters>
<parameter key="fos_elastica.slice_fetcher.mongodb.class">FOS\ElasticaBundle\Doctrine\MongoDB\SliceFetcher</parameter>
<parameter key="fos_elastica.provider.prototype.mongodb.class">FOS\ElasticaBundle\Doctrine\MongoDB\Provider</parameter>
<parameter key="fos_elastica.listener.prototype.mongodb.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.mongodb.class">FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer</parameter>
<parameter key="fos_elastica.manager.mongodb.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
</parameters>
<service id="fos_elastica.provider.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\Provider" public="true" abstract="true">
<services>
<service id="fos_elastica.slice_fetcher.mongodb" class="%fos_elastica.slice_fetcher.mongodb.class%">
</service>
<service id="fos_elastica.provider.prototype.mongodb" class="%fos_elastica.provider.prototype.mongodb.class%" public="true" abstract="true">
<argument /> <!-- object persister -->
<argument type="service" id="fos_elastica.indexable" />
<argument /> <!-- model -->
<argument type="collection" /> <!-- options -->
<argument type="service" id="doctrine_mongodb" />
<argument type="service" id="doctrine_mongodb" /> <!-- manager registry -->
<argument type="service" id="fos_elastica.slice_fetcher.mongodb" /> <!-- slice fetcher -->
</service>
<service id="fos_elastica.listener.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\Listener" public="false" abstract="true">
<service id="fos_elastica.listener.prototype.mongodb" class="%fos_elastica.listener.prototype.mongodb.class%" public="false" abstract="true">
<argument /> <!-- object persister -->
<argument /> <!-- model -->
<argument type="collection" /> <!-- events -->
<argument/> <!-- identifier -->
<argument type="service" id="fos_elastica.indexable" />
<argument type="collection" /> <!-- configuration -->
<argument>null</argument> <!-- logger -->
</service>
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer" public="false">
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="%fos_elastica.elastica_to_model_transformer.prototype.mongodb.class%" public="false" abstract="true">
<argument type="service" id="doctrine_mongodb" />
<argument /> <!-- model -->
<argument type="collection" /> <!-- options -->
@ -29,11 +41,9 @@
</call>
</service>
<service id="fos_elastica.manager.mongodb" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
<service id="fos_elastica.manager.mongodb" class="%fos_elastica.manager.mongodb.class%">
<argument type="service" id="doctrine_mongodb"/>
<argument type="service" id="annotation_reader"/>
</service>
</services>
</container>

View file

@ -1,27 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_elastica.slice_fetcher.orm.class">FOS\ElasticaBundle\Doctrine\ORM\SliceFetcher</parameter>
<parameter key="fos_elastica.provider.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\Provider</parameter>
<parameter key="fos_elastica.listener.prototype.orm.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer</parameter>
<parameter key="fos_elastica.manager.orm.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
</parameters>
<services>
<service id="fos_elastica.slice_fetcher.orm" class="%fos_elastica.slice_fetcher.orm.class%">
</service>
<service id="fos_elastica.provider.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\Provider" public="true" abstract="true">
<service id="fos_elastica.provider.prototype.orm" class="%fos_elastica.provider.prototype.orm.class%" public="true" abstract="true">
<argument /> <!-- object persister -->
<argument type="service" id="fos_elastica.indexable" />
<argument /> <!-- model -->
<argument type="collection" /> <!-- options -->
<argument type="service" id="doctrine" />
<argument type="service" id="doctrine" /> <!-- manager registry -->
<argument type="service" id="fos_elastica.slice_fetcher.orm" /> <!-- slice fetcher -->
</service>
<service id="fos_elastica.listener.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\Listener" public="false" abstract="true">
<service id="fos_elastica.listener.prototype.orm" class="%fos_elastica.listener.prototype.orm.class%" public="false" abstract="true">
<argument /> <!-- object persister -->
<argument /> <!-- model -->
<argument type="collection" /> <!-- events -->
<argument/> <!-- identifier -->
<argument /> <!-- check method -->
<argument type="service" id="fos_elastica.indexable" />
<argument type="collection" /> <!-- configuration -->
<argument>null</argument> <!-- logger -->
</service>
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer" public="false">
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="%fos_elastica.elastica_to_model_transformer.prototype.orm.class%" public="false" abstract="true">
<argument type="service" id="doctrine" />
<argument /> <!-- model -->
<argument type="collection" /> <!-- options -->
@ -30,11 +41,9 @@
</call>
</service>
<service id="fos_elastica.manager.orm" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
<argument type="service" id="doctrine"/>
<argument type="service" id="annotation_reader"/>
<service id="fos_elastica.manager.orm" class="%fos_elastica.manager.orm.class%">
<argument type="service" id="doctrine" />
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_elastica.object_persister.class">FOS\ElasticaBundle\Persister\ObjectPersister</parameter>
<parameter key="fos_elastica.object_serializer_persister.class">FOS\ElasticaBundle\Persister\ObjectSerializerPersister</parameter>
</parameters>
<services>
<service id="fos_elastica.object_persister" class="%fos_elastica.object_persister.class%" abstract="true">
<argument /> <!-- type -->
<argument /> <!-- model to elastica transformer -->
<argument /> <!-- model -->
<argument /> <!-- properties mapping -->
</service>
<service id="fos_elastica.object_serializer_persister" class="%fos_elastica.object_serializer_persister.class%" abstract="true">
<argument /> <!-- type -->
<argument /> <!-- model to elastica transformer -->
<argument /> <!-- model -->
<argument /> <!-- serializer -->
</service>
</services>
</container>

View file

@ -4,9 +4,9 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="fos_elastica.provider.prototype.propel" class="FOS\ElasticaBundle\Propel\Provider" public="true" abstract="true">
<argument /> <!-- object persister -->
<argument type="service" id="fos_elastica.indexable" />
<argument /> <!-- model -->
<argument type="collection" /> <!-- options -->
</service>
@ -19,10 +19,8 @@
</call>
</service>
<service id="fos_elastica.manager.propel" class="%fos_elastica.manager.class%">
<service id="fos_elastica.manager.propel" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
<argument type="service" id="annotation_reader"/>
</service>
</services>
</container>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_elastica.provider_registry.class">FOS\ElasticaBundle\Provider\ProviderRegistry</parameter>
</parameters>
<services>
<service id="fos_elastica.provider_registry" class="%fos_elastica.provider_registry.class%">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
</services>
</container>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="fos_elastica.serializer_callback_prototype" public="false" abstract="true">
<call method="setSerializer">
<argument type="service" id="fos_elastica.serializer" />
</call>
</service>
</services>
</container>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="fos_elastica.config_source.container" class="FOS\ElasticaBundle\Configuration\Source\ContainerSource" public="false">
<argument type="collection" /> <!-- index configs -->
<tag name="fos_elastica.config_source" />
</service>
</services>
</container>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_elastica.elastica_to_model_transformer.collection.class">FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection</parameter>
<parameter key="fos_elastica.model_to_elastica_transformer.class">FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer</parameter>
<parameter key="fos_elastica.model_to_elastica_identifier_transformer.class">FOS\ElasticaBundle\Transformer\ModelToElasticaIdentifierTransformer</parameter>
</parameters>
<services>
<service id="fos_elastica.model_to_elastica_transformer" class="%fos_elastica.model_to_elastica_transformer.class%" public="false" abstract="true">
<argument type="collection" /> <!-- options -->
<argument type="service" id="event_dispatcher" /> <!-- options -->
<call method="setPropertyAccessor">
<argument type="service" id="fos_elastica.property_accessor" />
</call>
</service>
<service id="fos_elastica.model_to_elastica_identifier_transformer" class="%fos_elastica.model_to_elastica_identifier_transformer.class%" public="false" abstract="true">
<argument type="collection" /> <!-- options -->
<call method="setPropertyAccessor">
<argument type="service" id="fos_elastica.property_accessor" />
</call>
</service>
<service id="fos_elastica.elastica_to_model_transformer.collection" class="%fos_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
<argument type="collection" /> <!-- transformers -->
</service>
</services>
</container>

View file

@ -0,0 +1,45 @@
Aliased Indexes
===============
You can set up FOSElasticaBundle to use aliases for indexes which allows you to run an
index population without resetting the index currently being used by the application.
> *Note*: When you're using an alias, resetting an individual type will still cause a
> reset for that type.
To configure FOSElasticaBundle to use aliases for an index, set the use_alias option to
true.
```yaml
fos_elastica:
indexes:
website:
use_alias: true
```
The process for setting up aliases on an existing application is slightly more complicated
because the bundle is not able to set an alias as the same name as an index. You have some
options on how to handle this:
1) Delete the index from Elasticsearch. This option will make searching unavailable in your
application until a population has completed itself, and an alias is created.
2) Change the index_name parameter for your index to something new, and manually alias the
current index to the new index_name, which will then be replaced when you run a repopulate.
```yaml
fos_elastica:
indexes:
website:
use_alias: true
index_name: website_prod
```
```bash
$ curl -XPOST 'http://localhost:9200/_aliases' -d '
{
"actions" : [
{ "add" : { "index" : "website", "alias" : "website_prod" } }
]
}'
```

View file

@ -0,0 +1,33 @@
##### Custom Properties
Since FOSElasticaBundle 3.1.0, we now dispatch an event for each transformation of an
object into an Elastica document which allows you to set custom properties on the Elastica
document for indexing.
Set up an event listener or subscriber for
`FOS\ElasticaBundle\Event\TransformEvent::POST_TRANSFORM` to be able to inject your own
parameters.
```php
class CustomPropertyListener implements EventSubscriberInterface
{
private $anotherService;
// ...
public function addCustomProperty(TransformEvent $event)
{
$document = $event->getDocument();
$custom = $this->anotherService->calculateCustom($event->getObject());
$document->set('custom', $custom);
}
public static function getSubscribedEvents()
{
return array(
TransformEvent::POST_TRANSFORM => 'addCustomProperty',
);
}
}
```

View file

@ -0,0 +1,76 @@
##### Custom Repositories
As well as the default repository you can create a custom repository for an entity and add
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
access to the finder:
```php
<?php
namespace Acme\ElasticaBundle\SearchRepository;
use FOS\ElasticaBundle\Repository;
class UserRepository extends Repository
{
public function findWithCustomQuery($searchText)
{
// build $query with Elastica objects
$this->find($query);
}
}
```
To use the custom repository specify it in the mapping for the entity:
```yaml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
user:
mappings:
# your mappings
persistence:
driver: orm
model: Application\UserBundle\Entity\User
provider: ~
finder: ~
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
```
Then the custom queries will be available when using the repository returned from the manager:
```php
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('fos_elastica.manager');
/** var FOS\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');
/** var array of Acme\UserBundle\Entity\User */
$users = $repository->findWithCustomQuery('bob');
```
Alternatively you can specify the custom repository using an annotation in the entity:
```php
<?php
namespace Application\UserBundle\Entity;
use FOS\ElasticaBundle\Annotation\Search;
/**
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
*/
class User
{
//---
}
```

View file

@ -0,0 +1,18 @@
Setting HTTP Headers on the Elastica Client
===========================================
It may be necessary to set HTTP headers on the Elastica client, for example an
Authorization header.
They can be set using the headers configuration key:
```yaml
# app/config/config.yml
fos_elastica:
clients:
default:
host: example.com
port: 80
headers:
Authorization: "Basic jdumrGK7rY9TMuQOPng7GZycmxyMHNoir=="
```

View file

@ -0,0 +1,35 @@
Logging and its performance considerations
==========================================
By default, FOSElasticaBundle sets a logger against each Elastica client configured and
logs all information sent to and received from Elasticsearch. This can lead to large
memory usage during population or reindexing of an index.
By default FOSElasticaBundle will only enable a logger when debug mode is enabled, meaning
in a production environment there wont be a logger enabled. To enable a logger anyway, you
can set the logger property of a client configuration to true or a service id of a logging
service you wish to use.
```yaml
# app/config/config.yml
fos_elastica:
clients:
default:
host: example.com
logger: true
```
Custom Logger Service
---------------------
It is also possible to specify a custom logger instance to be injected into each client by
specifying the service id of the logger you wish to use.
```yaml
# app/config/config.yml
fos_elastica:
clients:
default:
host: example.com
logger: 'acme.custom.logger'
```

View file

@ -0,0 +1,56 @@
Manual provider
===============
Create a service with the tag "fos_elastica.provider" and attributes for the
index and type for which the service will provide.
```yaml
# app/config/config.yml
services:
acme.search_provider.user:
class: Acme\UserBundle\Provider\UserProvider
arguments:
- @fos_elastica.index.website.user
tags:
- { name: fos_elastica.provider, index: website, type: user }
```
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
```php
namespace Acme\UserBundle\Provider;
use FOS\ElasticaBundle\Provider\ProviderInterface;
use Elastica\Type;
use Elastica\Document;
class UserProvider implements ProviderInterface
{
protected $userType;
public function __construct(Type $userType)
{
$this->userType = $userType;
}
/**
* Insert the repository objects in the type index
*
* @param \Closure $loggerClosure
* @param array $options
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
if ($loggerClosure) {
$loggerClosure('Indexing users');
}
$document = new Document();
$document->setData(array('username' => 'Bob'));
$this->userType->addDocuments(array($document));
}
}
```
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.

View file

@ -0,0 +1,21 @@
Multiple Connections
====================
You can define multiple endpoints for an Elastica client by specifying them as
multiple connections in the client configuration:
```yaml
fos_elastica:
clients:
default:
connections:
- url: http://es1.example.net:9200
- url: http://es2.example.net:9200
connection_strategy: RoundRobin
```
Elastica allows for definition of different connection strategies and by default
supports `RoundRobin` and `Simple`. You can see definitions for these strategies
in the `Elastica\Connection\Strategy` namespace.
For more information on Elastica clustering see http://elastica.io/getting-started/installation.html#section-connect-cluster

View file

@ -0,0 +1,59 @@
Suppressing Server Errors
=========================
By default, exceptions from the Elastica client library will propagate through
the bundle's Client class. For instance, if the Elasticsearch server is offline,
issuing a request will result in an `Elastica\Exception\Connection` being thrown.
Depending on your needs, it may be desirable to suppress these exceptions and
allow searches to fail silently.
One way to achieve this is to override the `fos_elastica.client.class` service
container parameter with a custom class. In the following example, we override
the `Client::request()` method and return the equivalent of an empty search
response if an exception occurred.
Sample client code:
-------------------
```php
<?php
namespace Acme\ElasticaBundle;
use Elastica\Exception\ExceptionInterface;
use Elastica\Request;
use Elastica\Response;
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
class Client extends BaseClient
{
public function request($path, $method = Request::GET, $data = array(), array $query = array())
{
try {
return parent::request($path, $method, $data, $query);
} catch (ExceptionInterface $e) {
if ($this->_logger) {
$this->_logger->warning('Failed to send a request to ElasticSearch', array(
'exception' => $e->getMessage(),
'path' => $path,
'method' => $method,
'data' => $data,
'query' => $query
));
}
return new Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
}
}
}
```
Configuration change:
---------------------
You must update a parameter in your `app/config/config.yml` file to point to your overridden client:
```yaml
parameters:
fos_elastica.client.class: Acme\ElasticaBundle\Client
```

22
Resources/doc/index.md Normal file
View file

@ -0,0 +1,22 @@
FOSElasticaBundle Documentation
===============================
Available documentation for FOSElasticaBundle
---------------------------------------------
* [Setup](setup.md)
* [Usage](usage.md)
* [Using a Serializer](serializer.md)
* [Types](types.md)
Cookbook Entries
----------------
* [Aliased Indexes](cookbook/aliased-indexes.md)
* [Custom Indexed Properties](cookbook/custom-properties.md)
* [Custom Repositories](cookbook/custom-repositories.md)
* [HTTP Headers for Elastica](cookbook/elastica-client-http-headers.md)
* Performance - [Logging](cookbook/logging.md)
* [Manual Providers](cookbook/manual-provider.md)
* [Clustering - Multiple Connections](cookbook/multiple-connections.md)
* [Suppressing server errors](cookbook/suppress-server-errors.md)

Some files were not shown because too many files have changed in this diff Show more