Compare commits
949 commits
Author | SHA1 | Date | |
---|---|---|---|
Simon Vieille | 30e351b1d5 | ||
Simon Vieille | e3abbdc700 | ||
4451bd07c6 | |||
1f8a330140 | |||
8f7f24e6d3 | |||
e71ec4ac8a | |||
adf7fb21e3 | |||
1287d9f0df | |||
c5728b5870 | |||
7baf494c56 | |||
ad20382e08 | |||
e933a49d07 | |||
69470d7e20 | |||
b6e01cd332 | |||
35276f469a | |||
ec9f23bd8d | |||
ae4cfd7e04 | |||
49a0c22724 | |||
8d8b04ead8 | |||
a59f2015b4 | |||
447d29ab9c | |||
73093beadb | |||
5181b02933 | |||
ac98549eb5 | |||
9c1c771799 | |||
3bb2f384ba | |||
d4f01e8d2e | |||
72a9dfa267 | |||
9cf0117c71 | |||
bb4618c101 | |||
6a07f7b24e | |||
dd3269d1ef | |||
84e5831a81 | |||
d5a9b7b235 | |||
4af9f442fd | |||
4081c32ca0 | |||
925410a66e | |||
b6c252aac3 | |||
133f71b88a | |||
c013ed9657 | |||
f72c51503a | |||
4e087af50d | |||
559b14b4a5 | |||
2215d07ff8 | |||
89db88c2a0 | |||
345b5d423d | |||
a8f41fa5ef | |||
e796d6179b | |||
dd388e4b25 | |||
81186e40db | |||
14af748840 | |||
19e9abaa53 | |||
5b88baeca6 | |||
b7c7f77383 | |||
181b5a0ac0 | |||
0009c858a7 | |||
f834499a2c | |||
3d69c08b5a | |||
cbb247978a | |||
a830a9b7b6 | |||
7b90c84daa | |||
034b3a9c60 | |||
cf9f7c6be8 | |||
4a564401b4 | |||
ef2671dd36 | |||
47785260a4 | |||
cb7b4c1dca | |||
4c87d24fc1 | |||
f6df88cc67 | |||
7fa14f713c | |||
55bfee22e8 | |||
07995d9b75 | |||
72a981ab51 | |||
1be1cb645c | |||
70fe702ccf | |||
3c87cc16c7 | |||
c5185a0307 | |||
e1d5ef72d2 | |||
797d066286 | |||
72589f8341 | |||
2401b1083c | |||
8e627ee011 | |||
6992beeb47 | |||
7bc88494a1 | |||
30d52bf0f0 | |||
58eed2dc7f | |||
d44525f6f3 | |||
67c0b79505 | |||
6bb2def21e | |||
e5d9c3ddbb | |||
906e2e0749 | |||
81f5f983c0 | |||
030b194c7b | |||
7f28be3c4e | |||
79e263d7a7 | |||
e772ca6450 | |||
55abe132c6 | |||
aef5940578 | |||
b9b0c1b961 | |||
64fa26e3d9 | |||
c4a2858265 | |||
7471c13d75 | |||
c901d60552 | |||
401446e1c4 | |||
1cea135dc5 | |||
32d190f554 | |||
5060fa4d4a | |||
b4c01f3641 | |||
1bf59d5b66 | |||
9188566dfe | |||
d7e9d9b8a6 | |||
a28b9d3069 | |||
c2c87a53e4 | |||
2ce2d7e610 | |||
905265ea0e | |||
ee9f7e5297 | |||
a0f11ff36f | |||
7c90660e02 | |||
7efcdad97c | |||
afbe1e03a1 | |||
303af508b2 | |||
c39b86c6c5 | |||
b6d46dba4a | |||
9f5ce217dc | |||
6ef6092f3f | |||
92aab4bcf6 | |||
e361b7c53b | |||
7fac93ff8b | |||
d731443aa5 | |||
156884527c | |||
5eaff9e61b | |||
3975ed3d5b | |||
d33e064801 | |||
1f7acc563a | |||
eaa32cbf22 | |||
f3e31e613e | |||
97848ca0d0 | |||
88e9f5aac6 | |||
ca57c42489 | |||
2664fec35e | |||
c45dcd955d | |||
7dc2f833c4 | |||
e211f31658 | |||
00df6c586f | |||
921377d5ed | |||
c4ee9fa83e | |||
b09bf3cf10 | |||
1e2da2d84f | |||
2d8903a330 | |||
e6d50c584c | |||
2a7459f327 | |||
c80b4efd3e | |||
1369a01dd7 | |||
901ea49a32 | |||
1b01aef46f | |||
be75b387a5 | |||
445f2f93f6 | |||
e7634d8ba2 | |||
419bf2ccf6 | |||
6f4e389dfd | |||
196aed6630 | |||
7c6fe4eaab | |||
d2de7ba6e8 | |||
197bb3ebad | |||
f9ce1dcd4e | |||
4c4e9ffe36 | |||
7fa7e44bee | |||
67ae044309 | |||
25d56d0a0f | |||
a88e4e38f8 | |||
5cdeac9b45 | |||
b3f87e414f | |||
71a86cada5 | |||
d0ce82ac2a | |||
428a1014ca | |||
1d5fe44ca4 | |||
cf586a4ef4 | |||
c4210a5c6d | |||
029ebb153a | |||
736163551c | |||
76dcd2f62e | |||
2958833012 | |||
6bea3c2154 | |||
001daeeac0 | |||
a7a23b92cb | |||
598a59927e | |||
0425379420 | |||
380727afbe | |||
69c2214bc5 | |||
af5fb7e97e | |||
22a2a223cc | |||
c08d86124a | |||
33ee047f83 | |||
49c5b2ee63 | |||
27385046ca | |||
517d6e679c | |||
420135dc85 | |||
20033709cf | |||
229d4cb982 | |||
84cf6c79c2 | |||
9d45de8b64 | |||
f5987a48b9 | |||
dafe8abe0e | |||
0d22c20d37 | |||
f9eb6577d1 | |||
c44f676224 | |||
9296534d30 | |||
f6e018f011 | |||
9a5b80e723 | |||
eaa6c2e085 | |||
64c5c19831 | |||
9befa90f41 | |||
659468ae3a | |||
001b38cf59 | |||
1ef55b1239 | |||
c748ec64e9 | |||
11ee25cfea | |||
e5410a5b65 | |||
9fbc622929 | |||
fad481d822 | |||
714502fa1f | |||
7cedc5ba5a | |||
d797af5b80 | |||
cdaf7105e0 | |||
d88d96bf55 | |||
bf38e59e49 | |||
90a554f627 | |||
d57d430ab3 | |||
be4af0d1af | |||
425aa3d3e1 | |||
21e5d906a7 | |||
815b836cdc | |||
55dcf6859a | |||
c200e8fdfd | |||
965b319d82 | |||
7fcbb64a15 | |||
5d65676659 | |||
c9a24436f3 | |||
9f85db9876 | |||
78db0b9b63 | |||
ad37a28356 | |||
474cbfa979 | |||
77f2b99a3e | |||
5cc8c2978f | |||
96dc613c71 | |||
4eacb5f4c8 | |||
a879d3c1c9 | |||
ffa73db1d2 | |||
4d52c327aa | |||
524474fdc6 | |||
ae03b3f3cf | |||
f6264f4149 | |||
2437a098ba | |||
ae3605828e | |||
2a20bb623c | |||
9a62187329 | |||
ca6991d494 | |||
95e445bd0d | |||
c97f0f1ddf | |||
4e990e0cee | |||
b0749afaf1 | |||
3ae382c933 | |||
949ea6963f | |||
b155f304e4 | |||
afbaf875b9 | |||
8905b4248c | |||
5cbb8ce1b6 | |||
b3c0d4fd44 | |||
ec5c05e8be | |||
e78950ddb7 | |||
5f335c37ab | |||
1e07d3c7fe | |||
089e7f0d2e | |||
aafb8c8e89 | |||
e225d841ed | |||
b49437529c | |||
94568d9554 | |||
2e23f4a1bf | |||
02d864f7e2 | |||
12797c60fa | |||
934c6af8b8 | |||
0383811834 | |||
ada3942576 | |||
813a4a5d26 | |||
7682d5a80a | |||
64be10447d | |||
629ca0df2e | |||
391e18dcbf | |||
e54cc3c243 | |||
66d2410999 | |||
f5932a8e47 | |||
fe19df365a | |||
14083496d7 | |||
5009673b6a | |||
833feee207 | |||
f6b9e57a9c | |||
366fb39606 | |||
8540f13bbf | |||
fa65784b47 | |||
1dc6856ef9 | |||
6a822504bc | |||
c52c32fb56 | |||
2e0aa064a2 | |||
12b724dd20 | |||
8060d3dcd7 | |||
dad15d0b38 | |||
53180e2810 | |||
8e88505a3f | |||
a79fa0242e | |||
c38dc107e7 | |||
be89ccf825 | |||
f97e66712a | |||
5a84d55129 | |||
f9745c8d21 | |||
a9ea78443f | |||
458b53240b | |||
e77aa0c180 | |||
f20392d78b | |||
f8a445b46c | |||
d532e6b1e3 | |||
3addfffc91 | |||
18143449cc | |||
b09c7fb50e | |||
352e3b68ac | |||
41c4d77b20 | |||
843c76b6ca | |||
6d2b7a8367 | |||
57fbc70015 | |||
2029aba76a | |||
6253d3f8df | |||
28d0ee925d | |||
2c208a4f10 | |||
70ad5c9b37 | |||
e1bbb87cfe | |||
6748c9c623 | |||
72e7b77dae | |||
1c5339ac40 | |||
e5754ef5fc | |||
6bbe61f319 | |||
b0841c18ec | |||
62f6cf0f8a | |||
5da8ee1a16 | |||
b1d64e358d | |||
eaa9f83997 | |||
ae02364e7c | |||
ca50617776 | |||
d9f3fa1a59 | |||
c93bbb9081 | |||
39f1033a34 | |||
90abc44968 | |||
c9fd1cc5d9 | |||
1748e9af18 | |||
806452813a | |||
35700cdd78 | |||
dd7b8d3210 | |||
0dcb77d749 | |||
2bd6aba7ef | |||
d33f2e547f | |||
b11d48fe89 | |||
8387667167 | |||
4e638a0492 | |||
165696a5a0 | |||
449c33aea3 | |||
f93a7d278f | |||
4cfe24ae02 | |||
088452cf88 | |||
1bc085141b | |||
a97ed80aee | |||
b3fdf7b256 | |||
f07e55417d | |||
22e5a1d4ab | |||
d7302847e4 | |||
998c69bfc3 | |||
a483471694 | |||
ab3587980c | |||
10fcbfc135 | |||
53332eb057 | |||
377b2843ba | |||
10f6149f8c | |||
6f444f1ce8 | |||
49284a4963 | |||
b6e2583455 | |||
d67d525b66 | |||
1bc148569b | |||
13c2d10e39 | |||
e74acb1e4f | |||
1628413e65 | |||
a89856be50 | |||
ff95945819 | |||
ec7f04a261 | |||
5855ac6fbd | |||
20810fa415 | |||
588c4e2d02 | |||
f15ca02859 | |||
0de48d2190 | |||
40e79b2abe | |||
a81a630520 | |||
8c146134da | |||
38b4074745 | |||
9f27c1dade | |||
e25a5420a5 | |||
37db040096 | |||
eb7758605e | |||
49f077cc30 | |||
befb0907cc | |||
e83ea0b6c8 | |||
1e44051442 | |||
361d80a720 | |||
720917f609 | |||
455ff9e0f7 | |||
2009f88109 | |||
514e63f26f | |||
4dc08cd006 | |||
41b347dfe4 | |||
de70e78b53 | |||
babe20800e | |||
7d13823488 | |||
dc01b189ed | |||
5292a2adad | |||
49521e9fc4 | |||
857c1c8e48 | |||
6a26c63b2c | |||
cdd6e3af45 | |||
6cd69ad131 | |||
7cf8cd446f | |||
6beafd23f1 | |||
da0abca38c | |||
48edc195cd | |||
2f9896c893 | |||
726892c586 | |||
33f85bec22 | |||
fe871c5ac4 | |||
0116a6ac4f | |||
7f53badad5 | |||
a1f7449efc | |||
245732781b | |||
418b9d72ce | |||
2b04f6cf34 | |||
eecdd3474a | |||
c8c72f5f0b | |||
6f8b3a5a0f | |||
1c74f61b4e | |||
b7d664c665 | |||
891dd51abe | |||
dd718c9a63 | |||
63cca11a0b | |||
20b16d4f8c | |||
44180793fc | |||
41bf07ec59 | |||
5fff46cc60 | |||
b8cc6d758c | |||
d03d494d30 | |||
efc1794d3f | |||
e6f1d9ffe8 | |||
92b7fd981b | |||
cb2eeba318 | |||
13e81fd7bb | |||
aa9d74a8a0 | |||
5a36d8f769 | |||
7d2776e27a | |||
3065c96a2c | |||
765e400278 | |||
ead3c95c88 | |||
1402bdc9e6 | |||
03bf793b65 | |||
3b1a756e6f | |||
5be3a8c22e | |||
d04a403f71 | |||
04390b37d1 | |||
48b785e876 | |||
0ae84d56c7 | |||
a121a77774 | |||
f348ebd026 | |||
559d7c13f2 | |||
93c6085fa5 | |||
1dcaadbe6f | |||
82e9809ceb | |||
5e54dcd955 | |||
5480e037e3 | |||
d0457549c6 | |||
076d51ac40 | |||
1f9c111193 | |||
1c7da33526 | |||
62dc3aaca0 | |||
b6d010a9d7 | |||
bbacad4bab | |||
ef872dd22d | |||
f0f6fc8129 | |||
b6d0627c2a | |||
4287a91d50 | |||
4a4716a55b | |||
7481376835 | |||
73ee750515 | |||
274fc00991 | |||
4ee81dc010 | |||
d33c5d0ece | |||
21f688e014 | |||
a2d4070fdc | |||
5f8b8003d1 | |||
e906d780ad | |||
05ee300ddb | |||
90022b0d0a | |||
e2e21b1e0c | |||
3bd9155f46 | |||
f565ec5638 | |||
292af9f039 | |||
e0ef8dff23 | |||
5d16ffb1bf | |||
11a87c5ce3 | |||
090f02b05d | |||
6c4740b292 | |||
a59385af7b | |||
0aa98d2295 | |||
e55c7e8632 | |||
09031457cd | |||
bad1769c84 | |||
eaf52fa907 | |||
ab792952c1 | |||
e16ece821d | |||
70e6cb2e7e | |||
da44ee65f6 | |||
e1ed7bdc67 | |||
8668537740 | |||
21474b844b | |||
fc64078575 | |||
4addb0d66f | |||
1ddd7c0e0c | |||
ca507a5e34 | |||
c97a4abceb | |||
8f1b52328f | |||
08193ec2fd | |||
759950aff6 | |||
3a279f8edb | |||
a227a3f076 | |||
2980ee818a | |||
5a0d860786 | |||
8b6dffbc14 | |||
ed21e60869 | |||
22a5d67d05 | |||
af2827df01 | |||
8a4848a16d | |||
25e59a311b | |||
cf3e35e892 | |||
1d700261ab | |||
5ec652063d | |||
f258c9ddc0 | |||
3027c687e2 | |||
2862259d8e | |||
b149ac235b | |||
e180f1fd61 | |||
d546b4d3f3 | |||
cacb40286c | |||
c82006e8ac | |||
40d3df9f92 | |||
4fb0545a78 | |||
a3077e1b53 | |||
97aadf4897 | |||
3c26f157aa | |||
3f784b0a5b | |||
52d5d0d55f | |||
7464625566 | |||
9b6b0b0148 | |||
08697a4d84 | |||
be4c99a0d3 | |||
d5c606c64a | |||
8a14fbd73b | |||
f972b4af59 | |||
97c98a0243 | |||
28641427d5 | |||
7f8a8e95e6 | |||
21ce3cf6ff | |||
467ccbf753 | |||
68092eb4db | |||
11d0dd8f91 | |||
6e0f144ec2 | |||
74d993b642 | |||
604193396f | |||
c68bb411ac | |||
ca57b0d72e | |||
43e026500c | |||
85c4dc92f9 | |||
8f49b246bc | |||
f97dc21030 | |||
cfaa85f1c5 | |||
9d1201099d | |||
c6b01a2c50 | |||
85a10613ba | |||
2cbf5f797b | |||
7b1fe89627 | |||
ac56775791 | |||
2b942a6edf | |||
9fca3516b2 | |||
6551028753 | |||
23382c895e | |||
2bfb637a00 | |||
41e132140e | |||
b40149cbdc | |||
3c01ebb775 | |||
e97b60fc7e | |||
79501dc319 | |||
0b3c1ebbc2 | |||
c15caf3096 | |||
e2b6177a33 | |||
b922315172 | |||
617b9d3f4b | |||
37cfdb0df7 | |||
77156b35aa | |||
300d189a9d | |||
dcd3cbb944 | |||
4a889406e9 | |||
ab0c27c481 | |||
7ae4be0062 | |||
a66a37aebd | |||
d10e8f56c8 | |||
5b6a1f7bd6 | |||
00f37835fc | |||
cce1ffb06c | |||
9e2e8ab1c9 | |||
1af85115df | |||
deb29233ba | |||
9beb277745 | |||
170864a30d | |||
5bee33eec9 | |||
8912e52980 | |||
8b785c57c3 | |||
7f3cfa49fb | |||
b11b4299ef | |||
0bf6e0b09a | |||
a386ffefe3 | |||
9d5a0c2ed9 | |||
fd5ef8005e | |||
7824678e70 | |||
c4cc199fbe | |||
a9c44b491f | |||
b9da709f22 | |||
16ecd7cca3 | |||
5a64420779 | |||
2dfbfffbd0 | |||
692d224b08 | |||
cd48a36687 | |||
00e9a49460 | |||
1fc94b2213 | |||
94ab3b1d3c | |||
00fde6847a | |||
929c1bfc0d | |||
860a1f8108 | |||
55edceadcb | |||
f374dbbaa2 | |||
fab42fa0ce | |||
43d1531cd4 | |||
16edbfa03b | |||
b7eff37f63 | |||
be065735c1 | |||
79327d281c | |||
50730cca3d | |||
814460dbf2 | |||
41c747907c | |||
c05e0caa9c | |||
00b67fd8a4 | |||
d6e8134189 | |||
23dfd3f595 | |||
b8a4a47b38 | |||
b5392df9fd | |||
f48ae85cc8 | |||
4ffc499c66 | |||
ee6f3cb04d | |||
64d27c37fa | |||
b204f5bf5f | |||
be3c2779ef | |||
813f57e3d0 | |||
e898deb6df | |||
c35cb1b25f | |||
0af8f6ce19 | |||
800e38f8aa | |||
8a9a9686ba | |||
965ee39c5a | |||
aafb6e53fb | |||
d8f836d179 | |||
1abe1f48dd | |||
bcf564d09c | |||
a139d18b22 | |||
c99eee9c4b | |||
73fd4fe6b0 | |||
c5ee26099b | |||
a161b0657a | |||
4b4a56db1e | |||
7e79fa6f35 | |||
8ffd1a7c3d | |||
b5037d1c77 | |||
36e38cb576 | |||
c24a647a43 | |||
ae4f676c25 | |||
8e03ed2663 | |||
4f5be038ef | |||
83e27ede93 | |||
e82b6e983f | |||
55920a3397 | |||
8c3863bb5f | |||
c99f0458df | |||
89284df831 | |||
1f26318875 | |||
920f16965f | |||
8146f8e9ab | |||
2e7d2f2452 | |||
91090ed37a | |||
440c36f537 | |||
cf4cc7f578 | |||
744f05814a | |||
a7b37f3c41 | |||
3fb9aaa56a | |||
4e11e20071 | |||
3f93f93a45 | |||
904a664dfe | |||
b12b92845c | |||
8f5f12315d | |||
7f3e3919e7 | |||
5e835f5b2c | |||
b03ee7cc76 | |||
8d11082a02 | |||
eff52c2c9b | |||
cbe6071969 | |||
dea32f9b56 | |||
f874e1d34f | |||
423576638a | |||
3ea70afd7e | |||
1992d6f0d6 | |||
7e85f79f8e | |||
737b73b947 | |||
f66858ff3e | |||
9c60a5ccac | |||
2171755e5a | |||
6995d75dc8 | |||
48d665e2ea | |||
c82999fd8e | |||
bfb0f823c1 | |||
ef869be10b | |||
94d89159bd | |||
76a3339e16 | |||
c73a151115 | |||
71ccacc6b3 | |||
decd8a318d | |||
3cfa2c1ef6 | |||
10ce9a6d0a | |||
6e62d41d06 | |||
02f3281a7a | |||
869b8b734e | |||
e9be67d59b | |||
82ec81cb08 | |||
f7e697aacf | |||
6ff09f8cc1 | |||
4055ff305f | |||
2119a4b39f | |||
93236752c4 | |||
b2314f21c6 | |||
d9367c7ed8 | |||
ea921c8abe | |||
973d67f0c1 | |||
ab84d89eb7 | |||
04ddfcce43 | |||
7717d4d580 | |||
9b5c26d3e3 | |||
e5fef260f2 | |||
1bcc518b31 | |||
5ea8bb9688 | |||
8fde6b235c | |||
62b28a813e | |||
7d90c1fdc1 | |||
9b2dc40bc1 | |||
79eb062cf7 | |||
10eae2f254 | |||
a36ec87f40 | |||
ea3191033f | |||
23f591bf01 | |||
a901304a59 | |||
5c8dd9b7a6 | |||
679480e496 | |||
e2cc4f965b | |||
e6bfda8e80 | |||
58eaf6b03e | |||
193ca3848b | |||
f4c0a4abda | |||
7bee84e2fa | |||
d49c530c55 | |||
e35134b780 | |||
afe5369e05 | |||
46804a32c8 | |||
b376f2ca80 | |||
23c96510d8 | |||
1f2fd479e3 | |||
41b024925b | |||
dd5c5c321a | |||
5d1f4f2d68 | |||
fcb9e8f2f5 | |||
af468837a4 | |||
ce5223f85d | |||
2e3d67fdc7 | |||
20a58c502c | |||
14fcc5faa1 | |||
3cdfcf1cea | |||
70c629f857 | |||
049e0da481 | |||
67a11a59ce | |||
b05af88931 | |||
af8f5f1ecc | |||
0505cb55f2 | |||
e269e14b10 | |||
3b391919c9 | |||
123da22ebd | |||
8ca25641f8 | |||
3b4b91729f | |||
d7980bc419 | |||
f0c4cb23e3 | |||
dbf0e27386 | |||
83cfd59902 | |||
0ede5ffc67 | |||
f58cf6e63c | |||
c7e0e88ece | |||
16ab1481d2 | |||
2732296aed | |||
affc50a2eb | |||
3d2252a688 | |||
76f583324e | |||
50ba50f848 | |||
886ffbe279 | |||
b7df8fb651 | |||
7a8664727c | |||
a787dcbe53 | |||
aa611605b6 | |||
e37d22c220 | |||
c0cf0b921c | |||
4f26eb6e21 | |||
a6ca1bec7c | |||
765d875d86 | |||
b721e86412 | |||
9dd4943af7 | |||
59bf8a1efd | |||
2bf7344fbb | |||
33397ec467 | |||
d0085057d5 | |||
835087eff1 | |||
7455145511 | |||
9f3b3daea2 | |||
c62822712c | |||
abe282550e | |||
f63a71a492 | |||
c0ae2c870f | |||
408b7aeebe | |||
0c02025e51 | |||
f69e969898 | |||
0d9e0f1172 | |||
5bea4da409 | |||
d5778bda4b | |||
2b858f0e42 | |||
303e48c8c6 | |||
08b4095687 | |||
ae80e3e4f3 | |||
486e78f3ee | |||
836481aff7 | |||
5a28372abf | |||
bb57c84aff | |||
a741ea804b | |||
99c1826d6d | |||
effd3583a4 | |||
cb20c4015e | |||
5834643d46 | |||
1c1c33d327 | |||
cbef5ac26d | |||
123816795a | |||
77961ffcd9 | |||
a8fcfbbb1e | |||
38d863e3ff | |||
146cf891cd | |||
38bdc44185 | |||
3ec4a48484 | |||
4b30686359 | |||
ccc920d46b | |||
a39170060f | |||
5f5ee8a15d | |||
3b715f2fdd | |||
a15dd2a411 | |||
86cdaa7c3c | |||
78a36c196d | |||
9613392e8b | |||
0f46f4b96d | |||
3863b2dba8 | |||
22789c1b01 | |||
24ca952d46 | |||
661c324e43 | |||
23cfb31651 | |||
15bcca4e7e | |||
f559700766 | |||
02f162fd79 | |||
4a80e27283 | |||
2046748516 | |||
118900120b | |||
e09225eb09 | |||
89a368ae35 | |||
2d2e209373 | |||
b360a36737 | |||
43eda480c5 | |||
d3a2335a55 | |||
494b73a860 | |||
9046d4d137 | |||
41f18a47af | |||
b60256e8fd | |||
3eb923cc84 | |||
ebeff1aae4 | |||
b7d7017381 | |||
3e50cc95b5 | |||
7a9255a9ed | |||
86e5afc469 | |||
d67cd8f9ba | |||
9a6ce35fb6 | |||
38f31eca3c | |||
dce701b259 | |||
b4ebbc0a55 | |||
6eff104d6d | |||
2193976c2c | |||
aa5bac2ccc | |||
96be3159bc | |||
539bbebdd5 | |||
094a505d12 | |||
dfe72d8947 | |||
0e0c12c7cf | |||
9c4ef3d8bd | |||
fdc0d3c227 | |||
5aab521b42 | |||
d0fdebb7d1 | |||
62b3d94f8e | |||
6036a8356e | |||
231e3a0b06 | |||
77a73b1db6 | |||
510d411055 | |||
ba976e3a41 | |||
8487ab1788 | |||
00aa83df80 | |||
19ae10ae27 | |||
55187d23bf | |||
0386f8ebbd | |||
ade3cfaef1 | |||
048b1ff625 | |||
564f1c843b | |||
4bd1aa7c90 | |||
0e3fedf3fb | |||
d4d05c2fc9 | |||
b9599052f9 | |||
7832fbc61a | |||
1a921ec2a3 | |||
69afe16558 |
5
.scrutinizer.yml
Normal file
5
.scrutinizer.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
imports:
|
||||
- php
|
||||
|
||||
tools:
|
||||
external_code_coverage: true
|
31
.travis.yml
31
.travis.yml
|
@ -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
16
Annotation/Search.php
Normal 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;
|
||||
}
|
25
CHANGELOG-2.0.md
Normal file
25
CHANGELOG-2.0.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
CHANGELOG for 2.0.x
|
||||
===================
|
||||
|
||||
This changelog references the relevant changes (bug and security fixes) done
|
||||
in 2.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/v2.0.0...v2.0.1
|
||||
|
||||
To generate a changelog summary since the last version, run
|
||||
`git log --no-merges --oneline v2.0.0...2.0.x`
|
||||
|
||||
* 2.0.2 (2013-06-06)
|
||||
|
||||
* 00e9a49: Allow Symfony dependencies until 3.0
|
||||
* 4b4a56d: Check for "indexes" key in Configuration::getNestings()
|
||||
* 8ffd1a7: Update install version and add links to compatibility info
|
||||
* 58e983f: Document installation via composer in README (closes #271)
|
||||
|
||||
* 2.0.1 (2013-04-04)
|
||||
|
||||
* f0d3a4d: Ensure mongo extension is available in Travis CI
|
||||
* 1f26318: Avoid using a feature not supported in PHP5.3
|
22
CHANGELOG-2.1.md
Normal file
22
CHANGELOG-2.1.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
CHANGELOG for 2.1.x
|
||||
===================
|
||||
|
||||
This changelog references the relevant changes (bug and security fixes) done
|
||||
in 2.1 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/v2.1.0...v2.1.1
|
||||
|
||||
To generate a changelog summary since the last version, run
|
||||
`git log --no-merges --oneline v2.1.0...2.1.x`
|
||||
|
||||
* 2.1.2 (2013-06-06)
|
||||
|
||||
* 00e9a49: Allow Symfony dependencies until 3.0
|
||||
|
||||
* 2.1.1 (2013-05-15)
|
||||
|
||||
* c05e0ca: Added documentation for ignoring missing hits
|
||||
* 00b67fd: Ignore missing index hits
|
76
CHANGELOG-3.0.md
Normal file
76
CHANGELOG-3.0.md
Normal 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
61
CHANGELOG-3.1.md
Normal 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
|
26
Client.php
26
Client.php
|
@ -2,31 +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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
187
Command/PopulateCommand.php
Executable file → Normal file
187
Command/PopulateCommand.php
Executable file → Normal file
|
@ -2,49 +2,200 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Command;
|
||||
|
||||
use FOS\ElasticaBundle\Event\IndexPopulateEvent;
|
||||
use FOS\ElasticaBundle\Event\TypePopulateEvent;
|
||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Helper\DialogHelper;
|
||||
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 Symfony\Component\Console\Helper\ProgressBar;
|
||||
|
||||
/**
|
||||
* Populate the search index
|
||||
* Populate the search index.
|
||||
*/
|
||||
class PopulateCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* @see Command
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
private $indexManager;
|
||||
|
||||
/**
|
||||
* @var ProgressClosureBuilder
|
||||
*/
|
||||
private $progressClosureBuilder;
|
||||
|
||||
/**
|
||||
* @var ProviderRegistry
|
||||
*/
|
||||
private $providerRegistry;
|
||||
|
||||
/**
|
||||
* @var Resetter
|
||||
*/
|
||||
private $resetter;
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\Console\Command\Command::configure()
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('fos:elastica:populate')
|
||||
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index to repopulate')
|
||||
->addOption('type', null, InputOption::VALUE_OPTIONAL, 'The type to repopulate')
|
||||
->addOption('no-reset', null, InputOption::VALUE_NONE, 'Do not reset index before populating')
|
||||
->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')
|
||||
->addOption('no-reset', null, InputOption::VALUE_NONE, 'Do not reset the indexes before they are populated.');
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* {@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%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (!$input->getOption('no-reset')) {
|
||||
$output->writeln('Resetting indexes');
|
||||
$this->getContainer()->get('fos_elastica.reseter')->reset();
|
||||
$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');
|
||||
}
|
||||
|
||||
$output->writeln('Populating indexes');
|
||||
$this->getContainer()->get('fos_elastica.populator')->populate(function($text) use ($output) {
|
||||
$output->writeLn($text);
|
||||
});
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('Refreshing indexes');
|
||||
array_map(function($index) {
|
||||
$index->refresh();
|
||||
}, $this->getContainer()->get('fos_elastica.index_manager')->getAllIndexes());
|
||||
if (null === $index && null !== $type) {
|
||||
throw new \InvalidArgumentException('Cannot specify type option without an index.');
|
||||
}
|
||||
|
||||
$output->writeln('Done');
|
||||
if (null !== $index) {
|
||||
if (null !== $type) {
|
||||
$this->populateIndexType($output, $index, $type, $reset, $options);
|
||||
} else {
|
||||
$this->populateIndex($output, $index, $reset, $options);
|
||||
}
|
||||
} else {
|
||||
$indexes = array_keys($this->indexManager->getAllIndexes());
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
$this->populateIndex($output, $index, $reset, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreates an index, populates its types, and refreshes the index.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $index
|
||||
* @param boolean $reset
|
||||
* @param array $options
|
||||
*/
|
||||
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
||||
{
|
||||
$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, true);
|
||||
}
|
||||
|
||||
$types = array_keys($this->providerRegistry->getIndexProviders($index));
|
||||
foreach ($types as $type) {
|
||||
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
|
||||
|
||||
$this->refreshIndex($output, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes/remaps an index type, populates it, and refreshes the index.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
* @param boolean $reset
|
||||
* @param array $options
|
||||
*/
|
||||
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
$provider = $this->providerRegistry->getProvider($index, $type);
|
||||
$loggerClosure = $this->progressClosureBuilder->build($output, 'Populating', $index, $type);
|
||||
$provider->populate($loggerClosure, $event->getOptions());
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
||||
|
|
103
Command/ProgressClosureBuilder.php
Normal file
103
Command/ProgressClosureBuilder.php
Normal 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
78
Command/ResetCommand.php
Executable 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
@ -22,18 +21,17 @@ class SearchCommand extends ContainerAwareCommand
|
|||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDefinition(array(
|
||||
new InputArgument('type', InputArgument::REQUIRED, 'The type to search in'),
|
||||
new InputArgument('query', InputArgument::REQUIRED, 'The text to search'),
|
||||
))
|
||||
->addOption('index', null, InputOption::VALUE_NONE, 'The index to search in')
|
||||
->setName('fos:elastica:search')
|
||||
->addArgument('type', InputArgument::REQUIRED, 'The type to search in')
|
||||
->addArgument('query', InputArgument::REQUIRED, 'The text to search')
|
||||
->addOption('index', null, InputOption::VALUE_REQUIRED, 'The index to search in')
|
||||
->addOption('limit', null, InputOption::VALUE_REQUIRED, 'The maximum number of documents to return', 20)
|
||||
->addOption('show-field', null, InputOption::VALUE_REQUIRED, 'Field to show, null uses the first field')
|
||||
->addOption('show-source', null, InputOption::VALUE_NONE, 'Show the documents sources')
|
||||
->addOption('show-id', null, InputOption::VALUE_NONE, 'Show the documents ids')
|
||||
->addOption('explain', null, InputOption::VALUE_NONE, 'Enables explanation for each hit on how its score was computed.')
|
||||
->setName('fos:elastica:search')
|
||||
->setDescription('Searches documents in a given type and index');
|
||||
->setDescription('Searches documents in a given type and index')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,10 +39,12 @@ class SearchCommand extends ContainerAwareCommand
|
|||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$index = $this->getContainer()->get('fos_elastica.index_manager')->getIndex($input->getOption('index'));
|
||||
$indexName = $input->getOption('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);
|
||||
}
|
||||
|
@ -57,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) {
|
||||
|
|
64
Configuration/ConfigManager.php
Normal file
64
Configuration/ConfigManager.php
Normal 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]);
|
||||
}
|
||||
}
|
124
Configuration/IndexConfig.php
Normal file
124
Configuration/IndexConfig.php
Normal 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;
|
||||
}
|
||||
}
|
44
Configuration/ManagerInterface.php
Normal file
44
Configuration/ManagerInterface.php
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
80
Configuration/Source/ContainerSource.php
Normal file
80
Configuration/Source/ContainerSource.php
Normal 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;
|
||||
}
|
||||
}
|
26
Configuration/Source/SourceInterface.php
Normal file
26
Configuration/Source/SourceInterface.php
Normal 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();
|
||||
}
|
113
Configuration/TypeConfig.php
Normal file
113
Configuration/TypeConfig.php
Normal 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;
|
||||
}
|
||||
}
|
|
@ -40,6 +40,16 @@ class ElasticaDataCollector extends DataCollector
|
|||
return $this->data['queries'];
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
$time = 0;
|
||||
foreach ($this->data['queries'] as $query) {
|
||||
$time += $query['executionMS'];
|
||||
}
|
||||
|
||||
return $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class AddProviderPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('fos_elastica.populator')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$providers = array();
|
||||
foreach ($container->findTaggedServiceIds('fos_elastica.provider') as $id => $attributes) {
|
||||
$providers[$id] = new Reference($id);
|
||||
}
|
||||
|
||||
$container->getDefinition('fos_elastica.populator')->replaceArgument(0, $providers);
|
||||
}
|
||||
}
|
36
DependencyInjection/Compiler/ConfigSourcePass.php
Normal file
36
DependencyInjection/Compiler/ConfigSourcePass.php
Normal 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);
|
||||
}
|
||||
}
|
38
DependencyInjection/Compiler/IndexPass.php
Normal file
38
DependencyInjection/Compiler/IndexPass.php
Normal 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);
|
||||
}
|
||||
}
|
70
DependencyInjection/Compiler/RegisterProvidersPass.php
Normal file
70
DependencyInjection/Compiler/RegisterProvidersPass.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
class RegisterProvidersPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* Mapping of class names to booleans indicating whether the class
|
||||
* implements ProviderInterface.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $implementations = array();
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::process()
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('fos_elastica.provider_registry')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Infer the default index name from the service alias
|
||||
$defaultIndex = substr($container->getAlias('fos_elastica.index'), 19);
|
||||
|
||||
$registry = $container->getDefinition('fos_elastica.provider_registry');
|
||||
$providers = $container->findTaggedServiceIds('fos_elastica.provider');
|
||||
|
||||
foreach ($providers as $providerId => $tags) {
|
||||
$index = $type = null;
|
||||
$class = $container->getDefinition($providerId)->getClass();
|
||||
|
||||
if (!$class || !$this->isProviderImplementation($class)) {
|
||||
throw new \InvalidArgumentException(sprintf('Elastica provider "%s" with class "%s" must implement ProviderInterface.', $providerId, $class));
|
||||
}
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if (!isset($tag['type'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Elastica provider "%s" must specify the "type" attribute.', $providerId));
|
||||
}
|
||||
|
||||
$index = isset($tag['index']) ? $tag['index'] : $defaultIndex;
|
||||
$type = $tag['type'];
|
||||
}
|
||||
|
||||
$registry->addMethodCall('addProvider', array($index, $type, $providerId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the class implements ProviderInterface.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function isProviderImplementation($class)
|
||||
{
|
||||
if (!isset($this->implementations[$class])) {
|
||||
$refl = new \ReflectionClass($class);
|
||||
$this->implementations[$class] = $refl->implementsInterface('FOS\ElasticaBundle\Provider\ProviderInterface');
|
||||
}
|
||||
|
||||
return $this->implementations[$class];
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ namespace FOS\ElasticaBundle\DependencyInjection\Compiler;
|
|||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use InvalidArgumentException;
|
||||
|
||||
|
@ -20,7 +19,7 @@ class TransformerPass implements CompilerPassInterface
|
|||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('fos_elastica.elastica_to_model_transformer.collection.prototype')) {
|
||||
if (!$container->hasDefinition('fos_elastica.elastica_to_model_transformer.collection')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -32,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,4 +44,4 @@ class TransformerPass implements CompilerPassInterface
|
|||
$index->replaceArgument(0, $indexTransformers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,35 @@ namespace FOS\ElasticaBundle\DependencyInjection;
|
|||
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
class Configuration
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* Stores supported database drivers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
||||
|
||||
/**
|
||||
* If the kernel is running in debug mode.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $debug;
|
||||
|
||||
public function __construct($debug)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the configuration tree.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface
|
||||
* @return TreeBuilder
|
||||
*/
|
||||
public function getConfigTree()
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder();
|
||||
$rootNode = $treeBuilder->root('fos_elastica', 'array');
|
||||
|
@ -24,17 +42,28 @@ class Configuration
|
|||
|
||||
$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()
|
||||
;
|
||||
|
||||
return $treeBuilder->buildTree();
|
||||
return $treeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the configuration for the "clients" key
|
||||
* Adds the configuration for the "clients" key.
|
||||
*/
|
||||
private function addClientsSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
|
@ -45,12 +74,67 @@ class Configuration
|
|||
->useAttributeAsKey('id')
|
||||
->prototype('array')
|
||||
->performNoDeepMerging()
|
||||
// BC - Renaming 'servers' node to 'connections'
|
||||
->beforeNormalization()
|
||||
->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['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()
|
||||
->scalarNode('url')->end()
|
||||
->scalarNode('host')->end()
|
||||
->scalarNode('port')->end()
|
||||
->arrayNode('connections')
|
||||
->requiresAtLeastOneElement()
|
||||
->prototype('array')
|
||||
->fixXmlConfig('header')
|
||||
->children()
|
||||
->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()
|
||||
|
@ -59,7 +143,7 @@ class Configuration
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds the configuration for the "indexes" key
|
||||
* Adds the configuration for the "indexes" key.
|
||||
*/
|
||||
private function addIndexesSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
|
@ -69,8 +153,11 @@ class Configuration
|
|||
->arrayNode('indexes')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->performNoDeepMerging()
|
||||
->children()
|
||||
->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)
|
||||
|
@ -78,57 +165,10 @@ class Configuration
|
|||
->end()
|
||||
->arrayNode('type_prototype')
|
||||
->children()
|
||||
->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()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('finder')
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('elastica_to_model_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('hydrate')->defaultTrue()->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('model_to_elastica_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->append($this->getPersistenceNode())
|
||||
->append($this->getSerializerNode())
|
||||
->end()
|
||||
->end()
|
||||
->variableNode('settings')->defaultValue(array())->end()
|
||||
|
@ -152,63 +192,83 @@ class Configuration
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->treatNullLike(array())
|
||||
->children()
|
||||
->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()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('finder')
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('elastica_to_model_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('hydrate')->defaultTrue()->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()
|
||||
;
|
||||
|
||||
|
@ -216,39 +276,42 @@ class Configuration
|
|||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
$node
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('variable')
|
||||
->treatNullLike(array());
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "dynamic_templates".
|
||||
*/
|
||||
public function getDynamicTemplateNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('dynamic_templates');
|
||||
|
||||
$node
|
||||
->prototype('array')
|
||||
->treatNullLike(array())
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('type')->defaultValue('string')->end()
|
||||
->scalarNode('boost')->end()
|
||||
->scalarNode('store')->end()
|
||||
->scalarNode('index')->end()
|
||||
->scalarNode('analyzer')->end()
|
||||
->scalarNode('term_vector')->end()
|
||||
->scalarNode('null_value')->end()
|
||||
->arrayNode('fields')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->treatNullLike(array())
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('type')->defaultValue('string')->end()
|
||||
->scalarNode('boost')->end()
|
||||
->scalarNode('store')->end()
|
||||
->scalarNode('index')->end()
|
||||
->scalarNode('analyzer')->end()
|
||||
->scalarNode('term_vector')->end()
|
||||
->scalarNode('null_value')->end()
|
||||
->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()
|
||||
|
@ -259,6 +322,23 @@ class Configuration
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_id".
|
||||
*/
|
||||
protected function getIdNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('_id');
|
||||
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('path')->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_source".
|
||||
*/
|
||||
|
@ -277,9 +357,225 @@ class Configuration
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('compress')->end()
|
||||
->scalarNode('compress_threshold')->end()
|
||||
->scalarNode('enabled')->defaultTrue()->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_boost".
|
||||
*/
|
||||
protected function getBoostNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('_boost');
|
||||
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('name')->end()
|
||||
->scalarNode('null_value')->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_routing".
|
||||
*/
|
||||
protected function getRoutingNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('_routing');
|
||||
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('required')->end()
|
||||
->scalarNode('path')->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Processor;
|
||||
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;
|
||||
|
@ -14,21 +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 = new Configuration();
|
||||
$processor = new Processor();
|
||||
$config = $processor->process($configuration->getConfigTree(), $configs);
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$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'])) {
|
||||
|
@ -41,197 +60,277 @@ 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']);
|
||||
$indexDefsByName = array_map(function($id) use ($container) {
|
||||
return $container->getDefinition($id);
|
||||
}, $indexIdsByName);
|
||||
if (isset($config['serializer'])) {
|
||||
$loader->load('serializer.xml');
|
||||
|
||||
$this->loadIndexManager($indexDefsByName, $container->getDefinition($indexIdsByName[$config['default_index']]), $container);
|
||||
$this->loadReseter($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($container->getParameter('kernel.debug'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configured clients.
|
||||
*
|
||||
* @param array $config 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) {
|
||||
$clientDef = $container->getDefinition('fos_elastica.client');
|
||||
$clientId = sprintf('fos_elastica.client.%s', $name);
|
||||
|
||||
$clientDef = new DefinitionDecorator('fos_elastica.client_prototype');
|
||||
$clientDef->replaceArgument(0, $clientConfig);
|
||||
|
||||
$clientId = sprintf('fos_elastica.client.%s', $name);
|
||||
$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 $config An array of indexes configurations
|
||||
* @param array $indexes An array of indexes configurations
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function loadIndexes(array $indexes, ContainerBuilder $container, array $clientIdsByName, $defaultClientName)
|
||||
private function loadIndexes(array $indexes, ContainerBuilder $container)
|
||||
{
|
||||
$indexIds = array();
|
||||
$indexableCallbacks = array();
|
||||
|
||||
foreach ($indexes as $name => $index) {
|
||||
$indexId = sprintf('fos_elastica.index.%s', $name);
|
||||
$indexName = isset($index['index_name']) ? $index['index_name'] : $name;
|
||||
|
||||
$indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
|
||||
$indexDef->replaceArgument(0, $indexName);
|
||||
$indexDef->addTag('fos_elastica.index', array(
|
||||
'name' => $name,
|
||||
));
|
||||
|
||||
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;
|
||||
$client = $this->getClient($index['client']);
|
||||
$indexDef->setFactoryService($client);
|
||||
}
|
||||
|
||||
$clientId = $clientIdsByName[$clientName];
|
||||
$indexId = sprintf('fos_elastica.index.%s', $name);
|
||||
$indexDefArgs = array($name);
|
||||
$indexDef = new Definition('%fos_elastica.index.class%', $indexDefArgs);
|
||||
$indexDef->setFactoryService($clientId);
|
||||
$indexDef->setFactoryMethod('getIndex');
|
||||
$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)
|
||||
{
|
||||
$abstractTransformerId = 'fos_elastica.elastica_to_model_transformer.collection.prototype';
|
||||
/* Note: transformer services may conflict with "collection.index", if
|
||||
* an index and type names were "collection" and an index, respectively.
|
||||
*/
|
||||
$transformerId = sprintf('fos_elastica.elastica_to_model_transformer.collection.%s', $name);
|
||||
$transformerDef = new DefinitionDecorator($abstractTransformerId);
|
||||
$transformerDef = new DefinitionDecorator('fos_elastica.elastica_to_model_transformer.collection');
|
||||
$container->setDefinition($transformerId, $transformerDef);
|
||||
|
||||
$abstractFinderId = 'fos_elastica.finder.prototype';
|
||||
$finderId = sprintf('fos_elastica.finder.%s', $name);
|
||||
$finderDef = new DefinitionDecorator($abstractFinderId);
|
||||
$finderDef->replaceArgument(0, new Reference($indexId));
|
||||
$finderDef = new DefinitionDecorator('fos_elastica.finder');
|
||||
$finderDef->replaceArgument(0, $index);
|
||||
$finderDef->replaceArgument(1, new Reference($transformerId));
|
||||
|
||||
$container->setDefinition($finderId, $finderDef);
|
||||
|
||||
return $finderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configured types.
|
||||
*
|
||||
* @param array $config An array of types configurations
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
* @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'];
|
||||
|
||||
$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];
|
||||
}
|
||||
}
|
||||
if (isset($type['mappings'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['properties'] = $type['mappings'];
|
||||
$typeName = sprintf('%s/%s', $indexName, $name);
|
||||
$this->typeFields[$typeName] = array_keys($type['mappings']);
|
||||
|
||||
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, $typeDef, $indexName, $name);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two arrays without reindexing numeric keys.
|
||||
* Loads the optional provider and finder for a type.
|
||||
*
|
||||
* @param array $array1 An array to merge
|
||||
* @param array $array2 An array to merge
|
||||
*
|
||||
* @return array The merged array
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return $array1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the optional provider and finder for a type
|
||||
*
|
||||
* @return null
|
||||
**/
|
||||
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'])) {
|
||||
$providerId = $this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName);
|
||||
$container->getDefinition('fos_elastica.populator')->addMethodCall('addProvider', array($providerId, new Reference($providerId)));
|
||||
$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.
|
||||
*/
|
||||
$abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
|
||||
$serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
|
||||
$serviceDef = new DefinitionDecorator($abstractId);
|
||||
|
@ -241,102 +340,201 @@ class FOSElasticaExtension extends Extension
|
|||
$argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
|
||||
|
||||
$serviceDef->replaceArgument($argPos, $typeConfig['model']);
|
||||
$serviceDef->replaceArgument($argPos + 1, array(
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
'hydrate' => $typeConfig['elastica_to_model_transformer']['hydrate']
|
||||
));
|
||||
$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 = sprintf('fos_elastica.model_to_elastica_transformer.prototype.auto');
|
||||
|
||||
$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($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)
|
||||
{
|
||||
$abstractId = sprintf('fos_elastica.object_persister.prototype');
|
||||
$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($abstractId);
|
||||
$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)]);
|
||||
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'];
|
||||
}
|
||||
$abstractProviderId = sprintf('fos_elastica.provider.prototype.%s', $typeConfig['driver']);
|
||||
|
||||
/* 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($abstractProviderId);
|
||||
$providerDef->replaceArgument(0, $typeDef);
|
||||
|
||||
// Doctrine has a mandatory service as second argument
|
||||
$argPos = ('propel' === $typeConfig['driver']) ? 1 : 2;
|
||||
|
||||
$providerDef->replaceArgument($argPos, new Reference($objectPersisterId));
|
||||
$providerDef->replaceArgument($argPos + 1, $typeConfig['model']);
|
||||
|
||||
$options = array('batch_size' => $typeConfig['provider']['batch_size']);
|
||||
|
||||
if ('propel' !== $typeConfig['driver']) {
|
||||
$options['query_builder_method'] = $typeConfig['provider']['query_builder_method'];
|
||||
$options['clear_object_manager'] = $typeConfig['provider']['clear_object_manager'];
|
||||
}
|
||||
|
||||
$providerDef->replaceArgument($argPos + 2, $options);
|
||||
$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(2, $typeConfig['model']);
|
||||
// Propel provider can simply ignore Doctrine-specific options
|
||||
$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.
|
||||
*/
|
||||
$abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
|
||||
$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.common.event_subscriber'); break;
|
||||
case 'orm':
|
||||
$tagName = 'doctrine.event_listener';
|
||||
break;
|
||||
case 'mongodb':
|
||||
$tagName = 'doctrine_mongodb.odm.event_listener';
|
||||
break;
|
||||
}
|
||||
|
||||
if (null !== $tagName) {
|
||||
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
|
||||
$listenerDef->addTag($tagName, array('event' => $event));
|
||||
}
|
||||
}
|
||||
|
||||
$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) {
|
||||
|
@ -348,64 +546,99 @@ 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'])) {
|
||||
return $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, $typeRef);
|
||||
$finderDef->replaceArgument(1, new Reference($elasticaToModelId));
|
||||
$container->setDefinition($finderId, $finderDef);
|
||||
}
|
||||
$abstractFinderId = 'fos_elastica.finder.prototype';
|
||||
$finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
|
||||
$finderDef = new DefinitionDecorator($abstractFinderId);
|
||||
$finderDef->replaceArgument(0, $typeDef);
|
||||
$finderDef->replaceArgument(1, new Reference($elasticaToModelId));
|
||||
$container->setDefinition($finderId, $finderDef);
|
||||
|
||||
$managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
|
||||
$managerId = sprintf('fos_elastica.manager.%s', $typeConfig['driver']);
|
||||
$managerDef = $container->getDefinition($managerId);
|
||||
$arguments = array( $typeConfig['model'], new Reference($finderId));
|
||||
if (isset($typeConfig['repository'])) {
|
||||
$arguments[] = $typeConfig['repository'];
|
||||
}
|
||||
|
||||
$managerDef->addMethodCall('addEntity', $arguments);
|
||||
|
||||
return $finderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the index manager
|
||||
* Loads the index manager.
|
||||
*
|
||||
* @return null
|
||||
* @param ContainerBuilder $container
|
||||
**/
|
||||
protected function loadIndexManager(array $indexDefs, $defaultIndexId, 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, $indexDefs);
|
||||
$managerDef->replaceArgument(1, new Reference('fos_elastica.index'));
|
||||
$managerDef->replaceArgument(0, $indexRefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the reseter
|
||||
* Makes sure a specific driver has been loaded.
|
||||
*
|
||||
* @return null
|
||||
**/
|
||||
protected function loadReseter(array $indexConfigs, ContainerBuilder $container)
|
||||
{
|
||||
$reseterDef = $container->getDefinition('fos_elastica.reseter');
|
||||
$reseterDef->replaceArgument(0, $indexConfigs);
|
||||
}
|
||||
|
||||
protected function loadDriver(ContainerBuilder $container, $driver)
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $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;
|
||||
|
@ -422,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'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,52 +2,56 @@
|
|||
|
||||
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 Elastica_Document;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* Repository to fetch the objects from
|
||||
* Manager registry.
|
||||
*
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $objectManager = null;
|
||||
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
|
||||
*/
|
||||
protected $options = array(
|
||||
'hydrate' => true,
|
||||
'identifier' => 'id'
|
||||
'hydrate' => true,
|
||||
'identifier' => 'id',
|
||||
'ignore_missing' => false,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
);
|
||||
|
||||
/**
|
||||
* Instantiates a new Mapper
|
||||
* Instantiates a new Mapper.
|
||||
*
|
||||
* @param object $objectManager
|
||||
* @param ManagerRegistry $registry
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($objectManager, $objectClass, array $options = array())
|
||||
public function __construct(ManagerRegistry $registry, $objectClass, array $options = array())
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->options = array_merge($this->options, $options);
|
||||
$this->registry = $registry;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,9 +66,12 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param array of elastica objects
|
||||
* @return array
|
||||
**/
|
||||
public function transform(array $elasticaObjects)
|
||||
|
@ -76,7 +83,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
}
|
||||
|
||||
$objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
|
||||
if (count($objects) < count($elasticaObjects)) {
|
||||
if (!$this->options['ignore_missing'] && count($objects) < count($elasticaObjects)) {
|
||||
throw new \RuntimeException('Cannot find corresponding Doctrine objects for all Elastica results.');
|
||||
};
|
||||
|
||||
|
@ -86,32 +93,34 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
}
|
||||
}
|
||||
|
||||
$identifierGetter = 'get'.ucfirst($this->options['identifier']);
|
||||
|
||||
// sort objects in the order of ids
|
||||
$idPos = array_flip($ids);
|
||||
usort($objects, function($a, $b) use ($idPos, $identifierGetter)
|
||||
{
|
||||
return $idPos[$a->$identifierGetter()] > $idPos[$b->$identifierGetter()];
|
||||
});
|
||||
$identifier = $this->options['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()
|
||||
{
|
||||
|
@ -119,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);
|
||||
}
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
|
||||
abstract class AbstractListener
|
||||
{
|
||||
/**
|
||||
* Object persister
|
||||
*
|
||||
* @var ObjectPersister
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Class of the domain model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* List of subscribed events
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
protected $esIdentifierField;
|
||||
protected $scheduledForRemoval;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
**/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id')
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->events = $events;
|
||||
$this->esIdentifierField = $esIdentifierField;
|
||||
$this->scheduledForRemoval = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
protected function scheduleForRemoval($object, $objectManager)
|
||||
{
|
||||
$metadata = $objectManager->getClassMetadata($this->objectClass);
|
||||
$esId = $metadata->getFieldValue($object, $this->esIdentifierField);
|
||||
$this->scheduledForRemoval[spl_object_hash($object)] = $esId;
|
||||
}
|
||||
|
||||
protected function removeIfScheduled($object)
|
||||
{
|
||||
$objectHash = spl_object_hash($object);
|
||||
if (isset($this->scheduledForRemoval[$objectHash])) {
|
||||
$this->objectPersister->deleteById($this->scheduledForRemoval[$objectHash]);
|
||||
unset($this->scheduledForRemoval[$objectHash]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,106 +2,169 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use Elastica\Exception\Bulk\ResponseException as BulkResponseException;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Elastica_Type;
|
||||
use Elastica_Document;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
abstract class AbstractProvider implements ProviderInterface
|
||||
abstract class AbstractProvider extends BaseAbstractProvider
|
||||
{
|
||||
/**
|
||||
* Elastica type
|
||||
*
|
||||
* @var Elastica_Type
|
||||
* @var SliceFetcherInterface
|
||||
*/
|
||||
protected $type;
|
||||
private $sliceFetcher;
|
||||
|
||||
/**
|
||||
* Domain model object manager
|
||||
*
|
||||
* @var object
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $objectManager;
|
||||
protected $managerRegistry;
|
||||
|
||||
/**
|
||||
* Object persister
|
||||
* Constructor.
|
||||
*
|
||||
* @var ObjectPersisterInterface
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $baseOptions
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
* @param SliceFetcherInterface $sliceFetcher
|
||||
*/
|
||||
protected $objectPersister;
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $baseOptions,
|
||||
ManagerRegistry $managerRegistry,
|
||||
SliceFetcherInterface $sliceFetcher = null
|
||||
) {
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
|
||||
|
||||
/**
|
||||
* Provider options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = array(
|
||||
'batch_size' => 100,
|
||||
'clear_object_manager' => true,
|
||||
'query_builder_method' => 'createQueryBuilder'
|
||||
);
|
||||
|
||||
public function __construct(Elastica_Type $type, $objectManager, ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->objectManager = $objectManager;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->options = array_merge($this->options, $options);
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
$this->sliceFetcher = $sliceFetcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the repository objects in the type index
|
||||
* Counts objects that would be indexed using the query builder.
|
||||
*
|
||||
* @param Closure $loggerClosure
|
||||
* @param object $queryBuilder
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function populate(Closure $loggerClosure)
|
||||
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.
|
||||
*
|
||||
* @param object $queryBuilder
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function fetchSlice($queryBuilder, $limit, $offset);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doPopulate($options, \Closure $loggerClosure = null)
|
||||
{
|
||||
$queryBuilder = $this->createQueryBuilder();
|
||||
$nbObjects = $this->countObjects($queryBuilder);
|
||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||
|
||||
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
||||
$queryBuilder = $this->createQueryBuilder($options['query_builder_method']);
|
||||
$nbObjects = $this->countObjects($queryBuilder);
|
||||
$offset = $options['offset'];
|
||||
|
||||
$stepStartTime = microtime(true);
|
||||
$objects = $this->fetchSlice($queryBuilder, $this->options['batch_size'], $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);
|
||||
|
||||
$this->objectPersister->insertMany($objects);
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
}
|
||||
} catch (BulkResponseException $e) {
|
||||
if (!$options['ignore_errors']) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($this->options['clear_object_manager']) {
|
||||
$this->objectManager->clear();
|
||||
if (null !== $loggerClosure) {
|
||||
$loggerClosure(
|
||||
$options['batch_size'],
|
||||
$nbObjects,
|
||||
sprintf('<error>%s</error>', $e->getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$stepNbObjects = count($objects);
|
||||
$stepCount = $stepNbObjects+$offset;
|
||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', 100*$stepCount/$nbObjects, $stepCount, $nbObjects, $objectsPerSecond));
|
||||
if ($options['clear_object_manager']) {
|
||||
$manager->clear();
|
||||
}
|
||||
|
||||
usleep($options['sleep']);
|
||||
|
||||
if (null !== $loggerClosure) {
|
||||
$loggerClosure($options['batch_size'], $nbObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the objects of a query builder
|
||||
*
|
||||
* @param queryBuilder
|
||||
* @return int
|
||||
**/
|
||||
protected abstract function countObjects($queryBuilder);
|
||||
* {@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
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects
|
||||
* 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
|
||||
* @return array of objects
|
||||
**/
|
||||
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the query builder used to fetch the documents to index
|
||||
*
|
||||
* @return query builder
|
||||
**/
|
||||
protected abstract function createQueryBuilder();
|
||||
$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
212
Doctrine/Listener.php
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,28 +3,28 @@
|
|||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
||||
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
|
||||
use Elastica_Document;
|
||||
|
||||
/**
|
||||
* 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 string $class the model class
|
||||
* @param string $identifierField like 'id'
|
||||
* @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->objectManager
|
||||
->createQueryBuilder($this->objectClass)
|
||||
return $this->registry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$this->options['query_builder_method']}($this->objectClass)
|
||||
->field($this->options['identifier'])->in($identifierValues)
|
||||
->hydrate($hydrate)
|
||||
->getQuery()
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
||||
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractListener;
|
||||
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
|
||||
class Listener extends AbstractListener implements EventSubscriber
|
||||
{
|
||||
public function postPersist(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$document = $eventArgs->getDocument();
|
||||
|
||||
if ($document instanceof $this->objectClass) {
|
||||
$this->objectPersister->insertOne($document);
|
||||
}
|
||||
}
|
||||
|
||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$document = $eventArgs->getDocument();
|
||||
|
||||
if ($document instanceof $this->objectClass) {
|
||||
$this->objectPersister->replaceOne($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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,41 +2,86 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Query\Builder;
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractProvider;
|
||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||
|
||||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* Counts the objects of a query builder
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @param queryBuilder
|
||||
* @return int
|
||||
**/
|
||||
* @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)
|
||||
{
|
||||
return $queryBuilder->getQuery()->count();
|
||||
if (!$queryBuilder instanceof Builder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->getQuery()
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects
|
||||
*
|
||||
* @param queryBuilder
|
||||
* @param int limit
|
||||
* @param int offset
|
||||
* @return array of objects
|
||||
**/
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
return $queryBuilder->limit($limit)->skip($offset)->getQuery()->execute()->toArray();
|
||||
if (!$queryBuilder instanceof Builder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->skip($offset)
|
||||
->limit($limit)
|
||||
->getQuery()
|
||||
->execute()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the query builder used to fetch the documents to index
|
||||
*
|
||||
* @return query builder
|
||||
**/
|
||||
protected function createQueryBuilder()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function createQueryBuilder($method)
|
||||
{
|
||||
return $this->objectManager->getRepository($this->objectClass)->{$this->options['query_builder_method']}();
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$method}();
|
||||
}
|
||||
}
|
||||
|
|
44
Doctrine/MongoDB/SliceFetcher.php
Normal file
44
Doctrine/MongoDB/SliceFetcher.php
Normal 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()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -3,23 +3,23 @@
|
|||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
|
||||
use Elastica_Document;
|
||||
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 string $class the model class
|
||||
* @param string $identifierField like 'id'
|
||||
* @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)
|
||||
|
@ -28,13 +28,25 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
return array();
|
||||
}
|
||||
$hydrationMode = $hydrate ? Query::HYDRATE_OBJECT : Query::HYDRATE_ARRAY;
|
||||
$qb = $this->objectManager
|
||||
->getRepository($this->objectClass)
|
||||
->createQueryBuilder('o');
|
||||
/* @var $qb \Doctrine\ORM\QueryBuilder */
|
||||
$qb->where($qb->expr()->in('o.'.$this->options['identifier'], ':values'))
|
||||
|
||||
$qb = $this->getEntityQueryBuilder();
|
||||
$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.
|
||||
*
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
protected function getEntityQueryBuilder()
|
||||
{
|
||||
$repository = $this->registry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass);
|
||||
|
||||
return $repository->{$this->options['query_builder_method']}(static::ENTITY_ALIAS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractListener;
|
||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
|
||||
class Listener extends AbstractListener implements EventSubscriber
|
||||
{
|
||||
public function postPersist(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getEntity();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
$this->objectPersister->insertOne($entity);
|
||||
}
|
||||
}
|
||||
|
||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getEntity();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
$this->objectPersister->replaceOne($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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,48 +2,117 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractProvider;
|
||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||
|
||||
class Provider extends AbstractProvider
|
||||
{
|
||||
const ENTITY_ALIAS = 'a';
|
||||
|
||||
/**
|
||||
* Counts the objects of a query builder
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @param queryBuilder
|
||||
* @return int
|
||||
**/
|
||||
* @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)
|
||||
{
|
||||
$qb = clone $queryBuilder;
|
||||
$qb->select($qb->expr()->count($queryBuilder->getRootAlias()))
|
||||
->resetDQLPart('orderBy'); // no need to order the query. It does not change the count and make the query less efficient.
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getSingleScalarResult();
|
||||
/* Clone the query builder before altering its field selection and DQL,
|
||||
* lest we leave the query builder in a bad state for fetchSlice().
|
||||
*/
|
||||
$qb = clone $queryBuilder;
|
||||
$rootAliases = $queryBuilder->getRootAliases();
|
||||
|
||||
return $qb
|
||||
->select($qb->expr()->count($rootAliases[0]))
|
||||
// Remove ordering for efficiency; it doesn't affect the count
|
||||
->resetDQLPart('orderBy')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects
|
||||
* This method should remain in sync with SliceFetcher::fetch until it is deprecated and removed.
|
||||
*
|
||||
* @param queryBuilder
|
||||
* @param int limit
|
||||
* @param int offset
|
||||
* @return array of objects
|
||||
**/
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
$queryBuilder->setFirstResult($offset);
|
||||
$queryBuilder->setMaxResults($limit);
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
|
||||
return $queryBuilder->getQuery()->getResult();
|
||||
/*
|
||||
* 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)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the query builder used to fetch the documents to index
|
||||
*
|
||||
* @return query builder
|
||||
**/
|
||||
protected function createQueryBuilder()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function createQueryBuilder($method)
|
||||
{
|
||||
return $this->objectManager->getRepository($this->objectClass)->{$this->options['query_builder_method']}('a');
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
// ORM query builders require an alias argument
|
||||
->{$method}(static::ENTITY_ALIAS);
|
||||
}
|
||||
}
|
||||
|
|
50
Doctrine/ORM/SliceFetcher.php
Normal file
50
Doctrine/ORM/SliceFetcher.php
Normal 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()
|
||||
;
|
||||
}
|
||||
}
|
43
Doctrine/RepositoryManager.php
Normal file
43
Doctrine/RepositoryManager.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use FOS\ElasticaBundle\Manager\RepositoryManager as BaseManager;
|
||||
|
||||
/**
|
||||
* @author Richard Miller <info@limethinking.co.uk>
|
||||
*
|
||||
* Allows retrieval of basic or custom repository for mapped Doctrine
|
||||
* entities/documents.
|
||||
*/
|
||||
class RepositoryManager extends BaseManager
|
||||
{
|
||||
protected $entities = array();
|
||||
protected $repositories = array();
|
||||
protected $managerRegistry;
|
||||
|
||||
public function __construct(ManagerRegistry $managerRegistry, Reader $reader)
|
||||
{
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
parent::__construct($reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* 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;
|
||||
}
|
||||
|
||||
return parent::getRepository($realEntityName);
|
||||
}
|
||||
}
|
24
Doctrine/SliceFetcherInterface.php
Normal file
24
Doctrine/SliceFetcherInterface.php
Normal 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
12
DynamicIndex.php
Normal 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
104
Elastica/Client.php
Normal 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
59
Elastica/Index.php
Normal 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
38
Event/IndexEvent.php
Normal 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;
|
||||
}
|
||||
}
|
70
Event/IndexPopulateEvent.php
Normal file
70
Event/IndexPopulateEvent.php
Normal 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
70
Event/IndexResetEvent.php
Normal 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
78
Event/TransformEvent.php
Normal 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;
|
||||
}
|
||||
}
|
51
Event/TypePopulateEvent.php
Normal file
51
Event/TypePopulateEvent.php
Normal 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
49
Event/TypeResetEvent.php
Normal 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;
|
||||
}
|
||||
}
|
11
Exception/AliasIsIndexException.php
Normal file
11
Exception/AliasIsIndexException.php
Normal 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));
|
||||
}
|
||||
}
|
11
Exception/InvalidArgumentTypeException.php
Normal file
11
Exception/InvalidArgumentTypeException.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Exception;
|
||||
|
||||
class InvalidArgumentTypeException extends \InvalidArgumentException
|
||||
{
|
||||
public function __construct($value, $expectedType)
|
||||
{
|
||||
parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
|
||||
}
|
||||
}
|
|
@ -2,18 +2,29 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\AddProviderPass;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new AddProviderPass());
|
||||
$container->addCompilerPass(new ConfigSourcePass());
|
||||
$container->addCompilerPass(new IndexPass());
|
||||
$container->addCompilerPass(new RegisterProvidersPass(), PassConfig::TYPE_BEFORE_REMOVING);
|
||||
$container->addCompilerPass(new TransformerPass());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ namespace FOS\ElasticaBundle\Finder;
|
|||
|
||||
interface FinderInterface
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
* @return array results
|
||||
*/
|
||||
function find($query, $limit = null);
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return array results
|
||||
*/
|
||||
public function find($query, $limit = null, $options = array());
|
||||
}
|
||||
|
|
|
@ -2,15 +2,29 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Finder;
|
||||
|
||||
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Elastica\Query;
|
||||
|
||||
interface PaginatedFinderInterface
|
||||
interface PaginatedFinderInterface extends FinderInterface
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
* @return Pagerfanta paginated results
|
||||
*/
|
||||
function findPaginated($query);
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return Pagerfanta paginated results
|
||||
*/
|
||||
public function findPaginated($query, $options = array());
|
||||
|
||||
/**
|
||||
* Creates a paginator adapter for this query.
|
||||
*
|
||||
* @param mixed $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return PaginatorAdapterInterface
|
||||
*/
|
||||
public function createPaginatorAdapter($query, $options = array());
|
||||
}
|
||||
|
|
|
@ -2,82 +2,109 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Finder;
|
||||
|
||||
use FOS\ElasticaBundle\Finder\FinderInterface;
|
||||
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_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 FinderInterface, PaginatedFinderInterface
|
||||
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);
|
||||
}
|
||||
|
||||
protected function search($query, $limit = null)
|
||||
/**
|
||||
* Find documents similar to one with passed id.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param array $params
|
||||
* @param array $query
|
||||
*
|
||||
* @return array of model objects
|
||||
**/
|
||||
public function moreLikeThis($id, $params = array(), $query = array())
|
||||
{
|
||||
$queryObject = Elastica_Query::create($query);
|
||||
$doc = new Document($id);
|
||||
$results = $this->searchable->moreLikeThis($doc, $params, $query)->getResults();
|
||||
|
||||
return $this->transformer->transform($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param null|int $limit
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function search($query, $limit = null, $options = array())
|
||||
{
|
||||
$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($paginatorAdapter);
|
||||
return new Pagerfanta(new FantaPaginatorAdapter($paginatorAdapter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a paginator adapter for this query
|
||||
*
|
||||
* @param Elastica_Query $query
|
||||
* @return TransformedPaginatorAdapter
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createPaginatorAdapter(Elastica_Query $query)
|
||||
public function createPaginatorAdapter($query, $options = array())
|
||||
{
|
||||
return new TransformedPaginatorAdapter($this->searchable, $query, $this->transformer);
|
||||
$query = Query::create($query);
|
||||
|
||||
return new TransformedPaginatorAdapter($this->searchable, $query, $options, $this->transformer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
197
Index/AliasProcessor.php
Normal 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
70
Index/IndexManager.php
Normal 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
134
Index/MappingBuilder.php
Normal 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
158
Index/Resetter.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,53 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use FOS\ElasticaBundle\Index\IndexManager as BaseIndexManager;
|
||||
|
||||
class IndexManager
|
||||
/**
|
||||
* @deprecated Use \FOS\ElasticaBundle\Index\IndexManager
|
||||
*/
|
||||
class IndexManager extends BaseIndexManager
|
||||
{
|
||||
protected $indexes;
|
||||
protected $defaultIndex;
|
||||
|
||||
public function __construct(array $indexes, $defaultIndex)
|
||||
{
|
||||
$this->indexes = $indexes;
|
||||
$this->defaultIndex = $defaultIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered indexes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllIndexes()
|
||||
{
|
||||
return $this->indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an index by its name
|
||||
*
|
||||
* @return Elastica_Index
|
||||
**/
|
||||
public function getIndex($name)
|
||||
{
|
||||
if (!$name) {
|
||||
return $this->getDefaultIndex();
|
||||
}
|
||||
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 Elastica_Index
|
||||
**/
|
||||
public function getDefaultIndex()
|
||||
{
|
||||
return $this->defaultIndex;
|
||||
}
|
||||
}
|
||||
|
|
18
LICENSE
18
LICENSE
|
@ -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.
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Logger;
|
||||
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Logger for the Elastica.
|
||||
|
@ -12,40 +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 $logger The Symfony logger
|
||||
* @param LoggerInterface|null $logger The Symfony logger
|
||||
* @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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Elastica_Type;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Stores the configured mappings for all types
|
||||
* Responsible for applying configured mappings to elastica types
|
||||
*/
|
||||
class MappingRegistry
|
||||
{
|
||||
/**
|
||||
* Configured mappings. See http://www.elasticsearch.org/guide/reference/mapping/
|
||||
* array(
|
||||
* "index_name/type_name" => array(type_object, mapping_array)
|
||||
* )
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mappings = null;
|
||||
|
||||
/**
|
||||
* Instanciates a new MappingSetter
|
||||
*
|
||||
* @param array mappings
|
||||
*/
|
||||
public function __construct($mappings)
|
||||
{
|
||||
$this->mappings = $mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mappings to all elastica types
|
||||
**/
|
||||
public function applyMappings()
|
||||
{
|
||||
foreach ($this->mappings as $pair) {
|
||||
list($type, $mappings) = $pair;
|
||||
$type->setMapping($mappings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type mapping field names
|
||||
*
|
||||
* @param Elastica_Type $type
|
||||
* @return array list of fields names
|
||||
*/
|
||||
public function getTypeFieldNames(Elastica_Type $type)
|
||||
{
|
||||
$key = sprintf('%s/%s', $type->getIndex()->getName(), $type->getType());
|
||||
if (!isset($this->mappings[$key])) {
|
||||
throw new InvalidArgumentException(sprintf('This type is not registered: "%s".', $key));
|
||||
}
|
||||
|
||||
return array_keys($this->mappings[$key][1]);
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
use Pagerfanta\Adapter\AdapterInterface;
|
||||
use Elastica_Searchable;
|
||||
use Elastica_Query;
|
||||
|
||||
/**
|
||||
* Implements the Pagerfanta\Adapter\AdapterInterface for use with Pagerfanta\Pagerfanta
|
||||
*
|
||||
* Allows pagination of Elastica_Query. Does not map results
|
||||
*/
|
||||
abstract class AbstractPaginatorAdapter implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* @var Elastica_SearchableInterface the object to search in
|
||||
*/
|
||||
protected $searchable = null;
|
||||
|
||||
/**
|
||||
* @var Elastica_Query the query to search
|
||||
*/
|
||||
protected $query = null;
|
||||
|
||||
/**
|
||||
* @see PaginatorAdapterInterface::__construct
|
||||
*
|
||||
* @param Elastica_SearchableInterface the object to search in
|
||||
* @param Elastica_Query the query to search
|
||||
*/
|
||||
public function __construct(Elastica_Searchable $searchable, Elastica_Query $query)
|
||||
{
|
||||
$this->searchable = $searchable;
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
protected function getElasticaResults($offset, $itemCountPerPage)
|
||||
{
|
||||
$query = clone $this->query;
|
||||
$query->setFrom($offset);
|
||||
$query->setLimit($itemCountPerPage);
|
||||
|
||||
return $this->searchable->search($query)->getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Pagerfanta\Adapter\AdapterInterface::getNbResults
|
||||
*/
|
||||
public function getNbResults()
|
||||
{
|
||||
return $this->searchable->count($this->query);
|
||||
}
|
||||
}
|
63
Paginator/FantaPaginatorAdapter.php
Normal file
63
Paginator/FantaPaginatorAdapter.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
use Pagerfanta\Adapter\AdapterInterface;
|
||||
|
||||
class FantaPaginatorAdapter implements AdapterInterface
|
||||
{
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @param \FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface $adapter
|
||||
*/
|
||||
public function __construct(PaginatorAdapterInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*/
|
||||
public function getNbResults()
|
||||
{
|
||||
return $this->adapter->getTotalHits();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function getSlice($offset, $length)
|
||||
{
|
||||
return $this->adapter->getResults($offset, $length)->toArray();
|
||||
}
|
||||
}
|
37
Paginator/PaginatorAdapterInterface.php
Normal file
37
Paginator/PaginatorAdapterInterface.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
interface PaginatorAdapterInterface
|
||||
{
|
||||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*/
|
||||
public function getTotalHits();
|
||||
|
||||
/**
|
||||
* Returns an slice of the results.
|
||||
*
|
||||
* @param integer $offset The offset.
|
||||
* @param integer $length The length.
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*/
|
||||
public function getResults($offset, $length);
|
||||
|
||||
/**
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
34
Paginator/PartialResultsInterface.php
Normal file
34
Paginator/PartialResultsInterface.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
interface PartialResultsInterface
|
||||
{
|
||||
/**
|
||||
* Returns the paginated results.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray();
|
||||
|
||||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*/
|
||||
public function getTotalHits();
|
||||
|
||||
/**
|
||||
* Returns the facets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns the aggregations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
|
@ -2,20 +2,168 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
use Elastica\SearchableInterface;
|
||||
use Elastica\Query;
|
||||
use Elastica\ResultSet;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Implements the Pagerfanta\Adapter\AdapterInterface for use with Zend\Paginator\Paginator
|
||||
*
|
||||
* Allows pagination of Elastica_Query. Does not map results
|
||||
* Allows pagination of Elastica\Query. Does not map results.
|
||||
*/
|
||||
class RawPaginatorAdapter extends AbstractPaginatorAdapter
|
||||
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||
{
|
||||
/**
|
||||
* @see Pagerfanta\Adapter\AdapterInterface::getSlice
|
||||
* @var SearchableInterface the object to search in
|
||||
*/
|
||||
public function getSlice($offset, $length)
|
||||
{
|
||||
$results = $this->getElasticaResults($offset, $length);
|
||||
private $searchable;
|
||||
|
||||
return array_map(function($result) { return $result->getSource(); }, $results);
|
||||
/**
|
||||
* @var Query the query to search
|
||||
*/
|
||||
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 SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
|
||||
{
|
||||
$this->searchable = $searchable;
|
||||
$this->query = $query;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paginated results.
|
||||
*
|
||||
* @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->setSize($itemCountPerPage);
|
||||
|
||||
$resultSet = $this->searchable->search($query, $this->options);
|
||||
$this->totalHits = $resultSet->getTotalHits();
|
||||
$this->facets = $resultSet->getFacets();
|
||||
$this->aggregations = $resultSet->getAggregations();
|
||||
|
||||
return $resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paginated results.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $itemCountPerPage
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*/
|
||||
public function getResults($offset, $itemCountPerPage)
|
||||
{
|
||||
return new RawPartialResults($this->getElasticaResults($offset, $itemCountPerPage));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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($genuineTotal = false)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
64
Paginator/RawPartialResults.php
Normal file
64
Paginator/RawPartialResults.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
use Elastica\ResultSet;
|
||||
use Elastica\Result;
|
||||
|
||||
/**
|
||||
* Raw partial results transforms to a simple array.
|
||||
*/
|
||||
class RawPartialResults implements PartialResultsInterface
|
||||
{
|
||||
protected $resultSet;
|
||||
|
||||
/**
|
||||
* @param ResultSet $resultSet
|
||||
*/
|
||||
public function __construct(ResultSet $resultSet)
|
||||
{
|
||||
$this->resultSet = $resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function (Result $result) {
|
||||
return $result->getSource();
|
||||
}, $this->resultSet->getResults());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTotalHits()
|
||||
{
|
||||
return $this->resultSet->getTotalHits();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
if ($this->resultSet->hasFacets()) {
|
||||
return $this->resultSet->getFacets();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
if ($this->resultSet->hasAggregations()) {
|
||||
return $this->resultSet->getAggregations();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -3,37 +3,34 @@
|
|||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use Elastica_Searchable;
|
||||
use Elastica_Query;
|
||||
use Elastica\SearchableInterface;
|
||||
use Elastica\Query;
|
||||
|
||||
/**
|
||||
* Implements the Pagerfanta\Adapter\AdapterInterface Interface for use with Zend\Paginator\Paginator
|
||||
*
|
||||
* Allows pagination of Elastica_Query
|
||||
* Allows pagination of \Elastica\Query.
|
||||
*/
|
||||
class TransformedPaginatorAdapter extends AbstractPaginatorAdapter
|
||||
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
||||
{
|
||||
protected $transformer;
|
||||
private $transformer;
|
||||
|
||||
/**
|
||||
* @param Elastica_SearchableInterface the object to search in
|
||||
* @param Elastica_Query the query to search
|
||||
* @param ElasticaToModelTransformerInterface the transformer for fetching the results
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Pagerfanta\Adapter\AdapterInterface::getSlice
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSlice($offset, $length)
|
||||
public function getResults($offset, $length)
|
||||
{
|
||||
$results = $this->getElasticaResults($offset, $length);
|
||||
|
||||
return $this->transformer->transform($results);
|
||||
return new TransformedPartialResults($this->getElasticaResults($offset, $length), $this->transformer);
|
||||
}
|
||||
}
|
||||
|
|
33
Paginator/TransformedPartialResults.php
Normal file
33
Paginator/TransformedPartialResults.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Paginator;
|
||||
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use Elastica\ResultSet;
|
||||
|
||||
/**
|
||||
* Partial transformed result set.
|
||||
*/
|
||||
class TransformedPartialResults extends RawPartialResults
|
||||
{
|
||||
protected $transformer;
|
||||
|
||||
/**
|
||||
* @param ResultSet $resultSet
|
||||
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
||||
*/
|
||||
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
||||
{
|
||||
parent::__construct($resultSet);
|
||||
|
||||
$this->transformer = $transformer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->transformer->transform($this->resultSet->getResults());
|
||||
}
|
||||
}
|
|
@ -2,16 +2,15 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Persister;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Elastica\Exception\BulkException;
|
||||
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
use Elastica_Type;
|
||||
use Elastica_Document;
|
||||
use Exception;
|
||||
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>
|
||||
*/
|
||||
|
@ -21,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;
|
||||
|
@ -30,77 +30,163 @@ 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 of domain model objects
|
||||
**/
|
||||
* @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
|
||||
*/
|
||||
protected function transformToElasticaDocument($object)
|
||||
public function transformToElasticaDocument($object)
|
||||
{
|
||||
return $this->transformer->transform($object, $this->fields);
|
||||
}
|
||||
|
|
|
@ -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 of domain model objects
|
||||
**/
|
||||
function insertMany(array $objects);
|
||||
* @param array $objects array of domain model 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);
|
||||
}
|
||||
|
|
50
Persister/ObjectSerializerPersister.php
Normal file
50
Persister/ObjectSerializerPersister.php
Normal 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;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Closure;
|
||||
|
||||
class Populator
|
||||
{
|
||||
protected $providers;
|
||||
|
||||
public function __construct(array $providers)
|
||||
{
|
||||
foreach ($providers as $name => $provider) {
|
||||
$this->addProvider($name, $provider);
|
||||
}
|
||||
}
|
||||
|
||||
public function addProvider($name, ProviderInterface $provider)
|
||||
{
|
||||
$this->providers[$name] = $provider;
|
||||
}
|
||||
|
||||
public function populate(Closure $loggerClosure)
|
||||
{
|
||||
foreach ($this->providers as $name => $provider) {
|
||||
$provider->populate(function($text) use ($name, $loggerClosure) {
|
||||
$loggerClosure(sprintf('Indexing %s, %s', $name, $text));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,74 +3,75 @@
|
|||
namespace FOS\ElasticaBundle\Propel;
|
||||
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use Elastica_Document;
|
||||
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',
|
||||
);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of elastica objects into an array of
|
||||
* model objects fetched from the propel repository
|
||||
* Transforms an array of Elastica document into an array of Propel entities
|
||||
* fetched from the database.
|
||||
*
|
||||
* @param array of elastica objects
|
||||
* @return array
|
||||
* @param array $elasticaObjects
|
||||
*
|
||||
* @return array|\ArrayObject
|
||||
*/
|
||||
public function transform(array $elasticaObjects)
|
||||
{
|
||||
$ids = array_map(function($elasticaObject) {
|
||||
return $elasticaObject->getId();
|
||||
}, $elasticaObjects);
|
||||
$ids = array();
|
||||
foreach ($elasticaObjects as $elasticaObject) {
|
||||
$ids[] = $elasticaObject->getId();
|
||||
}
|
||||
|
||||
$objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
|
||||
|
||||
$identifierGetter = 'get'.ucfirst($this->options['identifier']);
|
||||
|
||||
// sort objects in the order of ids
|
||||
// Sort objects in the order of their IDs
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
$sortCallback = $this->getSortingClosure($idPos, $identifier);
|
||||
|
||||
if (is_object($objects)) {
|
||||
$objects->uasort(function($a, $b) use ($idPos, $identifierGetter) {
|
||||
return $idPos[$a->$identifierGetter()] > $idPos[$b->$identifierGetter()];
|
||||
});
|
||||
$objects->uasort($sortCallback);
|
||||
} else {
|
||||
usort($objects, function($a, $b) use ($idPos, $identifierGetter) {
|
||||
return $idPos[$a->$identifierGetter()] > $idPos[$b->$identifierGetter()];
|
||||
});
|
||||
usort($objects, $sortCallback);
|
||||
}
|
||||
|
||||
return $objects;
|
||||
|
@ -84,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]);
|
||||
}
|
||||
|
||||
|
@ -108,13 +109,15 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch objects for theses identifier values
|
||||
* Fetch Propel entities for the given identifier values.
|
||||
*
|
||||
* @param string $class the model class
|
||||
* @param string $identifierField like 'id'
|
||||
* @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)
|
||||
{
|
||||
|
@ -124,7 +127,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
|
||||
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
||||
|
||||
if (!$hydrate) {
|
||||
if (! $hydrate) {
|
||||
return $query->toArray();
|
||||
}
|
||||
|
||||
|
@ -134,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)
|
||||
|
@ -144,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)
|
||||
{
|
||||
|
|
|
@ -2,75 +2,72 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Propel;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Elastica_Type;
|
||||
use Elastica_Document;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
||||
|
||||
/**
|
||||
* Propel provider
|
||||
* Propel provider.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class Provider implements ProviderInterface
|
||||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* Elastica type
|
||||
*
|
||||
* @var Elastica_Type
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Object persister
|
||||
*
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Provider options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = array(
|
||||
'batch_size' => 100,
|
||||
);
|
||||
|
||||
public function __construct(Elastica_Type $type, ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
||||
public function doPopulate($options, \Closure $loggerClosure = null)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$queryClass = $this->objectClass.'Query';
|
||||
$nbObjects = $queryClass::create()->count();
|
||||
|
||||
/**
|
||||
* Insert the repository objects in the type index
|
||||
*
|
||||
* @param Closure $loggerClosure
|
||||
*/
|
||||
public function populate(Closure $loggerClosure)
|
||||
{
|
||||
$queryClass = $this->objectClass . 'Query';
|
||||
$nbObjects = $queryClass::create()->count();
|
||||
$offset = $options['offset'];
|
||||
|
||||
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
||||
|
||||
$stepStartTime = microtime(true);
|
||||
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||
$objects = $queryClass::create()
|
||||
->limit($this->options['batch_size'])
|
||||
->limit($options['batch_size'])
|
||||
->offset($offset)
|
||||
->find();
|
||||
->find()
|
||||
->getArrayCopy();
|
||||
$objects = $this->filterObjects($options, $objects);
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
}
|
||||
|
||||
$this->objectPersister->insertMany($objects->getArrayCopy());
|
||||
usleep($options['sleep']);
|
||||
|
||||
$stepNbObjects = count($objects);
|
||||
$stepCount = $stepNbObjects+$offset;
|
||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', 100*$stepCount/$nbObjects, $stepCount, $nbObjects, $objectsPerSecond));
|
||||
if ($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' => null,
|
||||
'sleep' => 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function disableLogging()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
195
Provider/AbstractProvider.php
Normal file
195
Provider/AbstractProvider.php
Normal file
|
@ -0,0 +1,195 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* AbstractProvider.
|
||||
*/
|
||||
abstract class AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $baseOptions;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* @var OptionsResolver
|
||||
*/
|
||||
protected $resolver;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $baseOptions
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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,
|
||||
'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
240
Provider/Indexable.php
Normal 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
|
||||
));
|
||||
}
|
||||
}
|
26
Provider/IndexableInterface.php
Normal file
26
Provider/IndexableInterface.php
Normal 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);
|
||||
}
|
|
@ -2,19 +2,25 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Insert application domain objects into elastica types
|
||||
* Insert application domain objects into elastica types.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* Add all domain objects of a repository to the elastica type
|
||||
* Persists all domain objects to ElasticSearch for this provider.
|
||||
*
|
||||
* @param Closure $loggerClosure
|
||||
* 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);
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array());
|
||||
}
|
||||
|
|
107
Provider/ProviderRegistry.php
Normal file
107
Provider/ProviderRegistry.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* References persistence providers for each index and type.
|
||||
*/
|
||||
class ProviderRegistry implements ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
private $providers = array();
|
||||
|
||||
/**
|
||||
* Registers a provider for the specified index and type.
|
||||
*
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
* @param string $providerId
|
||||
*/
|
||||
public function addProvider($index, $type, $providerId)
|
||||
{
|
||||
if (!isset($this->providers[$index])) {
|
||||
$this->providers[$index] = array();
|
||||
}
|
||||
|
||||
$this->providers[$index][$type] = $providerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered providers.
|
||||
*
|
||||
* Providers will be indexed by "index/type" strings in the returned array.
|
||||
*
|
||||
* @return array of ProviderInterface instances
|
||||
*/
|
||||
public function getAllProviders()
|
||||
{
|
||||
$providers = array();
|
||||
|
||||
foreach ($this->providers as $index => $indexProviders) {
|
||||
foreach ($indexProviders as $type => $providerId) {
|
||||
$providers[sprintf('%s/%s', $index, $type)] = $this->container->get($providerId);
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all providers for an index.
|
||||
*
|
||||
* Providers will be indexed by "type" strings in the returned array.
|
||||
*
|
||||
* @param string $index
|
||||
*
|
||||
* @return ProviderInterface[]
|
||||
*
|
||||
* @throws \InvalidArgumentException if no providers were registered for the index
|
||||
*/
|
||||
public function getIndexProviders($index)
|
||||
{
|
||||
if (!isset($this->providers[$index])) {
|
||||
throw new \InvalidArgumentException(sprintf('No providers were registered for index "%s".', $index));
|
||||
}
|
||||
|
||||
$providers = array();
|
||||
|
||||
foreach ($this->providers[$index] as $type => $providerId) {
|
||||
$providers[$type] = $this->container->get($providerId);
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the provider for an index and type.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (!isset($this->providers[$index][$type])) {
|
||||
throw new \InvalidArgumentException(sprintf('No provider was registered for index "%s" and type "%s".', $index, $type));
|
||||
}
|
||||
|
||||
return $this->container->get($this->providers[$index][$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\DependencyInjection\ContainerAwareInterface::setContainer()
|
||||
*/
|
||||
public function setContainer(ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
}
|
568
README.md
568
README.md
|
@ -1,559 +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.
|
||||
> **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": "~1.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`.
|
||||
|
||||
#### 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`.
|
||||
|
||||
### 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 paquets 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 provider configuration.
|
||||
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider:
|
||||
identifier: id
|
||||
|
||||
#### Manual provider
|
||||
|
||||
Create a service with the tag "fos_elastica.provider".
|
||||
|
||||
<service id="acme.search_provider.user" class="Acme\UserBundle\Search\UserProvider">
|
||||
<tag name="fos_elastica.provider" />
|
||||
<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
|
||||
*/
|
||||
public function populate(Closure $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!
|
||||
|
||||
/** var Pagerfanta\Pagerfanta */
|
||||
$userPaginator = $finder->findPaginated('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 = $finder->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 = $finder->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 = $finder->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.
|
||||
|
||||
### 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:
|
||||
```
|
||||
Resources/meta/LICENSE
|
||||
|
|
|
@ -2,33 +2,66 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
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
|
||||
{
|
||||
protected $finder;
|
||||
|
||||
public function __construct($finder)
|
||||
public function __construct(PaginatedFinderInterface $finder)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return Paginator\PaginatorAdapterInterface
|
||||
*/
|
||||
public function createPaginatorAdapter($query, $options = array())
|
||||
{
|
||||
return $this->finder->createPaginatorAdapter($query, $options);
|
||||
}
|
||||
}
|
||||
|
|
35
Reseter.php
35
Reseter.php
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Elastica_Exception_Response;
|
||||
|
||||
/**
|
||||
* Deletes and recreates indexes
|
||||
**/
|
||||
class Reseter
|
||||
{
|
||||
/**
|
||||
* Index settings and mappings
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $indexConfigs;
|
||||
|
||||
public function __construct(array $indexConfigs)
|
||||
{
|
||||
$this->indexConfigs = $indexConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all indexes
|
||||
*
|
||||
* @return null
|
||||
**/
|
||||
public function reset()
|
||||
{
|
||||
foreach ($this->indexConfigs as $indexConfig) {
|
||||
$indexConfig['index']->create($indexConfig['config'], true);
|
||||
}
|
||||
}
|
||||
}
|
12
Resetter.php
Normal file
12
Resetter.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use FOS\ElasticaBundle\Index\Resetter as BaseResetter;
|
||||
|
||||
/**
|
||||
* @deprecated Use \FOS\ElasticaBundle\Index\Resetter
|
||||
*/
|
||||
class Resetter extends BaseResetter
|
||||
{
|
||||
}
|
|
@ -1,71 +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.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.client" class="%fos_elastica.client.class%">
|
||||
<argument /> <!-- config -->
|
||||
<call method="setLogger">
|
||||
<argument type="service" id="fos_elastica.logger" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.index_manager" class="FOS\ElasticaBundle\IndexManager">
|
||||
<argument /> <!-- indexes -->
|
||||
<argument /> <!-- default index -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.populator" class="FOS\ElasticaBundle\Populator">
|
||||
<argument /> <!-- providers -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.reseter" class="FOS\ElasticaBundle\Reseter">
|
||||
<argument /> <!-- index configs -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.object_persister.prototype" 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.prototype" class="FOS\ElasticaBundle\Finder\TransformedFinder" public="true" abstract="true">
|
||||
<argument /> <!-- searchable -->
|
||||
<argument /> <!-- transformer -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.model_to_elastica_transformer.prototype.auto" class="FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer" public="false" abstract="true">
|
||||
<argument /> <!-- options -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.collection.prototype" class="%fos_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
|
||||
<argument type="collection" /> <!-- transformers -->
|
||||
</service>
|
||||
<service id="fos_elastica.mapping_builder" class="%fos_elastica.mapping_builder.class%" />
|
||||
|
||||
<service id="fos_elastica.property_accessor" class="%fos_elastica.property_accessor.class%" />
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
|
53
Resources/config/index.xml
Normal file
53
Resources/config/index.xml
Normal 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>
|
|
@ -4,33 +4,46 @@
|
|||
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.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>
|
||||
|
||||
<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\ElasticaBundle\Doctrine\MongoDB\Provider" public="false" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument type="service" id="doctrine.odm.mongodb.document_manager" />
|
||||
<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" /> <!-- 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">
|
||||
<argument type="service" id="doctrine.odm.mongodb.document_manager" />
|
||||
<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 -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.manager.mongodb" class="%fos_elastica.manager.class%">
|
||||
<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>
|
||||
|
|
|
@ -1,36 +1,49 @@
|
|||
<?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="false" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||
<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" /> <!-- 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 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">
|
||||
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||
<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 -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.manager.orm" class="%fos_elastica.manager.class%">
|
||||
<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>
|
||||
|
|
27
Resources/config/persister.xml
Normal file
27
Resources/config/persister.xml
Normal 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>
|
|
@ -4,10 +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="false" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<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>
|
||||
|
@ -15,12 +14,13 @@
|
|||
<service id="fos_elastica.elastica_to_model_transformer.prototype.propel" class="FOS\ElasticaBundle\Propel\ElasticaToModelTransformer" public="false">
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</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>
|
||||
|
|
18
Resources/config/provider.xml
Normal file
18
Resources/config/provider.xml
Normal 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>
|
14
Resources/config/serializer.xml
Normal file
14
Resources/config/serializer.xml
Normal 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>
|
13
Resources/config/source.xml
Normal file
13
Resources/config/source.xml
Normal 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>
|
33
Resources/config/transformer.xml
Normal file
33
Resources/config/transformer.xml
Normal 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>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue