Compare commits
696 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30e351b1d5 | |||
| 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 | ||
|
|
58e983fb06 | ||
|
|
c24a647a43 | ||
|
|
ae4f676c25 | ||
|
|
8e03ed2663 | ||
|
|
4f5be038ef | ||
|
|
83e27ede93 |
170 changed files with 9646 additions and 2690 deletions
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
|
language: php
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 5.3
|
- 5.3
|
||||||
- 5.4
|
- 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:
|
before_script:
|
||||||
- echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
- /usr/share/elasticsearch/bin/elasticsearch -v
|
||||||
- composer install --dev
|
- 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;
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,13 @@ https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v2.0.0...v2.0.1
|
||||||
To generate a changelog summary since the last version, run
|
To generate a changelog summary since the last version, run
|
||||||
`git log --no-merges --oneline v2.0.0...2.0.x`
|
`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)
|
* 2.0.1 (2013-04-04)
|
||||||
|
|
||||||
* f0d3a4d: Ensure mongo extension is available in Travis CI
|
* f0d3a4d: Ensure mongo extension is available in Travis CI
|
||||||
|
|
|
||||||
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
|
||||||
29
Client.php
29
Client.php
|
|
@ -2,34 +2,11 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle;
|
namespace FOS\ElasticaBundle;
|
||||||
|
|
||||||
use Elastica_Client;
|
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
|
||||||
use FOS\ElasticaBundle\Logger\ElasticaLogger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Gordon Franke <info@nevalon.de>
|
* @deprecated Use \FOS\ElasticaBundle\Elastica\LoggingClient
|
||||||
*/
|
*/
|
||||||
class Client extends Elastica_Client
|
class Client extends BaseClient
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var ElasticaLogger
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function setLogger(ElasticaLogger $logger)
|
|
||||||
{
|
|
||||||
$this->logger = $logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function request($path, $method, $data = array(), array $query = array())
|
|
||||||
{
|
|
||||||
$start = microtime(true);
|
|
||||||
$response = parent::request($path, $method, $data, $query);
|
|
||||||
|
|
||||||
if (null !== $this->logger) {
|
|
||||||
$time = microtime(true) - $start;
|
|
||||||
$this->logger->logQuery($path, $method, $data, $time);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
129
Command/PopulateCommand.php
Executable file → Normal file
129
Command/PopulateCommand.php
Executable file → Normal file
|
|
@ -2,27 +2,38 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Command;
|
namespace FOS\ElasticaBundle\Command;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Event\IndexPopulateEvent;
|
||||||
|
use FOS\ElasticaBundle\Event\TypePopulateEvent;
|
||||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
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\InputOption;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Output\Output;
|
|
||||||
use FOS\ElasticaBundle\IndexManager;
|
use FOS\ElasticaBundle\IndexManager;
|
||||||
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
||||||
use FOS\ElasticaBundle\Resetter;
|
use FOS\ElasticaBundle\Resetter;
|
||||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
use Symfony\Component\Console\Helper\ProgressBar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the search index
|
* Populate the search index.
|
||||||
*/
|
*/
|
||||||
class PopulateCommand extends ContainerAwareCommand
|
class PopulateCommand extends ContainerAwareCommand
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||||
|
*/
|
||||||
|
private $dispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var IndexManager
|
* @var IndexManager
|
||||||
*/
|
*/
|
||||||
private $indexManager;
|
private $indexManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ProgressClosureBuilder
|
||||||
|
*/
|
||||||
|
private $progressClosureBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ProviderRegistry
|
* @var ProviderRegistry
|
||||||
*/
|
*/
|
||||||
|
|
@ -43,28 +54,58 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index to repopulate')
|
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index to repopulate')
|
||||||
->addOption('type', null, InputOption::VALUE_OPTIONAL, 'The type 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('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')
|
->setDescription('Populates search indexes from providers')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Symfony\Component\Console\Command\Command::initialize()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
|
$this->dispatcher = $this->getContainer()->get('event_dispatcher');
|
||||||
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
|
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
|
||||||
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
|
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
|
||||||
$this->resetter = $this->getContainer()->get('fos_elastica.resetter');
|
$this->resetter = $this->getContainer()->get('fos_elastica.resetter');
|
||||||
|
$this->progressClosureBuilder = new ProgressClosureBuilder();
|
||||||
|
|
||||||
|
if (!$input->getOption('no-overwrite-format') && class_exists('Symfony\\Component\\Console\\Helper\\ProgressBar')) {
|
||||||
|
ProgressBar::setFormatDefinition('normal', " %current%/%max% [%bar%] %percent:3s%%\n%message%");
|
||||||
|
ProgressBar::setFormatDefinition('verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%\n%message%");
|
||||||
|
ProgressBar::setFormatDefinition('very_verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%\n%message%");
|
||||||
|
ProgressBar::setFormatDefinition('debug', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%\n%message%");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Symfony\Component\Console\Command\Command::execute()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$index = $input->getOption('index');
|
$index = $input->getOption('index');
|
||||||
$type = $input->getOption('type');
|
$type = $input->getOption('type');
|
||||||
$reset = $input->getOption('no-reset') ? false : true;
|
$reset = !$input->getOption('no-reset');
|
||||||
|
$options = array(
|
||||||
|
'ignore_errors' => $input->getOption('ignore-errors'),
|
||||||
|
'offset' => $input->getOption('offset'),
|
||||||
|
'sleep' => $input->getOption('sleep')
|
||||||
|
);
|
||||||
|
if ($input->getOption('batch-size')) {
|
||||||
|
$options['batch_size'] = (int) $input->getOption('batch-size');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (null === $index && null !== $type) {
|
if (null === $index && null !== $type) {
|
||||||
throw new \InvalidArgumentException('Cannot specify type option without an index.');
|
throw new \InvalidArgumentException('Cannot specify type option without an index.');
|
||||||
|
|
@ -72,15 +113,15 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
|
|
||||||
if (null !== $index) {
|
if (null !== $index) {
|
||||||
if (null !== $type) {
|
if (null !== $type) {
|
||||||
$this->populateIndexType($output, $index, $type, $reset);
|
$this->populateIndexType($output, $index, $type, $reset, $options);
|
||||||
} else {
|
} else {
|
||||||
$this->populateIndex($output, $index, $reset);
|
$this->populateIndex($output, $index, $reset, $options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$indexes = array_keys($this->indexManager->getAllIndexes());
|
$indexes = array_keys($this->indexManager->getAllIndexes());
|
||||||
|
|
||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
$this->populateIndex($output, $index, $reset);
|
$this->populateIndex($output, $index, $reset, $options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,27 +132,26 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
* @param OutputInterface $output
|
* @param OutputInterface $output
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @param boolean $reset
|
* @param boolean $reset
|
||||||
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
private function populateIndex(OutputInterface $output, $index, $reset)
|
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
||||||
{
|
{
|
||||||
if ($reset) {
|
$event = new IndexPopulateEvent($index, $reset, $options);
|
||||||
|
$this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);
|
||||||
|
|
||||||
|
if ($event->isReset()) {
|
||||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||||
$this->resetter->resetIndex($index);
|
$this->resetter->resetIndex($index, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var $providers ProviderInterface[] */
|
$types = array_keys($this->providerRegistry->getIndexProviders($index));
|
||||||
$providers = $this->providerRegistry->getIndexProviders($index);
|
foreach ($types as $type) {
|
||||||
|
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
|
||||||
foreach ($providers as $type => $provider) {
|
|
||||||
$loggerClosure = function($message) use ($output, $index, $type) {
|
|
||||||
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
|
|
||||||
};
|
|
||||||
|
|
||||||
$provider->populate($loggerClosure);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
|
||||||
$this->indexManager->getIndex($index)->refresh();
|
|
||||||
|
$this->refreshIndex($output, $index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -121,22 +161,41 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @param boolean $reset
|
* @param boolean $reset
|
||||||
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
private function populateIndexType(OutputInterface $output, $index, $type, $reset)
|
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
|
||||||
{
|
{
|
||||||
if ($reset) {
|
$event = new TypePopulateEvent($index, $type, $reset, $options);
|
||||||
$output->writeln(sprintf('Resetting: %s/%s', $index, $type));
|
$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);
|
$this->resetter->resetIndexType($index, $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
$loggerClosure = function($message) use ($output, $index, $type) {
|
|
||||||
$output->writeln(sprintf('Populating: %s/%s, %s', $index, $type, $message));
|
|
||||||
};
|
|
||||||
|
|
||||||
$provider = $this->providerRegistry->getProvider($index, $type);
|
$provider = $this->providerRegistry->getProvider($index, $type);
|
||||||
$provider->populate($loggerClosure);
|
$loggerClosure = $this->progressClosureBuilder->build($output, 'Populating', $index, $type);
|
||||||
|
$provider->populate($loggerClosure, $event->getOptions());
|
||||||
|
|
||||||
$output->writeln(sprintf('Refreshing: %s', $index));
|
$this->dispatcher->dispatch(TypePopulateEvent::POST_TYPE_POPULATE, $event);
|
||||||
|
|
||||||
|
$this->refreshIndex($output, $index, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes an index.
|
||||||
|
*
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param string $index
|
||||||
|
* @param bool $postPopulate
|
||||||
|
*/
|
||||||
|
private function refreshIndex(OutputInterface $output, $index, $postPopulate = true)
|
||||||
|
{
|
||||||
|
if ($postPopulate) {
|
||||||
|
$this->resetter->postPopulate($index);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||||
$this->indexManager->getIndex($index)->refresh();
|
$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\InputOption;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Output\Output;
|
use Elastica\Query;
|
||||||
use Elastica_Query;
|
use Elastica\Result;
|
||||||
use Elastica_Result;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches a type
|
* Searches a type.
|
||||||
*/
|
*/
|
||||||
class SearchCommand extends ContainerAwareCommand
|
class SearchCommand extends ContainerAwareCommand
|
||||||
{
|
{
|
||||||
|
|
@ -41,11 +40,11 @@ class SearchCommand extends ContainerAwareCommand
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$indexName = $input->getOption('index');
|
$indexName = $input->getOption('index');
|
||||||
/** @var $index \Elastica_Index */
|
/** @var $index \Elastica\Index */
|
||||||
$index = $this->getContainer()->get('fos_elastica.index_manager')->getIndex($indexName ? $indexName : null);
|
$index = $this->getContainer()->get('fos_elastica.index_manager')->getIndex($indexName ? $indexName : null);
|
||||||
$type = $index->getType($input->getArgument('type'));
|
$type = $index->getType($input->getArgument('type'));
|
||||||
$query = Elastica_Query::create($input->getArgument('query'));
|
$query = Query::create($input->getArgument('query'));
|
||||||
$query->setLimit($input->getOption('limit'));
|
$query->setSize($input->getOption('limit'));
|
||||||
if ($input->getOption('explain')) {
|
if ($input->getOption('explain')) {
|
||||||
$query->setExplain(true);
|
$query->setExplain(true);
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +57,7 @@ class SearchCommand extends ContainerAwareCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatResult(Elastica_Result $result, $showField, $showSource, $showId, $explain)
|
protected function formatResult(Result $result, $showField, $showSource, $showId, $explain)
|
||||||
{
|
{
|
||||||
$source = $result->getSource();
|
$source = $result->getSource();
|
||||||
if ($showField) {
|
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
|
<?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;
|
namespace FOS\ElasticaBundle\Configuration;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Annotation\Search as BaseSearch;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation class for setting search repository.
|
* Annotation class for setting search repository.
|
||||||
*
|
*
|
||||||
* @author Richard Miller <info@limethinking.co.uk>
|
|
||||||
* @Annotation
|
* @Annotation
|
||||||
|
*
|
||||||
|
* @deprecated Use FOS\ElasticaBundle\Annotation\Search instead
|
||||||
* @Target("CLASS")
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -55,6 +55,7 @@ class RegisterProvidersPass implements CompilerPassInterface
|
||||||
* Returns whether the class implements ProviderInterface.
|
* Returns whether the class implements ProviderInterface.
|
||||||
*
|
*
|
||||||
* @param string $class
|
* @param string $class
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
private function isProviderImplementation($class)
|
private function isProviderImplementation($class)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class TransformerPass implements CompilerPassInterface
|
||||||
*/
|
*/
|
||||||
public function process(ContainerBuilder $container)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ class TransformerPass implements CompilerPassInterface
|
||||||
throw new InvalidArgumentException('The Transformer must have both a type and an index defined.');
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,4 +44,4 @@ class TransformerPass implements CompilerPassInterface
|
||||||
$index->replaceArgument(0, $indexTransformers);
|
$index->replaceArgument(0, $indexTransformers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,29 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
|
|
||||||
class Configuration implements ConfigurationInterface
|
class Configuration implements ConfigurationInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Stores supported database drivers.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
||||||
|
|
||||||
private $configArray = array();
|
/**
|
||||||
|
* If the kernel is running in debug mode.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $debug;
|
||||||
|
|
||||||
public function __construct($configArray){
|
public function __construct($debug)
|
||||||
$this->configArray = $configArray;
|
{
|
||||||
|
$this->debug = $debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the configuration tree.
|
* Generates the configuration tree.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Config\Definition\NodeInterface
|
* @return TreeBuilder
|
||||||
*/
|
*/
|
||||||
public function getConfigTreeBuilder()
|
public function getConfigTreeBuilder()
|
||||||
{
|
{
|
||||||
|
|
@ -31,9 +42,20 @@ class Configuration implements ConfigurationInterface
|
||||||
|
|
||||||
$rootNode
|
$rootNode
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('default_client')->end()
|
->scalarNode('default_client')
|
||||||
->scalarNode('default_index')->end()
|
->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()
|
->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()
|
->end()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -41,17 +63,7 @@ class Configuration implements ConfigurationInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the configuration tree.
|
* Adds the configuration for the "clients" key.
|
||||||
*
|
|
||||||
* @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface
|
|
||||||
*/
|
|
||||||
public function getConfigTree()
|
|
||||||
{
|
|
||||||
return $this->getConfigTreeBuilder()->buildTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the configuration for the "clients" key
|
|
||||||
*/
|
*/
|
||||||
private function addClientsSection(ArrayNodeDefinition $rootNode)
|
private function addClientsSection(ArrayNodeDefinition $rootNode)
|
||||||
{
|
{
|
||||||
|
|
@ -62,43 +74,67 @@ class Configuration implements ConfigurationInterface
|
||||||
->useAttributeAsKey('id')
|
->useAttributeAsKey('id')
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->performNoDeepMerging()
|
->performNoDeepMerging()
|
||||||
|
// BC - Renaming 'servers' node to 'connections'
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->ifTrue(function($v) { return isset($v['host']) && isset($v['port']); })
|
->ifTrue(function ($v) { return isset($v['servers']); })
|
||||||
->then(function($v) {
|
->then(function ($v) {
|
||||||
return array(
|
$v['connections'] = $v['servers'];
|
||||||
'servers' => array(
|
unset($v['servers']);
|
||||||
array(
|
|
||||||
'host' => $v['host'],
|
return $v;
|
||||||
'port' => $v['port'],
|
})
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
->end()
|
->end()
|
||||||
|
// Elastica names its properties with camel case, support both
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->ifTrue(function($v) { return isset($v['url']); })
|
->ifTrue(function ($v) { return isset($v['connection_strategy']); })
|
||||||
->then(function($v) {
|
->then(function ($v) {
|
||||||
return array(
|
$v['connectionStrategy'] = $v['connection_strategy'];
|
||||||
'servers' => array(
|
unset($v['connection_strategy']);
|
||||||
array(
|
|
||||||
'url' => $v['url'],
|
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()
|
->end()
|
||||||
->children()
|
->children()
|
||||||
->arrayNode('servers')
|
->arrayNode('connections')
|
||||||
|
->requiresAtLeastOneElement()
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
|
->fixXmlConfig('header')
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('url')->end()
|
->scalarNode('url')
|
||||||
|
->validate()
|
||||||
|
->ifTrue(function ($url) { return $url && substr($url, -1) !== '/'; })
|
||||||
|
->then(function ($url) { return $url.'/'; })
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
->scalarNode('host')->end()
|
->scalarNode('host')->end()
|
||||||
->scalarNode('port')->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()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->scalarNode('timeout')->end()
|
->scalarNode('timeout')->end()
|
||||||
->scalarNode('headers')->end()
|
->scalarNode('headers')->end()
|
||||||
|
->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
|
@ -107,7 +143,7 @@ class Configuration implements ConfigurationInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the configuration for the "indexes" key
|
* Adds the configuration for the "indexes" key.
|
||||||
*/
|
*/
|
||||||
private function addIndexesSection(ArrayNodeDefinition $rootNode)
|
private function addIndexesSection(ArrayNodeDefinition $rootNode)
|
||||||
{
|
{
|
||||||
|
|
@ -117,9 +153,11 @@ class Configuration implements ConfigurationInterface
|
||||||
->arrayNode('indexes')
|
->arrayNode('indexes')
|
||||||
->useAttributeAsKey('name')
|
->useAttributeAsKey('name')
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->performNoDeepMerging()
|
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('index_name')->end()
|
->scalarNode('index_name')
|
||||||
|
->info('Defaults to the name of the index, but can be modified if the index name is different in ElasticSearch')
|
||||||
|
->end()
|
||||||
|
->booleanNode('use_alias')->defaultValue(false)->end()
|
||||||
->scalarNode('client')->end()
|
->scalarNode('client')->end()
|
||||||
->scalarNode('finder')
|
->scalarNode('finder')
|
||||||
->treatNullLike(true)
|
->treatNullLike(true)
|
||||||
|
|
@ -129,58 +167,8 @@ class Configuration implements ConfigurationInterface
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('index_analyzer')->end()
|
->scalarNode('index_analyzer')->end()
|
||||||
->scalarNode('search_analyzer')->end()
|
->scalarNode('search_analyzer')->end()
|
||||||
->arrayNode('persistence')
|
->append($this->getPersistenceNode())
|
||||||
->validate()
|
->append($this->getSerializerNode())
|
||||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
|
||||||
->thenInvalid('Propel doesn\'t support listeners')
|
|
||||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
|
||||||
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
|
||||||
->end()
|
|
||||||
->children()
|
|
||||||
->scalarNode('driver')
|
|
||||||
->validate()
|
|
||||||
->ifNotInArray($this->supportedDrivers)
|
|
||||||
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->scalarNode('identifier')->defaultValue('id')->end()
|
|
||||||
->arrayNode('provider')
|
|
||||||
->children()
|
|
||||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
|
||||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
|
||||||
->scalarNode('clear_object_manager')->defaultTrue()->end()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('listener')
|
|
||||||
->children()
|
|
||||||
->scalarNode('insert')->defaultTrue()->end()
|
|
||||||
->scalarNode('update')->defaultTrue()->end()
|
|
||||||
->scalarNode('delete')->defaultTrue()->end()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->variableNode('is_indexable_callback')->defaultNull()->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('finder')
|
|
||||||
->children()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('elastica_to_model_transformer')
|
|
||||||
->addDefaultsIfNotSet()
|
|
||||||
->children()
|
|
||||||
->scalarNode('hydrate')->defaultTrue()->end()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('model_to_elastica_transformer')
|
|
||||||
->addDefaultsIfNotSet()
|
|
||||||
->children()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->variableNode('settings')->defaultValue(array())->end()
|
->variableNode('settings')->defaultValue(array())->end()
|
||||||
|
|
@ -204,68 +192,83 @@ class Configuration implements ConfigurationInterface
|
||||||
->useAttributeAsKey('name')
|
->useAttributeAsKey('name')
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->treatNullLike(array())
|
->treatNullLike(array())
|
||||||
->children()
|
->beforeNormalization()
|
||||||
->scalarNode('index_analyzer')->end()
|
->ifNull()
|
||||||
->scalarNode('search_analyzer')->end()
|
->thenEmptyArray()
|
||||||
->arrayNode('persistence')
|
|
||||||
->validate()
|
|
||||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
|
||||||
->thenInvalid('Propel doesn\'t support listeners')
|
|
||||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
|
||||||
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
|
||||||
->end()
|
|
||||||
->children()
|
|
||||||
->scalarNode('driver')
|
|
||||||
->validate()
|
|
||||||
->ifNotInArray($this->supportedDrivers)
|
|
||||||
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->scalarNode('model')->end()
|
|
||||||
->scalarNode('repository')->end()
|
|
||||||
->scalarNode('identifier')->defaultValue('id')->end()
|
|
||||||
->arrayNode('provider')
|
|
||||||
->children()
|
|
||||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
|
||||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
|
||||||
->scalarNode('clear_object_manager')->defaultTrue()->end()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('listener')
|
|
||||||
->children()
|
|
||||||
->scalarNode('insert')->defaultTrue()->end()
|
|
||||||
->scalarNode('update')->defaultTrue()->end()
|
|
||||||
->scalarNode('delete')->defaultTrue()->end()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->variableNode('is_indexable_callback')->defaultNull()->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('finder')
|
|
||||||
->children()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('elastica_to_model_transformer')
|
|
||||||
->addDefaultsIfNotSet()
|
|
||||||
->children()
|
|
||||||
->scalarNode('hydrate')->defaultTrue()->end()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->arrayNode('model_to_elastica_transformer')
|
|
||||||
->addDefaultsIfNotSet()
|
|
||||||
->children()
|
|
||||||
->scalarNode('service')->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
|
||||||
->end()
|
->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->getSourceNode())
|
||||||
->append($this->getBoostNode())
|
->append($this->getBoostNode())
|
||||||
->append($this->getRoutingNode())
|
->append($this->getRoutingNode())
|
||||||
|
->append($this->getParentNode())
|
||||||
|
->append($this->getAllNode())
|
||||||
|
->append($this->getTimestampNode())
|
||||||
|
->append($this->getTtlNode())
|
||||||
->end()
|
->end()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -273,143 +276,67 @@ class Configuration implements ConfigurationInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the array node used for "mappings".
|
* Returns the array node used for "properties".
|
||||||
*/
|
*/
|
||||||
protected function getMappingsNode()
|
protected function getPropertiesNode()
|
||||||
{
|
{
|
||||||
$builder = new TreeBuilder();
|
$builder = new TreeBuilder();
|
||||||
$node = $builder->root('mappings');
|
$node = $builder->root('properties');
|
||||||
|
|
||||||
$nestings = $this->getNestings();
|
$node
|
||||||
|
|
||||||
$childrenNode = $node
|
|
||||||
->useAttributeAsKey('name')
|
->useAttributeAsKey('name')
|
||||||
->prototype('array')
|
->prototype('variable')
|
||||||
->treatNullLike(array())
|
->treatNullLike(array());
|
||||||
->addDefaultsIfNotSet()
|
|
||||||
->children();
|
|
||||||
|
|
||||||
$this->addFieldConfig($childrenNode, $nestings);
|
|
||||||
|
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the field config to
|
* Returns the array node used for "dynamic_templates".
|
||||||
* @param array $nestings the nested mappings for the current field level
|
|
||||||
*/
|
*/
|
||||||
protected function addFieldConfig($node, $nestings)
|
public function getDynamicTemplateNode()
|
||||||
{
|
{
|
||||||
|
$builder = new TreeBuilder();
|
||||||
|
$node = $builder->root('dynamic_templates');
|
||||||
|
|
||||||
$node
|
$node
|
||||||
->scalarNode('type')->defaultValue('string')->end()
|
->prototype('array')
|
||||||
->scalarNode('boost')->end()
|
|
||||||
->scalarNode('store')->end()
|
|
||||||
->scalarNode('index')->end()
|
|
||||||
->scalarNode('index_analyzer')->end()
|
|
||||||
->scalarNode('search_analyzer')->end()
|
|
||||||
->scalarNode('analyzer')->end()
|
|
||||||
->scalarNode('term_vector')->end()
|
|
||||||
->scalarNode('null_value')->end()
|
|
||||||
->booleanNode('include_in_all')->defaultValue(true)->end()
|
|
||||||
->scalarNode('lat_lon')->end()
|
|
||||||
->scalarNode('index_name')->end()
|
|
||||||
->booleanNode('omit_norms')->end()
|
|
||||||
->scalarNode('index_options')->end()
|
|
||||||
->scalarNode('ignore_above')->end()
|
|
||||||
->scalarNode('position_offset_gap')->end()
|
|
||||||
->arrayNode('_parent')
|
|
||||||
->treatNullLike(array())
|
|
||||||
->children()
|
|
||||||
->scalarNode('type')->end()
|
|
||||||
->scalarNode('identifier')->defaultValue('id')->end()
|
|
||||||
->end()
|
|
||||||
->end();
|
|
||||||
|
|
||||||
if (isset($nestings['fields'])) {
|
|
||||||
$this->addNestedFieldConfig($node, $nestings, 'fields');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($nestings['properties'])) {
|
|
||||||
$this->addNestedFieldConfig($node, $nestings, 'properties');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the nested config to
|
|
||||||
* @param array $nestings The nestings for the current field level
|
|
||||||
* @param string $property the name of the nested property ('fields' or 'properties')
|
|
||||||
*/
|
|
||||||
protected function addNestedFieldConfig($node, $nestings, $property)
|
|
||||||
{
|
|
||||||
$childrenNode = $node
|
|
||||||
->arrayNode($property)
|
|
||||||
->useAttributeAsKey('name')
|
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->treatNullLike(array())
|
->children()
|
||||||
->addDefaultsIfNotSet()
|
->scalarNode('match')->end()
|
||||||
->children();
|
->scalarNode('unmatch')->end()
|
||||||
|
->scalarNode('match_mapping_type')->end()
|
||||||
$this->addFieldConfig($childrenNode, $nestings[$property]);
|
->scalarNode('path_match')->end()
|
||||||
|
->scalarNode('path_unmatch')->end()
|
||||||
$childrenNode
|
->scalarNode('match_pattern')->end()
|
||||||
|
->arrayNode('mapping')
|
||||||
|
->prototype('variable')
|
||||||
|
->treatNullLike(array())
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array The unique nested mappings for all types
|
* Returns the array node used for "_id".
|
||||||
*/
|
*/
|
||||||
protected function getNestings()
|
protected function getIdNode()
|
||||||
{
|
{
|
||||||
$nestings = array();
|
$builder = new TreeBuilder();
|
||||||
foreach ($this->configArray[0]['indexes'] as $index) {
|
$node = $builder->root('_id');
|
||||||
if (empty($index['types'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($index['types'] as $type) {
|
$node
|
||||||
$nestings = array_merge_recursive($nestings, $this->getNestingsForType($type['mappings'], $nestings));
|
->children()
|
||||||
}
|
->scalarNode('path')->end()
|
||||||
}
|
->end()
|
||||||
return $nestings;
|
;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return $node;
|
||||||
* @param array $mappings The mappings for the current type
|
|
||||||
* @return array The nested mappings defined for this type
|
|
||||||
*/
|
|
||||||
protected function getNestingsForType(array $mappings = null)
|
|
||||||
{
|
|
||||||
if ($mappings === null) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$nestings = array();
|
|
||||||
|
|
||||||
foreach ($mappings as $field) {
|
|
||||||
if (isset($field['fields'])) {
|
|
||||||
$this->addPropertyNesting($field, $nestings, 'fields');
|
|
||||||
} else if (isset($field['properties'])) {
|
|
||||||
$this->addPropertyNesting($field, $nestings, 'properties');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $nestings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $field The field mapping definition
|
|
||||||
* @param array $nestings The nestings array
|
|
||||||
* @param string $property The nested property name ('fields' or 'properties')
|
|
||||||
*/
|
|
||||||
protected function addPropertyNesting($field, &$nestings, $property)
|
|
||||||
{
|
|
||||||
if (!isset($nestings[$property])) {
|
|
||||||
$nestings[$property] = array();
|
|
||||||
}
|
|
||||||
$nestings[$property] = array_merge_recursive($nestings[$property], $this->getNestingsForType($field[$property]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -432,7 +359,7 @@ class Configuration implements ConfigurationInterface
|
||||||
->end()
|
->end()
|
||||||
->scalarNode('compress')->end()
|
->scalarNode('compress')->end()
|
||||||
->scalarNode('compress_threshold')->end()
|
->scalarNode('compress_threshold')->end()
|
||||||
->scalarNode('enabled')->end()
|
->scalarNode('enabled')->defaultTrue()->end()
|
||||||
->end()
|
->end()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -474,4 +401,181 @@ class Configuration implements ConfigurationInterface
|
||||||
|
|
||||||
return $node;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ namespace FOS\ElasticaBundle\DependencyInjection;
|
||||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
|
||||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
|
|
@ -13,20 +12,42 @@ use InvalidArgumentException;
|
||||||
|
|
||||||
class FOSElasticaExtension extends Extension
|
class FOSElasticaExtension extends Extension
|
||||||
{
|
{
|
||||||
protected $indexConfigs = array();
|
/**
|
||||||
protected $typeFields = array();
|
* Definition of elastica clients as configured by this extension.
|
||||||
protected $loadedDrivers = array();
|
*
|
||||||
|
* @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)
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$configuration = $this->getConfiguration($configs, $container);
|
$configuration = $this->getConfiguration($configs, $container);
|
||||||
$config = $this->processConfiguration($configuration, $configs);
|
$config = $this->processConfiguration($configuration, $configs);
|
||||||
|
|
||||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
$loader->load('config.xml');
|
|
||||||
|
|
||||||
if (empty($config['clients']) || empty($config['indexes'])) {
|
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'])) {
|
if (empty($config['default_client'])) {
|
||||||
|
|
@ -39,229 +60,277 @@ class FOSElasticaExtension extends Extension
|
||||||
$config['default_index'] = reset($keys);
|
$config['default_index'] = reset($keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
$clientIdsByName = $this->loadClients($config['clients'], $container);
|
if (isset($config['serializer'])) {
|
||||||
$indexIdsByName = $this->loadIndexes($config['indexes'], $container, $clientIdsByName, $config['default_client']);
|
$loader->load('serializer.xml');
|
||||||
$indexRefsByName = array_map(function($id) {
|
|
||||||
return new Reference($id);
|
|
||||||
}, $indexIdsByName);
|
|
||||||
|
|
||||||
$this->loadIndexManager($indexRefsByName, $container);
|
$this->loadSerializer($config['serializer'], $container);
|
||||||
$this->loadResetter($this->indexConfigs, $container);
|
}
|
||||||
|
|
||||||
|
$this->loadClients($config['clients'], $container);
|
||||||
$container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
|
$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->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);
|
$this->createDefaultManagerAlias($config['default_manager'], $container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $config
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*
|
||||||
|
* @return Configuration
|
||||||
|
*/
|
||||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
return new Configuration($config);
|
return new Configuration($container->getParameter('kernel.debug'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the configured clients.
|
* Loads the configured clients.
|
||||||
*
|
*
|
||||||
* @param array $clients An array of clients configurations
|
* @param array $clients An array of clients configurations
|
||||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function loadClients(array $clients, ContainerBuilder $container)
|
private function loadClients(array $clients, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$clientIds = array();
|
|
||||||
foreach ($clients as $name => $clientConfig) {
|
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);
|
$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);
|
$container->setDefinition($clientId, $clientDef);
|
||||||
|
|
||||||
$clientIds[$name] = $clientId;
|
$this->clients[$name] = array(
|
||||||
|
'id' => $clientId,
|
||||||
|
'reference' => new Reference($clientId),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $clientIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the configured indexes.
|
* Loads the configured indexes.
|
||||||
*
|
*
|
||||||
* @param array $indexes An array of indexes configurations
|
* @param array $indexes An array of indexes configurations
|
||||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||||
* @param array $clientIdsByName
|
*
|
||||||
* @param $defaultClientName
|
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
* @return array
|
* @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) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$clientId = $clientIdsByName[$clientName];
|
foreach ($indexes as $name => $index) {
|
||||||
$indexId = sprintf('fos_elastica.index.%s', $name);
|
$indexId = sprintf('fos_elastica.index.%s', $name);
|
||||||
$indexName = isset($index['index_name']) ? $index['index_name'] : $name;
|
$indexName = isset($index['index_name']) ? $index['index_name'] : $name;
|
||||||
$indexDefArgs = array($indexName);
|
|
||||||
$indexDef = new Definition('%fos_elastica.index.class%', $indexDefArgs);
|
$indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
|
||||||
$indexDef->setFactoryService($clientId);
|
$indexDef->replaceArgument(0, $indexName);
|
||||||
$indexDef->setFactoryMethod('getIndex');
|
$indexDef->addTag('fos_elastica.index', array(
|
||||||
|
'name' => $name,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isset($index['client'])) {
|
||||||
|
$client = $this->getClient($index['client']);
|
||||||
|
$indexDef->setFactoryService($client);
|
||||||
|
}
|
||||||
|
|
||||||
$container->setDefinition($indexId, $indexDef);
|
$container->setDefinition($indexId, $indexDef);
|
||||||
$typePrototypeConfig = isset($index['type_prototype']) ? $index['type_prototype'] : array();
|
$reference = new Reference($indexId);
|
||||||
$indexIds[$name] = $indexId;
|
|
||||||
$this->indexConfigs[$name] = array(
|
$this->indexConfigs[$name] = array(
|
||||||
'index' => new Reference($indexId),
|
'elasticsearch_name' => $indexName,
|
||||||
'config' => array(
|
'reference' => $reference,
|
||||||
'mappings' => array()
|
'name' => $name,
|
||||||
)
|
'settings' => $index['settings'],
|
||||||
|
'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : array(),
|
||||||
|
'use_alias' => $index['use_alias'],
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($index['finder']) {
|
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((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
|
||||||
}
|
|
||||||
$this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $indexIds;
|
$indexable = $container->getDefinition('fos_elastica.indexable');
|
||||||
|
$indexable->replaceArgument(0, $indexableCallbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the configured index finders.
|
* Loads the configured index finders.
|
||||||
*
|
*
|
||||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||||
* @param string $name The index name
|
* @param string $name The index name
|
||||||
* @param string $indexId The index service identifier
|
* @param Reference $index Reference to the related index
|
||||||
|
*
|
||||||
* @return string
|
* @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);
|
$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);
|
$container->setDefinition($transformerId, $transformerDef);
|
||||||
|
|
||||||
$abstractFinderId = 'fos_elastica.finder.prototype';
|
|
||||||
$finderId = sprintf('fos_elastica.finder.%s', $name);
|
$finderId = sprintf('fos_elastica.finder.%s', $name);
|
||||||
$finderDef = new DefinitionDecorator($abstractFinderId);
|
$finderDef = new DefinitionDecorator('fos_elastica.finder');
|
||||||
$finderDef->replaceArgument(0, new Reference($indexId));
|
$finderDef->replaceArgument(0, $index);
|
||||||
$finderDef->replaceArgument(1, new Reference($transformerId));
|
$finderDef->replaceArgument(1, new Reference($transformerId));
|
||||||
|
|
||||||
$container->setDefinition($finderId, $finderDef);
|
$container->setDefinition($finderId, $finderDef);
|
||||||
|
|
||||||
return $finderId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the configured types.
|
* Loads the configured types.
|
||||||
*
|
*
|
||||||
* @param array $types An array of types configurations
|
* @param array $types
|
||||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
* @param ContainerBuilder $container
|
||||||
* @param $indexName
|
* @param array $indexConfig
|
||||||
* @param $indexId
|
* @param array $indexableCallbacks
|
||||||
* @param array $typePrototypeConfig
|
|
||||||
*/
|
*/
|
||||||
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) {
|
foreach ($types as $name => $type) {
|
||||||
$type = self::deepArrayUnion($typePrototypeConfig, $type);
|
$indexName = $indexConfig['name'];
|
||||||
$typeId = sprintf('%s.%s', $indexId, $name);
|
|
||||||
$typeDefArgs = array($name);
|
$typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
|
||||||
$typeDef = new Definition('%fos_elastica.type.class%', $typeDefArgs);
|
$typeDef = new DefinitionDecorator('fos_elastica.type_prototype');
|
||||||
$typeDef->setFactoryService($indexId);
|
$typeDef->replaceArgument(0, $name);
|
||||||
$typeDef->setFactoryMethod('getType');
|
$typeDef->setFactoryService($indexConfig['reference']);
|
||||||
$container->setDefinition($typeId, $typeDef);
|
$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['_boost'])) {
|
|
||||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_boost'] = $type['_boost'];
|
foreach (array(
|
||||||
}
|
'persistence',
|
||||||
if (isset($type['_routing'])) {
|
'serializer',
|
||||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_routing'] = $type['_routing'];
|
'index_analyzer',
|
||||||
}
|
'search_analyzer',
|
||||||
if (isset($type['mappings'])) {
|
'date_detection',
|
||||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['properties'] = $type['mappings'];
|
'dynamic_date_formats',
|
||||||
$typeName = sprintf('%s/%s', $indexName, $name);
|
'numeric_detection',
|
||||||
$this->typeFields[$typeName] = $type['mappings'];
|
) as $field) {
|
||||||
|
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
|
||||||
|
$type[$field] :
|
||||||
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->indexConfigs[$indexName]['types'][$name] = $typeConfig;
|
||||||
|
|
||||||
if (isset($type['persistence'])) {
|
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['index_analyzer'])) {
|
|
||||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index_analyzer'] = $type['index_analyzer'];
|
if (isset($type['indexable_callback'])) {
|
||||||
|
$indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $type['indexable_callback'];
|
||||||
}
|
}
|
||||||
if (isset($type['search_analyzer'])) {
|
|
||||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['search_analyzer'] = $type['search_analyzer'];
|
if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
|
||||||
}
|
$typeSerializerId = sprintf('%s.serializer.callback', $typeId);
|
||||||
if (isset($type['index'])) {
|
$typeSerializerDef = new DefinitionDecorator('fos_elastica.serializer_callback_prototype');
|
||||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index'] = $type['index'];
|
|
||||||
|
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 $typeConfig
|
||||||
* @param array $array2 An array to merge
|
* @param ContainerBuilder $container
|
||||||
*
|
* @param Reference $typeRef
|
||||||
* @return array The merged array
|
* @param string $indexName
|
||||||
|
* @param string $typeName
|
||||||
*/
|
*/
|
||||||
static protected function deepArrayUnion($array1, $array2)
|
private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
|
||||||
{
|
|
||||||
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
|
|
||||||
*
|
|
||||||
* @param array $typeConfig
|
|
||||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
|
||||||
* @param \Symfony\Component\DependencyInjection\Definition $typeDef
|
|
||||||
* @param $indexName
|
|
||||||
* @param $typeName
|
|
||||||
*/
|
|
||||||
protected function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Definition $typeDef, $indexName, $typeName)
|
|
||||||
{
|
{
|
||||||
$this->loadDriver($container, $typeConfig['driver']);
|
$this->loadDriver($container, $typeConfig['driver']);
|
||||||
|
|
||||||
$elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
|
$elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
|
||||||
$modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($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'])) {
|
if (isset($typeConfig['provider'])) {
|
||||||
$this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName);
|
$this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
|
||||||
}
|
}
|
||||||
if (isset($typeConfig['finder'])) {
|
if (isset($typeConfig['finder'])) {
|
||||||
$this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeDef, $indexName, $typeName);
|
$this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
|
||||||
}
|
}
|
||||||
if (isset($typeConfig['listener'])) {
|
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'])) {
|
if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
|
||||||
return $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']);
|
$abstractId = sprintf('fos_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
|
||||||
$serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
|
$serviceId = sprintf('fos_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
|
||||||
$serviceDef = new DefinitionDecorator($abstractId);
|
$serviceDef = new DefinitionDecorator($abstractId);
|
||||||
|
|
@ -271,103 +340,201 @@ class FOSElasticaExtension extends Extension
|
||||||
$argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
|
$argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
|
||||||
|
|
||||||
$serviceDef->replaceArgument($argPos, $typeConfig['model']);
|
$serviceDef->replaceArgument($argPos, $typeConfig['model']);
|
||||||
$serviceDef->replaceArgument($argPos + 1, array(
|
$serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], array(
|
||||||
'identifier' => $typeConfig['identifier'],
|
'identifier' => $typeConfig['identifier'],
|
||||||
'hydrate' => $typeConfig['elastica_to_model_transformer']['hydrate']
|
)));
|
||||||
));
|
|
||||||
$container->setDefinition($serviceId, $serviceDef);
|
$container->setDefinition($serviceId, $serviceDef);
|
||||||
|
|
||||||
return $serviceId;
|
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'])) {
|
if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
|
||||||
return $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);
|
$serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
|
||||||
$serviceDef = new DefinitionDecorator($abstractId);
|
$serviceDef = new DefinitionDecorator($abstractId);
|
||||||
$serviceDef->replaceArgument(0, array(
|
$serviceDef->replaceArgument(0, array(
|
||||||
'identifier' => $typeConfig['identifier']
|
'identifier' => $typeConfig['identifier'],
|
||||||
));
|
));
|
||||||
$container->setDefinition($serviceId, $serviceDef);
|
$container->setDefinition($serviceId, $serviceDef);
|
||||||
|
|
||||||
return $serviceId;
|
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);
|
$serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
|
||||||
$serviceDef = new DefinitionDecorator($abstractId);
|
$serviceDef = new DefinitionDecorator($abstractId);
|
||||||
$serviceDef->replaceArgument(0, $typeDef);
|
foreach ($arguments as $i => $argument) {
|
||||||
$serviceDef->replaceArgument(1, new Reference($transformerId));
|
$serviceDef->replaceArgument($i, $argument);
|
||||||
$serviceDef->replaceArgument(2, $typeConfig['model']);
|
}
|
||||||
$serviceDef->replaceArgument(3, $this->typeFields[sprintf('%s/%s', $indexName, $typeName)]);
|
|
||||||
$container->setDefinition($serviceId, $serviceDef);
|
$container->setDefinition($serviceId, $serviceDef);
|
||||||
|
|
||||||
return $serviceId;
|
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'])) {
|
if (isset($typeConfig['provider']['service'])) {
|
||||||
return $typeConfig['provider']['service'];
|
return $typeConfig['provider']['service'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Note: provider services may conflict with "prototype.driver", if the
|
||||||
|
* index and type names were "prototype" and a driver, respectively.
|
||||||
|
*/
|
||||||
$providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
|
$providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
|
||||||
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.' . $typeConfig['driver']);
|
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.'.$typeConfig['driver']);
|
||||||
$providerDef->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
$providerDef->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
||||||
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||||
$providerDef->replaceArgument(1, $typeConfig['model']);
|
$providerDef->replaceArgument(2, $typeConfig['model']);
|
||||||
// Propel provider can simply ignore Doctrine-specific options
|
// Propel provider can simply ignore Doctrine-specific options
|
||||||
$providerDef->replaceArgument(2, array_diff_key($typeConfig['provider'], array('service' => 1)));
|
$providerDef->replaceArgument(3, array_merge(array_diff_key($typeConfig['provider'], array('service' => 1)), array(
|
||||||
|
'indexName' => $indexName,
|
||||||
|
'typeName' => $typeName,
|
||||||
|
)));
|
||||||
$container->setDefinition($providerId, $providerDef);
|
$container->setDefinition($providerId, $providerDef);
|
||||||
|
|
||||||
return $providerId;
|
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'])) {
|
if (isset($typeConfig['listener']['service'])) {
|
||||||
return $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']);
|
$abstractListenerId = sprintf('fos_elastica.listener.prototype.%s', $typeConfig['driver']);
|
||||||
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
|
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
|
||||||
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
||||||
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||||
$listenerDef->replaceArgument(1, $typeConfig['model']);
|
$listenerDef->replaceArgument(2, array(
|
||||||
$listenerDef->replaceArgument(3, $typeConfig['identifier']);
|
'identifier' => $typeConfig['identifier'],
|
||||||
$listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig));
|
'indexName' => $indexName,
|
||||||
|
'typeName' => $typeName,
|
||||||
|
));
|
||||||
|
$listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
|
||||||
|
new Reference($typeConfig['listener']['logger']) :
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
$tagName = null;
|
||||||
switch ($typeConfig['driver']) {
|
switch ($typeConfig['driver']) {
|
||||||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
case 'orm':
|
||||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
|
$tagName = 'doctrine.event_listener';
|
||||||
|
break;
|
||||||
|
case 'mongodb':
|
||||||
|
$tagName = 'doctrine_mongodb.odm.event_listener';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (isset($typeConfig['listener']['is_indexable_callback'])) {
|
|
||||||
$callback = $typeConfig['listener']['is_indexable_callback'];
|
|
||||||
|
|
||||||
if (is_array($callback)) {
|
if (null !== $tagName) {
|
||||||
list($class) = $callback + array(null);
|
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
|
||||||
if (is_string($class) && !class_exists($class)) {
|
$listenerDef->addTag($tagName, array('event' => $event));
|
||||||
$callback[0] = new Reference($class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$listenerDef->addMethodCall('setIsIndexableCallback', array($callback));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$container->setDefinition($listenerId, $listenerDef);
|
$container->setDefinition($listenerId, $listenerDef);
|
||||||
|
|
||||||
return $listenerId;
|
return $listenerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map Elastica to Doctrine events for the current driver.
|
||||||
|
*/
|
||||||
private function getDoctrineEvents(array $typeConfig)
|
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();
|
$events = array();
|
||||||
$eventMapping = array(
|
$eventMapping = array(
|
||||||
'insert' => array('postPersist'),
|
'insert' => array(constant($eventsClass.'::postPersist')),
|
||||||
'update' => array('postUpdate'),
|
'update' => array(constant($eventsClass.'::postUpdate')),
|
||||||
'delete' => array('postRemove', 'preRemove')
|
'delete' => array(constant($eventsClass.'::preRemove')),
|
||||||
|
'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush')),
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($eventMapping as $event => $doctrineEvents) {
|
foreach ($eventMapping as $event => $doctrineEvents) {
|
||||||
|
|
@ -379,15 +546,26 @@ class FOSElasticaExtension extends Extension
|
||||||
return $events;
|
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'])) {
|
if (isset($typeConfig['finder']['service'])) {
|
||||||
$finderId = $typeConfig['finder']['service'];
|
$finderId = $typeConfig['finder']['service'];
|
||||||
} else {
|
} else {
|
||||||
$abstractFinderId = 'fos_elastica.finder.prototype';
|
|
||||||
$finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
|
$finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
|
||||||
$finderDef = new DefinitionDecorator($abstractFinderId);
|
$finderDef = new DefinitionDecorator('fos_elastica.finder');
|
||||||
$finderDef->replaceArgument(0, $typeDef);
|
$finderDef->replaceArgument(0, $typeRef);
|
||||||
$finderDef->replaceArgument(1, new Reference($elasticaToModelId));
|
$finderDef->replaceArgument(1, new Reference($elasticaToModelId));
|
||||||
$container->setDefinition($finderId, $finderDef);
|
$container->setDefinition($finderId, $finderDef);
|
||||||
}
|
}
|
||||||
|
|
@ -404,41 +582,63 @@ class FOSElasticaExtension extends Extension
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the index manager
|
* Loads the index manager.
|
||||||
*
|
*
|
||||||
* @param array $indexRefsByName
|
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
**/
|
**/
|
||||||
protected function loadIndexManager(array $indexRefsByName, ContainerBuilder $container)
|
private function loadIndexManager(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
|
$indexRefs = array_map(function ($index) {
|
||||||
|
return $index['reference'];
|
||||||
|
}, $this->indexConfigs);
|
||||||
|
|
||||||
$managerDef = $container->getDefinition('fos_elastica.index_manager');
|
$managerDef = $container->getDefinition('fos_elastica.index_manager');
|
||||||
$managerDef->replaceArgument(0, $indexRefsByName);
|
$managerDef->replaceArgument(0, $indexRefs);
|
||||||
$managerDef->replaceArgument(1, new Reference('fos_elastica.index'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the resetter
|
* Makes sure a specific driver has been loaded.
|
||||||
*
|
*
|
||||||
* @param array $indexConfigs
|
* @param ContainerBuilder $container
|
||||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
* @param string $driver
|
||||||
*/
|
*/
|
||||||
protected function loadResetter(array $indexConfigs, ContainerBuilder $container)
|
private function loadDriver(ContainerBuilder $container, $driver)
|
||||||
{
|
|
||||||
$resetterDef = $container->getDefinition('fos_elastica.resetter');
|
|
||||||
$resetterDef->replaceArgument(0, $indexConfigs);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function loadDriver(ContainerBuilder $container, $driver)
|
|
||||||
{
|
{
|
||||||
if (in_array($driver, $this->loadedDrivers)) {
|
if (in_array($driver, $this->loadedDrivers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
$loader->load($driver.'.xml');
|
$loader->load($driver.'.xml');
|
||||||
$this->loadedDrivers[] = $driver;
|
$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)) {
|
if (0 == count($this->loadedDrivers)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -455,4 +655,21 @@ class FOSElasticaExtension extends Extension
|
||||||
$container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
|
$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,48 +2,52 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Doctrine;
|
namespace FOS\ElasticaBundle\Doctrine;
|
||||||
|
|
||||||
|
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||||
use FOS\ElasticaBundle\HybridResult;
|
use FOS\ElasticaBundle\HybridResult;
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
|
||||||
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
|
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
|
||||||
use Symfony\Component\Form\Util\PropertyPath;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps Elastica documents with Doctrine objects
|
* Maps Elastica documents with Doctrine objects
|
||||||
* This mapper assumes an exact match between
|
* This mapper assumes an exact match between
|
||||||
* elastica documents ids and doctrine object ids
|
* elastica documents ids and doctrine object ids.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Manager registry
|
* Manager registry.
|
||||||
|
*
|
||||||
|
* @var ManagerRegistry
|
||||||
*/
|
*/
|
||||||
protected $registry = null;
|
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
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $objectClass = null;
|
protected $objectClass = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional parameters
|
* Optional parameters.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $options = array(
|
protected $options = array(
|
||||||
'hydrate' => true,
|
'hydrate' => true,
|
||||||
'identifier' => 'id'
|
'identifier' => 'id',
|
||||||
|
'ignore_missing' => false,
|
||||||
|
'query_builder_method' => 'createQueryBuilder',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Mapper
|
* Instantiates a new Mapper.
|
||||||
*
|
*
|
||||||
* @param object $registry
|
* @param ManagerRegistry $registry
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
public function __construct($registry, $objectClass, array $options = array())
|
public function __construct(ManagerRegistry $registry, $objectClass, array $options = array())
|
||||||
{
|
{
|
||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
$this->objectClass = $objectClass;
|
$this->objectClass = $objectClass;
|
||||||
|
|
@ -62,10 +66,12 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms an array of elastica objects into an array of
|
* 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
|
* @param array $elasticaObjects of elastica objects
|
||||||
|
*
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
**/
|
**/
|
||||||
public function transform(array $elasticaObjects)
|
public function transform(array $elasticaObjects)
|
||||||
|
|
@ -77,7 +83,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
}
|
}
|
||||||
|
|
||||||
$objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
|
$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.');
|
throw new \RuntimeException('Cannot find corresponding Doctrine objects for all Elastica results.');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -87,32 +93,34 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$identifierProperty = new PropertyPath($this->options['identifier']);
|
|
||||||
|
|
||||||
// sort objects in the order of ids
|
// sort objects in the order of ids
|
||||||
$idPos = array_flip($ids);
|
$idPos = array_flip($ids);
|
||||||
usort($objects, function($a, $b) use ($idPos, $identifierProperty)
|
$identifier = $this->options['identifier'];
|
||||||
{
|
usort($objects, $this->getSortingClosure($idPos, $identifier));
|
||||||
return $idPos[$identifierProperty->getValue($a)] > $idPos[$identifierProperty->getValue($b)];
|
|
||||||
});
|
|
||||||
|
|
||||||
return $objects;
|
return $objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hybridTransform(array $elasticaObjects)
|
public function hybridTransform(array $elasticaObjects)
|
||||||
{
|
{
|
||||||
|
$indexedElasticaResults = array();
|
||||||
|
foreach ($elasticaObjects as $elasticaObject) {
|
||||||
|
$indexedElasticaResults[$elasticaObject->getId()] = $elasticaObject;
|
||||||
|
}
|
||||||
|
|
||||||
$objects = $this->transform($elasticaObjects);
|
$objects = $this->transform($elasticaObjects);
|
||||||
|
|
||||||
$result = array();
|
$result = array();
|
||||||
for ($i = 0; $i < count($elasticaObjects); $i++) {
|
foreach ($objects as $object) {
|
||||||
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
$id = $this->propertyAccessor->getValue($object, $this->options['identifier']);
|
||||||
|
$result[] = new HybridResult($indexedElasticaResults[$id], $object);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getIdentifierField()
|
public function getIdentifierField()
|
||||||
{
|
{
|
||||||
|
|
@ -120,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
|
* @return array of objects or arrays
|
||||||
*/
|
*/
|
||||||
protected abstract function findByIdentifiers(array $identifierValues, $hydrate);
|
abstract protected function findByIdentifiers(array $identifierValues, $hydrate);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Doctrine;
|
|
||||||
|
|
||||||
use Doctrine\Common\EventSubscriber;
|
|
||||||
use Doctrine\Common\Persistence\ObjectManager;
|
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
|
||||||
|
|
||||||
abstract class AbstractListener implements EventSubscriber
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Object persister
|
|
||||||
*
|
|
||||||
* @var ObjectPersister
|
|
||||||
*/
|
|
||||||
protected $objectPersister;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class of the domain model
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $objectClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of subscribed events
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $events;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of domain model field used as the ES identifier
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $esIdentifierField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for determining if an object should be indexed
|
|
||||||
*
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
protected $isIndexableCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Objects scheduled for removal
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $scheduledForRemoval = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param ObjectPersisterInterface $objectPersister
|
|
||||||
* @param string $objectClass
|
|
||||||
* @param array $events
|
|
||||||
* @param string $esIdentifierField
|
|
||||||
*/
|
|
||||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id')
|
|
||||||
{
|
|
||||||
$this->objectPersister = $objectPersister;
|
|
||||||
$this->objectClass = $objectClass;
|
|
||||||
$this->events = $events;
|
|
||||||
$this->esIdentifierField = $esIdentifierField;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
|
|
||||||
*/
|
|
||||||
public function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return $this->events;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the callback for determining object index eligibility.
|
|
||||||
*
|
|
||||||
* If callback is a string, it must be public method on the object class
|
|
||||||
* that expects no arguments and returns a boolean. Otherwise, the callback
|
|
||||||
* should expect the object for consideration as its only argument and
|
|
||||||
* return a boolean.
|
|
||||||
*
|
|
||||||
* @param callback $callback
|
|
||||||
* @throws \RuntimeException if the callback is not callable
|
|
||||||
*/
|
|
||||||
public function setIsIndexableCallback($callback)
|
|
||||||
{
|
|
||||||
if (is_string($callback)) {
|
|
||||||
if (!is_callable(array($this->objectClass, $callback))) {
|
|
||||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $this->objectClass, $callback));
|
|
||||||
}
|
|
||||||
} elseif (!is_callable($callback)) {
|
|
||||||
if (is_array($callback)) {
|
|
||||||
list($class, $method) = $callback + array(null, null);
|
|
||||||
if (is_object($class)) {
|
|
||||||
$class = get_class($class);
|
|
||||||
}
|
|
||||||
if ($class && $method) {
|
|
||||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $class, $method));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new \RuntimeException('Indexable callback is not callable.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->isIndexableCallback = $callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the object is indexable with respect to the callback.
|
|
||||||
*
|
|
||||||
* @param object $object
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function isObjectIndexable($object)
|
|
||||||
{
|
|
||||||
if (!$this->isIndexableCallback) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_string($this->isIndexableCallback)
|
|
||||||
? call_user_func(array($object, $this->isIndexableCallback))
|
|
||||||
: call_user_func($this->isIndexableCallback, $object);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules the object for removal.
|
|
||||||
*
|
|
||||||
* This is usually called during the pre-remove event.
|
|
||||||
*
|
|
||||||
* @param object $object
|
|
||||||
* @param ObjectManager $objectManager
|
|
||||||
*/
|
|
||||||
protected function scheduleForRemoval($object, ObjectManager $objectManager)
|
|
||||||
{
|
|
||||||
$metadata = $objectManager->getClassMetadata($this->objectClass);
|
|
||||||
$esId = $metadata->getFieldValue($object, $this->esIdentifierField);
|
|
||||||
$this->scheduledForRemoval[spl_object_hash($object)] = $esId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the object if it was scheduled for removal.
|
|
||||||
*
|
|
||||||
* This is usually called during the post-remove event.
|
|
||||||
*
|
|
||||||
* @param object $object
|
|
||||||
*/
|
|
||||||
protected function removeIfScheduled($object)
|
|
||||||
{
|
|
||||||
$objectHash = spl_object_hash($object);
|
|
||||||
if (isset($this->scheduledForRemoval[$objectHash])) {
|
|
||||||
$this->objectPersister->deleteById($this->scheduledForRemoval[$objectHash]);
|
|
||||||
unset($this->scheduledForRemoval[$objectHash]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,68 +3,65 @@
|
||||||
namespace FOS\ElasticaBundle\Doctrine;
|
namespace FOS\ElasticaBundle\Doctrine;
|
||||||
|
|
||||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||||
|
use Elastica\Exception\Bulk\ResponseException as BulkResponseException;
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||||
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
||||||
|
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
abstract class AbstractProvider extends BaseAbstractProvider
|
abstract class AbstractProvider extends BaseAbstractProvider
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var SliceFetcherInterface
|
||||||
|
*/
|
||||||
|
private $sliceFetcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ManagerRegistry
|
||||||
|
*/
|
||||||
protected $managerRegistry;
|
protected $managerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ObjectPersisterInterface $objectPersister
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
|
* @param IndexableInterface $indexable
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $baseOptions
|
||||||
* @param ManagerRegistry $managerRegistry
|
* @param ManagerRegistry $managerRegistry
|
||||||
|
* @param SliceFetcherInterface $sliceFetcher
|
||||||
*/
|
*/
|
||||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options, $managerRegistry)
|
public function __construct(
|
||||||
{
|
ObjectPersisterInterface $objectPersister,
|
||||||
parent::__construct($objectPersister, $objectClass, array_merge(array(
|
IndexableInterface $indexable,
|
||||||
'clear_object_manager' => true,
|
$objectClass,
|
||||||
'query_builder_method' => 'createQueryBuilder',
|
array $baseOptions,
|
||||||
), $options));
|
ManagerRegistry $managerRegistry,
|
||||||
|
SliceFetcherInterface $sliceFetcher = null
|
||||||
|
) {
|
||||||
|
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
|
||||||
|
|
||||||
$this->managerRegistry = $managerRegistry;
|
$this->managerRegistry = $managerRegistry;
|
||||||
}
|
$this->sliceFetcher = $sliceFetcher;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
|
||||||
*/
|
|
||||||
public function populate(\Closure $loggerClosure = null)
|
|
||||||
{
|
|
||||||
$queryBuilder = $this->createQueryBuilder();
|
|
||||||
$nbObjects = $this->countObjects($queryBuilder);
|
|
||||||
|
|
||||||
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$stepStartTime = microtime(true);
|
|
||||||
}
|
|
||||||
$objects = $this->fetchSlice($queryBuilder, $this->options['batch_size'], $offset);
|
|
||||||
|
|
||||||
$this->objectPersister->insertMany($objects);
|
|
||||||
|
|
||||||
if ($this->options['clear_object_manager']) {
|
|
||||||
$this->managerRegistry->getManagerForClass($this->objectClass)->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$stepNbObjects = count($objects);
|
|
||||||
$stepCount = $stepNbObjects + $offset;
|
|
||||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
|
||||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
|
||||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts objects that would be indexed using the query builder.
|
* Counts objects that would be indexed using the query builder.
|
||||||
*
|
*
|
||||||
* @param object $queryBuilder
|
* @param object $queryBuilder
|
||||||
|
*
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
protected abstract function countObjects($queryBuilder);
|
abstract protected function countObjects($queryBuilder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the query builder, which will be used to fetch objects to index.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
abstract protected function createQueryBuilder($method);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a slice of objects using the query builder.
|
* Fetches a slice of objects using the query builder.
|
||||||
|
|
@ -72,14 +69,102 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
||||||
* @param object $queryBuilder
|
* @param object $queryBuilder
|
||||||
* @param integer $limit
|
* @param integer $limit
|
||||||
* @param integer $offset
|
* @param integer $offset
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
|
abstract protected function fetchSlice($queryBuilder, $limit, $offset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the query builder, which will be used to fetch objects to index.
|
* {@inheritDoc}
|
||||||
*
|
|
||||||
* @return object
|
|
||||||
*/
|
*/
|
||||||
protected abstract function createQueryBuilder();
|
protected function doPopulate($options, \Closure $loggerClosure = null)
|
||||||
|
{
|
||||||
|
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||||
|
|
||||||
|
$queryBuilder = $this->createQueryBuilder($options['query_builder_method']);
|
||||||
|
$nbObjects = $this->countObjects($queryBuilder);
|
||||||
|
$offset = $options['offset'];
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||||
|
try {
|
||||||
|
$objects = $this->getSlice($queryBuilder, $options['batch_size'], $offset, $objects);
|
||||||
|
$objects = $this->filterObjects($options, $objects);
|
||||||
|
|
||||||
|
if (!empty($objects)) {
|
||||||
|
$this->objectPersister->insertMany($objects);
|
||||||
|
}
|
||||||
|
} catch (BulkResponseException $e) {
|
||||||
|
if (!$options['ignore_errors']) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $loggerClosure) {
|
||||||
|
$loggerClosure(
|
||||||
|
$options['batch_size'],
|
||||||
|
$nbObjects,
|
||||||
|
sprintf('<error>%s</error>', $e->getMessage())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($options['clear_object_manager']) {
|
||||||
|
$manager->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep($options['sleep']);
|
||||||
|
|
||||||
|
if (null !== $loggerClosure) {
|
||||||
|
$loggerClosure($options['batch_size'], $nbObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function configureOptions()
|
||||||
|
{
|
||||||
|
parent::configureOptions();
|
||||||
|
|
||||||
|
$this->resolver->setDefaults(array(
|
||||||
|
'clear_object_manager' => true,
|
||||||
|
'debug_logging' => false,
|
||||||
|
'ignore_errors' => false,
|
||||||
|
'offset' => 0,
|
||||||
|
'query_builder_method' => 'createQueryBuilder',
|
||||||
|
'sleep' => 0
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this Provider has a SliceFetcher defined, we use it instead of falling back to
|
||||||
|
* the fetchSlice methods defined in the ORM/MongoDB subclasses.
|
||||||
|
*
|
||||||
|
* @param $queryBuilder
|
||||||
|
* @param int $limit
|
||||||
|
* @param int $offset
|
||||||
|
* @param array $lastSlice
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getSlice($queryBuilder, $limit, $offset, $lastSlice)
|
||||||
|
{
|
||||||
|
if (!$this->sliceFetcher) {
|
||||||
|
return $this->fetchSlice($queryBuilder, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||||
|
$identifierFieldNames = $manager
|
||||||
|
->getClassMetadata($this->objectClass)
|
||||||
|
->getIdentifierFieldNames();
|
||||||
|
|
||||||
|
return $this->sliceFetcher->fetch(
|
||||||
|
$queryBuilder,
|
||||||
|
$limit,
|
||||||
|
$offset,
|
||||||
|
$lastSlice,
|
||||||
|
$identifierFieldNames
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
212
Doctrine/Listener.php
Normal file
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,22 +7,24 @@ use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
|
||||||
/**
|
/**
|
||||||
* Maps Elastica documents with Doctrine objects
|
* Maps Elastica documents with Doctrine objects
|
||||||
* This mapper assumes an exact match between
|
* 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
|
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Fetch objects for theses identifier values
|
* Fetch objects for theses identifier values.
|
||||||
|
*
|
||||||
|
* @param array $identifierValues ids values
|
||||||
|
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||||
*
|
*
|
||||||
* @param array $identifierValues ids values
|
|
||||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
|
||||||
* @return array of objects or arrays
|
* @return array of objects or arrays
|
||||||
*/
|
*/
|
||||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||||
{
|
{
|
||||||
return $this->registry
|
return $this->registry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->createQueryBuilder($this->objectClass)
|
->getRepository($this->objectClass)
|
||||||
|
->{$this->options['query_builder_method']}($this->objectClass)
|
||||||
->field($this->options['identifier'])->in($identifierValues)
|
->field($this->options['identifier'])->in($identifierValues)
|
||||||
->hydrate($hydrate)
|
->hydrate($hydrate)
|
||||||
->getQuery()
|
->getQuery()
|
||||||
|
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
|
||||||
|
|
||||||
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
|
|
||||||
use FOS\ElasticaBundle\Doctrine\AbstractListener;
|
|
||||||
|
|
||||||
class Listener extends AbstractListener
|
|
||||||
{
|
|
||||||
public function postPersist(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$document = $eventArgs->getDocument();
|
|
||||||
|
|
||||||
if ($document instanceof $this->objectClass && $this->isObjectIndexable($document)) {
|
|
||||||
$this->objectPersister->insertOne($document);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$document = $eventArgs->getDocument();
|
|
||||||
|
|
||||||
if ($document instanceof $this->objectClass) {
|
|
||||||
if ($this->isObjectIndexable($document)) {
|
|
||||||
$this->objectPersister->replaceOne($document);
|
|
||||||
} else {
|
|
||||||
$this->scheduleForRemoval($document, $eventArgs->getDocumentManager());
|
|
||||||
$this->removeIfScheduled($document);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function preRemove(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$document = $eventArgs->getDocument();
|
|
||||||
|
|
||||||
if ($document instanceof $this->objectClass) {
|
|
||||||
$this->scheduleForRemoval($document, $eventArgs->getDocumentManager());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postRemove(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$document = $eventArgs->getDocument();
|
|
||||||
|
|
||||||
if ($document instanceof $this->objectClass) {
|
|
||||||
$this->removeIfScheduled($document);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,42 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||||
class Provider extends AbstractProvider
|
class Provider extends AbstractProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
* Disables logging and returns the logger that was previously set.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function disableLogging()
|
||||||
|
{
|
||||||
|
$configuration = $this->managerRegistry
|
||||||
|
->getManagerForClass($this->objectClass)
|
||||||
|
->getConnection()
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
$logger = $configuration->getLoggerCallable();
|
||||||
|
$configuration->setLoggerCallable(null);
|
||||||
|
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||||
|
*
|
||||||
|
* @param mixed $logger
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function enableLogging($logger)
|
||||||
|
{
|
||||||
|
$configuration = $this->managerRegistry
|
||||||
|
->getManagerForClass($this->objectClass)
|
||||||
|
->getConnection()
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
$configuration->setLoggerCallable($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function countObjects($queryBuilder)
|
protected function countObjects($queryBuilder)
|
||||||
{
|
{
|
||||||
|
|
@ -23,7 +58,7 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||||
{
|
{
|
||||||
|
|
@ -32,21 +67,21 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
return $queryBuilder
|
return $queryBuilder
|
||||||
->limit($limit)
|
|
||||||
->skip($offset)
|
->skip($offset)
|
||||||
|
->limit($limit)
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->execute()
|
->execute()
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function createQueryBuilder()
|
protected function createQueryBuilder($method)
|
||||||
{
|
{
|
||||||
return $this->managerRegistry
|
return $this->managerRegistry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->getRepository($this->objectClass)
|
->getRepository($this->objectClass)
|
||||||
->{$this->options['query_builder_method']}();
|
->{$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()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,15 +8,18 @@ use Doctrine\ORM\Query;
|
||||||
/**
|
/**
|
||||||
* Maps Elastica documents with Doctrine objects
|
* Maps Elastica documents with Doctrine objects
|
||||||
* This mapper assumes an exact match between
|
* 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
|
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||||
{
|
{
|
||||||
|
const ENTITY_ALIAS = 'o';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch objects for theses identifier values
|
* Fetch objects for theses identifier values.
|
||||||
|
*
|
||||||
|
* @param array $identifierValues ids values
|
||||||
|
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||||
*
|
*
|
||||||
* @param array $identifierValues ids values
|
|
||||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
|
||||||
* @return array of objects or arrays
|
* @return array of objects or arrays
|
||||||
*/
|
*/
|
||||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||||
|
|
@ -25,14 +28,25 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
$hydrationMode = $hydrate ? Query::HYDRATE_OBJECT : Query::HYDRATE_ARRAY;
|
$hydrationMode = $hydrate ? Query::HYDRATE_OBJECT : Query::HYDRATE_ARRAY;
|
||||||
$qb = $this->registry
|
|
||||||
->getManagerForClass($this->objectClass)
|
$qb = $this->getEntityQueryBuilder();
|
||||||
->getRepository($this->objectClass)
|
$qb->andWhere($qb->expr()->in(static::ENTITY_ALIAS.'.'.$this->options['identifier'], ':values'))
|
||||||
->createQueryBuilder('o');
|
|
||||||
/* @var $qb \Doctrine\ORM\QueryBuilder */
|
|
||||||
$qb->where($qb->expr()->in('o.'.$this->options['identifier'], ':values'))
|
|
||||||
->setParameter('values', $identifierValues);
|
->setParameter('values', $identifierValues);
|
||||||
|
|
||||||
return $qb->getQuery()->setHydrationMode($hydrationMode)->execute();
|
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,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
|
||||||
|
|
||||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
|
||||||
use FOS\ElasticaBundle\Doctrine\AbstractListener;
|
|
||||||
|
|
||||||
class Listener extends AbstractListener
|
|
||||||
{
|
|
||||||
public function postPersist(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$entity = $eventArgs->getEntity();
|
|
||||||
|
|
||||||
if ($entity instanceof $this->objectClass && $this->isObjectIndexable($entity)) {
|
|
||||||
$this->objectPersister->insertOne($entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$entity = $eventArgs->getEntity();
|
|
||||||
|
|
||||||
if ($entity instanceof $this->objectClass) {
|
|
||||||
if ($this->isObjectIndexable($entity)) {
|
|
||||||
$this->objectPersister->replaceOne($entity);
|
|
||||||
} else {
|
|
||||||
$this->scheduleForRemoval($entity, $eventArgs->getEntityManager());
|
|
||||||
$this->removeIfScheduled($entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function preRemove(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$entity = $eventArgs->getEntity();
|
|
||||||
|
|
||||||
if ($entity instanceof $this->objectClass) {
|
|
||||||
$this->scheduleForRemoval($entity, $eventArgs->getEntityManager());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postRemove(LifecycleEventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$entity = $eventArgs->getEntity();
|
|
||||||
|
|
||||||
if ($entity instanceof $this->objectClass) {
|
|
||||||
$this->removeIfScheduled($entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,8 +8,45 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||||
|
|
||||||
class Provider extends AbstractProvider
|
class Provider extends AbstractProvider
|
||||||
{
|
{
|
||||||
|
const ENTITY_ALIAS = 'a';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
* Disables logging and returns the logger that was previously set.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function disableLogging()
|
||||||
|
{
|
||||||
|
$configuration = $this->managerRegistry
|
||||||
|
->getManagerForClass($this->objectClass)
|
||||||
|
->getConnection()
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
$logger = $configuration->getSQLLogger();
|
||||||
|
$configuration->setSQLLogger(null);
|
||||||
|
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||||
|
*
|
||||||
|
* @param mixed $logger
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function enableLogging($logger)
|
||||||
|
{
|
||||||
|
$configuration = $this->managerRegistry
|
||||||
|
->getManagerForClass($this->objectClass)
|
||||||
|
->getConnection()
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
$configuration->setSQLLogger($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function countObjects($queryBuilder)
|
protected function countObjects($queryBuilder)
|
||||||
{
|
{
|
||||||
|
|
@ -32,7 +69,9 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
* This method should remain in sync with SliceFetcher::fetch until it is deprecated and removed.
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||||
{
|
{
|
||||||
|
|
@ -40,6 +79,24 @@ class Provider extends AbstractProvider
|
||||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An orderBy DQL part is required to avoid fetching the same row twice.
|
||||||
|
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
|
||||||
|
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
|
||||||
|
* @see http://www.sqlite.org/lang_select.html#orderby
|
||||||
|
*/
|
||||||
|
$orderBy = $queryBuilder->getDQLPart('orderBy');
|
||||||
|
if (empty($orderBy)) {
|
||||||
|
$rootAliases = $queryBuilder->getRootAliases();
|
||||||
|
$identifierFieldNames = $this->managerRegistry
|
||||||
|
->getManagerForClass($this->objectClass)
|
||||||
|
->getClassMetadata($this->objectClass)
|
||||||
|
->getIdentifierFieldNames();
|
||||||
|
foreach ($identifierFieldNames as $fieldName) {
|
||||||
|
$queryBuilder->addOrderBy($rootAliases[0].'.'.$fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $queryBuilder
|
return $queryBuilder
|
||||||
->setFirstResult($offset)
|
->setFirstResult($offset)
|
||||||
->setMaxResults($limit)
|
->setMaxResults($limit)
|
||||||
|
|
@ -48,14 +105,14 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function createQueryBuilder()
|
protected function createQueryBuilder($method)
|
||||||
{
|
{
|
||||||
return $this->managerRegistry
|
return $this->managerRegistry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->getRepository($this->objectClass)
|
->getRepository($this->objectClass)
|
||||||
// ORM query builders require an alias argument
|
// ORM query builders require an alias argument
|
||||||
->{$this->options['query_builder_method']}('a');
|
->{$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()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,20 +25,19 @@ class RepositoryManager extends BaseManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return repository for entity
|
* Return repository for entity.
|
||||||
*
|
*
|
||||||
* Returns custom repository if one specified otherwise
|
* Returns custom repository if one specified otherwise
|
||||||
* returns a basic respository.
|
* returns a basic repository.
|
||||||
*/
|
*/
|
||||||
public function getRepository($entityName)
|
public function getRepository($entityName)
|
||||||
{
|
{
|
||||||
$realEntityName = $entityName;
|
$realEntityName = $entityName;
|
||||||
if (strpos($entityName, ':') !== false) {
|
if (strpos($entityName, ':') !== false) {
|
||||||
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
|
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
|
||||||
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias).'\\'.$simpleClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::getRepository($realEntityName);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,21 +2,28 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle;
|
namespace FOS\ElasticaBundle;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\DependencyInjection\Compiler\ConfigSourcePass;
|
||||||
|
use FOS\ElasticaBundle\DependencyInjection\Compiler\IndexPass;
|
||||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\RegisterProvidersPass;
|
use FOS\ElasticaBundle\DependencyInjection\Compiler\RegisterProvidersPass;
|
||||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\TransformerPass;
|
use FOS\ElasticaBundle\DependencyInjection\Compiler\TransformerPass;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bundle.
|
||||||
|
*/
|
||||||
class FOSElasticaBundle extends Bundle
|
class FOSElasticaBundle extends Bundle
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @see Symfony\Component\HttpKernel\Bundle\Bundle::build()
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function build(ContainerBuilder $container)
|
public function build(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
parent::build($container);
|
parent::build($container);
|
||||||
|
|
||||||
|
$container->addCompilerPass(new ConfigSourcePass());
|
||||||
|
$container->addCompilerPass(new IndexPass());
|
||||||
$container->addCompilerPass(new RegisterProvidersPass(), PassConfig::TYPE_BEFORE_REMOVING);
|
$container->addCompilerPass(new RegisterProvidersPass(), PassConfig::TYPE_BEFORE_REMOVING);
|
||||||
$container->addCompilerPass(new TransformerPass());
|
$container->addCompilerPass(new TransformerPass());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ namespace FOS\ElasticaBundle\Finder;
|
||||||
interface FinderInterface
|
interface FinderInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Searches for query results within a given limit
|
* Searches for query results within a given limit.
|
||||||
|
*
|
||||||
|
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||||
|
* @param int $limit How many results to get
|
||||||
|
* @param array $options
|
||||||
*
|
*
|
||||||
* @param mixed $query Can be a string, an array or an Elastica_Query object
|
|
||||||
* @param int $limit How many results to get
|
|
||||||
* @return array results
|
* @return array results
|
||||||
*/
|
*/
|
||||||
function find($query, $limit = null);
|
public function find($query, $limit = null, $options = array());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,27 @@ namespace FOS\ElasticaBundle\Finder;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
|
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
use Elastica_Query;
|
use Elastica\Query;
|
||||||
|
|
||||||
interface PaginatedFinderInterface extends FinderInterface
|
interface PaginatedFinderInterface extends FinderInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Searches for query results and returns them wrapped in a paginator
|
* Searches for query results and returns them wrapped in a paginator.
|
||||||
|
*
|
||||||
|
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||||
|
* @param array $options
|
||||||
*
|
*
|
||||||
* @param mixed $query Can be a string, an array or an Elastica_Query object
|
|
||||||
* @return Pagerfanta paginated results
|
* @return Pagerfanta paginated results
|
||||||
*/
|
*/
|
||||||
function findPaginated($query);
|
public function findPaginated($query, $options = array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a paginator adapter for this query
|
* Creates a paginator adapter for this query.
|
||||||
*
|
*
|
||||||
* @param mixed $query
|
* @param mixed $query
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
* @return PaginatorAdapterInterface
|
* @return PaginatorAdapterInterface
|
||||||
*/
|
*/
|
||||||
function createPaginatorAdapter($query);
|
public function createPaginatorAdapter($query, $options = array());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,76 +2,98 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Finder;
|
namespace FOS\ElasticaBundle\Finder;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Finder\PaginatedFinderInterface;
|
use Elastica\Document;
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||||
use FOS\ElasticaBundle\Paginator\TransformedPaginatorAdapter;
|
use FOS\ElasticaBundle\Paginator\TransformedPaginatorAdapter;
|
||||||
use FOS\ElasticaBundle\Paginator\FantaPaginatorAdapter;
|
use FOS\ElasticaBundle\Paginator\FantaPaginatorAdapter;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
use Elastica_Searchable;
|
use Elastica\SearchableInterface;
|
||||||
use Elastica_Query;
|
use Elastica\Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds elastica documents and map them to persisted objects
|
* Finds elastica documents and map them to persisted objects.
|
||||||
*/
|
*/
|
||||||
class TransformedFinder implements PaginatedFinderInterface
|
class TransformedFinder implements PaginatedFinderInterface
|
||||||
{
|
{
|
||||||
protected $searchable;
|
protected $searchable;
|
||||||
protected $transformer;
|
protected $transformer;
|
||||||
|
|
||||||
public function __construct(Elastica_Searchable $searchable, ElasticaToModelTransformerInterface $transformer)
|
public function __construct(SearchableInterface $searchable, ElasticaToModelTransformerInterface $transformer)
|
||||||
{
|
{
|
||||||
$this->searchable = $searchable;
|
$this->searchable = $searchable;
|
||||||
$this->transformer = $transformer;
|
$this->transformer = $transformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for a query string
|
* Search for a query string.
|
||||||
*
|
*
|
||||||
* @param string $query
|
* @param string $query
|
||||||
* @param integer $limit
|
* @param integer $limit
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
* @return array of model objects
|
* @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);
|
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);
|
return $this->transformer->hybridTransform($results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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())
|
||||||
|
{
|
||||||
|
$doc = new Document($id);
|
||||||
|
$results = $this->searchable->moreLikeThis($doc, $params, $query)->getResults();
|
||||||
|
|
||||||
|
return $this->transformer->transform($results);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $query
|
* @param $query
|
||||||
* @param null|int $limit
|
* @param null|int $limit
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function search($query, $limit = null)
|
protected function search($query, $limit = null, $options = array())
|
||||||
{
|
{
|
||||||
$queryObject = Elastica_Query::create($query);
|
$queryObject = Query::create($query);
|
||||||
if (null !== $limit) {
|
if (null !== $limit) {
|
||||||
$queryObject->setLimit($limit);
|
$queryObject->setSize($limit);
|
||||||
}
|
}
|
||||||
$results = $this->searchable->search($queryObject)->getResults();
|
$results = $this->searchable->search($queryObject, $options)->getResults();
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a paginator wrapping the result of a search
|
* Gets a paginator wrapping the result of a search.
|
||||||
*
|
*
|
||||||
* @param string $query
|
* @param string $query
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
* @return Pagerfanta
|
* @return Pagerfanta
|
||||||
*/
|
*/
|
||||||
public function findPaginated($query)
|
public function findPaginated($query, $options = array())
|
||||||
{
|
{
|
||||||
$queryObject = Elastica_Query::create($query);
|
$queryObject = Query::create($query);
|
||||||
$paginatorAdapter = $this->createPaginatorAdapter($queryObject);
|
$paginatorAdapter = $this->createPaginatorAdapter($queryObject, $options);
|
||||||
|
|
||||||
return new Pagerfanta(new FantaPaginatorAdapter($paginatorAdapter));
|
return new Pagerfanta(new FantaPaginatorAdapter($paginatorAdapter));
|
||||||
}
|
}
|
||||||
|
|
@ -79,9 +101,10 @@ class TransformedFinder implements PaginatedFinderInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function createPaginatorAdapter($query)
|
public function createPaginatorAdapter($query, $options = array())
|
||||||
{
|
{
|
||||||
$query = Elastica_Query::create($query);
|
$query = Query::create($query);
|
||||||
return new TransformedPaginatorAdapter($this->searchable, $query, $this->transformer);
|
|
||||||
|
return new TransformedPaginatorAdapter($this->searchable, $query, $options, $this->transformer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle;
|
namespace FOS\ElasticaBundle;
|
||||||
|
|
||||||
use Elastica_Result;
|
use Elastica\Result;
|
||||||
|
|
||||||
class HybridResult
|
class HybridResult
|
||||||
{
|
{
|
||||||
protected $result;
|
protected $result;
|
||||||
protected $transformed;
|
protected $transformed;
|
||||||
|
|
||||||
public function __construct(Elastica_Result $result, $transformed = null)
|
public function __construct(Result $result, $transformed = null)
|
||||||
{
|
{
|
||||||
$this->result = $result;
|
$this->result = $result;
|
||||||
$this->transformed = $transformed;
|
$this->transformed = $transformed;
|
||||||
|
|
@ -24,4 +24,4 @@ class HybridResult
|
||||||
{
|
{
|
||||||
return $this->result;
|
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,60 +2,11 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle;
|
namespace FOS\ElasticaBundle;
|
||||||
|
|
||||||
class IndexManager
|
use FOS\ElasticaBundle\Index\IndexManager as BaseIndexManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use \FOS\ElasticaBundle\Index\IndexManager
|
||||||
|
*/
|
||||||
|
class IndexManager extends BaseIndexManager
|
||||||
{
|
{
|
||||||
protected $indexesByName;
|
|
||||||
protected $defaultIndexName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param array $indexesByName
|
|
||||||
* @param \Elastica_Index $defaultIndex
|
|
||||||
*/
|
|
||||||
public function __construct(array $indexesByName, \Elastica_Index $defaultIndex)
|
|
||||||
{
|
|
||||||
$this->indexesByName = $indexesByName;
|
|
||||||
$this->defaultIndexName = $defaultIndex->getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all registered indexes
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getAllIndexes()
|
|
||||||
{
|
|
||||||
return $this->indexesByName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an index by its name
|
|
||||||
*
|
|
||||||
* @param string $name Index to return, or the default index if null
|
|
||||||
* @return \Elastica_Index
|
|
||||||
* @throws \InvalidArgumentException if no index exists for the given name
|
|
||||||
*/
|
|
||||||
public function getIndex($name = null)
|
|
||||||
{
|
|
||||||
if (null === $name) {
|
|
||||||
$name = $this->defaultIndexName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($this->indexesByName[$name])) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->indexesByName[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the default index
|
|
||||||
*
|
|
||||||
* @return \Elastica_Index
|
|
||||||
*/
|
|
||||||
public function getDefaultIndex()
|
|
||||||
{
|
|
||||||
return $this->getIndex($this->defaultIndexName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
LICENSE
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;
|
namespace FOS\ElasticaBundle\Logger;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger for the Elastica.
|
* Logger for the Elastica.
|
||||||
|
|
@ -12,41 +12,55 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||||
*
|
*
|
||||||
* @author Gordon Franke <info@nevalon.de>
|
* @author Gordon Franke <info@nevalon.de>
|
||||||
*/
|
*/
|
||||||
class ElasticaLogger
|
class ElasticaLogger implements LoggerInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
protected $logger;
|
protected $logger;
|
||||||
protected $queries;
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $queries = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
protected $debug;
|
protected $debug;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param LoggerInterface|null $logger The Symfony logger
|
* @param LoggerInterface|null $logger The Symfony logger
|
||||||
* @param bool $debug
|
* @param boolean $debug
|
||||||
*/
|
*/
|
||||||
public function __construct(LoggerInterface $logger = null, $debug = false)
|
public function __construct(LoggerInterface $logger = null, $debug = false)
|
||||||
{
|
{
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->queries = array();
|
|
||||||
$this->debug = $debug;
|
$this->debug = $debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a query.
|
* Logs a query.
|
||||||
*
|
*
|
||||||
* @param string $path Path to call
|
* @param string $path Path to call
|
||||||
* @param string $method Rest method to use (GET, POST, DELETE, PUT)
|
* @param string $method Rest method to use (GET, POST, DELETE, PUT)
|
||||||
* @param array $data arguments
|
* @param array $data Arguments
|
||||||
* @param float $time execution time
|
* @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) {
|
if ($this->debug) {
|
||||||
$this->queries[] = array(
|
$this->queries[] = array(
|
||||||
'path' => $path,
|
'path' => $path,
|
||||||
'method' => $method,
|
'method' => $method,
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'executionMS' => $time
|
'executionMS' => $time,
|
||||||
|
'connection' => $connection,
|
||||||
|
'queryString' => $query,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,4 +89,76 @@ class ElasticaLogger
|
||||||
{
|
{
|
||||||
return $this->queries;
|
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 Doctrine\Common\Annotations\Reader;
|
||||||
use FOS\ElasticaBundle\Finder\FinderInterface;
|
use FOS\ElasticaBundle\Finder\FinderInterface;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Richard Miller <info@limethinking.co.uk>
|
* @author Richard Miller <info@limethinking.co.uk>
|
||||||
*
|
*
|
||||||
|
|
@ -24,16 +25,16 @@ class RepositoryManager implements RepositoryManagerInterface
|
||||||
|
|
||||||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
|
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
|
||||||
{
|
{
|
||||||
$this->entities[$entityName]= array();
|
$this->entities[$entityName] = array();
|
||||||
$this->entities[$entityName]['finder'] = $finder;
|
$this->entities[$entityName]['finder'] = $finder;
|
||||||
$this->entities[$entityName]['repositoryName'] = $repositoryName;
|
$this->entities[$entityName]['repositoryName'] = $repositoryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return repository for entity
|
* Return repository for entity.
|
||||||
*
|
*
|
||||||
* Returns custom repository if one specified otherwise
|
* Returns custom repository if one specified otherwise
|
||||||
* returns a basic respository.
|
* returns a basic repository.
|
||||||
*/
|
*/
|
||||||
public function getRepository($entityName)
|
public function getRepository($entityName)
|
||||||
{
|
{
|
||||||
|
|
@ -58,23 +59,26 @@ class RepositoryManager implements RepositoryManagerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
$refClass = new \ReflectionClass($entityName);
|
$refClass = new \ReflectionClass($entityName);
|
||||||
$annotation = $this->reader->getClassAnnotation($refClass, 'FOS\\ElasticaBundle\\Configuration\\Search');
|
$annotation = $this->reader->getClassAnnotation($refClass, 'FOS\\ElasticaBundle\\Annotation\\Search');
|
||||||
if ($annotation) {
|
if ($annotation) {
|
||||||
$this->entities[$entityName]['repositoryName']
|
$this->entities[$entityName]['repositoryName']
|
||||||
= $annotation->repositoryClass;
|
= $annotation->repositoryClass;
|
||||||
|
|
||||||
return $annotation->repositoryClass;
|
return $annotation->repositoryClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'FOS\ElasticaBundle\Repository';
|
return 'FOS\ElasticaBundle\Repository';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $entityName
|
||||||
|
*/
|
||||||
private function createRepository($entityName)
|
private function createRepository($entityName)
|
||||||
{
|
{
|
||||||
$repositoryName = $this->getRepositoryName($entityName);
|
if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) {
|
||||||
if (!class_exists($repositoryName)) {
|
|
||||||
throw new RuntimeException(sprintf('%s repository for %s does not exist', $repositoryName, $entityName));
|
throw new RuntimeException(sprintf('%s repository for %s does not exist', $repositoryName, $entityName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new $repositoryName($this->entities[$entityName]['finder']);
|
return new $repositoryName($this->entities[$entityName]['finder']);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ use FOS\ElasticaBundle\Finder\FinderInterface;
|
||||||
*/
|
*/
|
||||||
interface RepositoryManagerInterface
|
interface RepositoryManagerInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds entity name and its finder.
|
* Adds entity name and its finder.
|
||||||
* Custom repository class name can also be added.
|
* Custom repository class name can also be added.
|
||||||
|
|
@ -24,7 +23,7 @@ interface RepositoryManagerInterface
|
||||||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
|
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return repository for entity
|
* Return repository for entity.
|
||||||
*
|
*
|
||||||
* Returns custom repository if one specified otherwise
|
* Returns custom repository if one specified otherwise
|
||||||
* returns a basic repository.
|
* returns a basic repository.
|
||||||
|
|
@ -32,5 +31,4 @@ interface RepositoryManagerInterface
|
||||||
* @param string $entityName
|
* @param string $entityName
|
||||||
*/
|
*/
|
||||||
public function getRepository($entityName);
|
public function getRepository($entityName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,13 @@
|
||||||
namespace FOS\ElasticaBundle\Paginator;
|
namespace FOS\ElasticaBundle\Paginator;
|
||||||
|
|
||||||
use Pagerfanta\Adapter\AdapterInterface;
|
use Pagerfanta\Adapter\AdapterInterface;
|
||||||
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
|
|
||||||
|
|
||||||
class FantaPaginatorAdapter implements AdapterInterface
|
class FantaPaginatorAdapter implements AdapterInterface
|
||||||
{
|
{
|
||||||
private $adapter;
|
private $adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PaginatorAdapterInterface $adapter
|
* @param \FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface $adapter
|
||||||
*/
|
*/
|
||||||
public function __construct(PaginatorAdapterInterface $adapter)
|
public function __construct(PaginatorAdapterInterface $adapter)
|
||||||
{
|
{
|
||||||
|
|
@ -21,8 +20,6 @@ class FantaPaginatorAdapter implements AdapterInterface
|
||||||
* Returns the number of results.
|
* Returns the number of results.
|
||||||
*
|
*
|
||||||
* @return integer The number of results.
|
* @return integer The number of results.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
public function getNbResults()
|
public function getNbResults()
|
||||||
{
|
{
|
||||||
|
|
@ -30,14 +27,34 @@ class FantaPaginatorAdapter implements AdapterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an slice of the results.
|
* Returns Facets.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getFacets()
|
||||||
|
{
|
||||||
|
return $this->adapter->getFacets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Aggregations.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
public function getAggregations()
|
||||||
|
{
|
||||||
|
return $this->adapter->getAggregations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a slice of the results.
|
||||||
*
|
*
|
||||||
* @param integer $offset The offset.
|
* @param integer $offset The offset.
|
||||||
* @param integer $length The length.
|
* @param integer $length The length.
|
||||||
*
|
*
|
||||||
* @return array|\Traversable The slice.
|
* @return array|\Traversable The slice.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
public function getSlice($offset, $length)
|
public function getSlice($offset, $length)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,14 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Paginator;
|
namespace FOS\ElasticaBundle\Paginator;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
|
|
||||||
|
|
||||||
interface PaginatorAdapterInterface
|
interface PaginatorAdapterInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns the number of results.
|
* Returns the number of results.
|
||||||
*
|
*
|
||||||
* @return integer The number of results.
|
* @return integer The number of results.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function getTotalHits();
|
public function getTotalHits();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an slice of the results.
|
* Returns an slice of the results.
|
||||||
|
|
@ -22,8 +18,20 @@ interface PaginatorAdapterInterface
|
||||||
* @param integer $length The length.
|
* @param integer $length The length.
|
||||||
*
|
*
|
||||||
* @return PartialResultsInterface
|
* @return PartialResultsInterface
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function getResults($offset, $length);
|
public function getResults($offset, $length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Facets.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getFacets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Aggregations.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAggregations();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,24 +8,27 @@ interface PartialResultsInterface
|
||||||
* Returns the paginated results.
|
* Returns the paginated results.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function toArray();
|
public function toArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of results.
|
* Returns the number of results.
|
||||||
*
|
*
|
||||||
* @return integer The number of results.
|
* @return integer The number of results.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function getTotalHits();
|
public function getTotalHits();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the facets
|
* Returns the facets.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function getFacets();
|
public function getFacets();
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Returns the aggregations.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAggregations();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,54 +2,96 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Paginator;
|
namespace FOS\ElasticaBundle\Paginator;
|
||||||
|
|
||||||
use Elastica_Searchable;
|
use Elastica\SearchableInterface;
|
||||||
use Elastica_Query;
|
use Elastica\Query;
|
||||||
use Elastica_ResultSet;
|
use Elastica\ResultSet;
|
||||||
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
|
use InvalidArgumentException;
|
||||||
use FOS\ElasticaBundle\Paginator\RawPartialResults;
|
|
||||||
use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows pagination of Elastica_Query. Does not map results
|
* Allows pagination of Elastica\Query. Does not map results.
|
||||||
*/
|
*/
|
||||||
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Elastica_Searchable the object to search in
|
* @var SearchableInterface the object to search in
|
||||||
*/
|
*/
|
||||||
private $searchable = null;
|
private $searchable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Elastica_Query the query to search
|
* @var Query the query to search
|
||||||
*/
|
*/
|
||||||
private $query = null;
|
private $query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array search options
|
||||||
|
*/
|
||||||
|
private $options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer the number of hits
|
||||||
|
*/
|
||||||
|
private $totalHits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array for the facets
|
||||||
|
*/
|
||||||
|
private $facets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array for the aggregations
|
||||||
|
*/
|
||||||
|
private $aggregations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see PaginatorAdapterInterface::__construct
|
* @see PaginatorAdapterInterface::__construct
|
||||||
*
|
*
|
||||||
* @param Elastica_Searchable $searchable the object to search in
|
* @param SearchableInterface $searchable the object to search in
|
||||||
* @param Elastica_Query $query the query to search
|
* @param Query $query the query to search
|
||||||
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
public function __construct(Elastica_Searchable $searchable, Elastica_Query $query)
|
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
|
||||||
{
|
{
|
||||||
$this->searchable = $searchable;
|
$this->searchable = $searchable;
|
||||||
$this->query = $query;
|
$this->query = $query;
|
||||||
|
$this->options = $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the paginated results.
|
* Returns the paginated results.
|
||||||
*
|
*
|
||||||
* @param $offset
|
* @param integer $offset
|
||||||
* @param $itemCountPerPage
|
* @param integer $itemCountPerPage
|
||||||
* @return Elastica_ResultSet
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return ResultSet
|
||||||
*/
|
*/
|
||||||
protected function getElasticaResults($offset, $itemCountPerPage)
|
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 = clone $this->query;
|
||||||
$query->setFrom($offset);
|
$query->setFrom($offset);
|
||||||
$query->setLimit($itemCountPerPage);
|
$query->setSize($itemCountPerPage);
|
||||||
|
|
||||||
return $this->searchable->search($query);
|
$resultSet = $this->searchable->search($query, $this->options);
|
||||||
|
$this->totalHits = $resultSet->getTotalHits();
|
||||||
|
$this->facets = $resultSet->getFacets();
|
||||||
|
$this->aggregations = $resultSet->getAggregations();
|
||||||
|
|
||||||
|
return $resultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,6 +99,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
*
|
*
|
||||||
* @param int $offset
|
* @param int $offset
|
||||||
* @param int $itemCountPerPage
|
* @param int $itemCountPerPage
|
||||||
|
*
|
||||||
* @return PartialResultsInterface
|
* @return PartialResultsInterface
|
||||||
*/
|
*/
|
||||||
public function getResults($offset, $itemCountPerPage)
|
public function getResults($offset, $itemCountPerPage)
|
||||||
|
|
@ -67,10 +110,60 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
/**
|
/**
|
||||||
* Returns the number of results.
|
* 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.
|
* @return integer The number of results.
|
||||||
*/
|
*/
|
||||||
public function getTotalHits()
|
public function getTotalHits($genuineTotal = false)
|
||||||
{
|
{
|
||||||
return $this->searchable->search($this->query)->getTotalHits();
|
if (! isset($this->totalHits)) {
|
||||||
|
$this->totalHits = $this->searchable->count($this->query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->query->hasParam('size') && !$genuineTotal
|
||||||
|
? min($this->totalHits, (integer) $this->query->getParam('size'))
|
||||||
|
: $this->totalHits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Facets.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getFacets()
|
||||||
|
{
|
||||||
|
if (! isset($this->facets)) {
|
||||||
|
$this->facets = $this->searchable->search($this->query)->getFacets();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->facets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Aggregations.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAggregations()
|
||||||
|
{
|
||||||
|
if (!isset($this->aggregations)) {
|
||||||
|
$this->aggregations = $this->searchable->search($this->query)->getAggregations();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->aggregations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Query.
|
||||||
|
*
|
||||||
|
* @return Query the search query
|
||||||
|
*/
|
||||||
|
public function getQuery()
|
||||||
|
{
|
||||||
|
return $this->query;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,20 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Paginator;
|
namespace FOS\ElasticaBundle\Paginator;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
|
use Elastica\ResultSet;
|
||||||
use Elastica_ResultSet;
|
use Elastica\Result;
|
||||||
use Elastica_Result;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw partial results transforms to a simple array
|
* Raw partial results transforms to a simple array.
|
||||||
*/
|
*/
|
||||||
class RawPartialResults implements PartialResultsInterface
|
class RawPartialResults implements PartialResultsInterface
|
||||||
{
|
{
|
||||||
protected $resultSet;
|
protected $resultSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Elastica_ResultSet $resultSet
|
* @param ResultSet $resultSet
|
||||||
*/
|
*/
|
||||||
public function __construct(Elastica_ResultSet $resultSet)
|
public function __construct(ResultSet $resultSet)
|
||||||
{
|
{
|
||||||
$this->resultSet = $resultSet;
|
$this->resultSet = $resultSet;
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +25,7 @@ class RawPartialResults implements PartialResultsInterface
|
||||||
*/
|
*/
|
||||||
public function toArray()
|
public function toArray()
|
||||||
{
|
{
|
||||||
return array_map(function(Elastica_Result $result) {
|
return array_map(function (Result $result) {
|
||||||
return $result->getSource();
|
return $result->getSource();
|
||||||
}, $this->resultSet->getResults());
|
}, $this->resultSet->getResults());
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +47,18 @@ class RawPartialResults implements PartialResultsInterface
|
||||||
return $this->resultSet->getFacets();
|
return $this->resultSet->getFacets();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getAggregations()
|
||||||
|
{
|
||||||
|
if ($this->resultSet->hasAggregations()) {
|
||||||
|
return $this->resultSet->getAggregations();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,25 +3,25 @@
|
||||||
namespace FOS\ElasticaBundle\Paginator;
|
namespace FOS\ElasticaBundle\Paginator;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||||
use FOS\ElasticaBundle\Paginator\TransformedPartialResults;
|
use Elastica\SearchableInterface;
|
||||||
use Elastica_Searchable;
|
use Elastica\Query;
|
||||||
use Elastica_Query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows pagination of Elastica_Query
|
* Allows pagination of \Elastica\Query.
|
||||||
*/
|
*/
|
||||||
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
||||||
{
|
{
|
||||||
private $transformer;
|
private $transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Elastica_Searchable $searchable the object to search in
|
* @param SearchableInterface $searchable the object to search in
|
||||||
* @param Elastica_Query $query the query to search
|
* @param Query $query the query to search
|
||||||
|
* @param array $options
|
||||||
* @param ElasticaToModelTransformerInterface $transformer the transformer for fetching the results
|
* @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;
|
$this->transformer = $transformer;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,20 @@
|
||||||
namespace FOS\ElasticaBundle\Paginator;
|
namespace FOS\ElasticaBundle\Paginator;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||||
use FOS\ElasticaBundle\Paginator\RawPartialResults;
|
use Elastica\ResultSet;
|
||||||
use Elastica_ResultSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial transformed result set
|
* Partial transformed result set.
|
||||||
*/
|
*/
|
||||||
class TransformedPartialResults extends RawPartialResults
|
class TransformedPartialResults extends RawPartialResults
|
||||||
{
|
{
|
||||||
protected $transformer;
|
protected $transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Elastica_ResultSet $resultSet
|
* @param ResultSet $resultSet
|
||||||
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
||||||
*/
|
*/
|
||||||
public function __construct(Elastica_ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
||||||
{
|
{
|
||||||
parent::__construct($resultSet);
|
parent::__construct($resultSet);
|
||||||
|
|
||||||
|
|
@ -31,4 +30,4 @@ class TransformedPartialResults extends RawPartialResults
|
||||||
{
|
{
|
||||||
return $this->transformer->transform($this->resultSet->getResults());
|
return $this->transformer->transform($this->resultSet->getResults());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Persister;
|
namespace FOS\ElasticaBundle\Persister;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Elastica\Exception\BulkException;
|
||||||
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
||||||
use Elastica_Type;
|
use Elastica\Type;
|
||||||
use Elastica_Document;
|
use Elastica\Document;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts, replaces and deletes single documents in an elastica type
|
* 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>
|
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,8 +20,9 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
protected $transformer;
|
protected $transformer;
|
||||||
protected $objectClass;
|
protected $objectClass;
|
||||||
protected $fields;
|
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->type = $type;
|
||||||
$this->transformer = $transformer;
|
$this->transformer = $transformer;
|
||||||
|
|
@ -27,75 +30,161 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
$this->fields = $fields;
|
$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
|
* 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
|
* @param object $object
|
||||||
*/
|
*/
|
||||||
public function insertOne($object)
|
public function insertOne($object)
|
||||||
{
|
{
|
||||||
$document = $this->transformToElasticaDocument($object);
|
$this->insertMany(array($object));
|
||||||
$this->type->addDocument($document);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces one object in the type
|
* Replaces one object in the type.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @return null
|
|
||||||
**/
|
**/
|
||||||
public function replaceOne($object)
|
public function replaceOne($object)
|
||||||
{
|
{
|
||||||
$document = $this->transformToElasticaDocument($object);
|
$this->replaceMany(array($object));
|
||||||
$this->type->deleteById($document->getId());
|
|
||||||
$this->type->addDocument($document);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes one object in the type
|
* Deletes one object in the type.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @return null
|
|
||||||
**/
|
**/
|
||||||
public function deleteOne($object)
|
public function deleteOne($object)
|
||||||
{
|
{
|
||||||
$document = $this->transformToElasticaDocument($object);
|
$this->deleteMany(array($object));
|
||||||
$this->type->deleteById($document->getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes one object in the type by id
|
* Deletes one object in the type by id.
|
||||||
*
|
*
|
||||||
* @param mixed $id
|
* @param mixed $id
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
**/
|
**/
|
||||||
public function deleteById($id)
|
public function deleteById($id)
|
||||||
{
|
{
|
||||||
$this->type->deleteById($id);
|
$this->deleteManyByIdentifiers(array($id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts an array of objects in the type
|
* Bulk insert an array of objects in the type for the given method.
|
||||||
*
|
*
|
||||||
* @param array $objects array of domain model objects
|
* @param array $objects array of domain model objects
|
||||||
**/
|
* @param string Method to call
|
||||||
|
*/
|
||||||
public function insertMany(array $objects)
|
public function insertMany(array $objects)
|
||||||
{
|
{
|
||||||
$documents = array();
|
$documents = array();
|
||||||
foreach ($objects as $object) {
|
foreach ($objects as $object) {
|
||||||
$documents[] = $this->transformToElasticaDocument($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
|
* @param object $object
|
||||||
* @return Elastica_Document the elastica document
|
*
|
||||||
|
* @return Document the elastica document
|
||||||
*/
|
*/
|
||||||
public function transformToElasticaDocument($object)
|
public function transformToElasticaDocument($object)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,47 +4,75 @@ namespace FOS\ElasticaBundle\Persister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts, replaces and deletes single documents in an elastica type
|
* 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>
|
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||||
*/
|
*/
|
||||||
interface ObjectPersisterInterface
|
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
|
* 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
|
* @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
|
* @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
|
* @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
|
* @param mixed $id
|
||||||
*
|
*/
|
||||||
* @return null
|
public function deleteById($id);
|
||||||
**/
|
|
||||||
function deleteById($id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts an array of objects in the type
|
* Bulk inserts an array of objects in the type.
|
||||||
*
|
*
|
||||||
* @param array $objects array of domain model objects
|
* @param array $objects array of domain model objects
|
||||||
**/
|
*/
|
||||||
function insertMany(array $objects);
|
public function insertMany(array $objects);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk updates an array of objects in the type.
|
||||||
|
*
|
||||||
|
* @param array $objects array of domain model objects
|
||||||
|
*/
|
||||||
|
public function replaceMany(array $objects);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk deletes an array of objects in the type.
|
||||||
|
*
|
||||||
|
* @param array $objects array of domain model objects
|
||||||
|
*/
|
||||||
|
public function deleteMany(array $objects);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk deletes records from an array of identifiers.
|
||||||
|
*
|
||||||
|
* @param array $identifiers array of domain model object identifiers
|
||||||
|
*/
|
||||||
|
public function deleteManyByIdentifiers(array $identifiers);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,74 +3,75 @@
|
||||||
namespace FOS\ElasticaBundle\Propel;
|
namespace FOS\ElasticaBundle\Propel;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\HybridResult;
|
use FOS\ElasticaBundle\HybridResult;
|
||||||
|
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||||
use Symfony\Component\Form\Util\PropertyPath;
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps Elastica documents with Propel objects
|
* Maps Elastica documents with Propel objects.
|
||||||
* This mapper assumes an exact match between
|
*
|
||||||
* elastica documents ids and propel object ids
|
* This mapper assumes an exact match between Elastica document IDs and Propel
|
||||||
|
* entity IDs.
|
||||||
*
|
*
|
||||||
* @author William Durand <william.durand1@gmail.com>
|
* @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
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $objectClass = null;
|
protected $objectClass = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional parameters
|
* Transformer options.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $options = array(
|
protected $options = array(
|
||||||
'hydrate' => true,
|
'hydrate' => true,
|
||||||
'identifier' => 'id'
|
'identifier' => 'id',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Mapper
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
public function __construct($objectClass, array $options = array())
|
public function __construct($objectClass, array $options = array())
|
||||||
{
|
{
|
||||||
$this->objectClass = $objectClass;
|
$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
|
* Transforms an array of Elastica document into an array of Propel entities
|
||||||
* model objects fetched from the propel repository
|
* fetched from the database.
|
||||||
*
|
*
|
||||||
* @param \Elastica_Document[] $elasticaObjects array of elastica objects
|
* @param array $elasticaObjects
|
||||||
* @return array
|
*
|
||||||
|
* @return array|\ArrayObject
|
||||||
*/
|
*/
|
||||||
public function transform(array $elasticaObjects)
|
public function transform(array $elasticaObjects)
|
||||||
{
|
{
|
||||||
$ids = array_map(function(\Elastica_Document $elasticaObject) {
|
$ids = array();
|
||||||
return $elasticaObject->getId();
|
foreach ($elasticaObjects as $elasticaObject) {
|
||||||
}, $elasticaObjects);
|
$ids[] = $elasticaObject->getId();
|
||||||
|
}
|
||||||
|
|
||||||
$objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
|
$objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
|
||||||
|
|
||||||
$identifierProperty = new PropertyPath($this->options['identifier']);
|
// Sort objects in the order of their IDs
|
||||||
|
|
||||||
// sort objects in the order of ids
|
|
||||||
$idPos = array_flip($ids);
|
$idPos = array_flip($ids);
|
||||||
|
$identifier = $this->options['identifier'];
|
||||||
|
$sortCallback = $this->getSortingClosure($idPos, $identifier);
|
||||||
|
|
||||||
if (is_object($objects)) {
|
if (is_object($objects)) {
|
||||||
$objects->uasort(function($a, $b) use ($idPos, $identifierProperty) {
|
$objects->uasort($sortCallback);
|
||||||
return $idPos[$identifierProperty->getValue($a)] > $idPos[$identifierProperty->getValue($b)];
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
usort($objects, function($a, $b) use ($idPos, $identifierProperty) {
|
usort($objects, $sortCallback);
|
||||||
return $idPos[$identifierProperty->getValue($a)] > $idPos[$identifierProperty->getValue($b)];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $objects;
|
return $objects;
|
||||||
|
|
@ -84,7 +85,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
$objects = $this->transform($elasticaObjects);
|
$objects = $this->transform($elasticaObjects);
|
||||||
|
|
||||||
$result = array();
|
$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]);
|
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,11 +109,15 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch objects for theses identifier values
|
* Fetch Propel entities for the given identifier values.
|
||||||
*
|
*
|
||||||
* @param array $identifierValues ids values
|
* If $hydrate is false, the returned array elements will be arrays.
|
||||||
* @param boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
* Otherwise, the results will be hydrated to instances of the model class.
|
||||||
* @return array of objects or arrays
|
*
|
||||||
|
* @param array $identifierValues Identifier values
|
||||||
|
* @param boolean $hydrate Whether or not to hydrate the results
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||||
{
|
{
|
||||||
|
|
@ -122,7 +127,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
|
|
||||||
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
||||||
|
|
||||||
if (!$hydrate) {
|
if (! $hydrate) {
|
||||||
return $query->toArray();
|
return $query->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,9 +137,10 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
/**
|
/**
|
||||||
* Create a query to use in the findByIdentifiers() method.
|
* Create a query to use in the findByIdentifiers() method.
|
||||||
*
|
*
|
||||||
* @param string $class the model class
|
* @param string $class Propel model class
|
||||||
* @param string $identifierField like 'id'
|
* @param string $identifierField Identifier field name (e.g. "id")
|
||||||
* @param array $identifierValues ids values
|
* @param array $identifierValues Identifier values
|
||||||
|
*
|
||||||
* @return \ModelCriteria
|
* @return \ModelCriteria
|
||||||
*/
|
*/
|
||||||
protected function createQuery($class, $identifierField, array $identifierValues)
|
protected function createQuery($class, $identifierField, array $identifierValues)
|
||||||
|
|
@ -142,13 +148,13 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
$queryClass = $class.'Query';
|
$queryClass = $class.'Query';
|
||||||
$filterMethod = 'filterBy'.$this->camelize($identifierField);
|
$filterMethod = 'filterBy'.$this->camelize($identifierField);
|
||||||
|
|
||||||
return $queryClass::create()
|
return $queryClass::create()->$filterMethod($identifierValues);
|
||||||
->$filterMethod($identifierValues)
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
||||||
|
*
|
||||||
|
* @param string $str
|
||||||
*/
|
*/
|
||||||
private function camelize($str)
|
private function camelize($str)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,39 +5,69 @@ namespace FOS\ElasticaBundle\Propel;
|
||||||
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Propel provider
|
* Propel provider.
|
||||||
*
|
*
|
||||||
* @author William Durand <william.durand1@gmail.com>
|
* @author William Durand <william.durand1@gmail.com>
|
||||||
*/
|
*/
|
||||||
class Provider extends AbstractProvider
|
class Provider extends AbstractProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function populate(\Closure $loggerClosure = null)
|
public function doPopulate($options, \Closure $loggerClosure = null)
|
||||||
{
|
{
|
||||||
$queryClass = $this->objectClass . 'Query';
|
$queryClass = $this->objectClass.'Query';
|
||||||
$nbObjects = $queryClass::create()->count();
|
$nbObjects = $queryClass::create()->count();
|
||||||
|
|
||||||
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
$offset = $options['offset'];
|
||||||
if ($loggerClosure) {
|
|
||||||
$stepStartTime = microtime(true);
|
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||||
|
$objects = $queryClass::create()
|
||||||
|
->limit($options['batch_size'])
|
||||||
|
->offset($offset)
|
||||||
|
->find()
|
||||||
|
->getArrayCopy();
|
||||||
|
$objects = $this->filterObjects($options, $objects);
|
||||||
|
if (!empty($objects)) {
|
||||||
|
$this->objectPersister->insertMany($objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
$objects = $queryClass::create()
|
usleep($options['sleep']);
|
||||||
->limit($this->options['batch_size'])
|
|
||||||
->offset($offset)
|
|
||||||
->find();
|
|
||||||
|
|
||||||
$this->objectPersister->insertMany($objects->getArrayCopy());
|
|
||||||
|
|
||||||
if ($loggerClosure) {
|
if ($loggerClosure) {
|
||||||
$stepNbObjects = count($objects);
|
$loggerClosure($options['batch_size'], $nbObjects);
|
||||||
$stepCount = $stepNbObjects + $offset;
|
|
||||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
|
||||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
|
||||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,193 @@
|
||||||
namespace FOS\ElasticaBundle\Provider;
|
namespace FOS\ElasticaBundle\Provider;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbstractProvider.
|
||||||
|
*/
|
||||||
abstract class AbstractProvider implements ProviderInterface
|
abstract class AbstractProvider implements ProviderInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $baseOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
protected $objectClass;
|
protected $objectClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ObjectPersisterInterface
|
||||||
|
*/
|
||||||
protected $objectPersister;
|
protected $objectPersister;
|
||||||
protected $options;
|
|
||||||
|
/**
|
||||||
|
* @var OptionsResolver
|
||||||
|
*/
|
||||||
|
protected $resolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IndexableInterface
|
||||||
|
*/
|
||||||
|
private $indexable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ObjectPersisterInterface $objectPersister
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
|
* @param IndexableInterface $indexable
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $baseOptions
|
||||||
*/
|
*/
|
||||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
public function __construct(
|
||||||
{
|
ObjectPersisterInterface $objectPersister,
|
||||||
$this->objectPersister = $objectPersister;
|
IndexableInterface $indexable,
|
||||||
|
$objectClass,
|
||||||
|
array $baseOptions = array()
|
||||||
|
) {
|
||||||
|
$this->baseOptions = $baseOptions;
|
||||||
|
$this->indexable = $indexable;
|
||||||
$this->objectClass = $objectClass;
|
$this->objectClass = $objectClass;
|
||||||
|
$this->objectPersister = $objectPersister;
|
||||||
|
$this->resolver = new OptionsResolver();
|
||||||
|
$this->configureOptions();
|
||||||
|
}
|
||||||
|
|
||||||
$this->options = array_merge(array(
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||||
|
{
|
||||||
|
$options = $this->resolveOptions($options);
|
||||||
|
|
||||||
|
$logger = !$options['debug_logging'] ?
|
||||||
|
$this->disableLogging() :
|
||||||
|
null;
|
||||||
|
|
||||||
|
$this->doPopulate($options, $loggerClosure);
|
||||||
|
|
||||||
|
if (null !== $logger) {
|
||||||
|
$this->enableLogging($logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables logging and returns the logger that was previously set.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
abstract protected function disableLogging();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform actual population.
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
* @param \Closure $loggerClosure
|
||||||
|
*/
|
||||||
|
abstract protected function doPopulate($options, \Closure $loggerClosure = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||||
|
*
|
||||||
|
* @param mixed $logger
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
abstract protected function enableLogging($logger);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the option resolver.
|
||||||
|
*/
|
||||||
|
protected function configureOptions()
|
||||||
|
{
|
||||||
|
$this->resolver->setDefaults(array(
|
||||||
'batch_size' => 100,
|
'batch_size' => 100,
|
||||||
), $options);
|
'skip_indexable_check' => false,
|
||||||
|
));
|
||||||
|
$this->resolver->setAllowedTypes(array(
|
||||||
|
'batch_size' => 'int'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->resolver->setRequired(array(
|
||||||
|
'indexName',
|
||||||
|
'typeName',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters objects away if they are not indexable.
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
* @param array $objects
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function filterObjects(array $options, array $objects)
|
||||||
|
{
|
||||||
|
if ($options['skip_indexable_check']) {
|
||||||
|
return $objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
$index = $options['indexName'];
|
||||||
|
$type = $options['typeName'];
|
||||||
|
|
||||||
|
$return = array();
|
||||||
|
foreach ($objects as $object) {
|
||||||
|
if (!$this->indexable->isObjectIndexable($index, $type, $object)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return[] = $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given object should be indexed or not.
|
||||||
|
*
|
||||||
|
* @deprecated To be removed in 4.0
|
||||||
|
*
|
||||||
|
* @param object $object
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isObjectIndexable($object)
|
||||||
|
{
|
||||||
|
return $this->indexable->isObjectIndexable(
|
||||||
|
$this->baseOptions['indexName'],
|
||||||
|
$this->baseOptions['typeName'],
|
||||||
|
$object
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string with RAM usage information (current and peak).
|
||||||
|
*
|
||||||
|
* @deprecated To be removed in 4.0
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getMemoryUsage()
|
||||||
|
{
|
||||||
|
$memory = round(memory_get_usage() / (1024 * 1024)); // to get usage in Mo
|
||||||
|
$memoryMax = round(memory_get_peak_usage() / (1024 * 1024)); // to get max usage in Mo
|
||||||
|
|
||||||
|
return sprintf('(RAM : current=%uMo peak=%uMo)', $memory, $memoryMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the base options provided by the class with options passed to the populate
|
||||||
|
* method and runs them through the resolver.
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function resolveOptions(array $options)
|
||||||
|
{
|
||||||
|
return $this->resolver->resolve(array_merge($this->baseOptions, $options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
240
Provider/Indexable.php
Normal file
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);
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace FOS\ElasticaBundle\Provider;
|
namespace FOS\ElasticaBundle\Provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert application domain objects into elastica types
|
* Insert application domain objects into elastica types.
|
||||||
*
|
*
|
||||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
@ -12,8 +12,15 @@ interface ProviderInterface
|
||||||
/**
|
/**
|
||||||
* Persists all domain objects to ElasticSearch for this provider.
|
* Persists all domain objects to ElasticSearch for this provider.
|
||||||
*
|
*
|
||||||
|
* The closure can expect 2 or 3 arguments:
|
||||||
|
* * The step size
|
||||||
|
* * The total number of objects
|
||||||
|
* * A message to output in error conditions (not normally provided)
|
||||||
|
*
|
||||||
* @param \Closure $loggerClosure
|
* @param \Closure $loggerClosure
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
function populate(\Closure $loggerClosure = null);
|
public function populate(\Closure $loggerClosure = null, array $options = array());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Provider;
|
namespace FOS\ElasticaBundle\Provider;
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
|
|
@ -59,8 +57,10 @@ class ProviderRegistry implements ContainerAwareInterface
|
||||||
*
|
*
|
||||||
* Providers will be indexed by "type" strings in the returned array.
|
* Providers will be indexed by "type" strings in the returned array.
|
||||||
*
|
*
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @return array of ProviderInterface instances
|
*
|
||||||
|
* @return ProviderInterface[]
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException if no providers were registered for the index
|
* @throws \InvalidArgumentException if no providers were registered for the index
|
||||||
*/
|
*/
|
||||||
public function getIndexProviders($index)
|
public function getIndexProviders($index)
|
||||||
|
|
@ -83,7 +83,9 @@ class ProviderRegistry implements ContainerAwareInterface
|
||||||
*
|
*
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @param string $type
|
* @param string $type
|
||||||
|
*
|
||||||
* @return ProviderInterface
|
* @return ProviderInterface
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException if no provider was registered for the index and type
|
* @throws \InvalidArgumentException if no provider was registered for the index and type
|
||||||
*/
|
*/
|
||||||
public function getProvider($index, $type)
|
public function getProvider($index, $type)
|
||||||
|
|
|
||||||
705
README.md
705
README.md
|
|
@ -1,696 +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:
|
||||||
|
|
||||||
### Install elasticsearch
|
- Integrates the Elastica library into a Symfony2 environment
|
||||||
|
- Automatically generate mappings using a serializer
|
||||||
|
- Listeners for Doctrine events for automatic indexing
|
||||||
|
|
||||||
http://www.elasticsearch.org/guide/reference/setup/installation.html
|
> **Note** Propel support is limited and contributions fixing issues are welcome!
|
||||||
|
|
||||||
### Install Elastica
|
[](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
||||||
|
[](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/?branch=master)
|
||||||
|
|
||||||
#### Download
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
**With submodule**
|
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
||||||
|
|
||||||
`git submodule add git://github.com/ruflin/Elastica vendor/elastica`
|
[Read the documentation for 3.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||||
|
|
||||||
**With clone**
|
[Read the documentation for 3.0.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/3.0.x/Resources/doc/index.md)
|
||||||
|
|
||||||
`git clone git://github.com/ruflin/Elastica vendor/elastica`
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
**Using the vendors script**
|
Installation instructions can be found in the [documentation](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md)
|
||||||
|
|
||||||
Add the following lines to your deps file:
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
[Elastica]
|
This bundle is under the MIT license. See the complete license in the bundle:
|
||||||
git=git://github.com/ruflin/Elastica.git
|
|
||||||
target=elastica
|
|
||||||
|
|
||||||
#### Register autoloading
|
Resources/meta/LICENSE
|
||||||
|
|
||||||
// app/autoload.php
|
|
||||||
|
|
||||||
$loader->registerPrefixes(array(
|
|
||||||
...
|
|
||||||
'Elastica' => __DIR__.'/../vendor/elastica/lib',
|
|
||||||
));
|
|
||||||
|
|
||||||
### Install ElasticaBundle
|
|
||||||
|
|
||||||
Use the master branch with Symfony2 master only, use the 2.0 branch with Symfony2.0.x releases.
|
|
||||||
|
|
||||||
#### Download
|
|
||||||
|
|
||||||
**With submodule**
|
|
||||||
|
|
||||||
`git submodule add git://github.com/Exercise/FOSElasticaBundle vendor/bundles/FOS/ElasticaBundle`
|
|
||||||
|
|
||||||
**With clone**
|
|
||||||
|
|
||||||
`git clone git://github.com/Exercise/FOSElasticaBundle vendor/bundles/FOS/ElasticaBundle`
|
|
||||||
|
|
||||||
**With the vendors script**
|
|
||||||
|
|
||||||
Add the following lines to your deps file:
|
|
||||||
|
|
||||||
[FOSElasticaBundle]
|
|
||||||
git=git://github.com/Exercise/FOSElasticaBundle.git
|
|
||||||
target=bundles/FOS/ElasticaBundle
|
|
||||||
|
|
||||||
For the 2.0 branch for use with Symfony2.0.x releases add the following:
|
|
||||||
|
|
||||||
[FOSElasticaBundle]
|
|
||||||
git=git://github.com/Exercise/FOSElasticaBundle.git
|
|
||||||
target=bundles/FOS/ElasticaBundle
|
|
||||||
version=origin/2.0
|
|
||||||
|
|
||||||
Run the vendors script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ php bin/vendors install
|
|
||||||
```
|
|
||||||
#### Register autoloading
|
|
||||||
|
|
||||||
// app/autoload.php
|
|
||||||
|
|
||||||
$loader->registerNamespaces(array(
|
|
||||||
...
|
|
||||||
'FOS' => __DIR__.'/../vendor/bundles',
|
|
||||||
));
|
|
||||||
|
|
||||||
#### Register the bundle
|
|
||||||
|
|
||||||
// app/AppKernel.php
|
|
||||||
|
|
||||||
public function registerBundles()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
// ...
|
|
||||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
|
||||||
// ...
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
### Basic configuration
|
|
||||||
|
|
||||||
#### Declare a client
|
|
||||||
|
|
||||||
Elasticsearch client is comparable to a database connection.
|
|
||||||
Most of the time, you will need only one.
|
|
||||||
|
|
||||||
#app/config/config.yml
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
|
|
||||||
#### Declare an index
|
|
||||||
|
|
||||||
Elasticsearch index is comparable to Doctrine entity manager.
|
|
||||||
Most of the time, you will need only one.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
|
|
||||||
Here we created a "website" index, that uses our "default" client.
|
|
||||||
|
|
||||||
Our index is now available as a service: `fos_elastica.index.website`. It is an instance of `Elastica_Index`.
|
|
||||||
|
|
||||||
If you need to have different index name from the service name, for example,
|
|
||||||
in order to have different indexes for different environments then you can
|
|
||||||
use the ```index_name``` key to change the index name. The service name will
|
|
||||||
remain the same across the environments:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
index_name: website_qa
|
|
||||||
|
|
||||||
The service id will be `fos_elastica.index.website` but the underlying index name is website_qa.
|
|
||||||
|
|
||||||
#### Declare a type
|
|
||||||
|
|
||||||
Elasticsearch type is comparable to Doctrine entity repository.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
username: { boost: 5 }
|
|
||||||
firstName: { boost: 3 }
|
|
||||||
lastName: { boost: 3 }
|
|
||||||
aboutMe: ~
|
|
||||||
|
|
||||||
Our type is now available as a service: `fos_elastica.index.website.user`. It is an instance of `Elastica_Type`.
|
|
||||||
|
|
||||||
### Declaring parent field
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
comment:
|
|
||||||
mappings:
|
|
||||||
post: {_parent: { type: "post", identifier: "id" } }
|
|
||||||
date: { boost: 5 }
|
|
||||||
content: ~
|
|
||||||
|
|
||||||
### Declaring `nested` or `object`
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
post:
|
|
||||||
mappings:
|
|
||||||
date: { boost: 5 }
|
|
||||||
title: { boost: 3 }
|
|
||||||
content: ~
|
|
||||||
comments:
|
|
||||||
type: "nested"
|
|
||||||
properties:
|
|
||||||
date: { boost: 5 }
|
|
||||||
content: ~
|
|
||||||
|
|
||||||
### Populate the types
|
|
||||||
|
|
||||||
php app/console fos:elastica:populate
|
|
||||||
|
|
||||||
This command deletes and creates the declared indexes and types.
|
|
||||||
It applies the configured mappings to the types.
|
|
||||||
|
|
||||||
This command needs providers to insert new documents in the elasticsearch types.
|
|
||||||
There are 2 ways to create providers.
|
|
||||||
If your elasticsearch type matches a Doctrine repository or a Propel query, go for the persistence automatic provider.
|
|
||||||
Or, for complete flexibility, go for manual provider.
|
|
||||||
|
|
||||||
#### Persistence automatic provider
|
|
||||||
|
|
||||||
If we want to index the entities from a Doctrine repository or a Propel query,
|
|
||||||
some configuration will let ElasticaBundle do it for us.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
username: { boost: 5 }
|
|
||||||
firstName: { boost: 3 }
|
|
||||||
# more mappings...
|
|
||||||
persistence:
|
|
||||||
driver: orm # orm, mongodb, propel are available
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider: ~
|
|
||||||
|
|
||||||
Three drivers are actually supported: orm, mongodb, and propel.
|
|
||||||
|
|
||||||
##### Use a custom Doctrine query builder
|
|
||||||
|
|
||||||
You can control which entities will be indexed by specifying a custom query builder method.
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider:
|
|
||||||
query_builder_method: createIsActiveQueryBuilder
|
|
||||||
|
|
||||||
Your repository must implement this method and return a Doctrine query builder.
|
|
||||||
|
|
||||||
> **Propel** doesn't support this feature yet.
|
|
||||||
|
|
||||||
##### Change the batch size
|
|
||||||
|
|
||||||
By default, ElasticaBundle will index documents by packets of 100.
|
|
||||||
You can change this value in the provider configuration.
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider:
|
|
||||||
batch_size: 100
|
|
||||||
|
|
||||||
##### Change the document identifier field
|
|
||||||
|
|
||||||
By default, ElasticaBundle will use the `id` field of your entities as the elasticsearch document identifier.
|
|
||||||
You can change this value in the persistence configuration.
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
identifier: id
|
|
||||||
|
|
||||||
#### Manual provider
|
|
||||||
|
|
||||||
Create a service with the tag "fos_elastica.provider" and attributes for the
|
|
||||||
index and type for which the service will provide.
|
|
||||||
|
|
||||||
<service id="acme.search_provider.user" class="Acme\UserBundle\Search\UserProvider">
|
|
||||||
<tag name="fos_elastica.provider" index="website" type="user" />
|
|
||||||
<argument type="service" id="fos_elastica.index.website.user" />
|
|
||||||
</service>
|
|
||||||
|
|
||||||
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Acme\UserBundle\Provider;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
|
||||||
use Elastica_Type;
|
|
||||||
|
|
||||||
class UserProvider implements ProviderInterface
|
|
||||||
{
|
|
||||||
protected $userType;
|
|
||||||
|
|
||||||
public function __construct(Elastica_Type $userType)
|
|
||||||
{
|
|
||||||
$this->userType = $userType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert the repository objects in the type index
|
|
||||||
*
|
|
||||||
* @param Closure $loggerClosure
|
|
||||||
*/
|
|
||||||
public function populate(Closure $loggerClosure = null)
|
|
||||||
{
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$loggerClosure('Indexing users');
|
|
||||||
}
|
|
||||||
|
|
||||||
$document = new \Elastica_Document();
|
|
||||||
$document->setData(array('username' => 'Bob'));
|
|
||||||
$this->userType->addDocuments(array($document));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
|
|
||||||
|
|
||||||
### Search
|
|
||||||
|
|
||||||
You can just use the index and type Elastica objects, provided as services, to perform searches.
|
|
||||||
|
|
||||||
/** var Elastica_Type */
|
|
||||||
$userType = $this->container->get('fos_elastica.index.website.user');
|
|
||||||
|
|
||||||
/** var Elastica_ResultSet */
|
|
||||||
$resultSet = $userType->search('bob');
|
|
||||||
|
|
||||||
#### Doctrine/Propel finder
|
|
||||||
|
|
||||||
If your elasticsearch type is bound to a Doctrine entity repository or a Propel query,
|
|
||||||
you can get your entities instead of Elastica results when you perform a search.
|
|
||||||
Declare that you want a Doctrine/Propel finder in your configuration:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
# your mappings
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider: ~
|
|
||||||
finder: ~
|
|
||||||
|
|
||||||
You can now use the `fos_elastica.finder.website.user` service:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Finder\TransformedFinder */
|
|
||||||
$finder = $container->get('fos_elastica.finder.website.user');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $finder->find('bob');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User limited to 10 results */
|
|
||||||
$users = $finder->find('bob', 10);
|
|
||||||
|
|
||||||
You can even get paginated results!
|
|
||||||
|
|
||||||
Pagerfanta:
|
|
||||||
|
|
||||||
/** var Pagerfanta\Pagerfanta */
|
|
||||||
$userPaginator = $finder->findPaginated('bob');
|
|
||||||
|
|
||||||
Knp paginator:
|
|
||||||
|
|
||||||
$paginator = $this->get('knp_paginator');
|
|
||||||
$userPaginator = $paginator->paginate($finder->createPaginatorAdapter('bob'));
|
|
||||||
|
|
||||||
You can also get both the Elastica results and the entities together from the finder.
|
|
||||||
You can then access the score, highlights etc. from the Elastica_Result whilst
|
|
||||||
still also getting the entity.
|
|
||||||
|
|
||||||
/** var array of FOS\ElasticaBundle\HybridResult */
|
|
||||||
$hybridResults = $finder->findHybrid('bob');
|
|
||||||
foreach ($hybridResults as $hybridResult) {
|
|
||||||
|
|
||||||
/** var Acme\UserBundle\Entity\User */
|
|
||||||
$user = $hybridResult->getTransformed();
|
|
||||||
|
|
||||||
/** var Elastica_Result */
|
|
||||||
$result = $hybridResult->getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Index wide finder
|
|
||||||
|
|
||||||
You can also define a finder that will work on the entire index. Adjust your index
|
|
||||||
configuration as per below:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
finder: ~
|
|
||||||
|
|
||||||
You can now use the index wide finder service `fos_elastica.finder.website`:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Finder\MappedFinder */
|
|
||||||
$finder = $container->get('fos_elastica.finder.website');
|
|
||||||
|
|
||||||
// Returns a mixed array of any objects mapped
|
|
||||||
$results = $finder->find('bob');
|
|
||||||
|
|
||||||
#### Repositories
|
|
||||||
|
|
||||||
As well as using the finder service for a particular Doctrine/Propel entity you
|
|
||||||
can use a manager service for each driver and get a repository for an entity to search
|
|
||||||
against. This allows you to use the same service rather than the particular finder. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
|
||||||
$repositoryManager = $container->get('fos_elastica.manager.orm');
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $repository->find('bob');
|
|
||||||
|
|
||||||
You can also specify the full name of the entity instead of the shortcut syntax:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('Application\UserBundle\Entity\User');
|
|
||||||
|
|
||||||
> The **2.0** branch doesn't support using `UserBundle:User` style syntax and you must use the full name of the entity. .
|
|
||||||
|
|
||||||
##### Default Manager
|
|
||||||
|
|
||||||
If you are only using one driver then its manager service is automatically aliased
|
|
||||||
to `fos_elastica.manager`. So the above example could be simplified to:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
|
||||||
$repositoryManager = $container->get('fos_elastica.manager');
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $repository->find('bob');
|
|
||||||
|
|
||||||
If you use multiple drivers then you can choose which one is aliased to `fos_elastica.manager`
|
|
||||||
using the `default_manager` parameter:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
default_manager: mongodb #defauults to orm
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
#--
|
|
||||||
|
|
||||||
##### Custom Repositories
|
|
||||||
|
|
||||||
As well as the default repository you can create a custom repository for an entity and add
|
|
||||||
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
|
||||||
access to the finder:
|
|
||||||
|
|
||||||
```
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Acme\ElasticaBundle\SearchRepository;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Repository;
|
|
||||||
|
|
||||||
class UserRepository extends Repository
|
|
||||||
{
|
|
||||||
public function findWithCustomQuery($searchText)
|
|
||||||
{
|
|
||||||
// build $query with Elastica objects
|
|
||||||
$this->find($query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To use the custom repository specify it in the mapping for the entity:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
# your mappings
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider: ~
|
|
||||||
finder: ~
|
|
||||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
|
||||||
|
|
||||||
Then the custom queries will be available when using the repository returned from the manager:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
|
||||||
$repositoryManager = $container->get('fos_elastica.manager');
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $repository->findWithCustomQuery('bob');
|
|
||||||
|
|
||||||
Alternatively you can specify the custom repository using an annotation in the entity:
|
|
||||||
|
|
||||||
```
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Application\UserBundle\Entity;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Configuration\Search;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
|
||||||
*/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
|
|
||||||
//---
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Realtime, selective index update
|
|
||||||
|
|
||||||
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically
|
|
||||||
when an object is added, updated or removed. It uses Doctrine lifecycle events.
|
|
||||||
Declare that you want to update the index in real time:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
# your mappings
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
listener: # by default, listens to "insert", "update" and "delete"
|
|
||||||
|
|
||||||
Now the index is automatically updated each time the state of the bound Doctrine repository changes.
|
|
||||||
No need to repopulate the whole "user" index when a new `User` is created.
|
|
||||||
|
|
||||||
You can also choose to only listen for some of the events:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
insert: true
|
|
||||||
update: false
|
|
||||||
delete: true
|
|
||||||
|
|
||||||
> **Propel** doesn't support this feature yet.
|
|
||||||
|
|
||||||
### Checking an entity method for listener
|
|
||||||
|
|
||||||
If you use listeners to update your index, you may need to validate your
|
|
||||||
entities before you index them (e.g. only index "public" entities). Typically,
|
|
||||||
you'll want the listener to be consistent with the provider's query criteria.
|
|
||||||
This may be achieved by using the `is_indexable_callback` config parameter:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
is_indexable_callback: "isPublic"
|
|
||||||
|
|
||||||
If `is_indexable_callback` is a string and the entity has a method with the
|
|
||||||
specified name, the listener will only index entities for which the method
|
|
||||||
returns `true`. Additionally, you may provide a service and method name pair:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
is_indexable_callback: [ "%custom_service_id%", "isIndexable" ]
|
|
||||||
|
|
||||||
In this case, the callback will be the `isIndexable()` method on the specified
|
|
||||||
service and the object being considered for indexing will be passed as the only
|
|
||||||
argument. This allows you to do more complex validation (e.g. ACL checks).
|
|
||||||
|
|
||||||
As you might expect, new entities will only be indexed if the callback returns
|
|
||||||
`true`. Additionally, modified entities will be updated or removed from the
|
|
||||||
index depending on whether the callback returns `true` or `false`, respectively.
|
|
||||||
The delete listener disregards the callback.
|
|
||||||
|
|
||||||
> **Propel** doesn't support this feature yet.
|
|
||||||
|
|
||||||
### 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:
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use FOS\ElasticaBundle\Finder\PaginatedFinderInterface;
|
||||||
/**
|
/**
|
||||||
* @author Richard Miller <info@limethinking.co.uk>
|
* @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.
|
* in the finder.
|
||||||
*/
|
*/
|
||||||
class Repository
|
class Repository
|
||||||
|
|
@ -19,23 +19,49 @@ class Repository
|
||||||
$this->finder = $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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPaginatorAdapter($query)
|
/**
|
||||||
|
* @param string $query
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return Paginator\PaginatorAdapterInterface
|
||||||
|
*/
|
||||||
|
public function createPaginatorAdapter($query, $options = array())
|
||||||
{
|
{
|
||||||
return $this->finder->createPaginatorAdapter($query);
|
return $this->finder->createPaginatorAdapter($query, $options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
Resetter.php
97
Resetter.php
|
|
@ -2,100 +2,11 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle;
|
namespace FOS\ElasticaBundle;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Index\Resetter as BaseResetter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes and recreates indexes
|
* @deprecated Use \FOS\ElasticaBundle\Index\Resetter
|
||||||
*/
|
*/
|
||||||
class Resetter
|
class Resetter extends BaseResetter
|
||||||
{
|
{
|
||||||
protected $indexConfigsByName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param array $indexConfigsByName
|
|
||||||
*/
|
|
||||||
public function __construct(array $indexConfigsByName)
|
|
||||||
{
|
|
||||||
$this->indexConfigsByName = $indexConfigsByName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes and recreates all indexes
|
|
||||||
*/
|
|
||||||
public function resetAllIndexes()
|
|
||||||
{
|
|
||||||
foreach ($this->indexConfigsByName as $indexConfig) {
|
|
||||||
$indexConfig['index']->create($indexConfig['config'], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes and recreates the named index
|
|
||||||
*
|
|
||||||
* @param string $indexName
|
|
||||||
* @throws \InvalidArgumentException if no index exists for the given name
|
|
||||||
*/
|
|
||||||
public function resetIndex($indexName)
|
|
||||||
{
|
|
||||||
$indexConfig = $this->getIndexConfig($indexName);
|
|
||||||
$indexConfig['index']->create($indexConfig['config'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes and recreates a mapping type for the named index
|
|
||||||
*
|
|
||||||
* @param string $indexName
|
|
||||||
* @param string $typeName
|
|
||||||
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
|
|
||||||
*/
|
|
||||||
public function resetIndexType($indexName, $typeName)
|
|
||||||
{
|
|
||||||
$indexConfig = $this->getIndexConfig($indexName);
|
|
||||||
|
|
||||||
if (!isset($indexConfig['config']['mappings'][$typeName]['properties'])) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('The mapping for index "%s" and type "%s" does not exist.', $indexName, $typeName));
|
|
||||||
}
|
|
||||||
|
|
||||||
$type = $indexConfig['index']->getType($typeName);
|
|
||||||
$type->delete();
|
|
||||||
$mapping = $this->createMapping($indexConfig['config']['mappings'][$typeName]);
|
|
||||||
$type->setMapping($mapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create type mapping object
|
|
||||||
*
|
|
||||||
* @param array $indexConfig
|
|
||||||
* @return \Elastica_Type_Mapping
|
|
||||||
*/
|
|
||||||
protected function createMapping($indexConfig)
|
|
||||||
{
|
|
||||||
$mapping = \Elastica_Type_Mapping::create($indexConfig['properties']);
|
|
||||||
|
|
||||||
foreach($indexConfig['properties'] as $type) {
|
|
||||||
if (!empty($type['_parent']) && $type['_parent'] !== '~') {
|
|
||||||
$mapping->setParam('_parent', array('type' => $type['_parent']['type']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an index config by its name
|
|
||||||
*
|
|
||||||
* @param string $indexName Index name
|
|
||||||
*
|
|
||||||
* @param $indexName
|
|
||||||
* @return array
|
|
||||||
* @throws \InvalidArgumentException if no index config exists for the given name
|
|
||||||
*/
|
|
||||||
protected function getIndexConfig($indexName)
|
|
||||||
{
|
|
||||||
if (!isset($this->indexConfigsByName[$indexName])) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('The configuration for index "%s" does not exist.', $indexName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->indexConfigsByName[$indexName];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,51 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<container xmlns="http://symfony.com/schema/dic/services"
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="fos_elastica.client.class">FOS\ElasticaBundle\Client</parameter>
|
<parameter key="fos_elastica.client.class">FOS\ElasticaBundle\Elastica\Client</parameter>
|
||||||
<parameter key="fos_elastica.index.class">Elastica_Index</parameter>
|
|
||||||
<parameter key="fos_elastica.type.class">Elastica_Type</parameter>
|
|
||||||
<parameter key="fos_elastica.logger.class">FOS\ElasticaBundle\Logger\ElasticaLogger</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.data_collector.class">FOS\ElasticaBundle\DataCollector\ElasticaDataCollector</parameter>
|
||||||
<parameter key="fos_elastica.manager.class">FOS\ElasticaBundle\Manager\RepositoryManager</parameter>
|
<parameter key="fos_elastica.mapping_builder.class">FOS\ElasticaBundle\Index\MappingBuilder</parameter>
|
||||||
<parameter key="fos_elastica.elastica_to_model_transformer.collection.class">FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection</parameter>
|
<parameter key="fos_elastica.property_accessor.class">Symfony\Component\PropertyAccess\PropertyAccessor</parameter>
|
||||||
<parameter key="fos_elastica.provider_registry.class">FOS\ElasticaBundle\Provider\ProviderRegistry</parameter>
|
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<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%">
|
<service id="fos_elastica.logger" class="%fos_elastica.logger.class%">
|
||||||
<argument type="service" id="logger" on-invalid="null" />
|
<argument type="service" id="logger" on-invalid="null" />
|
||||||
<argument>%kernel.debug%</argument>
|
<argument>%kernel.debug%</argument>
|
||||||
<tag name="monolog.logger" channel="elastica" />
|
<tag name="monolog.logger" channel="elastica" />
|
||||||
</service>
|
</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%">
|
<service id="fos_elastica.mapping_builder" class="%fos_elastica.mapping_builder.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">
|
<service id="fos_elastica.property_accessor" class="%fos_elastica.property_accessor.class%" />
|
||||||
<argument /> <!-- indexes -->
|
|
||||||
<argument /> <!-- default index -->
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service id="fos_elastica.resetter" class="FOS\ElasticaBundle\Resetter">
|
|
||||||
<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 -->
|
|
||||||
<call method="setPropertyAccessor">
|
|
||||||
<argument type="service" id="property_accessor" on-invalid="null" />
|
|
||||||
</call>
|
|
||||||
</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.provider_registry" class="%fos_elastica.provider_registry.class%">
|
|
||||||
<call method="setContainer">
|
|
||||||
<argument type="service" id="service_container" />
|
|
||||||
</call>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service id="fos_elastica.paginator.subscriber" class="FOS\ElasticaBundle\Subscriber\PaginateElasticaQuerySubscriber">
|
|
||||||
<tag name="knp_paginator.subscriber" />
|
|
||||||
</service>
|
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</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"
|
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">
|
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>
|
<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="true" abstract="true">
|
<service id="fos_elastica.provider.prototype.mongodb" class="%fos_elastica.provider.prototype.mongodb.class%" public="true" abstract="true">
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
<argument type="service" id="doctrine_mongodb" />
|
<argument type="service" id="doctrine_mongodb" /> <!-- manager registry -->
|
||||||
|
<argument type="service" id="fos_elastica.slice_fetcher.mongodb" /> <!-- slice fetcher -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<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 /> <!-- object persister -->
|
||||||
<argument /> <!-- model -->
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument type="collection" /> <!-- events -->
|
<argument type="collection" /> <!-- configuration -->
|
||||||
<argument/> <!-- identifier -->
|
<argument>null</argument> <!-- logger -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer" public="false">
|
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="%fos_elastica.elastica_to_model_transformer.prototype.mongodb.class%" public="false" abstract="true">
|
||||||
<argument type="service" id="doctrine_mongodb" />
|
<argument type="service" id="doctrine_mongodb" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
<call method="setPropertyAccessor">
|
||||||
|
<argument type="service" id="fos_elastica.property_accessor" />
|
||||||
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.manager.mongodb" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
|
<service id="fos_elastica.manager.mongodb" class="%fos_elastica.manager.mongodb.class%">
|
||||||
<argument type="service" id="doctrine_mongodb"/>
|
<argument type="service" id="doctrine_mongodb"/>
|
||||||
<argument type="service" id="annotation_reader"/>
|
<argument type="service" id="annotation_reader"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</container>
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,49 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<container xmlns="http://symfony.com/schema/dic/services"
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
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>
|
<services>
|
||||||
|
<service id="fos_elastica.slice_fetcher.orm" class="%fos_elastica.slice_fetcher.orm.class%">
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.provider.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\Provider" public="true" abstract="true">
|
<service id="fos_elastica.provider.prototype.orm" class="%fos_elastica.provider.prototype.orm.class%" public="true" abstract="true">
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
<argument type="service" id="doctrine" />
|
<argument type="service" id="doctrine" /> <!-- manager registry -->
|
||||||
|
<argument type="service" id="fos_elastica.slice_fetcher.orm" /> <!-- slice fetcher -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<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 /> <!-- object persister -->
|
||||||
<argument /> <!-- model -->
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument type="collection" /> <!-- events -->
|
<argument type="collection" /> <!-- configuration -->
|
||||||
<argument/> <!-- identifier -->
|
<argument>null</argument> <!-- logger -->
|
||||||
<argument /> <!-- check method -->
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer" public="false">
|
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="%fos_elastica.elastica_to_model_transformer.prototype.orm.class%" public="false" abstract="true">
|
||||||
<argument type="service" id="doctrine" />
|
<argument type="service" id="doctrine" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
<call method="setPropertyAccessor">
|
||||||
|
<argument type="service" id="fos_elastica.property_accessor" />
|
||||||
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.manager.orm" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
|
<service id="fos_elastica.manager.orm" class="%fos_elastica.manager.orm.class%">
|
||||||
<argument type="service" id="doctrine"/>
|
<argument type="service" id="doctrine" />
|
||||||
<argument type="service" id="annotation_reader"/>
|
<argument type="service" id="annotation_reader" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</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,9 +4,9 @@
|
||||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
|
||||||
<service id="fos_elastica.provider.prototype.propel" class="FOS\ElasticaBundle\Propel\Provider" public="true" abstract="true">
|
<service id="fos_elastica.provider.prototype.propel" class="FOS\ElasticaBundle\Propel\Provider" public="true" abstract="true">
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
</service>
|
</service>
|
||||||
|
|
@ -14,12 +14,13 @@
|
||||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.propel" class="FOS\ElasticaBundle\Propel\ElasticaToModelTransformer" public="false">
|
<service id="fos_elastica.elastica_to_model_transformer.prototype.propel" class="FOS\ElasticaBundle\Propel\ElasticaToModelTransformer" public="false">
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
<call method="setPropertyAccessor">
|
||||||
|
<argument type="service" id="fos_elastica.property_accessor" />
|
||||||
|
</call>
|
||||||
</service>
|
</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"/>
|
<argument type="service" id="annotation_reader"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</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>
|
||||||
45
Resources/doc/cookbook/aliased-indexes.md
Normal file
45
Resources/doc/cookbook/aliased-indexes.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
Aliased Indexes
|
||||||
|
===============
|
||||||
|
|
||||||
|
You can set up FOSElasticaBundle to use aliases for indexes which allows you to run an
|
||||||
|
index population without resetting the index currently being used by the application.
|
||||||
|
|
||||||
|
> *Note*: When you're using an alias, resetting an individual type will still cause a
|
||||||
|
> reset for that type.
|
||||||
|
|
||||||
|
To configure FOSElasticaBundle to use aliases for an index, set the use_alias option to
|
||||||
|
true.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
use_alias: true
|
||||||
|
```
|
||||||
|
|
||||||
|
The process for setting up aliases on an existing application is slightly more complicated
|
||||||
|
because the bundle is not able to set an alias as the same name as an index. You have some
|
||||||
|
options on how to handle this:
|
||||||
|
|
||||||
|
1) Delete the index from Elasticsearch. This option will make searching unavailable in your
|
||||||
|
application until a population has completed itself, and an alias is created.
|
||||||
|
|
||||||
|
2) Change the index_name parameter for your index to something new, and manually alias the
|
||||||
|
current index to the new index_name, which will then be replaced when you run a repopulate.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
use_alias: true
|
||||||
|
index_name: website_prod
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl -XPOST 'http://localhost:9200/_aliases' -d '
|
||||||
|
{
|
||||||
|
"actions" : [
|
||||||
|
{ "add" : { "index" : "website", "alias" : "website_prod" } }
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
33
Resources/doc/cookbook/custom-properties.md
Normal file
33
Resources/doc/cookbook/custom-properties.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
##### Custom Properties
|
||||||
|
|
||||||
|
Since FOSElasticaBundle 3.1.0, we now dispatch an event for each transformation of an
|
||||||
|
object into an Elastica document which allows you to set custom properties on the Elastica
|
||||||
|
document for indexing.
|
||||||
|
|
||||||
|
Set up an event listener or subscriber for
|
||||||
|
`FOS\ElasticaBundle\Event\TransformEvent::POST_TRANSFORM` to be able to inject your own
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
```php
|
||||||
|
class CustomPropertyListener implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
private $anotherService;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
public function addCustomProperty(TransformEvent $event)
|
||||||
|
{
|
||||||
|
$document = $event->getDocument();
|
||||||
|
$custom = $this->anotherService->calculateCustom($event->getObject());
|
||||||
|
|
||||||
|
$document->set('custom', $custom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
TransformEvent::POST_TRANSFORM => 'addCustomProperty',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
76
Resources/doc/cookbook/custom-repositories.md
Normal file
76
Resources/doc/cookbook/custom-repositories.md
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
##### Custom Repositories
|
||||||
|
|
||||||
|
As well as the default repository you can create a custom repository for an entity and add
|
||||||
|
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||||
|
access to the finder:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Acme\ElasticaBundle\SearchRepository;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Repository;
|
||||||
|
|
||||||
|
class UserRepository extends Repository
|
||||||
|
{
|
||||||
|
public function findWithCustomQuery($searchText)
|
||||||
|
{
|
||||||
|
// build $query with Elastica objects
|
||||||
|
$this->find($query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To use the custom repository specify it in the mapping for the entity:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default: { host: localhost, port: 9200 }
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
client: default
|
||||||
|
types:
|
||||||
|
user:
|
||||||
|
mappings:
|
||||||
|
# your mappings
|
||||||
|
persistence:
|
||||||
|
driver: orm
|
||||||
|
model: Application\UserBundle\Entity\User
|
||||||
|
provider: ~
|
||||||
|
finder: ~
|
||||||
|
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the custom queries will be available when using the repository returned from the manager:
|
||||||
|
|
||||||
|
```php
|
||||||
|
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||||
|
$repositoryManager = $container->get('fos_elastica.manager');
|
||||||
|
|
||||||
|
/** var FOS\ElasticaBundle\Repository */
|
||||||
|
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||||
|
|
||||||
|
/** var array of Acme\UserBundle\Entity\User */
|
||||||
|
$users = $repository->findWithCustomQuery('bob');
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively you can specify the custom repository using an annotation in the entity:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Application\UserBundle\Entity;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Annotation\Search;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
||||||
|
*/
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
18
Resources/doc/cookbook/elastica-client-http-headers.md
Normal file
18
Resources/doc/cookbook/elastica-client-http-headers.md
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
Setting HTTP Headers on the Elastica Client
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
It may be necessary to set HTTP headers on the Elastica client, for example an
|
||||||
|
Authorization header.
|
||||||
|
|
||||||
|
They can be set using the headers configuration key:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# app/config/config.yml
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default:
|
||||||
|
host: example.com
|
||||||
|
port: 80
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic jdumrGK7rY9TMuQOPng7GZycmxyMHNoir=="
|
||||||
|
```
|
||||||
35
Resources/doc/cookbook/logging.md
Normal file
35
Resources/doc/cookbook/logging.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
Logging and its performance considerations
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
By default, FOSElasticaBundle sets a logger against each Elastica client configured and
|
||||||
|
logs all information sent to and received from Elasticsearch. This can lead to large
|
||||||
|
memory usage during population or reindexing of an index.
|
||||||
|
|
||||||
|
By default FOSElasticaBundle will only enable a logger when debug mode is enabled, meaning
|
||||||
|
in a production environment there wont be a logger enabled. To enable a logger anyway, you
|
||||||
|
can set the logger property of a client configuration to true or a service id of a logging
|
||||||
|
service you wish to use.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# app/config/config.yml
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default:
|
||||||
|
host: example.com
|
||||||
|
logger: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom Logger Service
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
It is also possible to specify a custom logger instance to be injected into each client by
|
||||||
|
specifying the service id of the logger you wish to use.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# app/config/config.yml
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default:
|
||||||
|
host: example.com
|
||||||
|
logger: 'acme.custom.logger'
|
||||||
|
```
|
||||||
56
Resources/doc/cookbook/manual-provider.md
Normal file
56
Resources/doc/cookbook/manual-provider.md
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
Manual provider
|
||||||
|
===============
|
||||||
|
|
||||||
|
Create a service with the tag "fos_elastica.provider" and attributes for the
|
||||||
|
index and type for which the service will provide.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# app/config/config.yml
|
||||||
|
services:
|
||||||
|
acme.search_provider.user:
|
||||||
|
class: Acme\UserBundle\Provider\UserProvider
|
||||||
|
arguments:
|
||||||
|
- @fos_elastica.index.website.user
|
||||||
|
tags:
|
||||||
|
- { name: fos_elastica.provider, index: website, type: user }
|
||||||
|
```
|
||||||
|
|
||||||
|
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
|
||||||
|
namespace Acme\UserBundle\Provider;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||||
|
use Elastica\Type;
|
||||||
|
use Elastica\Document;
|
||||||
|
|
||||||
|
class UserProvider implements ProviderInterface
|
||||||
|
{
|
||||||
|
protected $userType;
|
||||||
|
|
||||||
|
public function __construct(Type $userType)
|
||||||
|
{
|
||||||
|
$this->userType = $userType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the repository objects in the type index
|
||||||
|
*
|
||||||
|
* @param \Closure $loggerClosure
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||||
|
{
|
||||||
|
if ($loggerClosure) {
|
||||||
|
$loggerClosure('Indexing users');
|
||||||
|
}
|
||||||
|
|
||||||
|
$document = new Document();
|
||||||
|
$document->setData(array('username' => 'Bob'));
|
||||||
|
$this->userType->addDocuments(array($document));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
|
||||||
21
Resources/doc/cookbook/multiple-connections.md
Normal file
21
Resources/doc/cookbook/multiple-connections.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
Multiple Connections
|
||||||
|
====================
|
||||||
|
|
||||||
|
You can define multiple endpoints for an Elastica client by specifying them as
|
||||||
|
multiple connections in the client configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default:
|
||||||
|
connections:
|
||||||
|
- url: http://es1.example.net:9200
|
||||||
|
- url: http://es2.example.net:9200
|
||||||
|
connection_strategy: RoundRobin
|
||||||
|
```
|
||||||
|
|
||||||
|
Elastica allows for definition of different connection strategies and by default
|
||||||
|
supports `RoundRobin` and `Simple`. You can see definitions for these strategies
|
||||||
|
in the `Elastica\Connection\Strategy` namespace.
|
||||||
|
|
||||||
|
For more information on Elastica clustering see http://elastica.io/getting-started/installation.html#section-connect-cluster
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue