Compare commits
961 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78c1a37f2e | ||
|
|
705f4d6f47 | ||
|
|
182a3a6f0b | ||
|
|
5852a5fe14 | ||
|
|
84170f7c51 | ||
|
|
9f86984925 | ||
|
|
92ea59ff3c | ||
|
|
8deba612bb | ||
|
|
74a13a3489 | ||
|
|
14fd0d9189 | ||
|
|
dfe29820e1 | ||
|
|
999f6ddd9b | ||
|
|
a70fd7936a | ||
|
|
62ef1d0727 | ||
|
|
54270ad887 | ||
|
|
008d1ff1b0 | ||
|
|
088b744991 | ||
|
|
94cf59a53e | ||
|
|
69bb24b3d9 | ||
|
|
32407c92f1 | ||
|
|
d8ce4ae787 | ||
|
|
dac9f83a55 | ||
|
|
9fb211e859 | ||
|
|
b5fd0a2738 | ||
|
|
30cd472e23 | ||
|
|
ed613dee94 | ||
|
|
801f08b936 | ||
|
|
4d8747f9ed | ||
|
|
ab878ab69c | ||
|
|
f30a3926e0 | ||
|
|
e7c98d66e4 | ||
|
|
82ec258f6d | ||
|
|
37c2d8433b | ||
|
|
0494128e15 | ||
|
|
9f3d192dff | ||
|
|
1fdd62d3df | ||
|
|
c2cbdcaa38 | ||
|
|
37aabbe860 | ||
|
|
010a977aac | ||
|
|
b6bd6e55e2 | ||
|
|
ce6dcd27cb | ||
|
|
a67f6de06b | ||
|
|
d1b77ee3fd | ||
|
|
affeb65d00 | ||
|
|
37816b77a1 | ||
|
|
ba29a45cc6 | ||
|
|
efc440a8f9 | ||
|
|
805383f1ec | ||
|
|
a3f6982a61 | ||
|
|
3a94b2a805 | ||
|
|
1e313f990c | ||
|
|
d6f5ac743d | ||
|
|
85c7b84d13 | ||
|
|
900592e302 | ||
|
|
8102c42888 | ||
|
|
9d34fc0231 | ||
|
|
aa08f4b58c | ||
|
|
4b591fc910 | ||
|
|
79bd0aabca | ||
|
|
6bf528ed5b | ||
|
|
318e440d0d | ||
|
|
3757c6d915 | ||
|
|
a153eb8116 | ||
|
|
8879e552ef | ||
|
|
386f06da8c | ||
|
|
ba00ba27a5 | ||
|
|
ad2ee531a3 | ||
|
|
60c922c0a5 | ||
|
|
b2068fd8bb | ||
|
|
595efb926f | ||
|
|
f6e5ff75ce | ||
|
|
216674cd3d | ||
|
|
f093322a09 | ||
|
|
26efe88510 | ||
|
|
da520112e7 | ||
|
|
7466db5c4d | ||
|
|
89e8538d91 | ||
|
|
a9e1f5d7d0 | ||
|
|
dc61982ced | ||
|
|
002b91d5e6 | ||
|
|
3196b66442 | ||
|
|
1a256a6033 | ||
|
|
20b4d5d93b | ||
|
|
c35545a97a | ||
|
|
15831490d9 | ||
|
|
79387a694a | ||
|
|
993bb038d9 | ||
|
|
5d31f9fc1a | ||
|
|
be43faf059 | ||
|
|
bcaf0837b4 | ||
|
|
319872d5bc | ||
|
|
6e333bb563 | ||
|
|
1ca1469126 | ||
|
|
f3d8a7bb71 | ||
|
|
99a113e220 | ||
|
|
c1acd78060 | ||
|
|
62dc2a9cd3 | ||
|
|
5b5425c402 | ||
|
|
34671dcd5d | ||
|
|
1170f342dd | ||
|
|
27ed7bdfb7 | ||
|
|
517bb98129 | ||
|
|
b1cfb5490a | ||
|
|
f499079bf7 | ||
|
|
0d7dc9357a | ||
|
|
82c76dd66d | ||
|
|
2021c6d07f | ||
|
|
3d4a38a10a | ||
|
|
8fd4946959 | ||
|
|
08ce1e41b3 | ||
|
|
a2bc59dcfa | ||
|
|
d0fc0ed528 | ||
|
|
57464ac592 | ||
|
|
0f2a44179d | ||
|
|
7721c83ccf | ||
|
|
c374e7593b | ||
|
|
3e5f16d146 | ||
|
|
be95971e58 | ||
|
|
2f19f62e3f | ||
|
|
668e46d57f | ||
|
|
df09c4085d | ||
|
|
5033018119 | ||
|
|
ccffffc912 | ||
|
|
9be707ea06 | ||
|
|
2b4c0f8168 | ||
|
|
f64d085b0f | ||
|
|
5916642b2b | ||
|
|
07b63150d1 | ||
|
|
e842068f3e | ||
|
|
9d51f32fa1 | ||
|
|
765b9d1acf | ||
|
|
c54f70350e | ||
|
|
c7a87ac776 | ||
|
|
bdecd98b40 | ||
|
|
189d347f50 | ||
|
|
114630f769 | ||
|
|
6c25763748 | ||
|
|
fdb354b2db | ||
|
|
45cb68115b | ||
|
|
dd890231ee | ||
|
|
1764b898bd | ||
|
|
b0df70f279 | ||
|
|
85560e17f8 | ||
|
|
57fb96a1b7 | ||
|
|
47d20761c5 | ||
|
|
50a8963ba2 | ||
|
|
2856631638 | ||
|
|
1299e5e5ff | ||
|
|
cf3d8f2ad3 | ||
|
|
15edf63ffd | ||
|
|
c26ae0835d | ||
|
|
fe73144075 | ||
|
|
2dc5e92b0c | ||
|
|
286493463b | ||
|
|
aec51c03f4 | ||
|
|
7195f77945 | ||
|
|
b59ccb4087 | ||
|
|
9178df179d | ||
|
|
8cefa7fc9e | ||
|
|
45599a4f51 | ||
|
|
4e9f041ce9 | ||
|
|
f24ee77730 | ||
|
|
9928652b79 | ||
|
|
56d16a4cee | ||
|
|
a500638891 | ||
|
|
8bce5ae8ed | ||
|
|
2dc924faf4 | ||
|
|
1f395c4e41 | ||
|
|
bfac6bbe3a | ||
|
|
def4ce3e6b | ||
|
|
504a358162 | ||
|
|
5898cfb3bc | ||
|
|
cf86245526 | ||
|
|
8316e7798e | ||
|
|
5f53732719 | ||
|
|
ee0c3dbede | ||
|
|
ebc01b3f54 | ||
|
|
f1fff71577 | ||
|
|
e0c632f0d3 | ||
|
|
f3cff7a52a | ||
|
|
fc4a34b87f | ||
|
|
76cd30d79c | ||
|
|
6c1489e1ef | ||
|
|
d9b103c350 | ||
|
|
43994ff19a | ||
|
|
95d0145a0b | ||
|
|
7e111a332e | ||
|
|
800d02839c | ||
|
|
19613b6e01 | ||
|
|
ec5590fd56 | ||
|
|
c1597b1c9a | ||
|
|
478efa092d | ||
|
|
ac90956f92 | ||
|
|
1da1dab013 | ||
|
|
80cadd54c4 | ||
|
|
b4ea572293 | ||
|
|
ae2ac84921 | ||
|
|
0891a9ded7 | ||
|
|
51baa3e154 | ||
|
|
1717bdac9e | ||
|
|
7f74d16a36 | ||
|
|
2cd8c0c6a0 | ||
|
|
ceed6f85c5 | ||
|
|
4ff7d05aa4 | ||
|
|
b762c1875f | ||
|
|
15f50408a9 | ||
|
|
f0946acd75 | ||
|
|
136dea00b2 | ||
|
|
3a4de68dd4 | ||
|
|
9c0f385ab2 | ||
|
|
d337cd1546 | ||
|
|
94029c7f6c | ||
|
|
aeab2851c1 | ||
|
|
126c9f8503 | ||
|
|
6e41cb94d8 | ||
|
|
3edc8049db | ||
|
|
e76dc21182 | ||
|
|
d80a2766ee | ||
|
|
2eb6f80ae8 | ||
|
|
eb168b6830 | ||
|
|
4701a7b5ab | ||
|
|
a2b1ddb054 | ||
|
|
4e0a0036e6 | ||
|
|
5651c2ee97 | ||
|
|
3660992148 | ||
|
|
602b6e3ce9 | ||
|
|
3e71b6ffc7 | ||
|
|
4efdaa28c9 | ||
|
|
8b2fd956e4 | ||
|
|
6ca5b9d4ba | ||
|
|
a8d42a62f5 | ||
|
|
a107f1f8b7 | ||
|
|
3c63209ab1 | ||
|
|
05dd57731a | ||
|
|
26be7bb1b9 | ||
|
|
2be051289f | ||
|
|
096ba46bcd | ||
|
|
07a9c5ee16 | ||
|
|
49cb195e5a | ||
|
|
7c31e98cf3 | ||
|
|
0b72761b44 | ||
|
|
8511d3714d | ||
|
|
d9e6be671a | ||
|
|
2128f2cd6c | ||
|
|
bb626c5320 | ||
|
|
25c1c56fc9 | ||
|
|
e7755b3fc5 | ||
|
|
cd4326ca69 | ||
|
|
9cb54167c4 | ||
|
|
732bdb40e0 | ||
|
|
2d58b02b13 | ||
|
|
7bf5956565 | ||
|
|
d0d922e78c | ||
|
|
6b6876186b | ||
|
|
1a30ef4247 | ||
|
|
916d83b705 | ||
|
|
6fa11f9b76 | ||
|
|
1215f0335a | ||
|
|
80773ad0af | ||
|
|
3389e84bb2 | ||
|
|
e6dfd9dd5e | ||
|
|
d63982491b | ||
|
|
fb00629345 | ||
|
|
cd7e7db809 | ||
|
|
e3189c33c7 | ||
|
|
0495d74a34 | ||
|
|
a1f3566632 | ||
|
|
78ea72a33b | ||
|
|
5c39babf69 | ||
|
|
361d293a93 | ||
|
|
049fa620c8 | ||
|
|
194f0028a4 | ||
|
|
1c62587165 | ||
|
|
696a5ad8e3 | ||
|
|
b0accb8447 | ||
|
|
2c05d4c9d5 | ||
|
|
d8e70a5ee8 | ||
|
|
30fececda4 | ||
|
|
1f0f97869f | ||
|
|
ea158554fc | ||
|
|
00dad8d0fb | ||
|
|
0312ecd2f6 | ||
|
|
ef539f48c8 | ||
|
|
1ac5086b17 | ||
|
|
2c019e5e91 | ||
|
|
a8a9ab16b6 | ||
|
|
8addd14c6d | ||
|
|
bf5ded8939 | ||
|
|
c0fb8c517b | ||
|
|
2234f0fb22 | ||
|
|
af6f300720 | ||
|
|
bcea07dff4 | ||
|
|
b0d8996576 | ||
|
|
e73372bc1a | ||
|
|
cac5ee4695 | ||
|
|
f846cd0a51 | ||
|
|
59427e29bb | ||
|
|
e8b5ba5dd6 | ||
|
|
417763782e | ||
|
|
21174924c3 | ||
|
|
b1049021c0 | ||
|
|
37e94ebcd5 | ||
|
|
081a7fc3d6 | ||
|
|
96f7e67489 | ||
|
|
40bc1861a7 | ||
|
|
57dbfd0e56 | ||
|
|
cb6210d573 | ||
|
|
53ce5808c4 | ||
|
|
3f3a6420e5 | ||
|
|
6330cee24a | ||
|
|
96f05c7c01 | ||
|
|
a259bd3e00 | ||
|
|
2e1ad71d08 | ||
|
|
646f4e1bd1 | ||
|
|
6206308fa8 | ||
|
|
da2effbfc9 | ||
|
|
31ee101d5d | ||
|
|
ad25fba0f0 | ||
|
|
b86ecb5487 | ||
|
|
5215574377 | ||
|
|
25f6dcf88d | ||
|
|
3fda07999d | ||
|
|
6f93043eab | ||
|
|
d7965adb86 | ||
|
|
9a185596ac | ||
|
|
31ebc18c12 | ||
|
|
eaa66edc93 | ||
|
|
03e7bfa1c7 | ||
|
|
431e4495af | ||
|
|
b3c501a73a | ||
|
|
c29587f66d | ||
|
|
6acfb43314 | ||
|
|
f02a2b0afc | ||
|
|
d7902c9618 | ||
|
|
565ec43398 | ||
|
|
60a419953f | ||
|
|
c64f5a66d9 | ||
|
|
9752950d82 | ||
|
|
37e8df8300 | ||
|
|
f0722561c3 | ||
|
|
c75b02814d | ||
|
|
eddc8b20bd | ||
|
|
353df3b841 | ||
|
|
a3954f3847 | ||
|
|
58d426d6aa | ||
|
|
5fb665e5eb | ||
|
|
b1e874aa87 | ||
|
|
c6ef174535 | ||
|
|
56eebda3fd | ||
|
|
a7cceebe9c | ||
|
|
8b237db382 | ||
|
|
42ca0efa5c | ||
|
|
912b9e4d45 | ||
|
|
d3cb048d2b | ||
|
|
d114073413 | ||
|
|
ab4a9a381f | ||
|
|
d99b6c52aa | ||
|
|
d76127de49 | ||
|
|
e04c694e3c | ||
|
|
b0cd0bf671 | ||
|
|
9ad38a9e7a | ||
|
|
04e6c5bce6 | ||
|
|
1e9463e466 | ||
|
|
7766475e52 | ||
|
|
5b1d063301 | ||
|
|
1cae021b47 | ||
|
|
9c2aa45b75 | ||
|
|
d78c0d45c8 | ||
|
|
4b61275814 | ||
|
|
6a15f6959e | ||
|
|
58015b4f40 | ||
|
|
c0ef0ab7c8 | ||
|
|
a7ccfbecab | ||
|
|
ca40889348 | ||
|
|
63168d68f5 | ||
|
|
1991308567 | ||
|
|
bf0f20e0d9 | ||
|
|
a0e4c9ea93 | ||
|
|
2d77d1ba27 | ||
|
|
e240e906b0 | ||
|
|
8a4c497ea7 | ||
|
|
de377f3849 | ||
|
|
ed23f7d9fc | ||
|
|
31bcb51ee4 | ||
|
|
b86cabdc1a | ||
|
|
373c4db7a4 | ||
|
|
6d3b2be339 | ||
|
|
6fd331da15 | ||
|
|
7bb90ac86a | ||
|
|
2e64a24cc7 | ||
|
|
87637dd6fe | ||
|
|
f413016ef0 | ||
|
|
6009e6e719 | ||
|
|
209272fb6c | ||
|
|
9b5f8de1f3 | ||
|
|
eaf07fcf41 | ||
|
|
18ce852146 | ||
|
|
db6715b282 | ||
|
|
f5d87d0aaa | ||
|
|
555ac53d22 | ||
|
|
413d4484b8 | ||
|
|
4b417ec15a | ||
|
|
3728f38ad7 | ||
|
|
4c1a4f9505 | ||
|
|
b66abf5a2c | ||
|
|
e362d32b3a | ||
|
|
4dac6375ed | ||
|
|
5d5c5dbced | ||
|
|
b4472e7167 | ||
|
|
84e76c1e30 | ||
|
|
c334f206ed | ||
|
|
5a88eb89f7 | ||
|
|
01e3ad7977 | ||
|
|
6671cb123f | ||
|
|
f04638348c | ||
|
|
248e177a6c | ||
|
|
60bb6480b4 | ||
|
|
7bdc3994dd | ||
|
|
2d1ab796ba | ||
|
|
3c951a0828 | ||
|
|
0c1fec027d | ||
|
|
276f0f81d9 | ||
|
|
f1f46b6983 | ||
|
|
6758a1d102 | ||
|
|
4b6ddc5289 | ||
|
|
d3aedaec03 | ||
|
|
286a9dc10e | ||
|
|
bb058bf19a | ||
|
|
05e7d3727d | ||
|
|
e065db51d3 | ||
|
|
5022eeaf4c | ||
|
|
2bafefd3f3 | ||
|
|
b2e34e1f57 | ||
|
|
aa0347cd2e | ||
|
|
a6912c9db1 | ||
|
|
962e4c8313 | ||
|
|
371a209ae3 | ||
|
|
06be43a8c1 | ||
|
|
409b13a9fb | ||
|
|
04d92f35cd | ||
|
|
7d41ffc47c | ||
|
|
bd575126a3 | ||
|
|
c163283d22 | ||
|
|
47efed8260 | ||
|
|
047dfeaafa | ||
|
|
78eaeebae8 | ||
|
|
3ceecf4e0c | ||
|
|
6db4eab95c | ||
|
|
daace7283c | ||
|
|
e37872bbca | ||
|
|
c2a13a1645 | ||
|
|
cb47b7541e | ||
|
|
ebe63865bd | ||
|
|
131437d28f | ||
|
|
776f33a83e | ||
|
|
befb852659 | ||
|
|
c5a3c583f2 | ||
|
|
94d2480b9e | ||
|
|
0522b2cf71 | ||
|
|
b5748782ef | ||
|
|
dd1e475ea4 | ||
|
|
5e51a78d6a | ||
|
|
c76ea84412 | ||
|
|
d2f334ca85 | ||
|
|
7810732608 | ||
|
|
ff2e6cc220 | ||
|
|
8fb51475f0 | ||
|
|
3083a18191 | ||
|
|
c81026e308 | ||
|
|
568ba283c8 | ||
|
|
0755c0c4b1 | ||
|
|
0f773b8dfe | ||
|
|
938c440764 | ||
|
|
ea5e1a3b9c | ||
|
|
d380321e2e | ||
|
|
8837fb46b9 | ||
|
|
e540d453a0 | ||
|
|
12c33ac975 | ||
|
|
3d89d02e9d | ||
|
|
496435c350 | ||
|
|
b8d5b27054 | ||
|
|
5c82651bde | ||
|
|
cbf198c8bd | ||
|
|
5d67b374fc | ||
|
|
61a76bfd1c | ||
|
|
c23e019ccd | ||
|
|
30da676626 | ||
|
|
109aef0645 | ||
|
|
41375af05f | ||
|
|
19ab7eb8a7 | ||
|
|
118a6aa872 | ||
|
|
6ba204a602 | ||
|
|
02121379e7 | ||
|
|
daa8361c91 | ||
|
|
56bcf8c209 | ||
|
|
a2beed97e7 | ||
|
|
4201107a0a | ||
|
|
23300cf73c | ||
|
|
de4eb78d83 | ||
|
|
d4ea338f19 | ||
|
|
0cbde38d77 | ||
|
|
4e0548c151 | ||
|
|
1e9f915921 | ||
|
|
db79ec0079 | ||
|
|
18d924317d | ||
|
|
635436a438 | ||
|
|
2d43975ce3 | ||
|
|
6cd615ea97 | ||
|
|
790fc63f7c | ||
|
|
08887043db | ||
|
|
01ae26ff2b | ||
|
|
af262d3b82 | ||
|
|
490aaa3dad | ||
|
|
57348f0650 | ||
|
|
c110553be9 | ||
|
|
646e75ddf4 | ||
|
|
eb0a257d8f | ||
|
|
3e6d65e837 | ||
|
|
e4ec4fc351 | ||
|
|
bba5cc50c6 | ||
|
|
b476562edc | ||
|
|
47b14ee8a1 | ||
|
|
251f6fc765 | ||
|
|
0dca29285c | ||
|
|
19d0058c53 | ||
|
|
141380dd26 | ||
|
|
1da5f64b91 | ||
|
|
9760e88b66 | ||
|
|
a2ff58f4f4 | ||
|
|
b24517e648 | ||
|
|
65e6310758 | ||
|
|
a468f75a7c | ||
|
|
bb5a57b468 | ||
|
|
b72968dfb9 | ||
|
|
4371e64d86 | ||
|
|
0e6685ee28 | ||
|
|
19d2d97d78 | ||
|
|
3d120456ff | ||
|
|
7e7f3aba4c | ||
|
|
2dca992496 | ||
|
|
b1f4e09a7c | ||
|
|
bc1408806f | ||
|
|
5046d0abc4 | ||
|
|
7890c94496 | ||
|
|
58b5b1ddac | ||
|
|
7570e4ce6d | ||
|
|
b57c97a962 | ||
|
|
7b3869ec04 | ||
|
|
3a30373d94 | ||
|
|
ca6a128c91 | ||
|
|
8d53ca73e7 | ||
|
|
a3c5159223 | ||
|
|
41089c1da8 | ||
|
|
a31097fc86 | ||
|
|
3b846551a3 | ||
|
|
3f9cb9bb59 | ||
|
|
bed9b035b7 | ||
|
|
e9898cdbbe | ||
|
|
679fd2bf92 | ||
|
|
957650dfe3 | ||
|
|
3801c37f51 | ||
|
|
182a82315f | ||
|
|
adc5038259 | ||
|
|
ea493eee1d | ||
|
|
b58a812c18 | ||
|
|
78c9336ad7 | ||
|
|
f37db1fa84 | ||
|
|
a33128791b | ||
|
|
080c17f175 | ||
|
|
cabd7178aa | ||
|
|
0179bbc046 | ||
|
|
a04596cdc5 | ||
|
|
54f204b1a5 | ||
|
|
a6606deb05 | ||
|
|
8ea1681519 | ||
|
|
67111ce764 | ||
|
|
a53ee10e41 | ||
|
|
8c58ab0155 | ||
|
|
c0f1112d34 | ||
|
|
0a03ec1bf8 | ||
|
|
41c2aa7488 | ||
|
|
fd5e041099 | ||
|
|
6a9cb9f1a3 | ||
|
|
0d635721a1 | ||
|
|
7cc7b3a2d5 | ||
|
|
4137f4a6f9 | ||
|
|
3bc3ee6d6a | ||
|
|
af19e62479 | ||
|
|
4ac8f8330a | ||
|
|
48be8602e3 | ||
|
|
2b26d74d9b | ||
|
|
957623094b | ||
|
|
0a386750c3 | ||
|
|
a39c13a574 | ||
|
|
bdf0082372 | ||
|
|
c0a83e7241 | ||
|
|
0bee6bed9e | ||
|
|
f40161f035 | ||
|
|
6794759da1 | ||
|
|
0bd3ef12ab | ||
|
|
d5b0e2c8be | ||
|
|
bffb58cc1f | ||
|
|
c001ab3610 | ||
|
|
85d392b4c7 | ||
|
|
9c529cd6c7 | ||
|
|
b7660bec6d | ||
|
|
496325a417 | ||
|
|
51bb055936 | ||
|
|
9373550439 | ||
|
|
aff15fdf9a | ||
|
|
49a672946d | ||
|
|
879a0232b0 | ||
|
|
b7604e44ab | ||
|
|
27773f0f76 | ||
|
|
c7f9f09278 | ||
|
|
9e99933c53 | ||
|
|
6b9c5d8f3d | ||
|
|
006e89d74a | ||
|
|
848bbd07d3 | ||
|
|
2a34be05ad | ||
|
|
0c25f254b2 | ||
|
|
7e090f45d9 | ||
|
|
1755c977ba | ||
|
|
3a2afd0cec | ||
|
|
e8e9147e85 | ||
|
|
e20f338505 | ||
|
|
e9fa768fc6 | ||
|
|
2012fed7c4 | ||
|
|
3af05ef74c | ||
|
|
082a3ec7b7 | ||
|
|
66b94018a6 | ||
|
|
6e53b4cba6 | ||
|
|
6b0f0f8313 | ||
|
|
c933d66b2d | ||
|
|
5d97bf7e4c | ||
|
|
ec9c57f467 | ||
|
|
1f53145523 | ||
|
|
2c5deb7e59 | ||
|
|
43e40f5f14 | ||
|
|
62013fd070 | ||
|
|
d647542d86 | ||
|
|
885a805594 | ||
|
|
7ec9288ecd | ||
|
|
f9923c7134 | ||
|
|
e7c70b8676 | ||
|
|
496bc127d8 | ||
|
|
945c71ba56 | ||
|
|
beaea0bc02 | ||
|
|
a21fa90834 | ||
|
|
abb82e0b03 | ||
|
|
8fd78c3a19 | ||
|
|
80a4625d89 | ||
|
|
a121598651 | ||
|
|
175f17af97 | ||
|
|
c6b5f9751e | ||
|
|
8c17da69b5 | ||
|
|
4bbb26b8dc | ||
|
|
6194a1604c | ||
|
|
4182a2ab8e | ||
|
|
abc0f2dcbe | ||
|
|
4a41b34f06 | ||
|
|
308124ba97 | ||
|
|
7ed7e8ae0d | ||
|
|
f8a3fe1982 | ||
|
|
18f2089617 | ||
|
|
9c490ebbc1 | ||
|
|
8faf9aec82 | ||
|
|
fe0a885d79 | ||
|
|
d3114be395 | ||
|
|
20faaf2fe0 | ||
|
|
64bf2eff7f | ||
|
|
28c29a7bae | ||
|
|
d8ff844bfa | ||
|
|
e4238710f3 | ||
|
|
7a87332537 | ||
|
|
f4b4bb0849 | ||
|
|
5d39787825 | ||
|
|
1ae91971d6 | ||
|
|
09e8adb7bb | ||
|
|
c5e2c78ed0 | ||
|
|
47aabed2e4 | ||
|
|
cca473eca3 | ||
|
|
c8745ccdb8 | ||
|
|
3123ce3684 | ||
|
|
19e0d08269 | ||
|
|
466f09f894 | ||
|
|
cd67e7c6b3 | ||
|
|
adfce471a1 | ||
|
|
54c8c5f791 | ||
|
|
1f0414e06b | ||
|
|
9fe01a7f58 | ||
|
|
2818ac6c26 | ||
|
|
2fb74c6aff | ||
|
|
53fabbab73 | ||
|
|
1a351cff88 | ||
|
|
1d179d9185 | ||
|
|
76b3371420 | ||
|
|
113ee02048 | ||
|
|
c8e17e2c7a | ||
|
|
7095955555 | ||
|
|
07b76a307c | ||
|
|
c3b5759ce4 | ||
|
|
1fcac25c17 | ||
|
|
33b8f54460 | ||
|
|
23a55420df | ||
|
|
cf86bd4ab6 | ||
|
|
d0b5a412c9 | ||
|
|
53a0ef78fd | ||
|
|
a5ec7c5662 | ||
|
|
708ea6869d | ||
|
|
e3ee17dee7 | ||
|
|
b30289f233 | ||
|
|
2d3fce177f | ||
|
|
caf7a2ca3a | ||
|
|
30d3056f90 | ||
|
|
01e9abb19d | ||
|
|
430ca21d7c | ||
|
|
1a878b4c80 | ||
|
|
bc825e4343 | ||
|
|
0df0b706df | ||
|
|
0f0e8d505d | ||
|
|
ef2a92f980 | ||
|
|
8fdb7ee674 | ||
|
|
7eea7e229b | ||
|
|
89e9dc8ed9 | ||
|
|
d5f3de54bb | ||
|
|
56feb5637e | ||
|
|
d34daeb33e | ||
|
|
57486b1ed0 | ||
|
|
d8fe17d2f5 | ||
|
|
d58434e2ea | ||
|
|
79d6e1cd74 | ||
|
|
46ad8d1877 | ||
|
|
f374bc53ba | ||
|
|
65240d1f14 | ||
|
|
fd832a70eb | ||
|
|
656ba51c26 | ||
|
|
9452862f0c | ||
|
|
1b6cae729b | ||
|
|
8fac41f115 | ||
|
|
2a1e52fc1e | ||
|
|
8bd2469465 | ||
|
|
5b60c3690e | ||
|
|
4941ef63ee | ||
|
|
75e7452143 | ||
|
|
12f6c6cef2 | ||
|
|
c1b86756c8 | ||
|
|
4ed580a906 | ||
|
|
21d7481f42 | ||
|
|
a7b7925495 | ||
|
|
2fefd1a77e | ||
|
|
1064f29d15 | ||
|
|
080ef6b896 | ||
|
|
8f8b4387b6 | ||
|
|
f89991f43b | ||
|
|
2778710cc1 | ||
|
|
e4507f42eb | ||
|
|
db8f29ce5a | ||
|
|
9a29881583 | ||
|
|
3b0e0421c2 | ||
|
|
f194dc9cae | ||
|
|
1b69e0644b | ||
|
|
a21267cf41 | ||
|
|
5a631d25c4 | ||
|
|
8ca41347ad | ||
|
|
dde4a59495 | ||
|
|
ff77d3ccd7 | ||
|
|
2fcdfab8ba | ||
|
|
5e4d8d7c5a | ||
|
|
a5514c2cf6 | ||
|
|
6c7bad518c | ||
|
|
80771cb16f | ||
|
|
43349f65ae | ||
|
|
828e7981f0 | ||
|
|
a789b97a43 | ||
|
|
8cb32d761c | ||
|
|
133448090c | ||
|
|
3a830ad2e9 | ||
|
|
dc7a8fd6ec | ||
|
|
c2a56cbf77 | ||
|
|
53854254e4 | ||
|
|
76efdc7e7b | ||
|
|
f9fa17f572 | ||
|
|
326af3ef5a | ||
|
|
0ab1e7639f | ||
|
|
651db263aa | ||
|
|
7009c9389b | ||
|
|
8fd871ff11 | ||
|
|
1fb6bea360 | ||
|
|
3a114172bc | ||
|
|
fb331264ea | ||
|
|
2fc81a8180 | ||
|
|
0a2fdc8cc3 | ||
|
|
d68b57511c | ||
|
|
b5b35baabb | ||
|
|
e34340bbaa | ||
|
|
2df0b911f2 | ||
|
|
92a262ff8a | ||
|
|
6f25f1bc45 | ||
|
|
a3d581725c | ||
|
|
da45e6140c | ||
|
|
3a948667d5 | ||
|
|
c172737816 | ||
|
|
b634951812 | ||
|
|
ff2d095402 | ||
|
|
207167b07d | ||
|
|
787d2d299d | ||
|
|
4ce56573cb | ||
|
|
11eda8d150 | ||
|
|
4df673d127 | ||
|
|
52c5e15008 | ||
|
|
bb5cfdc499 | ||
|
|
75bb240af9 | ||
|
|
e481b1ca84 | ||
|
|
805a66e9c1 | ||
|
|
e00fa84f1b | ||
|
|
8ed6665a78 | ||
|
|
898349f95a | ||
|
|
12f48b058b | ||
|
|
5813f61649 | ||
|
|
e008e0d18d | ||
|
|
423b80774a | ||
|
|
91d967b592 | ||
|
|
2a64afa6ff | ||
|
|
824a532590 | ||
|
|
bab15c2ca8 | ||
|
|
d12f33e1e8 | ||
|
|
f07f577094 | ||
|
|
d824e622c3 | ||
|
|
1184fce8c3 | ||
|
|
1c686c1766 | ||
|
|
74589346d0 | ||
|
|
babd429f5b | ||
|
|
76839436b5 | ||
|
|
77281ba466 | ||
|
|
03174708d0 | ||
|
|
52aa5cc857 | ||
|
|
6e43328941 | ||
|
|
a6544a1617 | ||
|
|
e033aa995b | ||
|
|
d79e35790f | ||
|
|
ceb60fdb54 | ||
|
|
01773f50d5 | ||
|
|
ef083b383e | ||
|
|
eecc07d8d6 | ||
|
|
3bc2fc5a85 | ||
|
|
5964e01b1d | ||
|
|
16f14ac39d | ||
|
|
63f7099350 | ||
|
|
7d595874aa | ||
|
|
d63e5abf34 | ||
|
|
7dabb32ac0 | ||
|
|
dd9d1c3bfd | ||
|
|
47cc9a0363 | ||
|
|
3360c2e96c | ||
|
|
6a7ac2f1b3 | ||
|
|
acdaffba99 | ||
|
|
ca67d9f4cb | ||
|
|
0db4bc6e11 | ||
|
|
6c777385d6 | ||
|
|
eac8a110ee | ||
|
|
5a68bcebf7 | ||
|
|
fb1083bf4e | ||
|
|
5bff9cacf2 | ||
|
|
79fa31304a | ||
|
|
a0b683e23b | ||
|
|
7b9a4594d9 | ||
|
|
75fd7f5c91 | ||
|
|
0b4804c363 | ||
|
|
6e6f04e292 | ||
|
|
87184af396 | ||
|
|
0f1debd4f3 | ||
|
|
a318f7cbc0 | ||
|
|
3d5835cdf3 | ||
|
|
008460e327 | ||
|
|
43cddbcf57 | ||
|
|
1d54e00a4d | ||
|
|
ccd3d7ab66 | ||
|
|
2228a6dcbe | ||
|
|
8142d02f01 | ||
|
|
451b29c718 | ||
|
|
a47d53d5ca | ||
|
|
d04ab3af2d | ||
|
|
971b2e230c | ||
|
|
ef2af245e7 | ||
|
|
7e5beb4cbb | ||
|
|
a8b93be199 | ||
|
|
ea1e611ccf | ||
|
|
e6add85795 | ||
|
|
b1c6865dcd | ||
|
|
8d2c3d6081 | ||
|
|
c36a0abe8c | ||
|
|
d92112e135 | ||
|
|
5341344e97 | ||
|
|
1e342335b4 | ||
|
|
444017fde2 | ||
|
|
6a2698dce2 | ||
|
|
5c62e47de7 | ||
|
|
379fb73795 | ||
|
|
29d97f611d | ||
|
|
c970a60765 | ||
|
|
d6adb6c0e5 | ||
|
|
84a75d02bb | ||
|
|
7108cffa8b | ||
|
|
ceeac55496 | ||
|
|
90c2864ddd | ||
|
|
2c9cce8466 | ||
|
|
d203f27479 | ||
|
|
f049fed33b | ||
|
|
261583e604 | ||
|
|
caafed81f8 | ||
|
|
42e3640151 | ||
|
|
b3a592bb3b | ||
|
|
90171ac2d1 | ||
|
|
28831c61da | ||
|
|
e425bc84ea | ||
|
|
07d44ddc1f | ||
|
|
30a4ae4ea0 | ||
|
|
7ef48e9bb4 | ||
|
|
f44b98ac3d | ||
|
|
009d4c6232 | ||
|
|
fff83160be | ||
|
|
ec387f15e0 | ||
|
|
7ade151ecc | ||
|
|
19b8e48084 | ||
|
|
9a058ea45c | ||
|
|
6e7c29ba8b | ||
|
|
0da0a696f2 | ||
|
|
efbb439dca | ||
|
|
9ebd9d89dd | ||
|
|
a6d15dbbe1 | ||
|
|
8f18d30e43 | ||
|
|
52e824cdd8 | ||
|
|
fbd631b22e | ||
|
|
05669a6778 | ||
|
|
e4ad10fc11 | ||
|
|
876269e81a | ||
|
|
e48462b250 | ||
|
|
c23a589620 | ||
|
|
3945930a81 | ||
|
|
6282f41f06 | ||
|
|
a68d8652e3 | ||
|
|
88961ddf5b | ||
|
|
c899c48263 | ||
|
|
0530b165f6 | ||
|
|
576f264e97 | ||
|
|
f0d190cff7 | ||
|
|
59b32e119c | ||
|
|
60a14e62e7 | ||
|
|
1db1e04b0a | ||
|
|
daae562085 | ||
|
|
d0f386695e | ||
|
|
55a78d55b3 | ||
|
|
5df187c596 | ||
|
|
cedfecbbbd | ||
|
|
425327aafa | ||
|
|
dfb7bd3fc8 | ||
|
|
3f64310b03 | ||
|
|
be2f2a32ff | ||
|
|
f8f2ea4392 | ||
|
|
28e24c9bcb |
98 changed files with 7311 additions and 4301 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -1,9 +1,16 @@
|
||||||
pom.xml
|
pom.xml*
|
||||||
*jar
|
*jar
|
||||||
/lib/
|
/lib/
|
||||||
/classes/
|
/classes/
|
||||||
.lein-failures
|
.lein-*
|
||||||
.lein-deps-sum
|
|
||||||
TAGS
|
TAGS
|
||||||
checkouts/*
|
checkouts/*
|
||||||
doc/*
|
doc/*
|
||||||
|
deploy.docs.sh
|
||||||
|
target/*
|
||||||
|
todo.org
|
||||||
|
.nrepl-*
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
/.clj-kondo/.cache
|
||||||
|
/.lsp/.cache
|
||||||
|
|
|
||||||
22
.travis.yml
22
.travis.yml
|
|
@ -1,4 +1,20 @@
|
||||||
language: clojure
|
language: clojure
|
||||||
before_install: lein plugin install lein-multi 1.1.0
|
sudo: required
|
||||||
before_script: ./bin/ci/before_script.sh
|
lein: lein
|
||||||
script: lein multi test
|
dist: xenial
|
||||||
|
before_script:
|
||||||
|
# Give MongoDB server some time to boot
|
||||||
|
- sleep 15
|
||||||
|
- mongod --version
|
||||||
|
- ./bin/ci/before_script.sh
|
||||||
|
script: lein do clean, javac, test
|
||||||
|
jdk:
|
||||||
|
- openjdk10
|
||||||
|
- oraclejdk11
|
||||||
|
- openjdk12
|
||||||
|
services:
|
||||||
|
- mongodb
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- 3.5.x-stable
|
||||||
|
|
|
||||||
13
CONTRIBUTING.md
Normal file
13
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
The project uses [Leiningen 2](http://leiningen.org) and requires a recent MongoDB to be running
|
||||||
|
locally. Make sure you have those two installed and then run tests against all supported Clojure versions using
|
||||||
|
|
||||||
|
./bin/ci/before_script.sh
|
||||||
|
lein all do clean, javac, test
|
||||||
|
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
Then create a branch and make your changes on it. Once you are done with your changes and all
|
||||||
|
tests pass, write a [good, detailed commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) submit a pull request on GitHub.
|
||||||
1099
ChangeLog.md
1099
ChangeLog.md
File diff suppressed because it is too large
Load diff
384
README.md
384
README.md
|
|
@ -1,366 +1,125 @@
|
||||||
# Monger
|
# Monger, a modern Clojure MongoDB Driver
|
||||||
|
[](https://travis-ci.org/xingzhefeng/monger)
|
||||||
|
Monger is an idiomatic [Clojure MongoDB driver](http://clojuremongodb.info) for a more civilized age.
|
||||||
|
|
||||||
Monger is an idiomatic Clojure wrapper around MongoDB Java driver. It offers powerful expressive query DSL, strives to support
|
It has batteries included, offers powerful expressive query DSL,
|
||||||
every MongoDB 2.0+ feature and is well maintained.
|
strives to support modern MongoDB features and have the "look and feel" and
|
||||||
|
flexibility of the MongoDB shell.
|
||||||
|
|
||||||
[](http://travis-ci.org/michaelklishin/monger)
|
Monger is built from for modern Clojure versions and sits on top of
|
||||||
|
the official MongoDB Java driver.
|
||||||
|
|
||||||
|
|
||||||
## Project Goals
|
## Project Goals
|
||||||
|
|
||||||
There is one MongoDB client for Clojure that has been around since 2009. So, why create another one? Monger authors
|
There is one MongoDB client for Clojure that has been around since 2009. So, why create another one? Monger authors
|
||||||
wanted a client that will
|
wanted a client that would
|
||||||
|
|
||||||
* Support most of MongoDB 2.0+ features but only those that really matter. Grouping the way it is done today, for example, does not (it is easier to just use Map/Reduce directly).
|
* Support most of modern MongoDB features, focus on those that really matter.
|
||||||
* Be well documented.
|
* Be [well documented](http://clojuremongodb.info).
|
||||||
* Be well tested.
|
* Be [well tested](https://github.com/michaelklishin/monger/tree/master/test/monger/test).
|
||||||
* Be maintained, do not carry technical debt from 2009 forever.
|
* Target modern Clojure versions.
|
||||||
* Integrate with libraries like clojure.data.json and Joda Time.
|
* Be as close to the Mongo shell query language as practical
|
||||||
|
* Integrate with libraries like Joda Time, [Cheshire](https://github.com/dakrone/cheshire), clojure.data.json, [Ragtime](https://github.com/weavejester/ragtime).
|
||||||
|
* Support URI connections to be friendly to Heroku and other PaaS providers.
|
||||||
|
* Not carry technical debt from 2009 forever.
|
||||||
* Integrate usage of JavaScript files and ClojureScript (as soon as the compiler gets artifact it is possible to depend on for easy embedding).
|
* Integrate usage of JavaScript files and ClojureScript (as soon as the compiler gets artifact it is possible to depend on for easy embedding).
|
||||||
* Learn from other clients like the Java and Ruby ones.
|
|
||||||
* Target Clojure 1.3.0 and later from the ground up.
|
|
||||||
|
|
||||||
|
|
||||||
## Documentation & Examples
|
|
||||||
|
|
||||||
We are working on documentation guides & examples site for the 1.0 release. In the meantime, please refer to the [test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test) for code examples.
|
## Project Maturity
|
||||||
|
|
||||||
## Community
|
Monger is not a young project: started in July 2011, it is over 7
|
||||||
|
years old with active production use from week 1.
|
||||||
[Monger has a mailing list](https://groups.google.com/forum/#!forum/clojure-monger). Feel free to join it and ask any questions you may have.
|
|
||||||
|
|
||||||
To subscribe for announcements of releases, important changes and so on, please follow [@ClojureWerkz](https://twitter.com/#!/clojurewerkz) on Twitter.
|
|
||||||
|
|
||||||
|
|
||||||
## This is a Work In Progress
|
|
||||||
|
|
||||||
Core Monger APIs are stabilized but it is still a work in progress. Keep that in mind. 1.0 will be released in 2012
|
|
||||||
together with documentation guides and dedicated website.
|
|
||||||
|
|
||||||
|
|
||||||
## Artifacts
|
## Artifacts
|
||||||
|
|
||||||
|
Monger artifacts are [released to
|
||||||
|
Clojars](https://clojars.org/com.novemberain/monger). If you are using
|
||||||
|
Maven, add the following repository definition to your `pom.xml`:
|
||||||
|
|
||||||
|
``` xml
|
||||||
|
<repository>
|
||||||
|
<id>clojars.org</id>
|
||||||
|
<url>http://clojars.org/repo</url>
|
||||||
|
</repository>
|
||||||
|
```
|
||||||
|
|
||||||
### The Most Recent Release
|
### The Most Recent Release
|
||||||
|
|
||||||
With Leiningen:
|
With Leiningen:
|
||||||
|
|
||||||
[com.novemberain/monger "1.0.0-beta2"]
|
[com.novemberain/monger "3.5.0"]
|
||||||
|
|
||||||
|
|
||||||
With Maven:
|
With Maven:
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.novemberain</groupId>
|
<groupId>com.novemberain</groupId>
|
||||||
<artifactId>monger</artifactId>
|
<artifactId>monger</artifactId>
|
||||||
<version>1.0.0-beta2</version>
|
<version>3.5.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
### Snapshots
|
|
||||||
|
|
||||||
If you are comfortable with using snapshots, snapshot artifacts are [released to Clojars](https://clojars.org/com.novemberain/monger) every 24 hours.
|
## Getting Started
|
||||||
|
|
||||||
With Leiningen:
|
Please refer to our [Getting Started
|
||||||
|
guide](http://clojuremongodb.info/articles/getting_started.html). Don't
|
||||||
[com.novemberain/monger "1.0.0-SNAPSHOT"]
|
hesitate to join our [mailing
|
||||||
|
list](https://groups.google.com/forum/#!forum/clojure-mongodb) and ask
|
||||||
|
questions, too!
|
||||||
|
|
||||||
|
|
||||||
With Maven:
|
## Documentation & Examples
|
||||||
|
|
||||||
<dependency>
|
Please see our [documentation guides site](http://clojuremongodb.info/) and [API reference](http://reference.clojuremongodb.info).
|
||||||
<groupId>com.novemberain</groupId>
|
|
||||||
<artifactId>monger</artifactId>
|
Our [test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test)
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
also has many code examples.
|
||||||
</dependency>
|
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
[Monger has a mailing list](https://groups.google.com/forum/#!forum/clojure-mongodb). Feel
|
||||||
|
free to join it and ask any questions you may have.
|
||||||
|
|
||||||
|
To subscribe for announcements of releases, important changes and so
|
||||||
|
on, please follow [@ClojureWerkz](https://twitter.com/#!/clojurewerkz)
|
||||||
|
on Twitter.
|
||||||
|
|
||||||
|
|
||||||
## Supported Clojure versions
|
## Supported Clojure versions
|
||||||
|
|
||||||
Monger is built from the ground up for Clojure 1.3 and up.
|
Monger requires Clojure 1.8+. The most recent
|
||||||
|
stable release is highly recommended.
|
||||||
|
|
||||||
|
|
||||||
## Connecting to MongoDB
|
## Continuous Integration Status
|
||||||
|
|
||||||
Monger supports working with multiple connections and/or databases but is optimized for applications that only use one connection
|
[](http://travis-ci.org/michaelklishin/monger)
|
||||||
and one database.
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(ns my.service.server
|
|
||||||
(:require [monger core util]))
|
|
||||||
|
|
||||||
;; localhost, default port
|
|
||||||
(monger.core/connect!)
|
|
||||||
|
|
||||||
;; given host, given port
|
|
||||||
(monger.core/connect! { :host "db.megacorp.internal" :port 7878 })
|
|
||||||
```
|
|
||||||
|
|
||||||
To set default database Monger will use, use `monger.core/get-db` and `monger.core/set-db!` functions in combination:
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(ns my.service.server
|
|
||||||
(:require [monger core]]))
|
|
||||||
|
|
||||||
;; localhost, default port
|
|
||||||
(monger.core/connect!)
|
|
||||||
(monger.core/set-db! (monger.core/get-db "monger-test"))
|
|
||||||
```
|
|
||||||
|
|
||||||
To set default write concern, use `monger.core/set-default-write-concern!` function:
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(monger.core/set-default-write-concern! WriteConcern/FSYNC_SAFE)
|
|
||||||
```
|
|
||||||
|
|
||||||
By default Monger will use `WriteConcern/SAFE` as write concern. We believe that MongoDB Java driver (as well as other
|
|
||||||
official drivers) are using very unsafe defaults when no exceptions are raised, even for network issues. This does not sound
|
|
||||||
like a good default for most applications: many applications use MongoDB because of the flexibility, not extreme write throughput
|
|
||||||
requirements.
|
|
||||||
|
|
||||||
## Inserting Documents
|
|
||||||
|
|
||||||
To insert documents, use `monger.collection/insert` and `monger.collection/insert-batch` functions.
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(ns my.service.server
|
|
||||||
(:use [monger.core :only [connect! connect set-db! get-db]]
|
|
||||||
[monger.collection :only [insert insert-batch]])
|
|
||||||
(:import [org.bson.types ObjectId]
|
|
||||||
[com.mongodb DB WriteConcern]))
|
|
||||||
|
|
||||||
;; localhost, default port
|
|
||||||
(connect!)
|
|
||||||
(set-db! (monger.core/get-db "monger-test"))
|
|
||||||
|
|
||||||
;; without document id
|
|
||||||
(insert "document" { :first_name "John" :last_name "Lennon" })
|
|
||||||
|
|
||||||
;; multiple documents at once
|
|
||||||
(insert-batch "document" [{ :first_name "John" :last_name "Lennon" }
|
|
||||||
{ :first_name "Paul" :last_name "McCartney" }])
|
|
||||||
|
|
||||||
;; with explicit document id
|
|
||||||
(insert "documents" { :_id (ObjectId.) :first_name "John" :last_name "Lennon" })
|
|
||||||
|
|
||||||
;; with a different write concern
|
|
||||||
(insert "documents" { :_id (ObjectId.) :first_name "John" :last_name "Lennon" } WriteConcern/JOURNAL_SAFE)
|
|
||||||
|
|
||||||
;; with a different database
|
|
||||||
(let [archive-db (get-db "monger-test.archive")]
|
|
||||||
(insert archive-db "documents" { :first_name "John" :last_name "Lennon" } WriteConcern/NORMAL))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Write Performance
|
|
||||||
|
|
||||||
Monger insert operations are efficient and have very little overhead compared to the underlying Java driver. Here
|
|
||||||
are some numbers on a MacBook Pro from fall 2010 with Core i7 and an Intel SSD drive:
|
|
||||||
|
|
||||||
```
|
|
||||||
Testing monger.test.stress
|
|
||||||
Inserting 1000 documents...
|
|
||||||
"Elapsed time: 38.317 msecs"
|
|
||||||
Inserting 10,000 documents...
|
|
||||||
"Elapsed time: 263.827 msecs"
|
|
||||||
Inserting 100,000 documents...
|
|
||||||
"Elapsed time: 1679.828 msecs"
|
|
||||||
```
|
|
||||||
|
|
||||||
With the `SAFE` write concern, it takes roughly 1.7 second to insert 100,000 documents.
|
|
||||||
|
|
||||||
|
|
||||||
## Regular Finders
|
|
||||||
|
|
||||||
`monger.collection` namespace provides several finder functions that try to follow MongoDB query language as closely as possible,
|
|
||||||
even when providing shortcuts for common cases.
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(ns my.service.finders
|
|
||||||
(:require [monger.collection :as mc])
|
|
||||||
(:use [monger.operators]))
|
|
||||||
|
|
||||||
;; find one document by id, as Clojure map
|
|
||||||
(mc/find-map-by-id "documents" (ObjectId. "4ec2d1a6b55634a935ea4ac8"))
|
|
||||||
|
|
||||||
;; find one document by id, as `com.mongodb.DBObject` instance
|
|
||||||
(mc/find-by-id "documents" (ObjectId. "4ec2d1a6b55634a935ea4ac8"))
|
|
||||||
|
|
||||||
;; find one document as Clojure map
|
|
||||||
(mc/find-one-as-map "documents" { :_id (ObjectId. "4ec2d1a6b55634a935ea4ac8") })
|
|
||||||
|
|
||||||
;; find one document by id, as `com.mongodb.DBObject` instance
|
|
||||||
(mc/find-one "documents" { :_id (ObjectId. "4ec2d1a6b55634a935ea4ac8") })
|
|
||||||
|
|
||||||
|
|
||||||
;; all documents as Clojure maps
|
|
||||||
(mc/find-maps "documents")
|
|
||||||
|
|
||||||
;; all documents as `com.mongodb.DBObject` instances
|
|
||||||
(mc/find "documents")
|
|
||||||
|
|
||||||
;; with a query, as Clojure maps
|
|
||||||
(mc/find-maps "documents" { :year 1998 })
|
|
||||||
|
|
||||||
;; with a query, as `com.mongodb.DBObject` instances
|
|
||||||
(mc/find "documents" { :year 1998 })
|
|
||||||
|
|
||||||
;; with a query that uses operators
|
|
||||||
(mc/find "products" { :price_in_subunits { $gt 4000 $lte 1200 } })
|
|
||||||
|
|
||||||
;; with a query that uses operators as strings
|
|
||||||
(mc/find "products" { :price_in_subunits { "$gt" 4000 "$lte" 1200 } })
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Powerful Query DSL
|
|
||||||
|
|
||||||
Every application that works with data stores has to query them. As a consequence, having an expressive powerful query DSL is a must
|
|
||||||
for client libraries like Monger.
|
|
||||||
|
|
||||||
Here is what monger.query DSL feels like:
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(with-collection "movies"
|
|
||||||
(find { :year { $lt 2010 $gte 2000 }, :revenue { $gt 20000000 } })
|
|
||||||
(fields [ :year :title :producer :cast :budget :revenue ])
|
|
||||||
(sort-by { :revenue -1 })
|
|
||||||
(skip 10)
|
|
||||||
(limit 20)
|
|
||||||
(hint "year-by-year-revenue-idx")
|
|
||||||
(snapshot))
|
|
||||||
```
|
|
||||||
|
|
||||||
It is easy to add new DSL elements, for example, adding pagination took literally less than 10 lines of Clojure code. Here is what
|
|
||||||
it looks like:
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(with-collection coll
|
|
||||||
(find {})
|
|
||||||
(paginate :page 1 :per-page 3)
|
|
||||||
(sort { :title 1 })
|
|
||||||
(read-preference ReadPreference/PRIMARY))
|
|
||||||
```
|
|
||||||
|
|
||||||
Query DSL supports composition, too:
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(let
|
|
||||||
[top3 (partial-query (limit 3))
|
|
||||||
by-population-desc (partial-query (sort { :population -1 }))
|
|
||||||
result (with-collection coll
|
|
||||||
(find {})
|
|
||||||
(merge top3)
|
|
||||||
(merge by-population-desc))]
|
|
||||||
;; ...
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
More code examples can be found [in our test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test).
|
|
||||||
|
|
||||||
|
|
||||||
## Updating Documents
|
|
||||||
|
|
||||||
Use `monger.collection/update` and `monger.collection/save`.
|
|
||||||
|
|
||||||
|
|
||||||
## Removing Documents
|
|
||||||
|
|
||||||
Use `monger.collection/remove`.
|
|
||||||
|
|
||||||
|
|
||||||
## Counting Documents
|
|
||||||
|
|
||||||
Use `monger.collection/count`, `monger.collection/empty?` and `monger.collection/any?`.
|
|
||||||
|
|
||||||
|
|
||||||
## Determening Whether Operation Succeeded (or Failed)
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## Validators with Validateur
|
|
||||||
|
|
||||||
Monger relies on [Validateur](http://github.com/michaelklishin/validateur) for data validation.
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## Integration With Popular Libraries
|
|
||||||
|
|
||||||
Because Monger was built for Clojure 1.3 and later, it can take advantage of relatively new powerful Clojure features such as protocols.
|
|
||||||
|
|
||||||
|
|
||||||
### Integration with clojure.data.json
|
|
||||||
|
|
||||||
Monger was created for AMQP and HTTP services that use JSON to serialize message payloads. When serializing documents to JSON, developers
|
|
||||||
usually want to represent `com.mongodb.ObjectId` instances as strings in resulting JSON documents. Monger integrates with [clojure.data.json](http://github.com/clojure/data.json) to
|
|
||||||
make that effortless.
|
|
||||||
|
|
||||||
Just load `monger.json` namespace and it will extend `clojure.data.json/WriteJSON` protocol to support `com.mongodb.ObjectId` instance. Then
|
|
||||||
functions like `clojure.data.json/write-json` will be able to serialize object ids as strings exactly the way you expect it to be.
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(ns my.service.handlers
|
|
||||||
;; Make clojure.data.json aware of ObjectId instances
|
|
||||||
(:require [monger.json]))
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Integration with Joda Time
|
|
||||||
|
|
||||||
Monger provides the `monger.joda-time` namespace that extend its own Clojure-to-DBObject conversion protocols as well as
|
|
||||||
[clojure.data.json](http://github.com/clojure/data.json) `WriteJSON` protocol to handle `org.joda.time.DateTime` instances. To use it, make sure that
|
|
||||||
you have JodaTime and clojure.data.json on your dependencies list then load `monger.joda-time` like so
|
|
||||||
|
|
||||||
``` clojure
|
|
||||||
(ns my.service.handlers
|
|
||||||
;; Make Monger conversion protocols and clojure.data.json aware of JodaTime's DateTime instances
|
|
||||||
(:require [monger.joda-time]))
|
|
||||||
```
|
|
||||||
|
|
||||||
Now `clojure.data.json/write-json` and related functions will serialize JodaTime date time objects using [ISO8601 date time format](http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html). In addition, functions that convert MongoDB documents to
|
|
||||||
Clojure maps will instantiate JodaTime date time objects from `java.util.Date` instances MongoDB Java driver uses.
|
|
||||||
|
|
||||||
|
|
||||||
## Map/Reduce. Using JavaScript Resources.
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## Operations On Indexes
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## Database Commands
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## GridFS Support
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## Helper Functions
|
|
||||||
|
|
||||||
To be documented.
|
|
||||||
|
|
||||||
|
|
||||||
## Monger Is a ClojureWerkz Project
|
## Monger Is a ClojureWerkz Project
|
||||||
|
|
||||||
Neocons is part of the group of libraries known as ClojureWerkz, together with
|
Monger is part of the [group of Clojure libraries known as ClojureWerkz](http://clojurewerkz.org), together with
|
||||||
[Neocons](https://github.com/michaelklishin/neocons), [Langohr](https://github.com/michaelklishin/langohr), [Elastisch](https://github.com/clojurewerkz/elastisch), [Quartzite](https://github.com/michaelklishin/quartzite) and several others.
|
[Cassaforte](http://clojurecassandra.info), [Langohr](http://clojurerabbitmq.info), [Elastisch](http://clojureelasticsearch.info), [Quartzite](http://clojurequartz.info) and several others.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Install [lein-multi](https://github.com/maravillas/lein-multi) with
|
Monger uses [Leiningen 2](https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md). Make sure you have it installed and then run tests against
|
||||||
|
supported Clojure versions using
|
||||||
|
|
||||||
lein plugin install lein-multi 1.1.0
|
./bin/ci/before_script.sh
|
||||||
|
lein all do clean, javac, test
|
||||||
|
|
||||||
then run tests against Clojure 1.3.0 and 1.4.0[-beta1] using
|
Or, if you don't have mongodb installed, you can use docker
|
||||||
|
|
||||||
lein multi test
|
docker-compose up
|
||||||
|
./bin/ci/before_script_docker.sh
|
||||||
|
lein all do clean, javac, test
|
||||||
|
|
||||||
Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
|
Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
|
||||||
on Github.
|
on Github.
|
||||||
|
|
@ -369,6 +128,7 @@ on Github.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (C) 2011-2012 Michael S. Klishin
|
Copyright (C) 2011-2018 [Michael S. Klishin](http://twitter.com/michaelklishin), Alex Petrov, and the ClojureWerkz team.
|
||||||
|
|
||||||
Distributed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html), the same as Clojure.
|
Double licensed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html) (the same as Clojure) or
|
||||||
|
the [Apache Public License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Check which MongoDB shell is available
|
||||||
|
if command -v mongosh >/dev/null 2>&1; then
|
||||||
|
MONGO_SHELL="mongosh"
|
||||||
|
elif command -v mongo >/dev/null 2>&1; then
|
||||||
|
MONGO_SHELL="mongo"
|
||||||
|
else
|
||||||
|
echo "Error: Neither mongo nor mongosh shell found. Please install MongoDB shell."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# MongoDB Java driver won't run authentication twice on the same DB instance,
|
# MongoDB Java driver won't run authentication twice on the same DB instance,
|
||||||
# so we need to use multiple DBs.
|
# so we need to use multiple DBs.
|
||||||
mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test
|
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test
|
||||||
mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test2
|
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test2
|
||||||
mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test3
|
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test3
|
||||||
mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test4
|
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test4
|
||||||
18
bin/ci/before_script_docker.sh
Executable file
18
bin/ci/before_script_docker.sh
Executable file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Check which MongoDB shell is available in the container
|
||||||
|
if docker exec mongo_test which mongosh >/dev/null 2>&1; then
|
||||||
|
MONGO_SHELL="mongosh"
|
||||||
|
elif docker exec mongo_test which mongo >/dev/null 2>&1; then
|
||||||
|
MONGO_SHELL="mongo"
|
||||||
|
else
|
||||||
|
echo "Error: Neither mongo nor mongosh shell found in the container."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MongoDB Java driver won't run authentication twice on the same DB instance,
|
||||||
|
# so we need to use multiple DBs.
|
||||||
|
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test
|
||||||
|
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test2
|
||||||
|
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test3
|
||||||
|
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test4
|
||||||
11
bin/ci/before_script_server_3.6.x.sh
Executable file
11
bin/ci/before_script_server_3.6.x.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# MongoDB seems to need some time to boot first. MK.
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# MongoDB Java driver won't run authentication twice on the same DB instance,
|
||||||
|
# so we need to use multiple DBs.
|
||||||
|
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test
|
||||||
|
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test2
|
||||||
|
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test3
|
||||||
|
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test4
|
||||||
8
bin/ci/install_mongodb.sh
Executable file
8
bin/ci/install_mongodb.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
|
||||||
|
|
||||||
|
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
|
||||||
|
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y mongodb-org
|
||||||
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Use root/example as user/password credentials
|
||||||
|
version: '3.1'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
container_name: mongo_test
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
(ns examples.basic_operations
|
|
||||||
(:gen-class)
|
|
||||||
(:require [monger core collection util])
|
|
||||||
(:import (com.mongodb Mongo DB DBObject))
|
|
||||||
(:use [clojure.tools.cli]))
|
|
||||||
|
|
||||||
(defn fix-paul-maccartneys-name
|
|
||||||
"Fix Paul McCartney's name"
|
|
||||||
[]
|
|
||||||
(let [ paul_mccartney (monger.collection/find-one "people" { :first_name "Raul" }) ]
|
|
||||||
|
|
||||||
|
|
||||||
))
|
|
||||||
|
|
||||||
(do
|
|
||||||
(let [
|
|
||||||
args *command-line-args*
|
|
||||||
parsed-args (cli args
|
|
||||||
["--port" "Mongodb port" :default 27017]
|
|
||||||
["--host" "Mongodb host" :default "127.0.0.1"]
|
|
||||||
["--db-name" :default "monger-example"]) ]
|
|
||||||
|
|
||||||
(monger.core/connect! (first parsed-args))
|
|
||||||
(monger.core/set-db! (monger.core/get-db "monger-example"))
|
|
||||||
|
|
||||||
(println "Does people connection exist: " (monger.collection/exists? "people"))
|
|
||||||
|
|
||||||
;; Insert a record to people collection
|
|
||||||
(monger.collection/insert "people" { :first_name "John" :last_name "Lennon" })
|
|
||||||
|
|
||||||
;; Count an amount of records just inserted
|
|
||||||
(println "People collection is: " (monger.collection/count "people"))
|
|
||||||
|
|
||||||
;; Insert several records
|
|
||||||
(monger.collection/insert-batch "people" [{ :first_name "Ringo" :last_name "Starr" }
|
|
||||||
{ :first_name "Raul" :last_name "McCartney" }
|
|
||||||
{ :first_name "George" :last_name "Harrison" } ])
|
|
||||||
|
|
||||||
(println "People collection is: " (monger.collection/count "people"))
|
|
||||||
|
|
||||||
;; Fix a typo in the inserted record
|
|
||||||
(monger.collection/update "people" { :first_name "Raul" } { "$set" { :first_name "Paul" } })
|
|
||||||
|
|
||||||
(println (monger.collection/find-one-as-map "people" { :first_name "Paul" }))
|
|
||||||
|
|
||||||
;; Now, let's add the index to that record
|
|
||||||
(monger.collection/update "people" { :first_name "Paul" } { "$set" { :years_on_stage 1 } })
|
|
||||||
|
|
||||||
(println (monger.collection/find-one-as-map "people" { :first_name "Paul" }))
|
|
||||||
|
|
||||||
;; Increment record 45 times
|
|
||||||
(dotimes [n 45]
|
|
||||||
(monger.collection/update "people" { :first_name "Paul" } { "$inc" { :years_on_stage 1 } })
|
|
||||||
(println (monger.collection/find-one-as-map "people" { :first_name "Paul" }))
|
|
||||||
)
|
|
||||||
|
|
||||||
;; Remove years_on_stage field
|
|
||||||
(monger.collection/update "people" { :first_name "Paul" } { "$unset" { :years_on_stage 1} })
|
|
||||||
|
|
||||||
;; Insert the record to the data set if it wasn't there yet
|
|
||||||
(monger.collection/update "people" { :first_name "Yoko" } { :first_name "Yoko" :last_name "Ono" } :upsert true)
|
|
||||||
|
|
||||||
;; Update multiple records
|
|
||||||
(monger.collection/update "people" { } { "$set" { :band "The Beatles" }} :multi true)
|
|
||||||
|
|
||||||
;; Save can act both like insert and update
|
|
||||||
(def ian_gillian
|
|
||||||
(monger.conversion/to-db-object
|
|
||||||
{ :first_name "Ian" :last_name "Gillan" }))
|
|
||||||
|
|
||||||
;; Performs insert
|
|
||||||
(monger.collection/save "people" ian_gillian)
|
|
||||||
|
|
||||||
;; Performs update
|
|
||||||
(monger.collection/save "people"
|
|
||||||
{ :_id (monger.util/get-id ian_gillian)
|
|
||||||
:first_name "Ian"
|
|
||||||
:last_name "Gillan" :band "Deep Purple" })
|
|
||||||
|
|
||||||
;; Remove people collection
|
|
||||||
(monger.collection/drop "people")
|
|
||||||
))
|
|
||||||
85
project.clj
85
project.clj
|
|
@ -1,27 +1,58 @@
|
||||||
(defproject com.novemberain/monger "1.0.0-SNAPSHOT"
|
(defproject com.novemberain/monger "4.0.0-SNAPSHOT"
|
||||||
:description "Monger is an experimental idiomatic Clojure wrapper around MongoDB Java driver"
|
:description "Monger is a Clojure MongoDB client for a more civilized age: friendly, flexible and with batteries included"
|
||||||
:license { :name "Eclipse Public License" }
|
:url "http://clojuremongodb.info"
|
||||||
:mailing-list {:name "clojure-monger"
|
:min-lein-version "2.5.1"
|
||||||
:archive "https://groups.google.com/group/clojure-monger"
|
:license {:name "Eclipse Public License"
|
||||||
:post "clojure-monger@googlegroups.com"}
|
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||||
:repositories { "sonatype"
|
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||||
{:url "http://oss.sonatype.org/content/repositories/releases"
|
[org.mongodb/mongodb-driver "3.12.11"]
|
||||||
:snapshots false
|
[clojurewerkz/support "1.5.0"]]
|
||||||
:releases {:checksum :fail :update :always}
|
:test-selectors {:default (fn [m]
|
||||||
}}
|
(and (not (:performance m))
|
||||||
:dependencies [[org.clojure/clojure "1.3.0"]
|
(not (:edge-features m))
|
||||||
[org.mongodb/mongo-java-driver "2.7.3"]
|
(not (:time-consuming m))))
|
||||||
[com.novemberain/validateur "1.0.0"]]
|
:focus :focus
|
||||||
:multi-deps {
|
:authentication :authentication
|
||||||
"1.4" [[org.clojure/clojure "1.4.0-beta1"]]
|
:updating :updating
|
||||||
:all [[org.mongodb/mongo-java-driver "2.7.3"]
|
:indexing :indexing
|
||||||
[com.novemberain/validateur "1.0.0"]]
|
:external :external
|
||||||
}
|
:cache :cache
|
||||||
:dev-dependencies [[org.clojure/data.json "0.1.2" :exclusions [org.clojure/clojure]]
|
:gridfs :gridfs
|
||||||
[clj-time "0.3.6" :exclusions [org.clojure/clojure]]
|
:command :command
|
||||||
[codox "0.3.4" :exclusions [org.clojure/clojure]]
|
:integration :integration
|
||||||
[org.clojure/tools.cli "0.2.1" :exclusions [org.clojure/clojure]]]
|
:performance :performance
|
||||||
:dev-resources-path "test/resources"
|
;; as in, edge mongodb server
|
||||||
:warn-on-reflection true
|
:edge-features :edge-features
|
||||||
:codox { :exclude [monger.internal.pagination] }
|
:time-consuming :time-consuming
|
||||||
:test-selectors {:focus (fn [v] (:focus v))})
|
:all (constantly true)}
|
||||||
|
:source-paths ["src/clojure"]
|
||||||
|
:java-source-paths ["src/java"]
|
||||||
|
:javac-options ["-target" "1.8" "-source" "1.8"]
|
||||||
|
:mailing-list {:name "clojure-mongodb"
|
||||||
|
:archive "https://groups.google.com/group/clojure-mongodb"
|
||||||
|
:post "clojure-mongodb@googlegroups.com"}
|
||||||
|
:profiles {:1.10 {:dependencies [[org.clojure/clojure "1.10.2"]]}
|
||||||
|
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]}
|
||||||
|
:dev {:resource-paths ["test/resources"]
|
||||||
|
:dependencies [[clj-time "0.15.1" :exclusions [org.clojure/clojure]]
|
||||||
|
[cheshire "5.8.1" :exclusions [org.clojure/clojure]]
|
||||||
|
[org.clojure/data.json "2.5.0" :exclusions [org.clojure/clojure]]
|
||||||
|
[org.clojure/tools.cli "0.4.1" :exclusions [org.clojure/clojure]]
|
||||||
|
[org.clojure/core.cache "0.7.1" :exclusions [org.clojure/clojure]]
|
||||||
|
[ring/ring-core "1.7.1" :exclusions [org.clojure/clojure]]
|
||||||
|
[com.novemberain/validateur "2.6.0" :exclusions [org.clojure/clojure]]
|
||||||
|
[ch.qos.logback/logback-classic "1.2.3" :exclusions [org.slf4j/slf4j-api]]
|
||||||
|
[ragtime/core "0.7.2" :exclusions [org.clojure/clojure]]]
|
||||||
|
:plugins [[lein-codox "0.10.5"]]
|
||||||
|
:codox {:source-paths ["src/clojure"]
|
||||||
|
:namespaces [#"^monger\.(?!internal)"]}}
|
||||||
|
;; only clj-time/JodaTime available, used to test monger.joda-time w/o clojure.data.json
|
||||||
|
:dev2 {:resource-paths ["test/resources"]
|
||||||
|
:dependencies [[clj-time "0.15.2" :exclusions [org.clojure/clojure]]]}}
|
||||||
|
:aliases {"all" ["with-profile" "dev:dev,1.10:dev,1.9:dev"]}
|
||||||
|
:repositories {"sonatype" {:url "https://oss.sonatype.org/content/repositories/releases"
|
||||||
|
:snapshots false
|
||||||
|
:releases {:checksum :fail :update :always}}
|
||||||
|
"sonatype-snapshots" {:url "https://oss.sonatype.org/content/repositories/snapshots"
|
||||||
|
:snapshots true
|
||||||
|
:releases {:checksum :fail :update :always}}})
|
||||||
|
|
|
||||||
85
src/clojure/monger/cache.clj
Normal file
85
src/clojure/monger/cache.clj
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.cache
|
||||||
|
"clojure.core.cache implementation(s) on top of MongoDB.
|
||||||
|
|
||||||
|
Related documentation guide: http://clojuremongodb.info/articles/integration.html"
|
||||||
|
(:require [monger.collection :as mc :refer [find-one find-by-id find-map-by-id]]
|
||||||
|
[clojure.core.cache :as cache]
|
||||||
|
[monger.conversion :as cnv])
|
||||||
|
(:import clojure.core.cache.CacheProtocol
|
||||||
|
[com.mongodb DB DBObject WriteConcern]
|
||||||
|
java.util.Map))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Implementation
|
||||||
|
;;
|
||||||
|
|
||||||
|
(def ^{:const true}
|
||||||
|
default-cache-collection "cache_entries")
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defrecord BasicMongerCache [db collection])
|
||||||
|
|
||||||
|
(extend-protocol cache/CacheProtocol
|
||||||
|
BasicMongerCache
|
||||||
|
(lookup [c k]
|
||||||
|
(let [m (mc/find-map-by-id (:db c) (:collection c) k)]
|
||||||
|
(:value m)))
|
||||||
|
(has? [c k]
|
||||||
|
(not (nil? (mc/find-by-id (:db c) (:collection c) k))))
|
||||||
|
(hit [this k]
|
||||||
|
this)
|
||||||
|
(miss [c k v]
|
||||||
|
(mc/insert (:db c) (:collection c) {:_id k :value v})
|
||||||
|
c)
|
||||||
|
(evict [c k]
|
||||||
|
(mc/remove-by-id (:db c) (:collection c) k)
|
||||||
|
c)
|
||||||
|
(seed [c m]
|
||||||
|
(mc/insert-batch (:db c) (:collection c) (map (fn [[k v]]
|
||||||
|
{:_id k :value v}) m))
|
||||||
|
c))
|
||||||
|
|
||||||
|
|
||||||
|
(defn basic-monger-cache-factory
|
||||||
|
([^DB db]
|
||||||
|
(BasicMongerCache. db default-cache-collection))
|
||||||
|
([^DB db collection]
|
||||||
|
(BasicMongerCache. db collection))
|
||||||
|
([^DB db collection base]
|
||||||
|
(cache/seed (BasicMongerCache. db collection) base)))
|
||||||
577
src/clojure/monger/collection.clj
Normal file
577
src/clojure/monger/collection.clj
Normal file
|
|
@ -0,0 +1,577 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;; Copyright (c) 2012 Toby Hede
|
||||||
|
;; Copyright (c) 2012 Baishampayan Ghose
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; Copyright (c) 2012 Toby Hede
|
||||||
|
;; Copyright (c) 2012 Baishampayan Ghose
|
||||||
|
;;
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.collection
|
||||||
|
"Provides key functionality for interaction with MongoDB: inserting, querying, updating and deleting documents, performing Aggregation Framework
|
||||||
|
queries, creating and dropping indexes, creating collections and more.
|
||||||
|
|
||||||
|
For more advanced read queries, see monger.query.
|
||||||
|
|
||||||
|
Related documentation guides:
|
||||||
|
|
||||||
|
* http://clojuremongodb.info/articles/getting_started.html
|
||||||
|
* http://clojuremongodb.info/articles/inserting.html
|
||||||
|
* http://clojuremongodb.info/articles/querying.html
|
||||||
|
* http://clojuremongodb.info/articles/updating.html
|
||||||
|
* http://clojuremongodb.info/articles/deleting.html
|
||||||
|
* http://clojuremongodb.info/articles/aggregation.html"
|
||||||
|
(:refer-clojure :exclude [find remove count drop distinct empty? any? update])
|
||||||
|
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern
|
||||||
|
DBCursor MapReduceCommand MapReduceCommand$OutputType AggregationOutput
|
||||||
|
AggregationOptions AggregationOptions$OutputMode]
|
||||||
|
[java.util List Map]
|
||||||
|
[java.util.concurrent TimeUnit]
|
||||||
|
[clojure.lang IPersistentMap ISeq]
|
||||||
|
org.bson.types.ObjectId)
|
||||||
|
(:require [monger.core :as mc]
|
||||||
|
[monger.result :as mres]
|
||||||
|
[monger.conversion :refer :all]
|
||||||
|
[monger.constraints :refer :all]
|
||||||
|
[monger.util :refer [into-array-list]]))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/insert
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^WriteResult insert
|
||||||
|
"Saves document to collection and returns a write result monger.result/acknowledged?
|
||||||
|
and related functions operate on. You can optionally specify a WriteConcern.
|
||||||
|
|
||||||
|
In case you need the exact inserted document returned, with the :_id key generated,
|
||||||
|
use monger.collection/insert-and-return instead."
|
||||||
|
([^DB db ^String coll document]
|
||||||
|
(.insert (.getCollection db (name coll))
|
||||||
|
(to-db-object document)
|
||||||
|
^WriteConcern mc/*mongodb-write-concern*))
|
||||||
|
([^DB db ^String coll document ^WriteConcern concern]
|
||||||
|
(.insert (.getCollection db (name coll))
|
||||||
|
(to-db-object document)
|
||||||
|
concern)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn ^clojure.lang.IPersistentMap insert-and-return
|
||||||
|
"Like monger.collection/insert but returns the inserted document as a persistent Clojure map.
|
||||||
|
|
||||||
|
If the :_id key wasn't set on the document, it will be generated and merged into the returned
|
||||||
|
map."
|
||||||
|
([^DB db ^String coll document]
|
||||||
|
(insert-and-return db coll document ^WriteConcern mc/*mongodb-write-concern*))
|
||||||
|
([^DB db ^String coll document ^WriteConcern concern]
|
||||||
|
;; MongoDB Java driver will generate the _id and set it but it
|
||||||
|
;; tries to mutate the inserted DBObject and it does not work
|
||||||
|
;; very well in our case, because that DBObject is short lived
|
||||||
|
;; and produced from the Clojure map we are passing in. Plus,
|
||||||
|
;; this approach is very awkward with immutable data structures
|
||||||
|
;; being the default. MK.
|
||||||
|
(let [doc (merge {:_id (ObjectId.)} document)]
|
||||||
|
(insert db coll doc concern)
|
||||||
|
doc)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn ^WriteResult insert-batch
|
||||||
|
"Saves documents to collection. You can optionally specify WriteConcern as a third argument."
|
||||||
|
([^DB db ^String coll ^List documents]
|
||||||
|
(.insert (.getCollection db (name coll))
|
||||||
|
^List (to-db-object documents)
|
||||||
|
^WriteConcern mc/*mongodb-write-concern*))
|
||||||
|
([^DB db ^String coll ^List documents ^WriteConcern concern]
|
||||||
|
(.insert (.getCollection db (name coll))
|
||||||
|
^List (to-db-object documents)
|
||||||
|
concern)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/find
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^DBCursor find
|
||||||
|
"Queries for objects in this collection.
|
||||||
|
This function returns DBCursor, which allows you to iterate over DBObjects.
|
||||||
|
If you want to manipulate clojure sequences maps, use find-maps."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(.find (.getCollection db (name coll))))
|
||||||
|
([^DB db ^String coll ^Map ref]
|
||||||
|
(.find (.getCollection db (name coll))
|
||||||
|
(to-db-object ref)))
|
||||||
|
([^DB db ^String coll ^Map ref fields]
|
||||||
|
(.find (.getCollection db (name coll))
|
||||||
|
(to-db-object ref)
|
||||||
|
(as-field-selector fields))))
|
||||||
|
|
||||||
|
(defn find-maps
|
||||||
|
"Queries for objects in this collection.
|
||||||
|
This function returns clojure Seq of Maps.
|
||||||
|
If you want to work directly with DBObject, use find."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(with-open [dbc (find db coll)]
|
||||||
|
(map (fn [x] (from-db-object x true)) dbc)))
|
||||||
|
([^DB db ^String coll ^Map ref]
|
||||||
|
(with-open [dbc (find db coll ref)]
|
||||||
|
(map (fn [x] (from-db-object x true)) dbc)))
|
||||||
|
([^DB db ^String coll ^Map ref fields]
|
||||||
|
(find-maps db coll ref fields true))
|
||||||
|
([^DB db ^String coll ^Map ref fields keywordize]
|
||||||
|
(with-open [dbc (find db coll ref fields)]
|
||||||
|
(map (fn [x] (from-db-object x keywordize)) dbc))))
|
||||||
|
|
||||||
|
(defn find-seq
|
||||||
|
"Queries for objects in this collection, returns ISeq of DBObjects."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(with-open [dbc (find db coll)]
|
||||||
|
(seq dbc)))
|
||||||
|
([^DB db ^String coll ^Map ref]
|
||||||
|
(with-open [dbc (find db coll ref)]
|
||||||
|
(seq dbc)))
|
||||||
|
([^DB db ^String coll ^Map ref fields]
|
||||||
|
(with-open [dbc (find db coll ref fields)]
|
||||||
|
(seq dbc))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/find-one
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^DBObject find-one
|
||||||
|
"Returns a single DBObject from this collection matching the query."
|
||||||
|
([^DB db ^String coll ^Map ref]
|
||||||
|
(.findOne (.getCollection db (name coll))
|
||||||
|
(to-db-object ref)))
|
||||||
|
([^DB db ^String coll ^Map ref fields]
|
||||||
|
(.findOne (.getCollection db (name coll))
|
||||||
|
(to-db-object ref)
|
||||||
|
^DBObject (as-field-selector fields))))
|
||||||
|
|
||||||
|
(defn ^IPersistentMap find-one-as-map
|
||||||
|
"Returns a single object converted to Map from this collection matching the query."
|
||||||
|
([^DB db ^String coll ^Map ref]
|
||||||
|
(from-db-object ^DBObject (find-one db coll ref) true))
|
||||||
|
([^DB db ^String coll ^Map ref fields]
|
||||||
|
(from-db-object ^DBObject (find-one db coll ref fields) true))
|
||||||
|
([^DB db ^String coll ^Map ref fields keywordize]
|
||||||
|
(from-db-object ^DBObject (find-one db coll ref fields) keywordize)))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/find-and-modify
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^IPersistentMap find-and-modify
|
||||||
|
"Atomically modify a document (at most one) and return it."
|
||||||
|
([^DB db ^String coll ^Map conditions ^Map document {:keys [fields sort remove return-new upsert keywordize] :or
|
||||||
|
{fields nil
|
||||||
|
sort nil
|
||||||
|
remove false
|
||||||
|
return-new false
|
||||||
|
upsert false
|
||||||
|
keywordize true}}]
|
||||||
|
(let [coll (.getCollection db (name coll))
|
||||||
|
maybe-fields (when fields (as-field-selector fields))
|
||||||
|
maybe-sort (when sort (to-db-object sort))]
|
||||||
|
(from-db-object
|
||||||
|
^DBObject (.findAndModify ^DBCollection coll (to-db-object conditions) maybe-fields maybe-sort remove
|
||||||
|
(to-db-object document) return-new upsert) keywordize))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/find-by-id
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^DBObject find-by-id
|
||||||
|
"Returns a single object with matching _id field."
|
||||||
|
([^DB db ^String coll id]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(find-one db coll {:_id id}))
|
||||||
|
([^DB db ^String coll id fields]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(find-one db coll {:_id id} fields)))
|
||||||
|
|
||||||
|
(defn ^IPersistentMap find-map-by-id
|
||||||
|
"Returns a single object, converted to map with matching _id field."
|
||||||
|
([^DB db ^String coll id]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(find-one-as-map db coll {:_id id}))
|
||||||
|
([^DB db ^String coll id fields]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(find-one-as-map db coll {:_id id} fields))
|
||||||
|
([^DB db ^String coll id fields keywordize]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(find-one-as-map db coll {:_id id} fields keywordize)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/count
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn count
|
||||||
|
"Returns the number of documents in this collection.
|
||||||
|
|
||||||
|
Takes optional conditions as an argument."
|
||||||
|
(^long [^DB db ^String coll]
|
||||||
|
(.count (.getCollection db (name coll))))
|
||||||
|
(^long [^DB db ^String coll ^Map conditions]
|
||||||
|
(.count (.getCollection db (name coll)) (to-db-object conditions))))
|
||||||
|
|
||||||
|
(defn any?
|
||||||
|
"Whether the collection has any items at all, or items matching query."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(> (count db coll) 0))
|
||||||
|
([^DB db ^String coll ^Map conditions]
|
||||||
|
(> (count db coll conditions) 0)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn empty?
|
||||||
|
"Whether the collection is empty."
|
||||||
|
[^DB db ^String coll]
|
||||||
|
(= (count db coll {}) 0))
|
||||||
|
|
||||||
|
;; monger.collection/update
|
||||||
|
|
||||||
|
(defn ^WriteResult update
|
||||||
|
"Performs an update operation.
|
||||||
|
|
||||||
|
Please note that update is potentially destructive operation. It updates document with the given set
|
||||||
|
emptying the fields not mentioned in the new document. In order to only change certain fields, use
|
||||||
|
\"$set\".
|
||||||
|
|
||||||
|
You can use all the MongoDB modifier operations ($inc, $set, $unset, $push, $pushAll, $addToSet, $pop, $pull
|
||||||
|
$pullAll, $rename, $bit) here as well.
|
||||||
|
|
||||||
|
It also takes options, such as :upsert and :multi.
|
||||||
|
By default :upsert and :multi are false."
|
||||||
|
([^DB db ^String coll ^Map conditions ^Map document]
|
||||||
|
(update db coll conditions document {}))
|
||||||
|
([^DB db ^String coll ^Map conditions ^Map document {:keys [upsert multi write-concern]
|
||||||
|
:or {upsert false
|
||||||
|
multi false
|
||||||
|
write-concern mc/*mongodb-write-concern*}}]
|
||||||
|
(.update (.getCollection db (name coll))
|
||||||
|
(to-db-object conditions)
|
||||||
|
(to-db-object document)
|
||||||
|
upsert
|
||||||
|
multi
|
||||||
|
write-concern)))
|
||||||
|
|
||||||
|
(defn ^WriteResult upsert
|
||||||
|
"Performs an upsert.
|
||||||
|
|
||||||
|
This is a convenience function that delegates to monger.collection/update and
|
||||||
|
sets :upsert to true.
|
||||||
|
|
||||||
|
See monger.collection/update documentation"
|
||||||
|
([^DB db ^String coll ^Map conditions ^Map document]
|
||||||
|
(upsert db coll conditions document {}))
|
||||||
|
([^DB db ^String coll ^Map conditions ^Map document {:keys [multi write-concern]
|
||||||
|
:or {multi false
|
||||||
|
write-concern mc/*mongodb-write-concern*}}]
|
||||||
|
(update db coll conditions document {:multi multi :write-concern write-concern :upsert true})))
|
||||||
|
|
||||||
|
(defn ^WriteResult update-by-id
|
||||||
|
"Update a document with given id"
|
||||||
|
([^DB db ^String coll id ^Map document]
|
||||||
|
(update-by-id db coll id document {}))
|
||||||
|
([^DB db ^String coll id ^Map document {:keys [upsert write-concern]
|
||||||
|
:or {upsert false
|
||||||
|
write-concern mc/*mongodb-write-concern*}}]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(.update (.getCollection db (name coll))
|
||||||
|
(to-db-object {:_id id})
|
||||||
|
(to-db-object document)
|
||||||
|
upsert
|
||||||
|
false
|
||||||
|
write-concern)))
|
||||||
|
|
||||||
|
(defn ^WriteResult update-by-ids
|
||||||
|
"Update documents by given ids"
|
||||||
|
([^DB db ^String coll ids ^Map document]
|
||||||
|
(update-by-ids db coll ids document {}))
|
||||||
|
([^DB db ^String coll ids ^Map document {:keys [upsert write-concern]
|
||||||
|
:or {upsert false
|
||||||
|
write-concern mc/*mongodb-write-concern*}}]
|
||||||
|
(check-not-nil! (seq ids) "ids must not be nil or empty")
|
||||||
|
(.update (.getCollection db (name coll))
|
||||||
|
(to-db-object {:_id {"$in" ids}})
|
||||||
|
(to-db-object document)
|
||||||
|
upsert
|
||||||
|
true
|
||||||
|
write-concern)))
|
||||||
|
|
||||||
|
|
||||||
|
;; monger.collection/save
|
||||||
|
|
||||||
|
(defn ^WriteResult save
|
||||||
|
"Saves an object to the given collection (does insert or update based on the object _id).
|
||||||
|
|
||||||
|
If the object is not present in the database, insert operation will be performed.
|
||||||
|
If the object is already in the database, it will be updated.
|
||||||
|
|
||||||
|
This function returns write result. If you want to get the exact persisted document back,
|
||||||
|
use `save-and-return`."
|
||||||
|
([^DB db ^String coll ^Map document]
|
||||||
|
(.save (.getCollection db (name coll))
|
||||||
|
(to-db-object document)
|
||||||
|
mc/*mongodb-write-concern*))
|
||||||
|
([^DB db ^String coll ^Map document ^WriteConcern write-concern]
|
||||||
|
(.save (.getCollection db (name coll))
|
||||||
|
(to-db-object document)
|
||||||
|
write-concern)))
|
||||||
|
|
||||||
|
(defn ^clojure.lang.IPersistentMap save-and-return
|
||||||
|
"Saves an object to the given collection (does insert or update based on the object _id).
|
||||||
|
|
||||||
|
If the object is not present in the database, insert operation will be performed.
|
||||||
|
If the object is already in the database, it will be updated.
|
||||||
|
|
||||||
|
This function returns the exact persisted document back, including the `:_id` key in
|
||||||
|
case of an insert.
|
||||||
|
|
||||||
|
If you want to get write result back, use `save`."
|
||||||
|
([^DB db ^String coll ^Map document]
|
||||||
|
(save-and-return db coll document ^WriteConcern mc/*mongodb-write-concern*))
|
||||||
|
([^DB db ^String coll ^Map document ^WriteConcern write-concern]
|
||||||
|
;; see the comment in insert-and-return. Here we additionally need to make sure to not scrap the :_id key if
|
||||||
|
;; it is already present. MK.
|
||||||
|
(let [doc (merge {:_id (ObjectId.)} document)]
|
||||||
|
(save db coll doc write-concern)
|
||||||
|
doc)))
|
||||||
|
|
||||||
|
|
||||||
|
;; monger.collection/remove
|
||||||
|
|
||||||
|
(defn ^WriteResult remove
|
||||||
|
"Removes objects from the database."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(.remove (.getCollection db (name coll)) (to-db-object {})))
|
||||||
|
([^DB db ^String coll ^Map conditions]
|
||||||
|
(.remove (.getCollection db (name coll)) (to-db-object conditions))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn ^WriteResult remove-by-id
|
||||||
|
"Removes a single document with given id"
|
||||||
|
[^DB db ^String coll id]
|
||||||
|
(check-not-nil! id "id must not be nil")
|
||||||
|
(let [coll (.getCollection db (name coll))]
|
||||||
|
(.remove coll (to-db-object {:_id id}))))
|
||||||
|
|
||||||
|
(defn purge-many
|
||||||
|
"Purges (removes all documents from) multiple collections. Intended
|
||||||
|
to be used in test environments."
|
||||||
|
[^DB db xs]
|
||||||
|
(doseq [coll xs]
|
||||||
|
(remove db coll)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/create-index
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn create-index
|
||||||
|
"Forces creation of index on a set of fields, if one does not already exists."
|
||||||
|
([^DB db ^String coll ^Map keys]
|
||||||
|
(.createIndex (.getCollection db (name coll)) (as-field-selector keys)))
|
||||||
|
([^DB db ^String coll ^Map keys ^Map options]
|
||||||
|
(.createIndex (.getCollection db (name coll))
|
||||||
|
(as-field-selector keys)
|
||||||
|
(to-db-object options))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/ensure-index
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ensure-index
|
||||||
|
"Creates an index on a set of fields, if one does not already exist.
|
||||||
|
This operation is inexpensive in the case when an index already exists.
|
||||||
|
|
||||||
|
Options are:
|
||||||
|
|
||||||
|
:unique (boolean) to create a unique index
|
||||||
|
:name (string) to specify a custom index name and not rely on the generated one"
|
||||||
|
([^DB db ^String coll ^Map keys]
|
||||||
|
(.createIndex (.getCollection db (name coll)) (as-field-selector keys)))
|
||||||
|
([^DB db ^String coll ^Map keys ^Map options]
|
||||||
|
(.createIndex (.getCollection db (name coll))
|
||||||
|
(as-field-selector keys)
|
||||||
|
(to-db-object options)))
|
||||||
|
([^DB db ^String coll ^Map keys ^String index-name unique?]
|
||||||
|
(.createIndex (.getCollection db (name coll))
|
||||||
|
(as-field-selector keys)
|
||||||
|
index-name
|
||||||
|
unique?)))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/indexes-on
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn indexes-on
|
||||||
|
"Return a list of the indexes for this collection."
|
||||||
|
[^DB db ^String coll]
|
||||||
|
(from-db-object (.getIndexInfo (.getCollection db (name coll))) true))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/drop-index
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn drop-index
|
||||||
|
"Drops an index from this collection."
|
||||||
|
[^DB db ^String coll idx]
|
||||||
|
(if (string? idx)
|
||||||
|
(.dropIndex (.getCollection db (name coll)) ^String idx)
|
||||||
|
(.dropIndex (.getCollection db (name coll)) (to-db-object idx))))
|
||||||
|
|
||||||
|
(defn drop-indexes
|
||||||
|
"Drops all indixes from this collection."
|
||||||
|
[^DB db ^String coll]
|
||||||
|
(.dropIndexes (.getCollection db (name coll))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/exists?, /create, /drop, /rename
|
||||||
|
;;
|
||||||
|
|
||||||
|
|
||||||
|
(defn exists?
|
||||||
|
"Checks whether collection with certain name exists."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(.collectionExists db coll)))
|
||||||
|
|
||||||
|
(defn create
|
||||||
|
"Creates a collection with a given name and options.
|
||||||
|
|
||||||
|
Options are:
|
||||||
|
|
||||||
|
:capped (pass true to create a capped collection)
|
||||||
|
:max (number of documents)
|
||||||
|
:size (max allowed size of the collection, in bytes)"
|
||||||
|
[^DB db ^String coll ^Map options]
|
||||||
|
(.createCollection db coll (to-db-object options)))
|
||||||
|
|
||||||
|
(defn drop
|
||||||
|
"Deletes collection from database."
|
||||||
|
[^DB db ^String coll]
|
||||||
|
(.drop (.getCollection db (name coll))))
|
||||||
|
|
||||||
|
(defn rename
|
||||||
|
"Renames collection."
|
||||||
|
([^DB db ^String from, ^String to]
|
||||||
|
(.rename (.getCollection db (name from)) (name to)))
|
||||||
|
([^DB db ^String from ^String to drop-target?]
|
||||||
|
(.rename (.getCollection db (name from)) (name to) drop-target?)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Map/Reduce
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn map-reduce
|
||||||
|
"Performs a map reduce operation"
|
||||||
|
([^DB db ^String coll ^String js-mapper ^String js-reducer ^String output ^Map query]
|
||||||
|
(let [coll (.getCollection db (name coll))]
|
||||||
|
(.mapReduce coll js-mapper js-reducer output (to-db-object query))))
|
||||||
|
([^DB db ^String coll ^String js-mapper ^String js-reducer ^String output ^MapReduceCommand$OutputType output-type ^Map query]
|
||||||
|
(let [coll (.getCollection db (name coll))]
|
||||||
|
(.mapReduce coll js-mapper js-reducer output output-type (to-db-object query)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/distinct
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn distinct
|
||||||
|
"Finds distinct values for a key"
|
||||||
|
([^DB db ^String coll ^String key]
|
||||||
|
(.distinct (.getCollection db (name coll)) ^String (to-db-object key)))
|
||||||
|
([^DB db ^String coll ^String key ^Map query]
|
||||||
|
(.distinct (.getCollection db (name coll)) ^String (to-db-object key) (to-db-object query))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Aggregation
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn- build-aggregation-options
|
||||||
|
^AggregationOptions
|
||||||
|
[{:keys [^Boolean allow-disk-use cursor ^Long max-time]}]
|
||||||
|
(cond-> (AggregationOptions/builder)
|
||||||
|
allow-disk-use (.allowDiskUse allow-disk-use)
|
||||||
|
cursor (.outputMode AggregationOptions$OutputMode/CURSOR)
|
||||||
|
max-time (.maxTime max-time TimeUnit/MILLISECONDS)
|
||||||
|
(:batch-size cursor) (.batchSize (int (:batch-size cursor)))
|
||||||
|
true .build))
|
||||||
|
|
||||||
|
(defn aggregate
|
||||||
|
"Executes an aggregation query. MongoDB 2.2+ only.
|
||||||
|
Accepts the options :allow-disk-use and :cursor (a map with the :batch-size
|
||||||
|
key), as described in the MongoDB manual. Additionally, the :max-time option
|
||||||
|
is supported, for specifying a limit on the execution time of the query in
|
||||||
|
milliseconds.
|
||||||
|
|
||||||
|
:keywordize option that control if resulting map keys will be turned into keywords, default is true.
|
||||||
|
|
||||||
|
See http://docs.mongodb.org/manual/applications/aggregation/ to learn more."
|
||||||
|
[^DB db ^String coll stages & opts]
|
||||||
|
(let [coll (.getCollection db (name coll))
|
||||||
|
agg-opts (build-aggregation-options opts)
|
||||||
|
pipe (into-array-list (to-db-object stages))
|
||||||
|
res (.aggregate coll pipe agg-opts)
|
||||||
|
{:keys [^Boolean keywordize]
|
||||||
|
:or {keywordize true}} opts]
|
||||||
|
(map #(from-db-object % keywordize) (iterator-seq res))))
|
||||||
|
|
||||||
|
(defn explain-aggregate
|
||||||
|
"Returns the explain plan for an aggregation query. MongoDB 2.2+ only.
|
||||||
|
|
||||||
|
See http://docs.mongodb.org/manual/applications/aggregation/ to learn more."
|
||||||
|
[^DB db ^String coll stages & opts]
|
||||||
|
(let [coll (.getCollection db (name coll))
|
||||||
|
agg-opts (build-aggregation-options opts)
|
||||||
|
pipe (into-array-list (to-db-object stages))
|
||||||
|
res (.explainAggregate coll pipe agg-opts)]
|
||||||
|
(from-db-object res true)))
|
||||||
|
;;
|
||||||
|
;; Misc
|
||||||
|
;;
|
||||||
|
|
||||||
|
(def ^{:const true}
|
||||||
|
system-collection-pattern #"^(system|fs)")
|
||||||
|
|
||||||
|
(defn system-collection?
|
||||||
|
"Evaluates to true if the given collection name refers to a system collection. System collections
|
||||||
|
are prefixed with system. or fs. (default GridFS collection prefix)"
|
||||||
|
[^String coll]
|
||||||
|
(re-find system-collection-pattern coll))
|
||||||
108
src/clojure/monger/command.clj
Normal file
108
src/clojure/monger/command.clj
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;; Copyright (c) 2012 Toby Hede
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; Copyright (c) 2012 Toby Hede
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.command
|
||||||
|
"Provides convenience functions for performing most commonly used MongoDB commands.
|
||||||
|
For a lower-level API that gives maximum flexibility, see `monger.core/command`. To use
|
||||||
|
MongoDB 2.2 Aggregation Framework, see `monger.collection/aggregate`.
|
||||||
|
|
||||||
|
Related documentation guides:
|
||||||
|
|
||||||
|
* http://clojuremongodb.info/articles/commands.html
|
||||||
|
* http://clojuremongodb.info/articles/aggregation.html
|
||||||
|
* http://clojuremongodb.info/articles/mapreduce.html"
|
||||||
|
(:require monger.core
|
||||||
|
[monger.conversion :refer :all])
|
||||||
|
(:import [com.mongodb MongoClient DB DBObject]))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn admin-command
|
||||||
|
"Executes a command on the admin database"
|
||||||
|
[^MongoClient conn m]
|
||||||
|
(monger.core/command (monger.core/admin-db conn) m))
|
||||||
|
|
||||||
|
(defn raw-admin-command
|
||||||
|
"Executes a command on the admin database"
|
||||||
|
[^MongoClient conn ^DBObject cmd]
|
||||||
|
(monger.core/raw-command (monger.core/admin-db conn) cmd))
|
||||||
|
|
||||||
|
(defn collection-stats
|
||||||
|
[^DB database collection]
|
||||||
|
(monger.core/command database {:collstats collection}))
|
||||||
|
|
||||||
|
(defn db-stats
|
||||||
|
[^DB database]
|
||||||
|
(monger.core/command database {:dbStats 1}))
|
||||||
|
|
||||||
|
|
||||||
|
(defn reindex-collection
|
||||||
|
"Forces an existing collection to be reindexed using the reindexCollection command"
|
||||||
|
[^DB database ^String collection]
|
||||||
|
(monger.core/command database {:reIndex collection}))
|
||||||
|
|
||||||
|
(defn rename-collection
|
||||||
|
"Changes the name of an existing collection using the renameCollection command"
|
||||||
|
[^DB db ^String from ^String to]
|
||||||
|
(monger.core/command db (sorted-map :renameCollection from :to to)))
|
||||||
|
|
||||||
|
(defn convert-to-capped
|
||||||
|
"Converts an existing, non-capped collection to a capped collection using the convertToCapped command"
|
||||||
|
[^DB db ^String collection ^long size]
|
||||||
|
(monger.core/command db (sorted-map :convertToCapped collection :size size)))
|
||||||
|
|
||||||
|
(defn empty-capped
|
||||||
|
"Removes all documents from a capped collection using the emptycapped command"
|
||||||
|
[^DB db ^String collection]
|
||||||
|
(monger.core/command db {:emptycapped collection}))
|
||||||
|
|
||||||
|
|
||||||
|
(defn compact
|
||||||
|
"Rewrites and defragments a single collection using the compact command. This also forces all indexes on the collection to be rebuilt"
|
||||||
|
[^DB db ^String collection]
|
||||||
|
(monger.core/command db {:compact collection}))
|
||||||
|
|
||||||
|
|
||||||
|
(defn server-status
|
||||||
|
[^DB db]
|
||||||
|
(monger.core/command db {:serverStatus 1}))
|
||||||
|
|
||||||
|
|
||||||
|
(defn top
|
||||||
|
[^MongoClient conn]
|
||||||
|
(monger.core/command (monger.core/admin-db conn) {:top 1}))
|
||||||
44
src/clojure/monger/constraints.clj
Normal file
44
src/clojure/monger/constraints.clj
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.constraints)
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(definline check-not-nil!
|
||||||
|
[ref ^String message]
|
||||||
|
`(when (nil? ~ref)
|
||||||
|
(throw (IllegalArgumentException. ~message))))
|
||||||
180
src/clojure/monger/conversion.clj
Normal file
180
src/clojure/monger/conversion.clj
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Portions of the code are Copyright (c) 2009 Andrew Boekhoff
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Portions of the code are Copyright (c) 2009 Andrew Boekhoff
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.conversion
|
||||||
|
"Provides functions that convert between MongoDB Java driver classes (DBObject, DBList) and Clojure
|
||||||
|
data structures (maps, collections). Most of the time, application developers won't need to use these
|
||||||
|
functions directly because Monger Query DSL and many other functions convert documents to Clojure sequences and
|
||||||
|
maps automatically. However, this namespace is part of the public API and guaranteed to be stable between minor releases.
|
||||||
|
|
||||||
|
Related documentation guides:
|
||||||
|
|
||||||
|
* http://clojuremongodb.info/articles/inserting.html
|
||||||
|
* http://clojuremongodb.info/articles/querying.html"
|
||||||
|
(:import [com.mongodb DBObject BasicDBObject BasicDBList DBCursor]
|
||||||
|
[clojure.lang IPersistentMap Named Keyword Ratio]
|
||||||
|
[java.util List Map Date Set]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
(org.bson.types Decimal128)))
|
||||||
|
|
||||||
|
(defprotocol ConvertToDBObject
|
||||||
|
(^com.mongodb.DBObject to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses"))
|
||||||
|
|
||||||
|
(extend-protocol ConvertToDBObject
|
||||||
|
nil
|
||||||
|
(to-db-object [input]
|
||||||
|
nil)
|
||||||
|
|
||||||
|
String
|
||||||
|
(to-db-object [^String input]
|
||||||
|
input)
|
||||||
|
|
||||||
|
Boolean
|
||||||
|
(to-db-object [^Boolean input]
|
||||||
|
input)
|
||||||
|
|
||||||
|
java.util.Date
|
||||||
|
(to-db-object [^java.util.Date input]
|
||||||
|
input)
|
||||||
|
|
||||||
|
Ratio
|
||||||
|
(to-db-object [^Ratio input]
|
||||||
|
(double input))
|
||||||
|
|
||||||
|
Keyword
|
||||||
|
(to-db-object [^Keyword input] (.getName input))
|
||||||
|
|
||||||
|
Named
|
||||||
|
(to-db-object [^Named input] (.getName input))
|
||||||
|
|
||||||
|
IPersistentMap
|
||||||
|
(to-db-object [^IPersistentMap input]
|
||||||
|
(let [o (BasicDBObject.)]
|
||||||
|
(doseq [[k v] input]
|
||||||
|
(.put o (to-db-object k) (to-db-object v)))
|
||||||
|
o))
|
||||||
|
|
||||||
|
List
|
||||||
|
(to-db-object [^List input] (map to-db-object input))
|
||||||
|
|
||||||
|
Set
|
||||||
|
(to-db-object [^Set input] (map to-db-object input))
|
||||||
|
|
||||||
|
DBObject
|
||||||
|
(to-db-object [^DBObject input] input)
|
||||||
|
|
||||||
|
com.mongodb.DBRef
|
||||||
|
(to-db-object [^com.mongodb.DBRef dbref]
|
||||||
|
dbref)
|
||||||
|
|
||||||
|
Object
|
||||||
|
(to-db-object [input]
|
||||||
|
input))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defprotocol ConvertFromDBObject
|
||||||
|
(from-db-object [input keywordize] "Converts given DBObject instance to a piece of Clojure data"))
|
||||||
|
|
||||||
|
(extend-protocol ConvertFromDBObject
|
||||||
|
nil
|
||||||
|
(from-db-object [_ _] nil)
|
||||||
|
|
||||||
|
Object
|
||||||
|
(from-db-object [input _] input)
|
||||||
|
|
||||||
|
Decimal128
|
||||||
|
(from-db-object [^Decimal128 input _]
|
||||||
|
(.bigDecimalValue input))
|
||||||
|
|
||||||
|
List
|
||||||
|
(from-db-object [^List input keywordize]
|
||||||
|
(mapv #(from-db-object % keywordize) input))
|
||||||
|
|
||||||
|
BasicDBList
|
||||||
|
(from-db-object [^BasicDBList input keywordize]
|
||||||
|
(mapv #(from-db-object % keywordize) input))
|
||||||
|
|
||||||
|
com.mongodb.DBRef
|
||||||
|
(from-db-object [^com.mongodb.DBRef input _]
|
||||||
|
input)
|
||||||
|
|
||||||
|
DBObject
|
||||||
|
(from-db-object [^DBObject input keywordize]
|
||||||
|
;; DBObject provides .toMap, but the implementation in
|
||||||
|
;; subclass GridFSFile unhelpfully throws
|
||||||
|
;; UnsupportedOperationException.
|
||||||
|
(persistent!
|
||||||
|
(reduce (if keywordize
|
||||||
|
(fn [m ^String k]
|
||||||
|
(assoc! m (keyword k) (from-db-object (.get input k) true)))
|
||||||
|
(fn [m ^String k]
|
||||||
|
(assoc! m k (from-db-object (.get input k) false))))
|
||||||
|
(transient {}) (.keySet input)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defprotocol ConvertToObjectId
|
||||||
|
(^org.bson.types.ObjectId to-object-id [input] "Instantiates ObjectId from input unless the input itself is an ObjectId instance. In that case, returns input as is."))
|
||||||
|
|
||||||
|
(extend-protocol ConvertToObjectId
|
||||||
|
String
|
||||||
|
(to-object-id [^String input]
|
||||||
|
(ObjectId. input))
|
||||||
|
|
||||||
|
Date
|
||||||
|
(to-object-id [^Date input]
|
||||||
|
(ObjectId. input))
|
||||||
|
|
||||||
|
ObjectId
|
||||||
|
(to-object-id [^ObjectId input]
|
||||||
|
input))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defprotocol FieldSelector
|
||||||
|
(^com.mongodb.DBObject as-field-selector [input] "Converts values to DBObject that can be used to specify a list of document fields (including negation support)"))
|
||||||
|
|
||||||
|
(extend-protocol FieldSelector
|
||||||
|
DBObject
|
||||||
|
(as-field-selector [^DBObject input]
|
||||||
|
input)
|
||||||
|
|
||||||
|
List
|
||||||
|
(as-field-selector [^List input]
|
||||||
|
(to-db-object (zipmap input (repeat 1))))
|
||||||
|
|
||||||
|
Object
|
||||||
|
(as-field-selector [input]
|
||||||
|
(to-db-object input)))
|
||||||
310
src/clojure/monger/core.clj
Normal file
310
src/clojure/monger/core.clj
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.core
|
||||||
|
"Thin idiomatic wrapper around MongoDB Java client. monger.core includes
|
||||||
|
fundamental functions that perform database/replica set connection, set default write concern, default database, performing commands
|
||||||
|
and so on. Most of the functionality is in other monger.* namespaces, in particular monger.collection, monger.query and monger.gridfs
|
||||||
|
|
||||||
|
Related documentation guides:
|
||||||
|
|
||||||
|
* http://clojuremongodb.info/articles/connecting.html
|
||||||
|
* http://clojuremongodb.info/articles/commands.html
|
||||||
|
* http://clojuremongodb.info/articles/gridfs.html"
|
||||||
|
(:refer-clojure :exclude [count])
|
||||||
|
(:require [monger.conversion :refer :all]
|
||||||
|
[monger.util :refer [into-array-list]])
|
||||||
|
(:import [com.mongodb MongoClient MongoClientURI MongoCredential DB WriteConcern DBObject DBCursor Bytes
|
||||||
|
MongoClientOptions MongoClientOptions$Builder ServerAddress MapReduceOutput MongoException]
|
||||||
|
[com.mongodb.gridfs GridFS]
|
||||||
|
[java.util Map]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Defaults
|
||||||
|
;;
|
||||||
|
|
||||||
|
(def ^:dynamic ^String *mongodb-host* "127.0.0.1")
|
||||||
|
(def ^:dynamic ^long *mongodb-port* 27017)
|
||||||
|
|
||||||
|
(def ^:dynamic ^WriteConcern *mongodb-write-concern* WriteConcern/ACKNOWLEDGED)
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^MongoClient connect
|
||||||
|
"Connects to MongoDB. When used without arguments, connects to
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
:host (\"127.0.0.1\" by default)
|
||||||
|
:port (27017 by default)"
|
||||||
|
{:arglists '([]
|
||||||
|
[server-address options]
|
||||||
|
[server-address options credentials]
|
||||||
|
[[server-address & more] options]
|
||||||
|
[{:keys [host port uri] :or { host *mongodb-host* port *mongodb-port*}}])}
|
||||||
|
([]
|
||||||
|
(MongoClient.))
|
||||||
|
([server-address ^MongoClientOptions options]
|
||||||
|
(if (coll? server-address)
|
||||||
|
;; connect to a replica set
|
||||||
|
(let [server-list (into-array-list server-address)]
|
||||||
|
(MongoClient. server-list options))
|
||||||
|
;; connect to a single instance
|
||||||
|
(MongoClient. ^ServerAddress server-address options)))
|
||||||
|
([server-address ^MongoClientOptions options credentials]
|
||||||
|
(let [creds (into-array-list (if (coll? credentials)
|
||||||
|
credentials
|
||||||
|
[credentials]))]
|
||||||
|
(if (coll? server-address)
|
||||||
|
(let [server-list (into-array-list server-address)]
|
||||||
|
(MongoClient. server-list ^java.util.List creds options))
|
||||||
|
(MongoClient. ^ServerAddress server-address ^java.util.List creds options))))
|
||||||
|
([{ :keys [host port uri] :or { host *mongodb-host* port *mongodb-port* }}]
|
||||||
|
(if uri
|
||||||
|
(MongoClient. (MongoClientURI. uri))
|
||||||
|
(MongoClient. ^String host ^Long port))))
|
||||||
|
|
||||||
|
(defn ^MongoClient connect-with-credentials
|
||||||
|
"Connect with provided credentials and default options"
|
||||||
|
([credentials]
|
||||||
|
(connect-with-credentials *mongodb-host* *mongodb-port* credentials))
|
||||||
|
([^String hostname credentials]
|
||||||
|
(connect-with-credentials hostname *mongodb-port* credentials))
|
||||||
|
([^String hostname ^long port credentials]
|
||||||
|
(MongoClient. (into-array-list [(ServerAddress. hostname port)])
|
||||||
|
(into-array-list (if (coll? credentials)
|
||||||
|
credentials
|
||||||
|
[credentials])))))
|
||||||
|
|
||||||
|
(defn get-db-names
|
||||||
|
"Gets a list of all database names present on the server"
|
||||||
|
[^MongoClient conn]
|
||||||
|
(set (.getDatabaseNames conn)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn ^DB get-db
|
||||||
|
"Get database reference by name."
|
||||||
|
[^MongoClient conn ^String name]
|
||||||
|
(.getDB conn name))
|
||||||
|
|
||||||
|
(defn drop-db
|
||||||
|
"Drops a database"
|
||||||
|
[^MongoClient conn ^String db]
|
||||||
|
(.dropDatabase conn db))
|
||||||
|
|
||||||
|
(defn ^GridFS get-gridfs
|
||||||
|
"Get GridFS for the given database."
|
||||||
|
[^MongoClient conn ^String name]
|
||||||
|
(GridFS. (.getDB conn name)))
|
||||||
|
|
||||||
|
(defn server-address
|
||||||
|
([^String hostname]
|
||||||
|
(ServerAddress. hostname))
|
||||||
|
([^String hostname ^Long port]
|
||||||
|
(ServerAddress. hostname port)))
|
||||||
|
|
||||||
|
(defn ^MongoClientOptions$Builder mongo-options-builder
|
||||||
|
[{:keys [add-cluster-listener add-cluster-listeners add-command-listener add-command-listeners
|
||||||
|
add-connection-pool-listener add-connection-pool-listeners add-server-listener add-server-listeners
|
||||||
|
add-server-monitor-listener add-server-monitor-listeners always-use-mbeans application-name
|
||||||
|
codec-registry compressor-list connect-timeout connections-per-host cursor-finalizer-enabled
|
||||||
|
db-decoder-factory db-encoder-factory description heartbeat-connect-timeout heartbeat-frequency
|
||||||
|
heartbeat-socket-timeout local-threshold max-connection-idle-time max-connection-life-time
|
||||||
|
max-wait-time min-connections-per-host min-heartbeat-frequency read-concern read-preference
|
||||||
|
required-replica-set-name retry-writes server-selection-timeout server-selector socket-keep-alive
|
||||||
|
socket-factory socket-timeout ssl-context ssl-enabled ssl-invalid-host-name-allowed
|
||||||
|
threads-allowed-to-block-for-connection-multiplier uuid-representation write-concern]}]
|
||||||
|
(let [mob (MongoClientOptions$Builder.)]
|
||||||
|
(when add-cluster-listener
|
||||||
|
(.addClusterListener mob add-cluster-listener))
|
||||||
|
(when add-cluster-listeners
|
||||||
|
(doseq [cluster-listener add-cluster-listeners]
|
||||||
|
(.addClusterListener mob cluster-listener)))
|
||||||
|
(when add-command-listener
|
||||||
|
(.addCommandListener mob add-command-listener))
|
||||||
|
(when add-command-listeners
|
||||||
|
(doseq [command-listener add-command-listeners]
|
||||||
|
(.addCommandListener mob command-listener)))
|
||||||
|
(when add-connection-pool-listener
|
||||||
|
(.addConnectionPoolListener mob add-connection-pool-listener))
|
||||||
|
(when add-connection-pool-listeners
|
||||||
|
(doseq [connection-pool-listener add-connection-pool-listeners]
|
||||||
|
(.addConnectionPoolListener mob connection-pool-listener)))
|
||||||
|
(when add-server-listener
|
||||||
|
(.addServerListener mob add-server-listener))
|
||||||
|
(when add-server-listeners
|
||||||
|
(doseq [server-listener add-server-listeners]
|
||||||
|
(.addServerListener mob server-listener)))
|
||||||
|
(when add-server-monitor-listener
|
||||||
|
(.addServerMonitorListener mob add-server-monitor-listener))
|
||||||
|
(when add-server-monitor-listeners
|
||||||
|
(doseq [server-monitor-listener add-server-monitor-listeners]
|
||||||
|
(.addServerMonitorListener mob server-monitor-listener)))
|
||||||
|
(when always-use-mbeans
|
||||||
|
(.alwaysUseMBeans mob always-use-mbeans))
|
||||||
|
(when application-name
|
||||||
|
(.applicationName mob application-name))
|
||||||
|
(when always-use-mbeans
|
||||||
|
(.alwaysUseMBeans mob always-use-mbeans))
|
||||||
|
(when codec-registry
|
||||||
|
(.codecRegistry mob codec-registry))
|
||||||
|
(when compressor-list
|
||||||
|
(.compressorList mob compressor-list))
|
||||||
|
(when connections-per-host
|
||||||
|
(.connectionsPerHost mob connections-per-host))
|
||||||
|
(when connect-timeout
|
||||||
|
(.connectTimeout mob connect-timeout))
|
||||||
|
(when cursor-finalizer-enabled
|
||||||
|
(.cursorFinalizerEnabled mob cursor-finalizer-enabled))
|
||||||
|
(when db-decoder-factory
|
||||||
|
(.dbDecoderFactory mob db-decoder-factory))
|
||||||
|
(when db-encoder-factory
|
||||||
|
(.dbEncoderFactory mob db-encoder-factory))
|
||||||
|
(when description
|
||||||
|
(.description mob description))
|
||||||
|
(when heartbeat-connect-timeout
|
||||||
|
(.heartbeatConnectTimeout mob heartbeat-connect-timeout))
|
||||||
|
(when heartbeat-frequency
|
||||||
|
(.heartbeatFrequency mob heartbeat-frequency))
|
||||||
|
(when heartbeat-socket-timeout
|
||||||
|
(.heartbeatSocketTimeout mob heartbeat-socket-timeout))
|
||||||
|
(when ssl-context
|
||||||
|
(.sslContext mob ssl-context))
|
||||||
|
(when local-threshold
|
||||||
|
(.localThreshold mob local-threshold))
|
||||||
|
(when max-connection-idle-time
|
||||||
|
(.maxConnectionIdleTime mob max-connection-idle-time))
|
||||||
|
(when max-wait-time
|
||||||
|
(.maxWaitTime mob max-wait-time))
|
||||||
|
(when max-connection-life-time
|
||||||
|
(.maxConnectionLifeTime mob max-connection-life-time))
|
||||||
|
(when min-connections-per-host
|
||||||
|
(.minConnectionsPerHost mob min-connections-per-host))
|
||||||
|
(when min-heartbeat-frequency
|
||||||
|
(.minHeartbeatFrequency mob min-heartbeat-frequency))
|
||||||
|
(when read-concern
|
||||||
|
(.readConcern mob read-concern))
|
||||||
|
(when read-preference
|
||||||
|
(.readPreference mob read-preference))
|
||||||
|
(when required-replica-set-name
|
||||||
|
(.requiredReplicaSetName mob required-replica-set-name))
|
||||||
|
(when retry-writes
|
||||||
|
(.retryWrites mob retry-writes))
|
||||||
|
(when server-selection-timeout
|
||||||
|
(.serverSelectionTimeout mob server-selection-timeout))
|
||||||
|
(when server-selector
|
||||||
|
(.serverSelector mob server-selector))
|
||||||
|
(when socket-keep-alive
|
||||||
|
(.socketKeepAlive mob socket-keep-alive))
|
||||||
|
(when socket-factory
|
||||||
|
(.socketFactory mob socket-factory))
|
||||||
|
(when socket-timeout
|
||||||
|
(.socketTimeout mob socket-timeout))
|
||||||
|
(when ssl-enabled
|
||||||
|
(.sslEnabled mob ssl-enabled))
|
||||||
|
(when ssl-invalid-host-name-allowed
|
||||||
|
(.sslInvalidHostNameAllowed mob ssl-invalid-host-name-allowed))
|
||||||
|
(when threads-allowed-to-block-for-connection-multiplier
|
||||||
|
(.threadsAllowedToBlockForConnectionMultiplier mob threads-allowed-to-block-for-connection-multiplier))
|
||||||
|
(when uuid-representation
|
||||||
|
(.uuidRepresentation mob uuid-representation))
|
||||||
|
(when write-concern
|
||||||
|
(.writeConcern mob write-concern))
|
||||||
|
mob))
|
||||||
|
|
||||||
|
(defn ^MongoClientOptions mongo-options
|
||||||
|
[opts]
|
||||||
|
(let [mob (mongo-options-builder opts)]
|
||||||
|
(.build mob)))
|
||||||
|
|
||||||
|
(defn disconnect
|
||||||
|
"Closes default connection to MongoDB"
|
||||||
|
[^MongoClient conn]
|
||||||
|
(.close conn))
|
||||||
|
|
||||||
|
(def ^:const admin-db-name "admin")
|
||||||
|
|
||||||
|
(defn ^DB admin-db
|
||||||
|
"Returns admin database"
|
||||||
|
[^MongoClient conn]
|
||||||
|
(get-db conn admin-db-name))
|
||||||
|
|
||||||
|
|
||||||
|
(defn set-default-write-concern!
|
||||||
|
[wc]
|
||||||
|
"Sets *mongodb-write-concert*"
|
||||||
|
(alter-var-root #'*mongodb-write-concern* (constantly wc)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn connect-via-uri
|
||||||
|
"Connects to MongoDB using a URI, returns the connection and database as a map with :conn and :db.
|
||||||
|
Commonly used for PaaS-based applications, for example, running on Heroku.
|
||||||
|
If username and password are provided, performs authentication."
|
||||||
|
[^String uri-string]
|
||||||
|
(let [uri (MongoClientURI. uri-string)
|
||||||
|
conn (MongoClient. uri)]
|
||||||
|
(if-let [dbName (.getDatabase uri)]
|
||||||
|
{:conn conn :db (.getDB conn dbName)}
|
||||||
|
(throw (IllegalArgumentException. "No database name specified in URI. Monger requires a database to be explicitly configured.")))))
|
||||||
|
|
||||||
|
(defn ^com.mongodb.CommandResult command
|
||||||
|
"Runs a database command (please check MongoDB documentation for the complete list of commands).
|
||||||
|
|
||||||
|
Ordering of keys in the command document may matter. Please use sorted maps instead of map literals, for example:
|
||||||
|
(array-map :near 50 :test 430 :num 10)
|
||||||
|
|
||||||
|
For commonly used commands (distinct, count, map/reduce, etc), use monger.command and monger.collection functions such as
|
||||||
|
/distinct, /count, /drop, /dropIndexes, and /mapReduce respectively."
|
||||||
|
[^DB database ^Map cmd]
|
||||||
|
(.command ^DB database ^DBObject (to-db-object cmd)))
|
||||||
|
|
||||||
|
(defn ^com.mongodb.CommandResult raw-command
|
||||||
|
"Like monger.core/command but accepts DBObjects"
|
||||||
|
[^DB database ^DBObject cmd]
|
||||||
|
(.command database cmd))
|
||||||
|
|
||||||
|
(defprotocol Countable
|
||||||
|
(count [this] "Returns size of the object"))
|
||||||
|
|
||||||
|
(extend-protocol Countable
|
||||||
|
DBCursor
|
||||||
|
(count [^DBCursor this]
|
||||||
|
(.count this))
|
||||||
|
|
||||||
|
MapReduceOutput
|
||||||
|
(count [^MapReduceOutput this]
|
||||||
|
;; MongoDB Java driver could use a lot more specific type than Iterable but
|
||||||
|
;; it always uses DBCollection#find to popular result set. MK.
|
||||||
|
(.count ^DBCursor (.results this))))
|
||||||
56
src/clojure/monger/credentials.clj
Normal file
56
src/clojure/monger/credentials.clj
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.credentials
|
||||||
|
"Helper functions for instantiating various types
|
||||||
|
of credentials."
|
||||||
|
(:require [clojurewerkz.support.chars :refer :all])
|
||||||
|
(:import [com.mongodb MongoCredential]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^MongoCredential create
|
||||||
|
"Creates a MongoCredential instance with an unspecified mechanism.
|
||||||
|
The client will negotiate the best mechanism based on the
|
||||||
|
version of the server that the client is authenticating to."
|
||||||
|
[^String username ^String database pwd]
|
||||||
|
(MongoCredential/createCredential username database (to-char-array pwd)))
|
||||||
|
|
||||||
|
(defn ^MongoCredential x509
|
||||||
|
"Creates a MongoCredential instance for the X509-based authentication
|
||||||
|
protocol."
|
||||||
|
[^String username]
|
||||||
|
(MongoCredential/createMongoX509Credential username))
|
||||||
|
|
||||||
143
src/clojure/monger/cursor.clj
Normal file
143
src/clojure/monger/cursor.clj
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.cursor
|
||||||
|
"Helper-functions for dbCursor object:
|
||||||
|
* to initialize new cursor,
|
||||||
|
* for CRUD functionality of options of dbCursor"
|
||||||
|
(:import [com.mongodb DB DBCursor Bytes]
|
||||||
|
[java.util List Map]
|
||||||
|
[java.lang Integer]
|
||||||
|
[clojure.lang Keyword])
|
||||||
|
(:require [monger.core]
|
||||||
|
[monger.conversion :refer [to-db-object from-db-object as-field-selector]]))
|
||||||
|
|
||||||
|
(defn ^DBCursor make-db-cursor
|
||||||
|
"initializes new db-cursor."
|
||||||
|
([^DB db ^String coll]
|
||||||
|
(make-db-cursor db coll {} {}))
|
||||||
|
([^DB db ^String coll ^Map ref]
|
||||||
|
(make-db-cursor db coll ref {}))
|
||||||
|
([^DB db ^String coll ^Map ref fields]
|
||||||
|
(.find
|
||||||
|
(.getCollection db (name coll))
|
||||||
|
(to-db-object ref)
|
||||||
|
(as-field-selector fields))))
|
||||||
|
|
||||||
|
(def cursor-options {:awaitdata Bytes/QUERYOPTION_AWAITDATA
|
||||||
|
;;:exhaust Bytes/QUERYOPTION_EXHAUST - not human settable
|
||||||
|
:notimeout Bytes/QUERYOPTION_NOTIMEOUT
|
||||||
|
:oplogreplay Bytes/QUERYOPTION_OPLOGREPLAY
|
||||||
|
:partial Bytes/QUERYOPTION_PARTIAL
|
||||||
|
:slaveok Bytes/QUERYOPTION_SLAVEOK
|
||||||
|
:tailable Bytes/QUERYOPTION_TAILABLE})
|
||||||
|
|
||||||
|
(defn get-options
|
||||||
|
"Returns map of cursor's options with current state."
|
||||||
|
[^DBCursor db-cur]
|
||||||
|
(into {}
|
||||||
|
(for [[opt option-mask] cursor-options]
|
||||||
|
[opt (< 0 (bit-and (.getOptions db-cur) option-mask))])))
|
||||||
|
|
||||||
|
(defn add-option!
|
||||||
|
[^DBCursor db-cur ^String opt]
|
||||||
|
(.addOption db-cur (get cursor-options (keyword opt) 0)))
|
||||||
|
|
||||||
|
(defn remove-option!
|
||||||
|
[^DBCursor db-cur ^String opt]
|
||||||
|
(.setOptions db-cur (bit-and-not (.getOptions db-cur)
|
||||||
|
(get cursor-options (keyword opt) 0))))
|
||||||
|
|
||||||
|
(defmulti add-options (fn [db-cur opts] (class opts)))
|
||||||
|
(defmethod add-options Map [^DBCursor db-cur options]
|
||||||
|
"Changes options by using map of settings, which key specifies name of settings
|
||||||
|
and boolean value specifies new state of the setting.
|
||||||
|
usage:
|
||||||
|
(add-options db-cur {:notimeout true, :tailable false})
|
||||||
|
returns:
|
||||||
|
^DBCursor object."
|
||||||
|
(doseq [[opt value] (seq options)]
|
||||||
|
(if (= true value)
|
||||||
|
(add-option! db-cur opt)
|
||||||
|
(remove-option! db-cur opt)))
|
||||||
|
db-cur)
|
||||||
|
|
||||||
|
(defmethod add-options List [^DBCursor db-cur options]
|
||||||
|
"Takes list of options and activates these options
|
||||||
|
usage:
|
||||||
|
(add-options db-cur [:notimeout :tailable])
|
||||||
|
returns:
|
||||||
|
^DBCursor object"
|
||||||
|
(doseq [opt (seq options)]
|
||||||
|
(add-option! db-cur opt))
|
||||||
|
db-cur)
|
||||||
|
|
||||||
|
(defmethod add-options Integer [^DBCursor db-cur, option]
|
||||||
|
"Takes com.mongodb.Byte value and adds it to current settings.
|
||||||
|
usage:
|
||||||
|
(add-options db-cur com.mongodb.Bytes/QUERYOPTION_NOTIMEOUT)
|
||||||
|
returns:
|
||||||
|
^DBCursor object"
|
||||||
|
(.addOption db-cur option)
|
||||||
|
db-cur)
|
||||||
|
|
||||||
|
(defmethod add-options Keyword [^DBCursor db-cur, option]
|
||||||
|
"Takes just one keyword as name of settings and applies it to the db-cursor.
|
||||||
|
usage:
|
||||||
|
(add-options db-cur :notimeout)
|
||||||
|
returns:
|
||||||
|
^DBCursor object"
|
||||||
|
(add-option! db-cur option)
|
||||||
|
db-cur)
|
||||||
|
|
||||||
|
(defmethod add-options :default [^DBCursor db-cur, options]
|
||||||
|
"Using add-options with not supported type of options just passes unchanged cursor"
|
||||||
|
db-cur)
|
||||||
|
|
||||||
|
(defn ^DBCursor reset-options
|
||||||
|
"Resets cursor options to default value and returns cursor"
|
||||||
|
[^DBCursor db-cur]
|
||||||
|
(.resetOptions db-cur)
|
||||||
|
db-cur)
|
||||||
|
|
||||||
|
(defmulti format-as (fn [db-cur as] as))
|
||||||
|
|
||||||
|
(defmethod format-as :map [db-cur as]
|
||||||
|
(map #(from-db-object %1 true) db-cur))
|
||||||
|
|
||||||
|
(defmethod format-as :seq [db-cur as]
|
||||||
|
(seq db-cur))
|
||||||
|
|
||||||
|
(defmethod format-as :default [db-cur as]
|
||||||
|
db-cur)
|
||||||
|
|
||||||
62
src/clojure/monger/db.clj
Normal file
62
src/clojure/monger/db.clj
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;; Copyright (c) 2012 Toby Hede
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; Copyright (c) 2012 Toby Hede
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.db
|
||||||
|
"Functions that provide operations on databases"
|
||||||
|
(:refer-clojure :exclude [find remove count drop distinct empty?])
|
||||||
|
(:import [com.mongodb Mongo DB DBCollection])
|
||||||
|
(:require monger.core
|
||||||
|
[monger.conversion :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn add-user
|
||||||
|
"Adds a new user for this db"
|
||||||
|
[^DB db ^String username ^chars password]
|
||||||
|
(.addUser db username password))
|
||||||
|
|
||||||
|
|
||||||
|
(defn drop-db
|
||||||
|
"Drops the currently set database (via core/set-db) or the specified database."
|
||||||
|
[^DB db]
|
||||||
|
(.dropDatabase db))
|
||||||
|
|
||||||
|
(defn get-collection-names
|
||||||
|
"Returns a set containing the names of all collections in this database."
|
||||||
|
([^DB db]
|
||||||
|
(set (.getCollectionNames db))))
|
||||||
212
src/clojure/monger/gridfs.clj
Normal file
212
src/clojure/monger/gridfs.clj
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.gridfs
|
||||||
|
"Provides functions and macros for working with GridFS: storing files in GridFS, streaming files from GridFS,
|
||||||
|
finding stored files.
|
||||||
|
|
||||||
|
Related documentation guide: http://clojuremongodb.info/articles/gridfs.html"
|
||||||
|
(:refer-clojure :exclude [remove find])
|
||||||
|
(:require monger.core
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[monger.conversion :refer :all]
|
||||||
|
[clojurewerkz.support.fn :refer [fpartial]])
|
||||||
|
(:import [com.mongodb DB DBObject]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
[com.mongodb.gridfs GridFS GridFSInputFile]
|
||||||
|
[java.io InputStream ByteArrayInputStream File]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Implementation
|
||||||
|
;;
|
||||||
|
|
||||||
|
(def
|
||||||
|
^{:doc "Type object for a Java primitive byte array."
|
||||||
|
:private true
|
||||||
|
}
|
||||||
|
byte-array-type (class (make-array Byte/TYPE 0)))
|
||||||
|
|
||||||
|
;; ...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
|
||||||
|
(defn remove
|
||||||
|
[^GridFS fs query]
|
||||||
|
(.remove fs ^DBObject (to-db-object query)))
|
||||||
|
|
||||||
|
(defn remove-all
|
||||||
|
[^GridFS fs]
|
||||||
|
(remove fs {}))
|
||||||
|
|
||||||
|
(defn all-files
|
||||||
|
([^GridFS fs]
|
||||||
|
(.getFileList fs (to-db-object {})))
|
||||||
|
([^GridFS fs query]
|
||||||
|
(.getFileList fs query)))
|
||||||
|
|
||||||
|
(def ^{:private true} converter
|
||||||
|
(fpartial from-db-object true))
|
||||||
|
|
||||||
|
(defn files-as-maps
|
||||||
|
([^GridFS fs]
|
||||||
|
(files-as-maps fs {}))
|
||||||
|
([^GridFS fs query]
|
||||||
|
(map converter (all-files fs (to-db-object query)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Plumbing (low-level API)
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defprotocol InputStreamFactory
|
||||||
|
(^InputStream to-input-stream [input] "Makes InputStream out of the given input"))
|
||||||
|
|
||||||
|
(extend byte-array-type
|
||||||
|
InputStreamFactory
|
||||||
|
{:to-input-stream (fn [^bytes input]
|
||||||
|
(ByteArrayInputStream. input))})
|
||||||
|
|
||||||
|
(extend-protocol InputStreamFactory
|
||||||
|
String
|
||||||
|
(to-input-stream [^String input]
|
||||||
|
(io/make-input-stream input {:encoding "UTF-8"}))
|
||||||
|
|
||||||
|
File
|
||||||
|
(to-input-stream [^File input]
|
||||||
|
(io/make-input-stream input {:encoding "UTF-8"}))
|
||||||
|
|
||||||
|
InputStream
|
||||||
|
(to-input-stream [^InputStream input]
|
||||||
|
input))
|
||||||
|
|
||||||
|
(defprotocol GridFSInputFileFactory
|
||||||
|
(^GridFSInputFile create-gridfs-file [input ^GridFS fs] "Creates a file entry"))
|
||||||
|
|
||||||
|
(extend byte-array-type
|
||||||
|
GridFSInputFileFactory
|
||||||
|
{:create-gridfs-file (fn [^bytes input ^GridFS fs]
|
||||||
|
(.createFile fs input))})
|
||||||
|
|
||||||
|
(extend-protocol GridFSInputFileFactory
|
||||||
|
String
|
||||||
|
(create-gridfs-file [^String input ^GridFS fs]
|
||||||
|
(.createFile fs (io/file input)))
|
||||||
|
|
||||||
|
File
|
||||||
|
(create-gridfs-file [^File input ^GridFS fs]
|
||||||
|
(.createFile fs input))
|
||||||
|
|
||||||
|
InputStream
|
||||||
|
(create-gridfs-file [^InputStream input ^GridFS fs]
|
||||||
|
(.createFile fs input)))
|
||||||
|
|
||||||
|
(defn ^GridFSInputFile make-input-file
|
||||||
|
[^GridFS fs input]
|
||||||
|
(create-gridfs-file input fs))
|
||||||
|
|
||||||
|
(defmacro store
|
||||||
|
[^GridFSInputFile input & body]
|
||||||
|
`(let [^GridFSInputFile f# (doto ~input ~@body)]
|
||||||
|
(.save f# GridFS/DEFAULT_CHUNKSIZE)
|
||||||
|
(from-db-object f# true)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Higher-level API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn save
|
||||||
|
[^GridFSInputFile input]
|
||||||
|
(.save input GridFS/DEFAULT_CHUNKSIZE)
|
||||||
|
(from-db-object input true))
|
||||||
|
|
||||||
|
(defn filename
|
||||||
|
[^GridFSInputFile input ^String s]
|
||||||
|
(.setFilename input s)
|
||||||
|
input)
|
||||||
|
|
||||||
|
(defn content-type
|
||||||
|
[^GridFSInputFile input ^String s]
|
||||||
|
(.setContentType input s)
|
||||||
|
input)
|
||||||
|
|
||||||
|
(defn metadata
|
||||||
|
[^GridFSInputFile input md]
|
||||||
|
(.setMetaData input (to-db-object md))
|
||||||
|
input)
|
||||||
|
|
||||||
|
(defmacro store-file
|
||||||
|
[^GridFSInputFile input & body]
|
||||||
|
`(let [f# (-> ~input ~@body)]
|
||||||
|
(save f#)))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Finders
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn find
|
||||||
|
[^GridFS fs query]
|
||||||
|
(.find fs (to-db-object query)))
|
||||||
|
|
||||||
|
(defn find-by-filename
|
||||||
|
[^GridFS fs ^String filename]
|
||||||
|
(.find fs (to-db-object {"filename" filename})))
|
||||||
|
|
||||||
|
(defn find-by-md5
|
||||||
|
[^GridFS fs ^String md5]
|
||||||
|
(.find fs (to-db-object {"md5" md5})))
|
||||||
|
|
||||||
|
(defn find-one
|
||||||
|
[^GridFS fs query]
|
||||||
|
(.findOne fs (to-db-object query)))
|
||||||
|
|
||||||
|
(defn find-maps
|
||||||
|
[^GridFS fs query]
|
||||||
|
(map converter (find fs query)))
|
||||||
|
|
||||||
|
(defn find-one-as-map
|
||||||
|
[^GridFS fs query]
|
||||||
|
(converter (find-one fs query)))
|
||||||
|
|
||||||
|
(defn find-by-id
|
||||||
|
[^GridFS fs ^ObjectId id]
|
||||||
|
(.findOne fs id))
|
||||||
|
|
||||||
|
(defn find-map-by-id
|
||||||
|
[^GridFS fs ^ObjectId id]
|
||||||
|
(converter (find-by-id fs id)))
|
||||||
39
src/clojure/monger/internal/pagination.clj
Normal file
39
src/clojure/monger/internal/pagination.clj
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.internal.pagination)
|
||||||
|
|
||||||
|
(defn offset-for
|
||||||
|
[^long page ^long per-page]
|
||||||
|
(* per-page
|
||||||
|
(- (max page 1) 1)))
|
||||||
83
src/clojure/monger/joda_time.clj
Normal file
83
src/clojure/monger/joda_time.clj
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.joda-time
|
||||||
|
"An optional convenience namespaces for applications that heavily use dates and would prefer use JodaTime types
|
||||||
|
transparently when storing and loading them from MongoDB and serializing to JSON and/or with Clojure reader.
|
||||||
|
|
||||||
|
Enables automatic conversion of JodaTime date/time/instant instances to JDK dates (java.util.Date) when documents
|
||||||
|
are serialized and the other way around when documents are loaded. Extends clojure.data.json/Write-JSON protocol for
|
||||||
|
JodaTime types.
|
||||||
|
|
||||||
|
To use it, make sure you add dependencies on clj-time (or JodaTime) and clojure.data.json."
|
||||||
|
(:import [org.joda.time DateTime DateTimeZone ReadableInstant]
|
||||||
|
[org.joda.time.format ISODateTimeFormat])
|
||||||
|
(:require [monger.conversion :refer :all]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(extend-protocol ConvertToDBObject
|
||||||
|
org.joda.time.base.AbstractInstant
|
||||||
|
(to-db-object [^AbstractInstant input]
|
||||||
|
(to-db-object (.toDate input)))
|
||||||
|
org.joda.time.base.AbstractPartial
|
||||||
|
(to-db-object [^AbstractPartial input]
|
||||||
|
(to-db-object (.toDate input))))
|
||||||
|
|
||||||
|
(extend-protocol ConvertFromDBObject
|
||||||
|
java.util.Date
|
||||||
|
(from-db-object [^java.util.Date input keywordize]
|
||||||
|
(org.joda.time.DateTime. input)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Reader extensions
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defmethod print-dup org.joda.time.base.AbstractInstant
|
||||||
|
[^org.joda.time.base.AbstractInstant d out]
|
||||||
|
(print-dup (.toDate d) out))
|
||||||
|
|
||||||
|
|
||||||
|
(defmethod print-dup org.joda.time.base.AbstractPartial
|
||||||
|
[^org.joda.time.base.AbstractPartial d out]
|
||||||
|
(print-dup (.toDate d) out))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; JSON serialization
|
||||||
|
;;
|
||||||
|
|
||||||
|
(require 'clojurewerkz.support.json)
|
||||||
50
src/clojure/monger/js.clj
Normal file
50
src/clojure/monger/js.clj
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.js
|
||||||
|
"Kept for backwards compatibility. Please use clojurewerkz.support.js from now on."
|
||||||
|
(:require [clojurewerkz.support.js :as js]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn load-resource
|
||||||
|
"Loads a JavaScript resource (file from the classpath) and returns its content as a string.
|
||||||
|
The .js suffix at the end may be omitted.
|
||||||
|
|
||||||
|
Used primarily for map/reduce queries."
|
||||||
|
(^String [^String path]
|
||||||
|
(js/load-resource path)))
|
||||||
116
src/clojure/monger/json.clj
Normal file
116
src/clojure/monger/json.clj
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.json
|
||||||
|
"Provides clojure.data.json/Write-JSON protocol extension for MongoDB-specific types, such as
|
||||||
|
org.bson.types.ObjectId"
|
||||||
|
(:import org.bson.types.ObjectId
|
||||||
|
org.bson.types.BSONTimestamp))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Implementation
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; copied from clojure.reducers
|
||||||
|
(defmacro ^:private compile-if
|
||||||
|
"Evaluate `exp` and if it returns logical true and doesn't error, expand to
|
||||||
|
`then`. Else expand to `else`.
|
||||||
|
|
||||||
|
(compile-if (Class/forName \"java.util.concurrent.ForkJoinTask\")
|
||||||
|
(do-cool-stuff-with-fork-join)
|
||||||
|
(fall-back-to-executor-services))"
|
||||||
|
[exp then else]
|
||||||
|
(if (try (eval exp)
|
||||||
|
(catch Throwable _ false))
|
||||||
|
`(do ~then)
|
||||||
|
`(do ~else)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(require 'clojurewerkz.support.json)
|
||||||
|
|
||||||
|
;; all this madness would not be necessary if some people cared about backwards
|
||||||
|
;; compatiblity of the libraries they maintain. Shame on the clojure.data.json maintainer. MK.
|
||||||
|
(compile-if (and (find-ns 'clojure.data.json)
|
||||||
|
clojure.data.json/JSONWriter)
|
||||||
|
(try
|
||||||
|
(extend-protocol clojure.data.json/JSONWriter
|
||||||
|
ObjectId
|
||||||
|
(-write
|
||||||
|
([^ObjectId object out]
|
||||||
|
(clojure.data.json/write (.toString object) out))
|
||||||
|
([^ObjectId object out options]
|
||||||
|
(clojure.data.json/write (.toString object) out options))))
|
||||||
|
|
||||||
|
(extend-protocol clojure.data.json/JSONWriter
|
||||||
|
BSONTimestamp
|
||||||
|
(-write
|
||||||
|
([^BSONTimestamp object out]
|
||||||
|
(clojure.data.json/write {:time (.getTime object) :inc (.getInc object)} out))
|
||||||
|
([^BSONTimestamp object out options]
|
||||||
|
(clojure.data.json/write {:time (.getTime object) :inc (.getInc object)} out options))))
|
||||||
|
|
||||||
|
(catch Throwable _
|
||||||
|
false))
|
||||||
|
(comment "Nothing to do, clojure.data.json is not available"))
|
||||||
|
|
||||||
|
(compile-if (and (find-ns 'clojure.data.json)
|
||||||
|
clojure.data.json/Write-JSON)
|
||||||
|
(try
|
||||||
|
(extend-protocol clojure.data.json/Write-JSON
|
||||||
|
ObjectId
|
||||||
|
(write-json [^ObjectId object out escape-unicode?]
|
||||||
|
(clojure.data.json/write-json (.toString object) out escape-unicode?)))
|
||||||
|
(catch Throwable _
|
||||||
|
false))
|
||||||
|
(comment "Nothing to do, clojure.data.json 0.1.x is not available"))
|
||||||
|
|
||||||
|
|
||||||
|
(try
|
||||||
|
(require 'cheshire.generate)
|
||||||
|
(catch Throwable t
|
||||||
|
false))
|
||||||
|
|
||||||
|
(try
|
||||||
|
(cheshire.generate/add-encoder ObjectId
|
||||||
|
(fn [^ObjectId oid ^com.fasterxml.jackson.core.json.WriterBasedJsonGenerator generator]
|
||||||
|
(.writeString generator (.toString oid))))
|
||||||
|
(cheshire.generate/add-encoder BSONTimestamp
|
||||||
|
(fn [^BSONTimestamp ts ^com.fasterxml.jackson.core.json.WriterBasedJsonGenerator generator]
|
||||||
|
(cheshire.generate/encode-map {:time (.getTime ts) :inc (.getInc ts)} generator)))
|
||||||
|
(catch Throwable t
|
||||||
|
false))
|
||||||
459
src/clojure/monger/operators.clj
Normal file
459
src/clojure/monger/operators.clj
Normal file
|
|
@ -0,0 +1,459 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.operators
|
||||||
|
"Provides vars that represent various MongoDB operators, for example, $gt or $in or $regex.
|
||||||
|
They can be passed in queries as strings but using vars from this namespace makes the code
|
||||||
|
a bit cleaner and closer to what you would see in a MongoDB shell query.
|
||||||
|
|
||||||
|
Related documentation guide: http://clojuremongodb.info/articles/querying.html")
|
||||||
|
|
||||||
|
(defmacro ^{:private true} defoperator
|
||||||
|
[operator]
|
||||||
|
`(def ^{:const true} ~(symbol (str operator)) ~(str operator)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; QUERY OPERATORS
|
||||||
|
;;
|
||||||
|
|
||||||
|
(declare $gt $gte $lt $lte $all $in $nin $eq $ne $elemMatch $regex $options)
|
||||||
|
|
||||||
|
;; $gt is "greater than" comparator
|
||||||
|
;; $gte is "greater than or equals" comparator
|
||||||
|
;; $gt is "less than" comparator
|
||||||
|
;; $lte is "less than or equals" comparator
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/find "libraries" { :users { $gt 10 } })
|
||||||
|
;; (monger.collection/find "libraries" { :users { $gte 10 } })
|
||||||
|
;; (monger.collection/find "libraries" { :users { $lt 10 } })
|
||||||
|
;; (monger.collection/find "libraries" { :users { $lte 10 } })
|
||||||
|
(defoperator $gt)
|
||||||
|
(defoperator $gte)
|
||||||
|
(defoperator $lt)
|
||||||
|
(defoperator $lte)
|
||||||
|
|
||||||
|
;; $all matches all values in the array
|
||||||
|
;;
|
||||||
|
;; EXAMPLES
|
||||||
|
;; (mgcol/find-maps "languages" { :tags { $all [ "functional" "object-oriented" ] } } )
|
||||||
|
(defoperator $all)
|
||||||
|
|
||||||
|
;; The $in operator is analogous to the SQL IN modifier, allowing you to specify an array of possible matches.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES
|
||||||
|
;; (mgcol/find-maps "languages" { :tags { $in [ "functional" "object-oriented" ] } } )
|
||||||
|
(defoperator $in)
|
||||||
|
|
||||||
|
;; The $nin operator is similar to $in, but it selects objects for which the specified field does not
|
||||||
|
;; have any value in the specified array.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES
|
||||||
|
;; (mgcol/find-maps "languages" { :tags { $nin [ "functional" ] } } )
|
||||||
|
(defoperator $nin)
|
||||||
|
|
||||||
|
;; $eq is "equals" comparator
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/find "libraries" { :language { $eq "Clojure" }})
|
||||||
|
(defoperator $eq)
|
||||||
|
|
||||||
|
;; $ne is "non-equals" comparator
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/find "libraries" { :language { $ne "Clojure" }})
|
||||||
|
(defoperator $ne)
|
||||||
|
|
||||||
|
;; $elemMatch checks if an element in an array matches the specified expression
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; ;; Matches element with :text "Nice" and :rating greater than or equal 1
|
||||||
|
;; (monger.collection/find "comments" { $elemMatch { :text "Nice!" :rating { $gte 1 } } })
|
||||||
|
(defoperator $elemMatch)
|
||||||
|
|
||||||
|
(defoperator $regex)
|
||||||
|
(defoperator $options)
|
||||||
|
|
||||||
|
;; comment on a query predicate
|
||||||
|
|
||||||
|
(declare $comment $explain $hint $maxTimeMS $orderBy $query $returnKey $showDiskLoc $natural)
|
||||||
|
|
||||||
|
(defoperator $comment)
|
||||||
|
(defoperator $explain)
|
||||||
|
(defoperator $hint)
|
||||||
|
(defoperator $maxTimeMS)
|
||||||
|
(defoperator $orderBy)
|
||||||
|
(defoperator $query)
|
||||||
|
(defoperator $returnKey)
|
||||||
|
(defoperator $showDiskLoc)
|
||||||
|
(defoperator $natural)
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; EVALUATION (QUERY)
|
||||||
|
;;
|
||||||
|
|
||||||
|
(declare $expr $jsonSchema $where $and $or $nor)
|
||||||
|
|
||||||
|
(defoperator $expr)
|
||||||
|
(defoperator $jsonSchema)
|
||||||
|
|
||||||
|
;; Matches documents that satisfy a JavaScript expression.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;;
|
||||||
|
;; (monger.collection/find "people" { $where "this.placeOfBirth === this.address.city" })
|
||||||
|
(defoperator $where)
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; LOGIC OPERATORS
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; $and lets you use a boolean and in the query. Logical and means that all the given expressions should be true for positive match.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;;
|
||||||
|
;; ;; Matches all libraries where :language is "Clojure" and :users is greater than 10
|
||||||
|
;; (monger.collection/find "libraries" { $and [{ :language "Clojure" } { :users { $gt 10 } }] })
|
||||||
|
(defoperator $and)
|
||||||
|
|
||||||
|
;; $or lets you use a boolean or in the query. Logical or means that one of the given expressions should be true for positive match.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;;
|
||||||
|
;; ;; Matches all libraries whose :name is "mongoid" or :language is "Ruby"
|
||||||
|
;; (monger.collection.find "libraries" { $or [ { :name "mongoid" } { :language "Ruby" } ] })
|
||||||
|
(defoperator $or)
|
||||||
|
|
||||||
|
;; @nor lets you use a boolean expression, opposite to "all" in the query (think: neither). Give $nor a list of expressions, all of which should
|
||||||
|
;; be false for positive match.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;;
|
||||||
|
;; (monger.collection/find "libraries" { $nor [{ :language "Clojure" } {:users { $gt 10 } } ]})
|
||||||
|
(defoperator $nor)
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; ATOMIC MODIFIERS
|
||||||
|
;;
|
||||||
|
|
||||||
|
(declare $inc $mul $set $unset $setOnInsert $rename $push $position $each $addToSet $pop $pull $pullAll $bit $bitsAllClear $bitsAllSet $bitsAnyClear $bitsAnySet $exists $mod $size $type $not)
|
||||||
|
|
||||||
|
;; $inc increments one or many fields for the given value, otherwise sets the field to value
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/update "scores" { :_id user-id } { :score 10 } })
|
||||||
|
;; (monger.collection/update "scores" { :_id user-id } { :score 20 :bonus 10 } })
|
||||||
|
(defoperator $inc)
|
||||||
|
|
||||||
|
(defoperator $mul)
|
||||||
|
|
||||||
|
;; $set sets an existing (or non-existing) field (or set of fields) to value
|
||||||
|
;; $set supports all datatypes.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/update "things" { :_id oid } { $set { :weight 20.5 } })
|
||||||
|
;; (monger.collection/update "things" { :_id oid } { $set { :weight 20.5 :height 12.5 } })
|
||||||
|
(defoperator $set)
|
||||||
|
|
||||||
|
;; $unset deletes a given field, non-existing fields are ignored.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/update "things" { :_id oid } { $unset { :weight 1 } })
|
||||||
|
(defoperator $unset)
|
||||||
|
|
||||||
|
;; $setOnInsert assigns values to fields during an upsert only when using the upsert option to the update operation performs an insert.
|
||||||
|
;; New in version 2.4. http://docs.mongodb.org/manual/reference/operator/setOnInsert/
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/find-and-modify "things" {:_id oid} {$set {:lastseen now} $setOnInsert {:firstseen now}} :upsert true)
|
||||||
|
(defoperator $setOnInsert)
|
||||||
|
|
||||||
|
;; $rename renames a given field
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (monger.collection/update "things" { :_id oid } { $rename { :old_field_name "new_field_name" } })
|
||||||
|
(defoperator $rename)
|
||||||
|
|
||||||
|
;; $push appends _single_ value to field, if field is an existing array, otherwise sets field to the array [value] if field is not present.
|
||||||
|
;; If field is present but is not an array, an error condition is raised.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update "docs" { :_id oid } { $push { :tags "modifiers" } })
|
||||||
|
(defoperator $push)
|
||||||
|
|
||||||
|
;; $position modifies the behavior of $push per https://docs.mongodb.com/manual/reference/operator/update/position/
|
||||||
|
(defoperator $position)
|
||||||
|
|
||||||
|
;; $each is a modifier for the $push and $addToSet operators for appending multiple values to an array field.
|
||||||
|
;; Without the $each modifier $push and $addToSet will append an array as a single value.
|
||||||
|
;; MongoDB 2.4 adds support for the $each modifier to the $push operator.
|
||||||
|
;; In MongoDB 2.2 the $each modifier can only be used with the $addToSet operator.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update coll { :_id oid } { $push { :tags { $each ["mongodb" "docs"] } } })
|
||||||
|
(defoperator $each)
|
||||||
|
|
||||||
|
;; $addToSet Adds value to the array only if its not in the array already, if field is an existing array, otherwise sets field to the
|
||||||
|
;; array value if field is not present. If field is present but is not an array, an error condition is raised.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
|
||||||
|
(defoperator $addToSet)
|
||||||
|
|
||||||
|
;; $pop removes the last element in an array, if 1 is passed.
|
||||||
|
;; if -1 is passed, removes the first element in an array
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update coll { :_id oid } { $pop { :tags 1 } })
|
||||||
|
;; (mgcol/update coll { :_id oid } { $pop { :tags 1 :categories 1 } })
|
||||||
|
(defoperator $pop)
|
||||||
|
|
||||||
|
;; $pull removes all occurrences of value from field, if field is an array. If field is present but is not an array, an error condition
|
||||||
|
;; is raised.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update coll { :_id oid } { $pull { :measurements 1.2 } })
|
||||||
|
(defoperator $pull)
|
||||||
|
|
||||||
|
;; $pullAll removes all occurrences of each value in value_array from field, if field is an array. If field is present but is not an array
|
||||||
|
;; an error condition is raised.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update coll { :_id oid } { $pullAll { :measurements 1.2 } })
|
||||||
|
;; (mgcol/update coll { :_id oid } { $pullAll { :measurements { $gte 1.2 } } })
|
||||||
|
(defoperator $pullAll)
|
||||||
|
|
||||||
|
(defoperator $bit)
|
||||||
|
(defoperator $bitsAllClear)
|
||||||
|
(defoperator $bitsAllSet)
|
||||||
|
(defoperator $bitsAnyClear)
|
||||||
|
(defoperator $bitsAnySet)
|
||||||
|
|
||||||
|
(defoperator $exists)
|
||||||
|
(defoperator $mod)
|
||||||
|
(defoperator $size)
|
||||||
|
(defoperator $type)
|
||||||
|
(defoperator $not)
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Aggregation in 4.2
|
||||||
|
;;
|
||||||
|
|
||||||
|
(declare $addFields $bucket $bucketAuto $collStats $facet $geoNear $graphLookup $indexStats $listSessions $lookup $match $merge $out $planCacheStats $project $redact $replaceRoot $replaceWith $sample $limit $skip $unwind $group $sort $sortByCount $currentOp $listLocalSessions $cmp $min $max $avg $stdDevPop $stdDevSamp $sum $let $first $last $abs $add $ceil $divide $exp $floor $ln $log $log10 $multiply $pow $round $sqrt $subtract $trunc $literal $arrayElemAt $arrayToObject $concatArrays $filter $indexOfArray $isArray $map $objectToArray $range $reduce $reverseArray $zip $mergeObjects $allElementsTrue $anyElementsTrue $setDifference $setEquals $setIntersection $setIsSubset $setUnion $strcasecmp $substr $substrBytes $substrCP $toLower $toString $toUpper $concat $indexOfBytes $indexOfCP $ltrim $regexFind $regexFindAll $regexMatch $rtrim $split $strLenBytes $subLenCP $trim $sin $cos $tan $asin $acos $atan $atan2 $asinh $acosh $atanh $radiansToDegrees $degreesToRadians $convert $toBool $toDecimal $toDouble $toInt $toLong $toObjectId $dayOfMonth $dayOfWeek $dayOfYear $hour $minute $month $second $millisecond $week $year $isoDate $dateFromParts $dateFromString $dateToParts $dateToString $isoDayOfWeek $isoWeek $isoWeekYear $toDate $ifNull $cond $switch)
|
||||||
|
|
||||||
|
(defoperator $addFields)
|
||||||
|
(defoperator $bucket)
|
||||||
|
(defoperator $bucketAuto)
|
||||||
|
(defoperator $collStats)
|
||||||
|
(defoperator $facet)
|
||||||
|
(defoperator $geoNear)
|
||||||
|
(defoperator $graphLookup)
|
||||||
|
(defoperator $indexStats)
|
||||||
|
(defoperator $listSessions)
|
||||||
|
(defoperator $lookup)
|
||||||
|
(defoperator $match)
|
||||||
|
(defoperator $merge)
|
||||||
|
(defoperator $out)
|
||||||
|
(defoperator $planCacheStats)
|
||||||
|
(defoperator $project)
|
||||||
|
(defoperator $redact)
|
||||||
|
(defoperator $replaceRoot)
|
||||||
|
(defoperator $replaceWith)
|
||||||
|
(defoperator $sample)
|
||||||
|
(defoperator $limit)
|
||||||
|
(defoperator $skip)
|
||||||
|
(defoperator $unwind)
|
||||||
|
(defoperator $group)
|
||||||
|
(defoperator $sort)
|
||||||
|
(defoperator $sortByCount)
|
||||||
|
|
||||||
|
(defoperator $currentOp)
|
||||||
|
(defoperator $listLocalSessions)
|
||||||
|
|
||||||
|
(defoperator $cmp)
|
||||||
|
|
||||||
|
(defoperator $min)
|
||||||
|
(defoperator $max)
|
||||||
|
(defoperator $avg)
|
||||||
|
(defoperator $stdDevPop)
|
||||||
|
(defoperator $stdDevSamp)
|
||||||
|
(defoperator $sum)
|
||||||
|
(defoperator $let)
|
||||||
|
|
||||||
|
(defoperator $first)
|
||||||
|
(defoperator $last)
|
||||||
|
|
||||||
|
(defoperator $abs)
|
||||||
|
(defoperator $add)
|
||||||
|
(defoperator $ceil)
|
||||||
|
(defoperator $divide)
|
||||||
|
(defoperator $exp)
|
||||||
|
(defoperator $floor)
|
||||||
|
(defoperator $ln)
|
||||||
|
(defoperator $log)
|
||||||
|
(defoperator $log10)
|
||||||
|
(defoperator $multiply)
|
||||||
|
(defoperator $pow)
|
||||||
|
(defoperator $round)
|
||||||
|
(defoperator $sqrt)
|
||||||
|
(defoperator $subtract)
|
||||||
|
(defoperator $trunc)
|
||||||
|
(defoperator $literal)
|
||||||
|
|
||||||
|
(defoperator $arrayElemAt)
|
||||||
|
(defoperator $arrayToObject)
|
||||||
|
(defoperator $concatArrays)
|
||||||
|
(defoperator $filter)
|
||||||
|
(defoperator $indexOfArray)
|
||||||
|
(defoperator $isArray)
|
||||||
|
(defoperator $map)
|
||||||
|
(defoperator $objectToArray)
|
||||||
|
(defoperator $range)
|
||||||
|
(defoperator $reduce)
|
||||||
|
(defoperator $reverseArray)
|
||||||
|
(defoperator $zip)
|
||||||
|
(defoperator $mergeObjects)
|
||||||
|
|
||||||
|
(defoperator $allElementsTrue)
|
||||||
|
(defoperator $anyElementsTrue)
|
||||||
|
(defoperator $setDifference)
|
||||||
|
(defoperator $setEquals)
|
||||||
|
(defoperator $setIntersection)
|
||||||
|
(defoperator $setIsSubset)
|
||||||
|
(defoperator $setUnion)
|
||||||
|
|
||||||
|
(defoperator $strcasecmp)
|
||||||
|
(defoperator $substr)
|
||||||
|
(defoperator $substrBytes)
|
||||||
|
(defoperator $substrCP)
|
||||||
|
(defoperator $toLower)
|
||||||
|
(defoperator $toString)
|
||||||
|
(defoperator $toUpper)
|
||||||
|
(defoperator $concat)
|
||||||
|
(defoperator $indexOfBytes)
|
||||||
|
(defoperator $indexOfCP)
|
||||||
|
(defoperator $ltrim)
|
||||||
|
(defoperator $regexFind)
|
||||||
|
(defoperator $regexFindAll)
|
||||||
|
(defoperator $regexMatch)
|
||||||
|
(defoperator $rtrim)
|
||||||
|
(defoperator $split)
|
||||||
|
(defoperator $strLenBytes)
|
||||||
|
(defoperator $subLenCP)
|
||||||
|
(defoperator $trim)
|
||||||
|
|
||||||
|
(defoperator $sin)
|
||||||
|
(defoperator $cos)
|
||||||
|
(defoperator $tan)
|
||||||
|
(defoperator $asin)
|
||||||
|
(defoperator $acos)
|
||||||
|
(defoperator $atan)
|
||||||
|
(defoperator $atan2)
|
||||||
|
(defoperator $asinh)
|
||||||
|
(defoperator $acosh)
|
||||||
|
(defoperator $atanh)
|
||||||
|
(defoperator $radiansToDegrees)
|
||||||
|
(defoperator $degreesToRadians)
|
||||||
|
|
||||||
|
(defoperator $convert)
|
||||||
|
(defoperator $toBool)
|
||||||
|
(defoperator $toDecimal)
|
||||||
|
(defoperator $toDouble)
|
||||||
|
(defoperator $toInt)
|
||||||
|
(defoperator $toLong)
|
||||||
|
(defoperator $toObjectId)
|
||||||
|
|
||||||
|
(defoperator $dayOfMonth)
|
||||||
|
(defoperator $dayOfWeek)
|
||||||
|
(defoperator $dayOfYear)
|
||||||
|
(defoperator $hour)
|
||||||
|
(defoperator $minute)
|
||||||
|
(defoperator $month)
|
||||||
|
(defoperator $second)
|
||||||
|
(defoperator $millisecond)
|
||||||
|
(defoperator $week)
|
||||||
|
(defoperator $year)
|
||||||
|
(defoperator $isoDate)
|
||||||
|
(defoperator $dateFromParts)
|
||||||
|
(defoperator $dateFromString)
|
||||||
|
(defoperator $dateToParts)
|
||||||
|
(defoperator $dateToString)
|
||||||
|
(defoperator $isoDayOfWeek)
|
||||||
|
(defoperator $isoWeek)
|
||||||
|
(defoperator $isoWeekYear)
|
||||||
|
(defoperator $toDate)
|
||||||
|
|
||||||
|
|
||||||
|
(defoperator $ifNull)
|
||||||
|
(defoperator $cond)
|
||||||
|
(defoperator $switch)
|
||||||
|
|
||||||
|
;; Geospatial
|
||||||
|
(declare $geoWithin $geoIntersects $near $nearSphere $geometry $maxDistance $minDistance $center $centerSphere $box $polygon $slice)
|
||||||
|
(defoperator $geoWithin)
|
||||||
|
(defoperator $geoIntersects)
|
||||||
|
(defoperator $near)
|
||||||
|
(defoperator $nearSphere)
|
||||||
|
(defoperator $geometry)
|
||||||
|
(defoperator $maxDistance)
|
||||||
|
(defoperator $minDistance)
|
||||||
|
(defoperator $center)
|
||||||
|
(defoperator $centerSphere)
|
||||||
|
(defoperator $box)
|
||||||
|
(defoperator $polygon)
|
||||||
|
|
||||||
|
(defoperator $slice)
|
||||||
|
|
||||||
|
;; full text search
|
||||||
|
(declare $text $meta $search $language $natural $currentDate $isolated $count)
|
||||||
|
(defoperator $text)
|
||||||
|
(defoperator $meta)
|
||||||
|
(defoperator $search)
|
||||||
|
(defoperator $language)
|
||||||
|
(defoperator $natural)
|
||||||
|
|
||||||
|
;; $currentDate operator sets the value of a field to the current date, either as a Date or a timestamp. The default type is Date.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update coll { :_id oid } { $currentDate { :lastModified true } })
|
||||||
|
(defoperator $currentDate)
|
||||||
|
|
||||||
|
;; Isolates intermediate multi-document updates from other clients.
|
||||||
|
;;
|
||||||
|
;; EXAMPLES:
|
||||||
|
;; (mgcol/update "libraries" { :language "Clojure", $isolated 1 } { $inc { :popularity 1 } } {:multi true})
|
||||||
|
(defoperator $isolated)
|
||||||
|
|
||||||
|
(defoperator $count)
|
||||||
189
src/clojure/monger/query.clj
Normal file
189
src/clojure/monger/query.clj
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.query
|
||||||
|
"Provides an expressive Query DSL that is very close to that in the Mongo shell (within reason).
|
||||||
|
This is the most flexible and recommended way to query with Monger. Queries can be composed, like in Korma.
|
||||||
|
|
||||||
|
Related documentation guide: http://clojuremongodb.info/articles/querying.html"
|
||||||
|
(:refer-clojure :exclude [select find sort])
|
||||||
|
(:require [monger.core]
|
||||||
|
[monger.internal pagination]
|
||||||
|
[monger.cursor :as cursor :refer [add-options]]
|
||||||
|
[monger.conversion :refer :all]
|
||||||
|
[monger.operators :refer :all])
|
||||||
|
(:import [com.mongodb DB DBCollection DBObject DBCursor ReadPreference]
|
||||||
|
[java.util.concurrent TimeUnit]
|
||||||
|
java.util.List))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Implementation
|
||||||
|
;;
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Cursor/chain methods
|
||||||
|
;;
|
||||||
|
;; Monger query is an auxiliary construction that helps to create funciton chains through cursors.
|
||||||
|
;; You can specify several chained actions that will be performed on the certain collection through
|
||||||
|
;; query fields.
|
||||||
|
;;
|
||||||
|
;; Existing query fields:
|
||||||
|
;;
|
||||||
|
;; :fields - selects which fields are returned. The default is all fields. _id is included by default.
|
||||||
|
;; :sort - adds a sort to the query.
|
||||||
|
;; :fields - set of fields to retrieve during query execution
|
||||||
|
;; :skip - Skips the first N results.
|
||||||
|
;; :limit - Returns a maximum of N results.
|
||||||
|
;; :batch-size - limits the nubmer of elements returned in one batch.
|
||||||
|
;; :snapshot - sses snapshot mode for the query. Snapshot mode assures no duplicates are returned, or objects missed
|
||||||
|
;; which were present at both the start and end of the query's execution (if an object is new during the query, or
|
||||||
|
;; deleted during the query, it may or may not be returned, even with snapshot mode). Note that short query responses
|
||||||
|
;; (less than 1MB) are always effectively snapshotted. Currently, snapshot mode may not be used with sorting or explicit hints.
|
||||||
|
(defn empty-query
|
||||||
|
([]
|
||||||
|
{
|
||||||
|
:query {}
|
||||||
|
:sort {}
|
||||||
|
:fields []
|
||||||
|
:skip 0
|
||||||
|
:limit 0
|
||||||
|
:batch-size 256
|
||||||
|
:snapshot false
|
||||||
|
:keywordize-fields true
|
||||||
|
})
|
||||||
|
([^DBCollection coll]
|
||||||
|
(merge (empty-query) { :collection coll })))
|
||||||
|
|
||||||
|
(defn exec
|
||||||
|
[{:keys [^DBCollection collection
|
||||||
|
query
|
||||||
|
fields
|
||||||
|
skip
|
||||||
|
limit
|
||||||
|
sort
|
||||||
|
batch-size
|
||||||
|
hint
|
||||||
|
snapshot
|
||||||
|
read-preference
|
||||||
|
keywordize-fields
|
||||||
|
max-time
|
||||||
|
options]
|
||||||
|
:or { limit 0 batch-size 256 skip 0 } }]
|
||||||
|
(with-open [cursor (doto (.find collection (to-db-object query) (as-field-selector fields))
|
||||||
|
(.limit limit)
|
||||||
|
(.skip skip)
|
||||||
|
(.sort (to-db-object sort))
|
||||||
|
(.batchSize batch-size))]
|
||||||
|
(when snapshot
|
||||||
|
(.snapshot cursor))
|
||||||
|
(when hint
|
||||||
|
(.hint cursor (to-db-object hint)))
|
||||||
|
(when read-preference
|
||||||
|
(.setReadPreference cursor read-preference))
|
||||||
|
(when max-time
|
||||||
|
(.maxTime cursor max-time TimeUnit/MILLISECONDS))
|
||||||
|
(when options
|
||||||
|
(add-options cursor options))
|
||||||
|
(map (fn [x] (from-db-object x keywordize-fields))
|
||||||
|
cursor)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn find
|
||||||
|
[m query]
|
||||||
|
(merge m { :query query }))
|
||||||
|
|
||||||
|
(defn fields
|
||||||
|
[m flds]
|
||||||
|
(merge m { :fields flds }))
|
||||||
|
|
||||||
|
(defn sort
|
||||||
|
[m srt]
|
||||||
|
(merge m { :sort srt }))
|
||||||
|
|
||||||
|
(defn skip
|
||||||
|
[m ^long n]
|
||||||
|
(merge m { :skip n }))
|
||||||
|
|
||||||
|
(defn limit
|
||||||
|
[m ^long n]
|
||||||
|
(merge m { :limit n }))
|
||||||
|
|
||||||
|
(defn batch-size
|
||||||
|
[m ^long n]
|
||||||
|
(merge m { :batch-size n }))
|
||||||
|
|
||||||
|
(defn hint
|
||||||
|
[m h]
|
||||||
|
(merge m { :hint h }))
|
||||||
|
|
||||||
|
(defn snapshot
|
||||||
|
[m]
|
||||||
|
(merge m { :snapshot true }))
|
||||||
|
|
||||||
|
(defn read-preference
|
||||||
|
[m ^ReadPreference rp]
|
||||||
|
(merge m { :read-preference rp }))
|
||||||
|
|
||||||
|
(defn max-time
|
||||||
|
[m ^long max-time]
|
||||||
|
(merge m { :max-time max-time }))
|
||||||
|
|
||||||
|
(defn options
|
||||||
|
[m opts]
|
||||||
|
(merge m { :options opts }))
|
||||||
|
|
||||||
|
(defn keywordize-fields
|
||||||
|
[m bool]
|
||||||
|
(merge m { :keywordize-fields bool }))
|
||||||
|
|
||||||
|
(defn paginate
|
||||||
|
[m & { :keys [page per-page] :or { page 1 per-page 10 } }]
|
||||||
|
(merge m { :limit per-page :skip (monger.internal.pagination/offset-for page per-page) }))
|
||||||
|
|
||||||
|
(defmacro with-collection
|
||||||
|
[db coll & body]
|
||||||
|
`(let [coll# ~coll
|
||||||
|
^DB db# ~db
|
||||||
|
db-coll# (if (string? coll#)
|
||||||
|
(.getCollection db# coll#)
|
||||||
|
coll#)
|
||||||
|
query# (-> (empty-query db-coll#) ~@body)]
|
||||||
|
(exec query#)))
|
||||||
|
|
||||||
|
(defmacro partial-query
|
||||||
|
[& body]
|
||||||
|
`(-> {} ~@body))
|
||||||
65
src/clojure/monger/ragtime.clj
Normal file
65
src/clojure/monger/ragtime.clj
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.ragtime
|
||||||
|
"Ragtime integration"
|
||||||
|
(:refer-clojure :exclude [find sort])
|
||||||
|
(:require [ragtime.protocols :as proto]
|
||||||
|
[monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.query :refer [with-collection find sort]])
|
||||||
|
(:import java.util.Date
|
||||||
|
[com.mongodb DB WriteConcern]))
|
||||||
|
|
||||||
|
|
||||||
|
(def ^{:const true}
|
||||||
|
migrations-collection "meta.migrations")
|
||||||
|
|
||||||
|
|
||||||
|
(extend-type com.mongodb.DB
|
||||||
|
proto/DataStore
|
||||||
|
(add-migration-id [db id]
|
||||||
|
(mc/insert db migrations-collection {:_id id :created_at (Date.)} WriteConcern/FSYNC_SAFE))
|
||||||
|
(remove-migration-id [db id]
|
||||||
|
(mc/remove-by-id db migrations-collection id))
|
||||||
|
(applied-migration-ids [db]
|
||||||
|
(let [xs (with-collection db migrations-collection
|
||||||
|
(find {})
|
||||||
|
(sort {:created_at 1}))]
|
||||||
|
(vec (map :_id xs)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn flush-migrations!
|
||||||
|
"REMOVES all the information about previously performed migrations"
|
||||||
|
[^DB db]
|
||||||
|
(mc/remove db migrations-collection))
|
||||||
72
src/clojure/monger/result.clj
Normal file
72
src/clojure/monger/result.clj
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.result
|
||||||
|
"Provides functions that determine if a query (or other database operation)
|
||||||
|
was successful or not.
|
||||||
|
|
||||||
|
Related documentation guides:
|
||||||
|
|
||||||
|
* http://clojuremongodb.info/articles/inserting.html
|
||||||
|
* http://clojuremongodb.info/articles/updating.html
|
||||||
|
* http://clojuremongodb.info/articles/commands.html
|
||||||
|
* http://clojuremongodb.info/articles/mapreduce.html"
|
||||||
|
(:import [com.mongodb WriteResult CommandResult])
|
||||||
|
(:require monger.conversion))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defprotocol WriteResultPredicates
|
||||||
|
(acknowledged? [input] "Returns true if write result is a success")
|
||||||
|
(updated-existing? [input] "Returns true if write result has updated an existing document"))
|
||||||
|
|
||||||
|
(extend-protocol WriteResultPredicates
|
||||||
|
WriteResult
|
||||||
|
(acknowledged?
|
||||||
|
[^WriteResult result]
|
||||||
|
(.wasAcknowledged result))
|
||||||
|
(updated-existing?
|
||||||
|
[^WriteResult result]
|
||||||
|
(.isUpdateOfExisting result))
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
(acknowledged?
|
||||||
|
[^CommandResult result]
|
||||||
|
(.ok result)))
|
||||||
|
|
||||||
|
(defn affected-count
|
||||||
|
"Get the number of documents affected"
|
||||||
|
[^WriteResult result]
|
||||||
|
(.getN result))
|
||||||
135
src/clojure/monger/ring/session_store.clj
Normal file
135
src/clojure/monger/ring/session_store.clj
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns monger.ring.session-store
|
||||||
|
(:require [ring.middleware.session.store :as ringstore]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.core :as mg]
|
||||||
|
[monger.conversion :refer :all])
|
||||||
|
(:import [java.util UUID Date]
|
||||||
|
[com.mongodb DB]
|
||||||
|
ring.middleware.session.store.SessionStore))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Implementation
|
||||||
|
;;
|
||||||
|
|
||||||
|
(def ^{:const true}
|
||||||
|
default-session-store-collection "web_sessions")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; this session store stores Clojure data structures using Clojure reader. It will correctly store every
|
||||||
|
;; data structure Clojure reader can serialize and read but won't make the data useful to applications
|
||||||
|
;; in other languages.
|
||||||
|
|
||||||
|
(defrecord ClojureReaderBasedMongoDBSessionStore [^DB db ^String collection-name])
|
||||||
|
|
||||||
|
(defmethod print-dup java.util.Date
|
||||||
|
[^java.util.Date d ^java.io.Writer out]
|
||||||
|
(.write out
|
||||||
|
(str "#="
|
||||||
|
`(java.util.Date. ~(.getYear d)
|
||||||
|
~(.getMonth d)
|
||||||
|
~(.getDate d)
|
||||||
|
~(.getHours d)
|
||||||
|
~(.getMinutes d)
|
||||||
|
~(.getSeconds d)))))
|
||||||
|
|
||||||
|
(defmethod print-dup org.bson.types.ObjectId
|
||||||
|
[oid ^java.io.Writer out]
|
||||||
|
(.write out
|
||||||
|
(str "#="
|
||||||
|
`(org.bson.types.ObjectId. ~(str oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(extend-protocol ringstore/SessionStore
|
||||||
|
ClojureReaderBasedMongoDBSessionStore
|
||||||
|
|
||||||
|
(read-session [store key]
|
||||||
|
(if key
|
||||||
|
(if-let [m (mc/find-one-as-map (.db store) (.collection-name store) {:_id key})]
|
||||||
|
(read-string (:value m))
|
||||||
|
{})
|
||||||
|
{}))
|
||||||
|
|
||||||
|
(write-session [store key data]
|
||||||
|
(let [date (Date.)
|
||||||
|
key (or key (str (UUID/randomUUID)))
|
||||||
|
value (binding [*print-dup* true]
|
||||||
|
(pr-str (assoc data :_id key)))]
|
||||||
|
(mc/save (.db store) (.collection-name store) {:_id key :value value :date date})
|
||||||
|
key))
|
||||||
|
|
||||||
|
(delete-session [store key]
|
||||||
|
(mc/remove-by-id (.db store) (.collection-name store) key)
|
||||||
|
nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defn session-store
|
||||||
|
[^DB db ^String s]
|
||||||
|
(ClojureReaderBasedMongoDBSessionStore. db s))
|
||||||
|
|
||||||
|
|
||||||
|
;; this session store won't store namespaced keywords correctly but stores results in a way
|
||||||
|
;; that applications in other languages can read. DO NOT use it with Friend.
|
||||||
|
|
||||||
|
(defrecord MongoDBSessionStore [^DB db ^String collection-name])
|
||||||
|
|
||||||
|
(extend-protocol ringstore/SessionStore
|
||||||
|
MongoDBSessionStore
|
||||||
|
|
||||||
|
(read-session [store key]
|
||||||
|
(if-let [m (and key
|
||||||
|
(mc/find-one-as-map (.db store) (.collection-name store) {:_id key}))]
|
||||||
|
m
|
||||||
|
{}))
|
||||||
|
|
||||||
|
(write-session [store key data]
|
||||||
|
(let [key (or key (str (UUID/randomUUID)))]
|
||||||
|
(mc/save (.db store) (.collection-name store) (assoc data :date (Date.) :_id key))
|
||||||
|
key))
|
||||||
|
|
||||||
|
(delete-session [store key]
|
||||||
|
(mc/remove-by-id (.db store) (.collection-name store) key)
|
||||||
|
nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defn monger-store
|
||||||
|
[^DB db ^String s]
|
||||||
|
(MongoDBSessionStore. db s))
|
||||||
82
src/clojure/monger/util.clj
Normal file
82
src/clojure/monger/util.clj
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
;; This source code is dual-licensed under the Apache License, version
|
||||||
|
;; 2.0, and the Eclipse Public License, version 1.0.
|
||||||
|
;;
|
||||||
|
;; The APL v2.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
|
||||||
|
;;
|
||||||
|
;; Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
;; you may not use this file except in compliance with the License.
|
||||||
|
;; You may obtain a copy of the License at
|
||||||
|
;;
|
||||||
|
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
;;
|
||||||
|
;; Unless required by applicable law or agreed to in writing, software
|
||||||
|
;; distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
;; See the License for the specific language governing permissions and
|
||||||
|
;; limitations under the License.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;;
|
||||||
|
;; The EPL v1.0:
|
||||||
|
;;
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; This program and the accompanying materials are made available under the terms of
|
||||||
|
;; the Eclipse Public License Version 1.0,
|
||||||
|
;; which accompanies this distribution and is available at
|
||||||
|
;; http://www.eclipse.org/legal/epl-v10.html.
|
||||||
|
;; ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(ns ^{:doc "Provides various utility functions, primarily for working with document ids."} monger.util
|
||||||
|
(:refer-clojure :exclude [random-uuid])
|
||||||
|
(:import java.security.SecureRandom
|
||||||
|
java.math.BigInteger
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
com.mongodb.DBObject
|
||||||
|
clojure.lang.IPersistentMap
|
||||||
|
java.util.Map)
|
||||||
|
(:refer-clojure :exclude [random-uuid]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn ^String random-uuid
|
||||||
|
"Generates a secure random UUID string"
|
||||||
|
[]
|
||||||
|
(.toString (java.util.UUID/randomUUID)))
|
||||||
|
|
||||||
|
(defn ^String random-str
|
||||||
|
"Generates a secure random string"
|
||||||
|
[^long n, ^long num-base]
|
||||||
|
(.toString (new BigInteger n (SecureRandom.)) num-base))
|
||||||
|
|
||||||
|
(defn ^ObjectId object-id
|
||||||
|
"Returns a new BSON object id, or converts str to BSON object id"
|
||||||
|
([]
|
||||||
|
(ObjectId.))
|
||||||
|
([^String s]
|
||||||
|
(ObjectId. s)))
|
||||||
|
|
||||||
|
(defprotocol GetDocumentId
|
||||||
|
(get-id [input] "Returns document id"))
|
||||||
|
|
||||||
|
(extend-protocol GetDocumentId
|
||||||
|
DBObject
|
||||||
|
(get-id
|
||||||
|
[^DBObject object]
|
||||||
|
(.get object "_id"))
|
||||||
|
|
||||||
|
IPersistentMap
|
||||||
|
(get-id
|
||||||
|
[^IPersistentMap object]
|
||||||
|
(or (:_id object) (object "_id"))))
|
||||||
|
|
||||||
|
(defn into-array-list
|
||||||
|
"Coerce a j.u.Collection into a j.u.ArrayList."
|
||||||
|
^java.util.ArrayList [^java.util.Collection coll]
|
||||||
|
(java.util.ArrayList. coll))
|
||||||
0
src/java/.gitkeep
Normal file
0
src/java/.gitkeep
Normal file
|
|
@ -1,545 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;; Copyright (c) 2012 Toby Hede
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.collection
|
|
||||||
(:refer-clojure :exclude [find remove count drop distinct empty?])
|
|
||||||
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType]
|
|
||||||
[java.util List Map]
|
|
||||||
[clojure.lang IPersistentMap ISeq]
|
|
||||||
[org.bson.types ObjectId])
|
|
||||||
(:require [monger core result])
|
|
||||||
(:use [monger.conversion]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Implementation
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn- fields-to-db-object
|
|
||||||
[^List fields]
|
|
||||||
(zipmap fields (repeat 1)))
|
|
||||||
|
|
||||||
(definline check-not-nil!
|
|
||||||
[ref ^String message]
|
|
||||||
`(when (nil? ~ref)
|
|
||||||
(throw (IllegalArgumentException. ~message))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/insert
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ^WriteResult insert
|
|
||||||
"Saves @document@ to @collection@. You can optionally specify WriteConcern.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
|
|
||||||
(monger.collection/insert \"people\" { :name \"Joe\", :age 30 })
|
|
||||||
|
|
||||||
(monger.collection/insert \"people\" { :name \"Joe\", :age 30, WriteConcern/SAFE })
|
|
||||||
"
|
|
||||||
([^String collection ^DBObject document]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.insert ^DBCollection coll ^DBObject (to-db-object document) ^WriteConcern monger.core/*mongodb-write-concern*)))
|
|
||||||
([^String collection ^DBObject document ^WriteConcern concern]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.insert ^DBCollection coll ^DBObject (to-db-object document) ^WriteConcern concern)))
|
|
||||||
([^DB db ^String collection ^DBObject document ^WriteConcern concern]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.insert ^DBCollection coll ^DBObject (to-db-object document) ^WriteConcern concern))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn ^WriteResult insert-batch
|
|
||||||
"Saves @documents@ do @collection@. You can optionally specify WriteConcern as a third argument.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
|
|
||||||
(monger.collection/insert-batch \"people\" [{ :name \"Joe\", :age 30 }, { :name \"Paul\", :age 27 }])
|
|
||||||
|
|
||||||
(monger.collection/insert-batch \"people\" [{ :name \"Joe\", :age 30 }, { :name \"Paul\", :age 27 }] WriteConcern/NORMAL)
|
|
||||||
|
|
||||||
"
|
|
||||||
([^String collection, ^List documents]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.insert ^DBCollection coll ^List (to-db-object documents) ^WriteConcern monger.core/*mongodb-write-concern*)))
|
|
||||||
([^String collection, ^List documents, ^WriteConcern concern]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.insert ^DBCollection coll ^List (to-db-object documents) ^WriteConcern concern)))
|
|
||||||
([^DB db ^String collection, ^List documents, ^WriteConcern concern]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.insert ^DBCollection coll ^List (to-db-object documents) ^WriteConcern concern))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/find
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ^DBCursor find
|
|
||||||
"Queries for objects in this collection.
|
|
||||||
This function returns DBCursor, which allows you to iterate over DBObjects.
|
|
||||||
If you want to manipulate clojure sequences maps, please @find-maps@.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
;; return all objects in this collection.
|
|
||||||
(mgcol/find \"people\")
|
|
||||||
|
|
||||||
;; return all objects matching query
|
|
||||||
(mgcol/find \"people\" { :company \"Comp Corp\"})
|
|
||||||
|
|
||||||
;; return all objects matching query, taking only specified fields
|
|
||||||
(mgcol/find \"people\" { :company \"Comp Corp\"} [:first_name :last_name])
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.find coll)))
|
|
||||||
([^String collection ^Map ref]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.find ^DBCollection coll ^DBObject (to-db-object ref))))
|
|
||||||
([^String collection ^Map ref ^List fields]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)
|
|
||||||
map-of-fields (fields-to-db-object fields)]
|
|
||||||
(.find ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields))))
|
|
||||||
([^DB db ^String collection ^Map ref ^List fields]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)
|
|
||||||
map-of-fields (fields-to-db-object fields)]
|
|
||||||
(.find ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields)))))
|
|
||||||
|
|
||||||
(defn ^ISeq find-maps
|
|
||||||
"Queries for objects in this collection.
|
|
||||||
This function returns clojure Seq of Maps.
|
|
||||||
If you want to work directly with DBObject, use find.
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(map (fn [x] (from-db-object x true)) (find collection)))
|
|
||||||
([^String collection ^Map ref]
|
|
||||||
(map (fn [x] (from-db-object x true)) (find collection ref)))
|
|
||||||
([^String collection ^Map ref ^List fields]
|
|
||||||
(map (fn [x] (from-db-object x true)) (find collection ref fields)))
|
|
||||||
([^DB db ^String collection ^Map ref ^List fields]
|
|
||||||
(map (fn [x] (from-db-object x true)) (find db collection ref fields))))
|
|
||||||
|
|
||||||
(defn ^ISeq find-seq
|
|
||||||
"Queries for objects in this collection, returns ISeq of DBObjects."
|
|
||||||
([^String collection]
|
|
||||||
(seq (find collection)))
|
|
||||||
([^String collection ^Map ref]
|
|
||||||
(seq (find collection ref)))
|
|
||||||
([^String collection ^Map ref ^List fields]
|
|
||||||
(seq (find collection ref fields)))
|
|
||||||
([^DB db ^String collection ^Map ref ^List fields]
|
|
||||||
(seq (find db collection ref fields))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/find-one
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ^DBObject find-one
|
|
||||||
"Returns a single DBObject from this collection matching the query.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
|
|
||||||
(mgcol/find-one collection { :language \"Clojure\" })
|
|
||||||
|
|
||||||
;; Return only :language field.
|
|
||||||
;; Note that _id field is always returned.
|
|
||||||
(mgcol/find-one collection { :language \"Clojure\" } [:language])
|
|
||||||
|
|
||||||
"
|
|
||||||
([^String collection ^Map ref]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.findOne ^DBCollection coll ^DBObject (to-db-object ref))))
|
|
||||||
([^String collection ^Map ref ^List fields]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)
|
|
||||||
map-of-fields (fields-to-db-object fields)]
|
|
||||||
(.findOne ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields))))
|
|
||||||
([^DB db ^String collection ^Map ref ^List fields]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)
|
|
||||||
map-of-fields (fields-to-db-object fields)]
|
|
||||||
(.findOne ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields)))))
|
|
||||||
|
|
||||||
(defn ^IPersistentMap find-one-as-map
|
|
||||||
"Returns a single object converted to Map from this collection matching the query."
|
|
||||||
([^String collection ^Map ref]
|
|
||||||
(from-db-object ^DBObject (find-one collection ref) true))
|
|
||||||
([^String collection ^Map ref ^List fields]
|
|
||||||
(from-db-object ^DBObject (find-one collection ref fields) true))
|
|
||||||
([^String collection ^Map ref ^List fields keywordize]
|
|
||||||
(from-db-object ^DBObject (find-one collection ref fields) keywordize)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/find-by-id
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ^DBObject find-by-id
|
|
||||||
"Returns a single object with matching _id field.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
|
|
||||||
(mgcol/find-one-by-id collection (ObjectId. \"4ef45ab4744e9fd632640e2d\"))
|
|
||||||
|
|
||||||
;; Return only :language field.
|
|
||||||
;; Note that _id field is always returned.
|
|
||||||
(mgcol/find-one-by-id collection (ObjectId. \"4ef45ab4744e9fd632640e2d\") [:language])
|
|
||||||
"
|
|
||||||
([^String collection id]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(find-one collection { :_id id }))
|
|
||||||
([^String collection id ^List fields]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(find-one collection { :_id id } fields))
|
|
||||||
([^DB db ^String collection id ^List fields]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(find-one db collection { :_id id } fields)))
|
|
||||||
|
|
||||||
(defn ^IPersistentMap find-map-by-id
|
|
||||||
"Returns a single object, converted to map with matching _id field."
|
|
||||||
([^String collection id]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(from-db-object ^DBObject (find-one-as-map collection { :_id id }) true))
|
|
||||||
([^String collection id ^List fields]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(from-db-object ^DBObject (find-one-as-map collection { :_id id } fields) true))
|
|
||||||
([^String collection id ^List fields keywordize]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(from-db-object ^DBObject (find-one-as-map collection { :_id id } fields) keywordize)))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/group
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
|
||||||
;; TBD
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/count
|
|
||||||
;;
|
|
||||||
(defn count
|
|
||||||
"Returns the number of documents in this collection.
|
|
||||||
|
|
||||||
Takes optional conditions as an argument.
|
|
||||||
|
|
||||||
(monger.collection/count collection)
|
|
||||||
|
|
||||||
(monger.collection/count collection { :first_name \"Paul\" })"
|
|
||||||
(^long [^String collection]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.count coll)))
|
|
||||||
(^long [^String collection ^Map conditions]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.count coll (to-db-object conditions))))
|
|
||||||
(^long [^DB db ^String collection ^Map conditions]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.count coll (to-db-object conditions)))))
|
|
||||||
|
|
||||||
(defn any?
|
|
||||||
"Wether the collection has any items at all, or items matching query.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
|
|
||||||
;; wether the collection has any items
|
|
||||||
(mgcol/any? collection)
|
|
||||||
|
|
||||||
(mgcol/any? collection { :language \"Clojure\" }))
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(> (count collection) 0))
|
|
||||||
([^String collection ^Map conditions]
|
|
||||||
(> (count collection conditions) 0))
|
|
||||||
([^DB db ^String collection ^Map conditions]
|
|
||||||
(> (count db collection conditions) 0)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn empty?
|
|
||||||
"Wether the collection is empty.
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
(mgcol/empty? \"things\")
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(= (count collection) 0))
|
|
||||||
([^DB db ^String collection]
|
|
||||||
(= (count db collection {}) 0)))
|
|
||||||
|
|
||||||
;; monger.collection/update
|
|
||||||
|
|
||||||
(defn ^WriteResult update
|
|
||||||
"Performs an update operation.
|
|
||||||
|
|
||||||
Please note that update is potentially destructive operation. It will update your document with the given set
|
|
||||||
emptying the fields not mentioned in (^Map document). In order to only change certain fields, please use
|
|
||||||
\"$set\".
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.collection/update \"people\" { :first_name \"Raul\" } { \"$set\" { :first_name \"Paul\" } })
|
|
||||||
|
|
||||||
You can use all the Mongodb Modifier Operations ($inc, $set, $unset, $push, $pushAll, $addToSet, $pop, $pull
|
|
||||||
$pullAll, $rename, $bit) here, as well
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.collection/update \"people\" { :first_name \"Paul\" } { \"$set\" { :index 1 } })
|
|
||||||
(monger.collection/update \"people\" { :first_name \"Paul\" } { \"$inc\" { :index 5 } })
|
|
||||||
|
|
||||||
(monger.collection/update \"people\" { :first_name \"Paul\" } { \"$unset\" { :years_on_stage 1} })
|
|
||||||
|
|
||||||
It also takes modifiers, such as :upsert and :multi.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
;; add :band field to all the records found in \"people\" collection, otherwise only the first matched record
|
|
||||||
;; will be updated
|
|
||||||
(monger.collection/update \"people\" { } { \"$set\" { :band \"The Beatles\" }} :multi true)
|
|
||||||
|
|
||||||
;; inserts the record if it did not exist in the collection
|
|
||||||
(monger.collection/update \"people\" { :first_name \"Yoko\" } { :first_name \"Yoko\" :last_name \"Ono\" } :upsert true)
|
|
||||||
|
|
||||||
By default :upsert and :multi are false."
|
|
||||||
([^String collection ^Map conditions ^Map document & { :keys [upsert multi write-concern] :or { upsert false
|
|
||||||
multi false
|
|
||||||
write-concern monger.core/*mongodb-write-concern* } }]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.update coll (to-db-object conditions) (to-db-object document) upsert multi write-concern))))
|
|
||||||
|
|
||||||
(defn ^WriteResult update-by-id
|
|
||||||
"Update a document with given id"
|
|
||||||
[^String collection ^ObjectId id ^Map document & { :keys [upsert write-concern] :or { upsert false
|
|
||||||
write-concern monger.core/*mongodb-write-concern* } }]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.update coll (to-db-object { :_id id }) (to-db-object document) upsert false write-concern)))
|
|
||||||
|
|
||||||
|
|
||||||
;; monger.collection/save
|
|
||||||
|
|
||||||
(defn ^WriteResult save
|
|
||||||
"Saves an object to the given collection (does insert or update based on the object _id).
|
|
||||||
|
|
||||||
If the object is not present in the database, insert operation will be performed.
|
|
||||||
If the object is already in the database, it will be updated.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.collection/save \"people\" { :first_name \"Ian\" :last_name \"Gillan\" })
|
|
||||||
"
|
|
||||||
([^String collection ^Map document]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.save coll (to-db-object document) monger.core/*mongodb-write-concern*)))
|
|
||||||
([^String collection ^Map document ^WriteConcern write-concern]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.save coll document write-concern)))
|
|
||||||
([^DB db ^String collection ^Map document ^WriteConcern write-concern]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.save coll document write-concern))))
|
|
||||||
|
|
||||||
|
|
||||||
;; monger.collection/remove
|
|
||||||
|
|
||||||
(defn ^WriteResult remove
|
|
||||||
"Removes objects from the database.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.collection/remove collection) ;; Removes all documents from DB
|
|
||||||
|
|
||||||
(monger.collection/remove collection { :language \"Clojure\" }) ;; Removes documents based on given query
|
|
||||||
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.remove coll (to-db-object {}))))
|
|
||||||
([^String collection ^Map conditions]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.remove coll (to-db-object conditions))))
|
|
||||||
([^DB db ^String collection ^Map conditions]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.remove coll (to-db-object conditions)))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn ^WriteResult remove-by-id
|
|
||||||
"Removes a single document with given id"
|
|
||||||
([^String collection ^ObjectId id]
|
|
||||||
(remove-by-id monger.core/*mongodb-database* collection id))
|
|
||||||
([^DB db ^String collection ^ObjectId id]
|
|
||||||
(check-not-nil! id "id must not be nil")
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.remove coll (to-db-object { :_id id })))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/create-index
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn create-index
|
|
||||||
"Forces creation of index on a set of fields, if one does not already exists.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
;; Will create an index on \"language\" field
|
|
||||||
(monger.collection/create-index collection { \"language\" 1 })
|
|
||||||
|
|
||||||
"
|
|
||||||
([^String collection ^Map keys]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.createIndex coll (to-db-object keys))))
|
|
||||||
([^DB db ^String collection ^Map keys]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.createIndex coll (to-db-object keys)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/ensure-index
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ensure-index
|
|
||||||
"Creates an index on a set of fields, if one does not already exist.
|
|
||||||
ensureIndex in Java driver is optimized and is inexpensive if the index already exists.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.collection/ensure-index collection { \"language\" 1 })
|
|
||||||
|
|
||||||
"
|
|
||||||
([^String collection, ^Map keys]
|
|
||||||
(let [coll ^DBCollection (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.ensureIndex ^DBCollection coll ^DBObject (to-db-object keys))))
|
|
||||||
([^String collection, ^Map keys, ^String name]
|
|
||||||
(let [coll ^DBCollection (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.ensureIndex coll ^DBObject (to-db-object keys) ^String name))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/indexes-on
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn indexes-on
|
|
||||||
"Return a list of the indexes for this collection.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.collection/indexes-on collection)
|
|
||||||
|
|
||||||
"
|
|
||||||
[^String collection]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(from-db-object (.getIndexInfo coll) true)))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/drop-index
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn drop-index
|
|
||||||
"Drops an index from this collection."
|
|
||||||
([^String collection ^String name]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.dropIndex coll name)))
|
|
||||||
([^DB db ^String collection ^String name]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.dropIndex coll name))))
|
|
||||||
|
|
||||||
(defn drop-indexes
|
|
||||||
"Drops an indices from this collection."
|
|
||||||
([^String collection]
|
|
||||||
(.dropIndexes ^DBCollection (.getCollection monger.core/*mongodb-database* collection)))
|
|
||||||
([^DB db ^String collection]
|
|
||||||
(.dropIndexes ^DBCollection (.getCollection db collection))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/exists?, /create, /drop, /rename
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
|
||||||
(defn exists?
|
|
||||||
"Checks weather collection with certain name exists.
|
|
||||||
|
|
||||||
EXAMPLE:
|
|
||||||
|
|
||||||
(monger.collection/exists? \"coll\")
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(.collectionExists monger.core/*mongodb-database* collection))
|
|
||||||
([^DB db ^String collection]
|
|
||||||
(.collectionExists db collection)))
|
|
||||||
|
|
||||||
(defn create
|
|
||||||
"Creates a collection with a given name and options."
|
|
||||||
([^String collection ^Map options]
|
|
||||||
(.createCollection monger.core/*mongodb-database* collection (to-db-object options)))
|
|
||||||
([^DB db ^String collection ^Map options]
|
|
||||||
(.createCollection db collection (to-db-object options))))
|
|
||||||
|
|
||||||
(defn drop
|
|
||||||
"Deletes collection from database.
|
|
||||||
|
|
||||||
EXAMPLE:
|
|
||||||
|
|
||||||
(monger.collection/drop \"collection-to-drop\")
|
|
||||||
"
|
|
||||||
([^String collection]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.drop coll)))
|
|
||||||
([^DB db ^String collection]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.drop coll))))
|
|
||||||
|
|
||||||
(defn rename
|
|
||||||
"Renames collection.
|
|
||||||
|
|
||||||
EXAMPLE:
|
|
||||||
|
|
||||||
(monger.collection/rename \"old_name\" \"new_name\")
|
|
||||||
"
|
|
||||||
([^String from, ^String to]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* from)]
|
|
||||||
(.rename coll to)))
|
|
||||||
([^String from ^String to ^Boolean drop-target]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* from)]
|
|
||||||
(.rename coll to drop-target)))
|
|
||||||
([^DB db ^String from ^String to ^Boolean drop-target]
|
|
||||||
(let [^DBCollection coll (.getCollection db from)]
|
|
||||||
(.rename coll to drop-target))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Map/Reduce
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn map-reduce
|
|
||||||
"Performs a map reduce operation"
|
|
||||||
([^String collection, ^String js-mapper, ^String js-reducer, ^String output, ^Map query]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.mapReduce coll js-mapper js-reducer output (to-db-object query))))
|
|
||||||
([^String collection, ^String js-mapper, ^String js-reducer, ^String output, ^MapReduceCommand$OutputType output-type, ^Map query]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.mapReduce coll js-mapper js-reducer output output-type (to-db-object query)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/distinct
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn distinct
|
|
||||||
"Finds distinct values for a key"
|
|
||||||
([^String collection ^String key]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.distinct coll ^String (to-db-object key))))
|
|
||||||
([^String collection ^String key ^Map query]
|
|
||||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
|
||||||
(.distinct coll ^String (to-db-object key) ^DBObject (to-db-object query))))
|
|
||||||
([^DB db ^String collection ^String key ^Map query]
|
|
||||||
(let [^DBCollection coll (.getCollection db collection)]
|
|
||||||
(.distinct coll ^String (to-db-object key) ^DBObject (to-db-object query)))))
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;; Copyright (c) 2012 Toby Hede
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.command
|
|
||||||
(:use [monger.conversion])
|
|
||||||
(:refer-clojure :exclude [find remove count drop distinct empty?])
|
|
||||||
(:import [com.mongodb Mongo DB DBObject CommandResult]
|
|
||||||
[java.util Map])
|
|
||||||
(:require [monger core]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn collection-stats
|
|
||||||
([collection]
|
|
||||||
(collection-stats monger.core/*mongodb-database* collection))
|
|
||||||
([^DB database collection]
|
|
||||||
(monger.core/command database { :collstats collection })))
|
|
||||||
|
|
||||||
(defn db-stats
|
|
||||||
([]
|
|
||||||
(db-stats monger.core/*mongodb-database*))
|
|
||||||
([^DB database]
|
|
||||||
(monger.core/command database {:dbStats 1 })))
|
|
||||||
|
|
||||||
|
|
||||||
(defn reindex-collection
|
|
||||||
([collection]
|
|
||||||
(reindex-collection monger.core/*mongodb-database* collection))
|
|
||||||
([^DB database collection]
|
|
||||||
(monger.core/command database { :reIndex collection })))
|
|
||||||
|
|
||||||
|
|
||||||
(defn server-status
|
|
||||||
([]
|
|
||||||
(server-status monger.core/*mongodb-database*))
|
|
||||||
([^DB database]
|
|
||||||
(monger.core/command database {:serverStatus 1 })))
|
|
||||||
|
|
||||||
|
|
||||||
(defn top []
|
|
||||||
(monger.core/command (monger.core/get-db "admin") {:top 1 }))
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
;; Original author is Andrew Boekhoff
|
|
||||||
;;
|
|
||||||
;; Portions of the code are Copyright (c) 2009 Andrew Boekhoff
|
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; 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.
|
|
||||||
|
|
||||||
(ns monger.conversion
|
|
||||||
(:import [com.mongodb DBObject BasicDBObject BasicDBList DBCursor]
|
|
||||||
[clojure.lang IPersistentMap Keyword]
|
|
||||||
[java.util List Map Date]
|
|
||||||
[org.bson.types ObjectId]))
|
|
||||||
|
|
||||||
(defprotocol ConvertToDBObject
|
|
||||||
(to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses"))
|
|
||||||
|
|
||||||
(extend-protocol ConvertToDBObject
|
|
||||||
nil
|
|
||||||
(to-db-object [input]
|
|
||||||
input)
|
|
||||||
|
|
||||||
Object
|
|
||||||
(to-db-object [input]
|
|
||||||
input)
|
|
||||||
|
|
||||||
Keyword
|
|
||||||
(to-db-object [^Keyword input] (.getName input))
|
|
||||||
|
|
||||||
IPersistentMap
|
|
||||||
(to-db-object [^IPersistentMap input]
|
|
||||||
(let [o (BasicDBObject.)]
|
|
||||||
(doseq [[k v] input]
|
|
||||||
(.put o (to-db-object k) (to-db-object v)))
|
|
||||||
o))
|
|
||||||
|
|
||||||
List
|
|
||||||
(to-db-object [^List input] (map to-db-object input))
|
|
||||||
|
|
||||||
DBObject
|
|
||||||
(to-db-object [^DBObject input] input))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(declare associate-pairs)
|
|
||||||
(defprotocol ConvertFromDBObject
|
|
||||||
(from-db-object [input keywordize] "Converts given DBObject instance to a piece of Clojure data"))
|
|
||||||
|
|
||||||
(extend-protocol ConvertFromDBObject
|
|
||||||
nil
|
|
||||||
(from-db-object [input keywordize] input)
|
|
||||||
|
|
||||||
Object
|
|
||||||
(from-db-object [input keywordize] input)
|
|
||||||
|
|
||||||
Map
|
|
||||||
(from-db-object [^Map input keywordize]
|
|
||||||
(associate-pairs (.entrySet input) keywordize))
|
|
||||||
|
|
||||||
List
|
|
||||||
(from-db-object [^List input keywordize]
|
|
||||||
(vec (map #(from-db-object % keywordize) input)))
|
|
||||||
|
|
||||||
BasicDBList
|
|
||||||
(from-db-object [^BasicDBList input keywordize]
|
|
||||||
(vec (map #(from-db-object % keywordize) input)))
|
|
||||||
|
|
||||||
DBObject
|
|
||||||
(from-db-object [^DBObject input keywordize]
|
|
||||||
;; DBObject provides .toMap, but the implementation in
|
|
||||||
;; subclass GridFSFile unhelpfully throws
|
|
||||||
;; UnsupportedOperationException. This part is taken from congomongo and
|
|
||||||
;; may need revisiting at a later point. MK.
|
|
||||||
(associate-pairs (for [key-set (.keySet input)] [key-set (.get input key-set)])
|
|
||||||
keywordize)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn- associate-pairs [pairs keywordize]
|
|
||||||
;; Taking the keywordize test out of the fn reduces derefs
|
|
||||||
;; dramatically, which was the main barrier to matching pure-Java
|
|
||||||
;; performance for this marshalling. Taken from congomongo. MK.
|
|
||||||
(reduce (if keywordize
|
|
||||||
(fn [m [^String k v]]
|
|
||||||
(assoc m (keyword k) (from-db-object v true)))
|
|
||||||
(fn [m [^String k v]]
|
|
||||||
(assoc m k (from-db-object v false))))
|
|
||||||
{} (reverse pairs)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defprotocol ConvertToObjectId
|
|
||||||
(to-object-id [input] "Instantiates ObjectId from input unless the input itself is an ObjectId instance. In that case, returns input as is."))
|
|
||||||
|
|
||||||
(extend-protocol ConvertToObjectId
|
|
||||||
String
|
|
||||||
(to-object-id [^String input]
|
|
||||||
(ObjectId. input))
|
|
||||||
|
|
||||||
Date
|
|
||||||
(to-object-id [^Date input]
|
|
||||||
(ObjectId. input))
|
|
||||||
|
|
||||||
ObjectId
|
|
||||||
(to-object-id [^ObjectId input]
|
|
||||||
input))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,260 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns ^{:author "Michael S. Klishin"
|
|
||||||
:doc "Thin idiomatic wrapper around MongoDB Java client. monger.core includes
|
|
||||||
fundamental functions that work with connections & databases. Most of functionality
|
|
||||||
is in other monger.* namespaces, in particular monger.collection."}
|
|
||||||
monger.core
|
|
||||||
(:refer-clojure :exclude [count])
|
|
||||||
(:use [monger.conversion])
|
|
||||||
(:import [com.mongodb Mongo DB WriteConcern DBObject DBCursor CommandResult Bytes MongoOptions ServerAddress]
|
|
||||||
[com.mongodb.gridfs GridFS]
|
|
||||||
[java.util Map]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Defaults
|
|
||||||
;;
|
|
||||||
|
|
||||||
(def ^:dynamic ^String *mongodb-host* "localhost")
|
|
||||||
(def ^:dynamic ^long *mongodb-port* 27017)
|
|
||||||
|
|
||||||
(declare ^:dynamic ^Mongo *mongodb-connection*)
|
|
||||||
(declare ^:dynamic ^DB *mongodb-database*)
|
|
||||||
(def ^:dynamic ^WriteConcern *mongodb-write-concern* WriteConcern/SAFE)
|
|
||||||
|
|
||||||
(declare ^:dynamic ^GridFS *mongodb-gridfs*)
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ^Mongo connect
|
|
||||||
"Connects to MongoDB. When used without arguments, connects to
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
:host (*mongodb-host* by default)
|
|
||||||
:port (*mongodb-port* by default)
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.core/connect)
|
|
||||||
(monger.core/connect { :host \"db3.intranet.local\", :port 27787 })
|
|
||||||
"
|
|
||||||
([]
|
|
||||||
(Mongo.))
|
|
||||||
([^ServerAddress server-address ^MongoOptions options]
|
|
||||||
(Mongo. server-address options))
|
|
||||||
([{ :keys [host port] :or { host *mongodb-host*, port *mongodb-port* }}]
|
|
||||||
(Mongo. ^String host ^Long port)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn ^DB get-db-names
|
|
||||||
"Gets a list of all database names present on the server"
|
|
||||||
([]
|
|
||||||
(get-db-names *mongodb-connection*))
|
|
||||||
([^Mongo connection]
|
|
||||||
(set (.getDatabaseNames connection))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn ^DB get-db
|
|
||||||
"Get database reference by name.
|
|
||||||
|
|
||||||
EXAMPLES
|
|
||||||
|
|
||||||
(monger.core/get-db \"myapp_production\")
|
|
||||||
(monger.core/get-db connection \"myapp_production\")"
|
|
||||||
([^String name]
|
|
||||||
(.getDB *mongodb-connection* name))
|
|
||||||
([^Mongo connection, ^String name]
|
|
||||||
(.getDB connection name)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn authenticate
|
|
||||||
([^String db ^String username ^chars password]
|
|
||||||
(authenticate *mongodb-connection* db username password))
|
|
||||||
([^Mongo connection ^String db ^String username ^chars password]
|
|
||||||
(.authenticate (.getDB connection db) username password)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro with-connection
|
|
||||||
[conn & body]
|
|
||||||
`(binding [*mongodb-connection* ~conn]
|
|
||||||
(do ~@body)))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro with-db
|
|
||||||
[db & body]
|
|
||||||
`(binding [*mongodb-database* ~db]
|
|
||||||
(do ~@body)))
|
|
||||||
|
|
||||||
(defmacro with-gridfs
|
|
||||||
[fs & body]
|
|
||||||
`(binding [*mongodb-gridfs* ~fs]
|
|
||||||
(do ~@body)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn server-address
|
|
||||||
([^String hostname]
|
|
||||||
(ServerAddress. hostname))
|
|
||||||
([^String hostname ^long port]
|
|
||||||
(ServerAddress. hostname port)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn mongo-options
|
|
||||||
[& { :keys [connections-per-host threads-allowed-to-block-for-connection-multiplier
|
|
||||||
max-wait-time connect-timeout socket-timeout socket-keep-alive auto-connect-retry max-auto-connect-retry-time
|
|
||||||
safe w w-timeout fsync j] }]
|
|
||||||
(let [mo (MongoOptions.)]
|
|
||||||
(when connections-per-host
|
|
||||||
(set! (. mo connectionsPerHost) connections-per-host))
|
|
||||||
(when threads-allowed-to-block-for-connection-multiplier
|
|
||||||
(set! (. mo threadsAllowedToBlockForConnectionMultiplier) threads-allowed-to-block-for-connection-multiplier))
|
|
||||||
(when max-wait-time
|
|
||||||
(set! (. mo maxWaitTime) max-wait-time))
|
|
||||||
(when connect-timeout
|
|
||||||
(set! (. mo connectTimeout) connect-timeout))
|
|
||||||
(when socket-timeout
|
|
||||||
(set! (. mo socketTimeout) socket-timeout))
|
|
||||||
(when socket-keep-alive
|
|
||||||
(set! (. mo socketKeepAlive) socket-keep-alive))
|
|
||||||
(when auto-connect-retry
|
|
||||||
(set! (. mo autoConnectRetry) auto-connect-retry))
|
|
||||||
(when max-auto-connect-retry-time
|
|
||||||
(set! (. mo maxAutoConnectRetryTime) max-auto-connect-retry-time))
|
|
||||||
(when safe
|
|
||||||
(set! (. mo safe) safe))
|
|
||||||
(when w
|
|
||||||
(set! (. mo w) w))
|
|
||||||
(when w-timeout
|
|
||||||
(set! (. mo wtimeout) w-timeout))
|
|
||||||
(when j
|
|
||||||
(set! (. mo j) j))
|
|
||||||
(when fsync
|
|
||||||
(set! (. mo fsync) fsync))
|
|
||||||
mo))
|
|
||||||
|
|
||||||
|
|
||||||
(defn set-connection!
|
|
||||||
"Sets given MongoDB connection as default by altering *mongodb-connection* var"
|
|
||||||
^Mongo [^Mongo conn]
|
|
||||||
(alter-var-root (var *mongodb-connection*) (constantly conn)))
|
|
||||||
|
|
||||||
(defn connect!
|
|
||||||
"Connect to MongoDB, store connection in the *mongodb-connection* var"
|
|
||||||
^Mongo [& args]
|
|
||||||
(let [c (apply connect args)]
|
|
||||||
(set-connection! c)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn set-db!
|
|
||||||
"Sets *mongodb-database* var to given db, updates *mongodb-gridfs* var state. Recommended to be used for
|
|
||||||
applications that only use one database."
|
|
||||||
[db]
|
|
||||||
(alter-var-root (var *mongodb-database*) (constantly db))
|
|
||||||
(alter-var-root (var *mongodb-gridfs*) (constantly (GridFS. db))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn set-default-write-concern!
|
|
||||||
[wc]
|
|
||||||
"Set *mongodb-write-concert* var to :wc
|
|
||||||
|
|
||||||
Unlike the official Java driver, Monger uses WriteConcern/SAFE by default. We think defaults should be safe first
|
|
||||||
and WebScale fast second."
|
|
||||||
(def ^:dynamic *mongodb-write-concern* wc))
|
|
||||||
|
|
||||||
(defn ^CommandResult command
|
|
||||||
"Runs a database command (please check MongoDB documentation for the complete list of commands). Some common commands
|
|
||||||
are:
|
|
||||||
|
|
||||||
{ :buildinfo 1 } returns version number and build information about the current MongoDB server, should be executed via admin DB.
|
|
||||||
|
|
||||||
{ :collstats collection-name [ :scale scale ] } returns stats about given collection.
|
|
||||||
|
|
||||||
{ :dbStats 1 } returns the stats of current database
|
|
||||||
|
|
||||||
{ :dropDatabase 1 } deletes the current database
|
|
||||||
|
|
||||||
{ :findAndModify find-and-modify-config } runs find, modify and return for the given query.
|
|
||||||
Takes :query, :sory, :remove, :update, :new, :fields and :upsert arguments.
|
|
||||||
Please refer MongoDB documentation for details. http://www.mongodb.org/display/DOCS/findAndModify+Command
|
|
||||||
|
|
||||||
{ :fsync config } performs a full fsync, that flushes all pending writes to database, provides an optional write lock that will make
|
|
||||||
backups easier.
|
|
||||||
Please refer MongoDB documentation for details :http://www.mongodb.org/display/DOCS/fsync+Command
|
|
||||||
|
|
||||||
{ :getLastError 1 } returns the status of the last operation on current connection.
|
|
||||||
|
|
||||||
{ :group group-config } performs grouping aggregation, docs and support for grouping are TBD in Monger.
|
|
||||||
|
|
||||||
{ :listCommands 1 } displays the list of available commands.
|
|
||||||
|
|
||||||
{ :profile new-profile-level } sets the database profiler to profile level N.
|
|
||||||
|
|
||||||
{ :reIndex coll } performs re-index on a given collection.
|
|
||||||
|
|
||||||
{ :renameCollection old-name :to new-name } renames collection from old-name to new-name
|
|
||||||
|
|
||||||
{ :repairDatabase 1 } repair and compact the current database (may be very time-consuming, depending on DB size)
|
|
||||||
|
|
||||||
Replica set commands
|
|
||||||
{ :isMaster 1 } checks if this server is a master server.
|
|
||||||
{ :replSetGetStatus 1 } get the status of a replica set.
|
|
||||||
{ :replSetInitiate replica-config } initiate a replica set with given config.
|
|
||||||
{ :replSetReconfig replica-config } set a given config for replica set.
|
|
||||||
{ :replSetStepDown seconds } manually tell a member to step down as primary. It will become primary again after specified amount of seconds.
|
|
||||||
{ :replSetFreeze seconds } freeze state of member, call with 0 to unfreeze.
|
|
||||||
{ :resync 1 } start a full resync of a replica slave
|
|
||||||
For more information, please refer Mongodb Replica Set Command guide: http://www.mongodb.org/display/DOCS/Replica+Set+Commands
|
|
||||||
|
|
||||||
{ :serverStatus 1 } gets administrative statistics about the server.
|
|
||||||
|
|
||||||
{ :shutdown 1 } shuts the MongoDB server down.
|
|
||||||
|
|
||||||
{ :top 1 } get a breakdown of usage by collection.
|
|
||||||
|
|
||||||
{ :validate namespace-name } validate the namespace (collection or index). May be very time-consuming, depending on DB size.
|
|
||||||
|
|
||||||
For :distinct, :count, :drop, :dropIndexes, :mapReduce we suggest to use monger/collection #distinct, #count, #drop, #dropIndexes, :mapReduce respectively.
|
|
||||||
"
|
|
||||||
([^Map cmd]
|
|
||||||
(.command ^DB *mongodb-database* ^DBObject (to-db-object cmd)))
|
|
||||||
([^DB database ^Map cmd]
|
|
||||||
(.command ^DB database ^DBObject (to-db-object cmd))))
|
|
||||||
|
|
||||||
(defprotocol Countable
|
|
||||||
(count [this] "Returns size of the object"))
|
|
||||||
|
|
||||||
(extend-protocol Countable
|
|
||||||
DBCursor
|
|
||||||
(count [^DBCursor this]
|
|
||||||
(.count this)))
|
|
||||||
|
|
||||||
(defn ^DBObject get-last-error
|
|
||||||
"Returns the the error (if there is one) from the previous operation on this connection.
|
|
||||||
|
|
||||||
The result of this command looks like:
|
|
||||||
|
|
||||||
#<CommandResult { \"serverUsed\" : \"127.0.0.1:27017\" , \"n\" : 0 , \"connectionId\" : 66 , \"err\" : null , \"ok\" : 1.0}>\"
|
|
||||||
|
|
||||||
The value for err will be null if no error occurred, or a description otherwise.
|
|
||||||
|
|
||||||
Important note: when calling this method directly, it is undefined which connection \"getLastError\" is called on.
|
|
||||||
You may need to explicitly use a \"consistent Request\", see requestStart() For most purposes it is better not to call this method directly but instead use WriteConcern."
|
|
||||||
([]
|
|
||||||
(.getLastError ^DB *mongodb-database*))
|
|
||||||
([^DB database]
|
|
||||||
(.getLastError ^DB database))
|
|
||||||
([^DB database ^Integer w ^Integer wtimeout ^Boolean fsync]
|
|
||||||
(.getLastError ^DB database w wtimeout fsync))
|
|
||||||
([^DB database ^WriteConcern write-concern]
|
|
||||||
(.getLastError ^DB database write-concern)))
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;; Copyright (c) 2012 Toby Hede
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.db
|
|
||||||
(:refer-clojure :exclude [find remove count drop distinct empty?])
|
|
||||||
(:import [com.mongodb Mongo DB DBCollection])
|
|
||||||
(:require [monger core]))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn add-user
|
|
||||||
"Adds a new user for this db"
|
|
||||||
([^String username, ^chars password]
|
|
||||||
(.addUser ^DB monger.core/*mongodb-database* username password))
|
|
||||||
([^DB database ^String username, ^chars password]
|
|
||||||
(.addUser ^DB database username password)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn drop-db
|
|
||||||
"Drops the currently set database (via core/set-db) or the specified database."
|
|
||||||
([]
|
|
||||||
(.dropDatabase ^DB monger.core/*mongodb-database*))
|
|
||||||
([^DB database]
|
|
||||||
(.dropDatabase ^DB database)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn get-collection-names
|
|
||||||
"Returns a set containing the names of all collections in this database."
|
|
||||||
([]
|
|
||||||
(set (.getCollectionNames ^DB monger.core/*mongodb-database*)))
|
|
||||||
([^DB database]
|
|
||||||
(set (.getCollectionNames ^DB database))))
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.gridfs
|
|
||||||
(:refer-clojure :exclude [remove find])
|
|
||||||
(:require [monger.core]
|
|
||||||
[clojure.java.io :as io])
|
|
||||||
(:use [monger.conversion])
|
|
||||||
(:import [com.mongodb DB DBObject]
|
|
||||||
[com.mongodb.gridfs GridFS GridFSInputFile]
|
|
||||||
[java.io InputStream File]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Implementation
|
|
||||||
;;
|
|
||||||
|
|
||||||
(def
|
|
||||||
^{:doc "Type object for a Java primitive byte array."
|
|
||||||
:private true
|
|
||||||
}
|
|
||||||
byte-array-type (class (make-array Byte/TYPE 0)))
|
|
||||||
|
|
||||||
;; ...
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
|
||||||
(defn remove
|
|
||||||
([]
|
|
||||||
(remove {}))
|
|
||||||
([query]
|
|
||||||
(.remove ^GridFS monger.core/*mongodb-gridfs* ^DBObject (to-db-object query)))
|
|
||||||
([^GridFS fs query]
|
|
||||||
(.remove fs ^DBObject (to-db-object query))))
|
|
||||||
|
|
||||||
(defn remove-all
|
|
||||||
([]
|
|
||||||
(remove {}))
|
|
||||||
([^GridFS fs]
|
|
||||||
(remove fs {})))
|
|
||||||
|
|
||||||
(defn all-files
|
|
||||||
([]
|
|
||||||
(.getFileList ^GridFS monger.core/*mongodb-gridfs*))
|
|
||||||
([query]
|
|
||||||
(.getFileList ^GridFS monger.core/*mongodb-gridfs* query))
|
|
||||||
([^GridFS fs query]
|
|
||||||
(.getFileList fs query)))
|
|
||||||
|
|
||||||
|
|
||||||
(defprotocol GridFSInputFileFactory
|
|
||||||
(^GridFSInputFile make-input-file [input] "Makes GridFSInputFile out of given input"))
|
|
||||||
|
|
||||||
(extend byte-array-type
|
|
||||||
GridFSInputFileFactory
|
|
||||||
{ :make-input-file (fn [^bytes input]
|
|
||||||
(.createFile ^GridFS monger.core/*mongodb-gridfs* input)) })
|
|
||||||
|
|
||||||
(extend-protocol GridFSInputFileFactory
|
|
||||||
String
|
|
||||||
(make-input-file [^String input]
|
|
||||||
(.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" })))
|
|
||||||
|
|
||||||
File
|
|
||||||
(make-input-file [^File input]
|
|
||||||
(.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" })))
|
|
||||||
|
|
||||||
InputStream
|
|
||||||
(make-input-file [^InputStream input]
|
|
||||||
(.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream input)))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro store
|
|
||||||
[^GridFSInputFile input & body]
|
|
||||||
`(let [^GridFSInputFile f# (doto ~input ~@body)]
|
|
||||||
(.save f# GridFS/DEFAULT_CHUNKSIZE)
|
|
||||||
(from-db-object f# true)))
|
|
||||||
|
|
||||||
|
|
||||||
(defprotocol Finders
|
|
||||||
(find [input] "Finds multiple files using given input (an ObjectId, filename or query)")
|
|
||||||
(find-one [input] "Finds one file using given input (an ObjectId, filename or query)"))
|
|
||||||
|
|
||||||
(extend-protocol Finders
|
|
||||||
String
|
|
||||||
(find [^String input]
|
|
||||||
(vec (.find ^GridFS monger.core/*mongodb-gridfs* input)))
|
|
||||||
(find-one [^String input]
|
|
||||||
(.findOne ^GridFS monger.core/*mongodb-gridfs* input))
|
|
||||||
|
|
||||||
org.bson.types.ObjectId
|
|
||||||
(find-one [^org.bson.types.ObjectId input]
|
|
||||||
(.findOne ^GridFS monger.core/*mongodb-gridfs* input))
|
|
||||||
|
|
||||||
|
|
||||||
DBObject
|
|
||||||
(find [^DBObject input]
|
|
||||||
(vec (.find ^GridFS monger.core/*mongodb-gridfs* input)))
|
|
||||||
(find-one [^DBObject input]
|
|
||||||
(.findOne ^GridFS monger.core/*mongodb-gridfs* input))
|
|
||||||
|
|
||||||
clojure.lang.PersistentArrayMap
|
|
||||||
(find [^clojure.lang.PersistentArrayMap input]
|
|
||||||
(find (to-db-object input))))
|
|
||||||
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.internal.fn)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Implementation
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn- apply-to-values [m f]
|
|
||||||
"Applies function f to all values in map m"
|
|
||||||
(into {} (for [[k v] m]
|
|
||||||
[k (f v)])))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn fpartial
|
|
||||||
"Like clojure.core/partial but prepopulates last N arguments (first is passed in later)"
|
|
||||||
[f & args]
|
|
||||||
(fn [arg & more] (apply f arg (concat args more))))
|
|
||||||
|
|
||||||
(defprotocol IFNExpansion
|
|
||||||
(expand-all [x] "Replaces functions with their invocation results, recursively expands maps, evaluates all other values to themselves")
|
|
||||||
(expand-all-with [x f] "Replaces functions with their invocation results that function f is applied to, recursively expands maps, evaluates all other values to themselves"))
|
|
||||||
|
|
||||||
(extend-protocol IFNExpansion
|
|
||||||
java.lang.Integer
|
|
||||||
(expand-all [i] i)
|
|
||||||
(expand-all-with [i f] i)
|
|
||||||
|
|
||||||
java.lang.Long
|
|
||||||
(expand-all [l] l)
|
|
||||||
(expand-all-with [l f] l)
|
|
||||||
|
|
||||||
java.lang.String
|
|
||||||
(expand-all [s] s)
|
|
||||||
(expand-all-with [s f] s)
|
|
||||||
|
|
||||||
java.lang.Float
|
|
||||||
(expand-all [fl] fl)
|
|
||||||
(expand-all-with [fl f] fl)
|
|
||||||
|
|
||||||
java.lang.Double
|
|
||||||
(expand-all [d] d)
|
|
||||||
(expand-all-with [d f] d)
|
|
||||||
|
|
||||||
;; maps are also functions, so be careful here. MK.
|
|
||||||
clojure.lang.IPersistentMap
|
|
||||||
(expand-all [m] (apply-to-values m expand-all))
|
|
||||||
(expand-all-with [m f] (apply-to-values m (fpartial expand-all-with f)))
|
|
||||||
|
|
||||||
clojure.lang.PersistentVector
|
|
||||||
(expand-all [v] (map expand-all v))
|
|
||||||
(expand-all-with [v f] (map (fpartial expand-all-with f) v))
|
|
||||||
|
|
||||||
;; this distinguishes functions from maps, sets and so on, which are also
|
|
||||||
;; clojure.lang.AFn subclasses. MK.
|
|
||||||
clojure.lang.AFunction
|
|
||||||
(expand-all [f] (f))
|
|
||||||
(expand-all-with [f expander] (expander f))
|
|
||||||
|
|
||||||
Object
|
|
||||||
(expand-all [x] x)
|
|
||||||
(expand-all-with [x f] x))
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.internal.pagination)
|
|
||||||
|
|
||||||
(defn offset-for
|
|
||||||
[^long page ^long per-page]
|
|
||||||
(* per-page
|
|
||||||
(- (max page 1) 1)))
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.joda-time
|
|
||||||
(:import [org.joda.time DateTime DateTimeZone ReadableInstant]
|
|
||||||
[org.joda.time.format ISODateTimeFormat])
|
|
||||||
(:use [monger.conversion])
|
|
||||||
(:require [clojure.data.json :as json]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(extend-protocol ConvertToDBObject
|
|
||||||
org.joda.time.DateTime
|
|
||||||
(to-db-object [^DateTime input]
|
|
||||||
(to-db-object (.toDate input))))
|
|
||||||
|
|
||||||
(extend-protocol ConvertFromDBObject
|
|
||||||
java.util.Date
|
|
||||||
(from-db-object [^java.util.Date input keywordize]
|
|
||||||
(org.joda.time.DateTime. input)))
|
|
||||||
|
|
||||||
|
|
||||||
(extend-protocol json/Write-JSON
|
|
||||||
org.joda.time.DateTime
|
|
||||||
(write-json [^DateTime object out escape-unicode?]
|
|
||||||
(json/write-json (.print (ISODateTimeFormat/dateTime) ^ReadableInstant object) out escape-unicode?)))
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.js
|
|
||||||
(:require [clojure.java.io :as io]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Implementation
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn- normalize-resource
|
|
||||||
[^String path]
|
|
||||||
(if (.endsWith path ".js")
|
|
||||||
path
|
|
||||||
(str path ".js")))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn load-resource
|
|
||||||
(^String [^String path]
|
|
||||||
(slurp (io/resource (normalize-resource path)))))
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.json
|
|
||||||
(:import (org.bson.types ObjectId))
|
|
||||||
(:require [clojure.data.json :as json]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(extend-protocol json/Write-JSON
|
|
||||||
ObjectId
|
|
||||||
(write-json [^ObjectId object out escape-unicode?]
|
|
||||||
(json/write-json (.toString object) out escape-unicode?)))
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.operators)
|
|
||||||
|
|
||||||
(defmacro ^{:private true} defoperator
|
|
||||||
[operator]
|
|
||||||
(let [op# (str operator)
|
|
||||||
op-sym# (symbol op#)]
|
|
||||||
`(def ~op-sym# ~op#)))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; QUERY OPERATORS
|
|
||||||
;;
|
|
||||||
|
|
||||||
;; $gt is "greater than" comparator
|
|
||||||
;; $gte is "greater than or equals" comparator
|
|
||||||
;; $gt is "less than" comparator
|
|
||||||
;; $lte is "less than or equals" comparator
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (monger.collection/find "libraries" { :users { $gt 10 } })
|
|
||||||
;; (monger.collection/find "libraries" { :users { $gte 10 } })
|
|
||||||
;; (monger.collection/find "libraries" { :users { $lt 10 } })
|
|
||||||
;; (monger.collection/find "libraries" { :users { $lte 10 } })
|
|
||||||
(defoperator $gt)
|
|
||||||
(defoperator $gte)
|
|
||||||
(defoperator $lt)
|
|
||||||
(defoperator $lte)
|
|
||||||
|
|
||||||
;; $all matches all values in the array
|
|
||||||
;;
|
|
||||||
;; EXAMPLES
|
|
||||||
;; (mgcol/find-maps "languages" { :tags { $all [ "functional" "object-oriented" ] } } )
|
|
||||||
(defoperator $all)
|
|
||||||
|
|
||||||
;; The $in operator is analogous to the SQL IN modifier, allowing you to specify an array of possible matches.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES
|
|
||||||
;; (mgcol/find-maps "languages" { :tags { $in [ "functional" "object-oriented" ] } } )
|
|
||||||
(defoperator $in)
|
|
||||||
|
|
||||||
;; The $nin operator is similar to $in, but it selects objects for which the specified field does not
|
|
||||||
;; have any value in the specified array.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES
|
|
||||||
;; (mgcol/find-maps "languages" { :tags { $nin [ "functional" ] } } )
|
|
||||||
(defoperator $nin)
|
|
||||||
|
|
||||||
;; $ne is "non-equals" comparator
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (monger.collection/find "libraries" {$ne { :language "Clojure" }})
|
|
||||||
(defoperator $ne)
|
|
||||||
|
|
||||||
;; $elemMatch checks if an element in an array matches the specified expression
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; ;; Matches element with :text "Nice" and :rating greater than or equal 1
|
|
||||||
;; (monger.collection/find "comments" { $elemMatch { :text "Nice!" :rating { $gte 1 } } })
|
|
||||||
(defoperator $elemMatch)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; LOGIC OPERATORS
|
|
||||||
;;
|
|
||||||
|
|
||||||
;; $and lets you use a boolean and in the query. Logical and means that all the given expressions should be true for positive match.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;;
|
|
||||||
;; ;; Matches all libraries where :language is "Clojure" and :users is greater than 10
|
|
||||||
;; (monger.collection/find "libraries" { $and [{ :language "Clojure" } { :users { $gt 10 } }] })
|
|
||||||
(defoperator $and)
|
|
||||||
|
|
||||||
;; $or lets you use a boolean or in the query. Logical or means that one of the given expressions should be true for positive match.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;;
|
|
||||||
;; ;; Matches all libraries whose :name is "mongoid" or :language is "Ruby"
|
|
||||||
;; (monger.collection.find "libraries" { $or [ { :name "mongoid" } { :language "Ruby" } ] })
|
|
||||||
(defoperator $or)
|
|
||||||
|
|
||||||
;; @nor lets you use a boolean expression, opposite to "all" in the query (think: neither). Give $nor a list of expressions, all of which should
|
|
||||||
;; be false for positive match.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;;
|
|
||||||
;; (monger.collection/find "libraries" { $nor [{ :language "Clojure" } {:users { $gt 10 } } ]})
|
|
||||||
(defoperator $nor)
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; ATOMIC MODIFIERS
|
|
||||||
;;
|
|
||||||
|
|
||||||
;; $inc increments one or many fields for the given value, otherwise sets the field to value
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (monger.collection/update "scores" { :_id user-id } { :score 10 } })
|
|
||||||
;; (monger.collection/update "scores" { :_id user-id } { :score 20 :bonus 10 } })
|
|
||||||
(defoperator $inc)
|
|
||||||
|
|
||||||
;; $set sets an existing (or non-existing) field (or set of fields) to value
|
|
||||||
;; $set supports all datatypes.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (monger.collection/update "things" { :_id oid } { $set { :weight 20.5 } })
|
|
||||||
;; (monger.collection/update "things" { :_id oid } { $set { :weight 20.5 :height 12.5 } })
|
|
||||||
(defoperator $set)
|
|
||||||
|
|
||||||
;; $unset deletes a given field, non-existing fields are ignored.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (monger.collection/update "things" { :_id oid } { $unset { :weight 1 } })
|
|
||||||
(defoperator $unset)
|
|
||||||
|
|
||||||
;; $rename renames a given field
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (monger.collection/update "things" { :_id oid } { $rename { :old_field_name "new_field_name" } })
|
|
||||||
(defoperator $rename)
|
|
||||||
|
|
||||||
;; $push appends _single_ value to field, if field is an existing array, otherwise sets field to the array [value] if field is not present.
|
|
||||||
;; If field is present but is not an array, an error condition is raised.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (mgcol/update "docs" { :_id oid } { $push { :tags "modifiers" } })
|
|
||||||
(defoperator $push)
|
|
||||||
|
|
||||||
;; $pushAll appends each value in value_array to field, if field is an existing array, otherwise sets field to the array value_array
|
|
||||||
;; if field is not present. If field is present but is not an array, an error condition is raised.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (mgcol/update coll { :_id oid } { $pushAll { :tags ["mongodb" "docs"] } })
|
|
||||||
(defoperator $pushAll)
|
|
||||||
|
|
||||||
;; $addToSet Adds value to the array only if its not in the array already, if field is an existing array, otherwise sets field to the
|
|
||||||
;; array value if field is not present. If field is present but is not an array, an error condition is raised.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
|
|
||||||
(defoperator $addToSet)
|
|
||||||
|
|
||||||
;; $pop removes the last element in an array, if 1 is passed.
|
|
||||||
;; if -1 is passed, removes the first element in an array
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (mgcol/update coll { :_id oid } { $pop { :tags 1 } })
|
|
||||||
;; (mgcol/update coll { :_id oid } { $pop { :tags 1 :categories 1 } })
|
|
||||||
(defoperator $pop)
|
|
||||||
|
|
||||||
;; $pull removes all occurrences of value from field, if field is an array. If field is present but is not an array, an error condition
|
|
||||||
;; is raised.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (mgcol/update coll { :_id oid } { $pull { :measurements 1.2 } })
|
|
||||||
(defoperator $pull)
|
|
||||||
|
|
||||||
;; $pullAll removes all occurrences of each value in value_array from field, if field is an array. If field is present but is not an array
|
|
||||||
;; an error condition is raised.
|
|
||||||
;;
|
|
||||||
;; EXAMPLES:
|
|
||||||
;; (mgcol/update coll { :_id oid } { $pull { :measurements 1.2 } })
|
|
||||||
;; (mgcol/update coll { :_id oid } { $pull { :measurements { $gte 1.2 } } })
|
|
||||||
(defoperator $pullAll)
|
|
||||||
|
|
||||||
(defoperator $bit)
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.query
|
|
||||||
(:refer-clojure :exclude [select find sort])
|
|
||||||
(:require [monger.core]
|
|
||||||
[monger.internal pagination])
|
|
||||||
(:import [com.mongodb DB DBCollection DBObject DBCursor ReadPreference]
|
|
||||||
[java.util List])
|
|
||||||
(:use [monger conversion operators]))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Implementation
|
|
||||||
;;
|
|
||||||
|
|
||||||
(def ^{ :dynamic true } *query-collection*)
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Cursor/chain methods
|
|
||||||
;;
|
|
||||||
;; Monger query is an auxiliary construction that helps to create funciton chains through cursors.
|
|
||||||
;; You can specify several chained actions that will be performed on the certain collection through
|
|
||||||
;; query fields.
|
|
||||||
;;
|
|
||||||
;; Existing query fields:
|
|
||||||
;;
|
|
||||||
;; :fields - selects which fields are returned. The default is all fields. _id is always returned.
|
|
||||||
;; :sort - adds a sort to the query.
|
|
||||||
;; :fields - set of fields to retrieve during query execution
|
|
||||||
;; :skip - Skips the first N results.
|
|
||||||
;; :limit - Returns a maximum of N results.
|
|
||||||
;; :batch-size - limits the nubmer of elements returned in one batch.
|
|
||||||
;; :hint - force Mongo to use a specific index for a query in order to improve performance.
|
|
||||||
;; :snapshot - sses snapshot mode for the query. Snapshot mode assures no duplicates are returned, or objects missed
|
|
||||||
;; which were present at both the start and end of the query's execution (if an object is new during the query, or
|
|
||||||
;; deleted during the query, it may or may not be returned, even with snapshot mode). Note that short query responses
|
|
||||||
;; (less than 1MB) are always effectively snapshotted. Currently, snapshot mode may not be used with sorting or explicit hints.
|
|
||||||
(defn empty-query
|
|
||||||
([]
|
|
||||||
{
|
|
||||||
:query {}
|
|
||||||
:sort {}
|
|
||||||
:fields []
|
|
||||||
:skip 0
|
|
||||||
:limit 0
|
|
||||||
:batch-size 256
|
|
||||||
:hint nil
|
|
||||||
:snapshot false
|
|
||||||
:keywordize-fields true
|
|
||||||
})
|
|
||||||
([^DBCollection coll]
|
|
||||||
(merge (empty-query) { :collection coll })))
|
|
||||||
|
|
||||||
(defn- fields-to-db-object
|
|
||||||
[^List fields]
|
|
||||||
(to-db-object (zipmap fields (repeat 1))))
|
|
||||||
|
|
||||||
(defn exec
|
|
||||||
[{ :keys [collection query fields skip limit sort batch-size hint snapshot read-preference keywordize-fields] :or { limit 0 batch-size 256 skip 0 } }]
|
|
||||||
(let [cursor (doto ^DBCursor (.find ^DBCollection collection (to-db-object query) (fields-to-db-object fields))
|
|
||||||
(.limit limit)
|
|
||||||
(.skip skip)
|
|
||||||
(.sort (to-db-object sort))
|
|
||||||
(.batchSize batch-size)
|
|
||||||
(.hint ^DBObject (to-db-object hint)))]
|
|
||||||
(when snapshot
|
|
||||||
(.snapshot cursor))
|
|
||||||
(when read-preference
|
|
||||||
(.setReadPreference cursor read-preference))
|
|
||||||
(map (fn [x] (from-db-object x keywordize-fields))
|
|
||||||
(seq cursor))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn find
|
|
||||||
[m query]
|
|
||||||
(merge m { :query query }))
|
|
||||||
|
|
||||||
(defn fields
|
|
||||||
[m flds]
|
|
||||||
(merge m { :fields flds }))
|
|
||||||
|
|
||||||
(defn sort
|
|
||||||
[m srt]
|
|
||||||
(merge m { :sort srt }))
|
|
||||||
|
|
||||||
(defn skip
|
|
||||||
[m ^long n]
|
|
||||||
(merge m { :skip n }))
|
|
||||||
|
|
||||||
(defn limit
|
|
||||||
[m ^long n]
|
|
||||||
(merge m { :limit n }))
|
|
||||||
|
|
||||||
(defn batch-size
|
|
||||||
[m ^long n]
|
|
||||||
(merge m { :batch-size n }))
|
|
||||||
|
|
||||||
(defn hint
|
|
||||||
[m h]
|
|
||||||
(merge m { :hint h }))
|
|
||||||
|
|
||||||
(defn snapshot
|
|
||||||
[m]
|
|
||||||
(merge m { :snapshot true }))
|
|
||||||
|
|
||||||
(defn read-preference
|
|
||||||
[m ^ReadPreference rp]
|
|
||||||
(merge m { :read-preference rp }))
|
|
||||||
|
|
||||||
(defn keywordize-fields
|
|
||||||
[m bool]
|
|
||||||
(merge m { :keywordize-fields bool }))
|
|
||||||
|
|
||||||
(defn paginate
|
|
||||||
[m & { :keys [page per-page] :or { page 1 per-page 10 } }]
|
|
||||||
(merge m { :limit per-page :skip (monger.internal.pagination/offset-for page per-page) }))
|
|
||||||
|
|
||||||
(defmacro with-collection
|
|
||||||
[^String coll & body]
|
|
||||||
`(binding [*query-collection* (if (string? ~coll)
|
|
||||||
(.getCollection ^DB monger.core/*mongodb-database* ~coll)
|
|
||||||
~coll)]
|
|
||||||
(let [query# (-> (empty-query *query-collection*) ~@body)]
|
|
||||||
(exec query#))))
|
|
||||||
|
|
||||||
(defmacro partial-query
|
|
||||||
[& body]
|
|
||||||
`(-> {} ~@body))
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.result
|
|
||||||
(:import [com.mongodb DBObject WriteResult MapReduceOutput]
|
|
||||||
[clojure.lang IPersistentMap])
|
|
||||||
(:require [monger conversion]))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defprotocol MongoCommandResult
|
|
||||||
(ok? [input] "Returns true if command result is a success")
|
|
||||||
(has-error? [input] "Returns true if command result indicates an error")
|
|
||||||
(updated-existing? [input] "Returns true if command result has `updatedExisting` field set to true"))
|
|
||||||
|
|
||||||
(extend-protocol MongoCommandResult
|
|
||||||
DBObject
|
|
||||||
(ok?
|
|
||||||
[^DBObject result]
|
|
||||||
(.contains [true "true" 1 1.0] (.get result "ok")))
|
|
||||||
(has-error?
|
|
||||||
[^DBObject result]
|
|
||||||
;; yes, this is exactly the logic MongoDB Java driver uses.
|
|
||||||
(> (count (str (.get result "err"))) 0))
|
|
||||||
(updated-existing?
|
|
||||||
[^DBObject result]
|
|
||||||
(let [v ^Boolean (.get result "updatedExisting")]
|
|
||||||
(and v (Boolean/valueOf v))))
|
|
||||||
|
|
||||||
|
|
||||||
WriteResult
|
|
||||||
(ok?
|
|
||||||
[^WriteResult result]
|
|
||||||
(and (not (nil? result)) (ok? (.getLastError result))))
|
|
||||||
(has-error?
|
|
||||||
[^WriteResult result]
|
|
||||||
(has-error? (.getLastError result)))
|
|
||||||
(updated-existing?
|
|
||||||
[^WriteResult result]
|
|
||||||
(updated-existing? (.getLastError result)))
|
|
||||||
|
|
||||||
MapReduceOutput
|
|
||||||
(ok?
|
|
||||||
[^MapReduceOutput result]
|
|
||||||
(ok? ^DBObject (.getRaw result))))
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.testing
|
|
||||||
(:require [monger.collection :as mc]
|
|
||||||
[monger.result :as mr])
|
|
||||||
(:use [monger.internal.fn :only (expand-all expand-all-with) :as fntools])
|
|
||||||
(:import [org.bson.types ObjectId]))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defmacro defcleaner
|
|
||||||
"Defines a fixture function that removes all documents from a collection. If collection is not specified,
|
|
||||||
a conventionally named var will be used. Supposed to be used with clojure.test/use-fixtures but may
|
|
||||||
be useful on its own.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
(defcleaner events) ;; collection name will be taken from the events-collection var
|
|
||||||
(defcleaner people \"accounts\") ;; collection name is given
|
|
||||||
"
|
|
||||||
[entities & coll-name]
|
|
||||||
(let [coll-arg (if coll-name
|
|
||||||
(str (first coll-name))
|
|
||||||
(symbol (str entities "-collection")))
|
|
||||||
fn-name (symbol (str "purge-" entities))]
|
|
||||||
`(defn ~fn-name
|
|
||||||
[f#]
|
|
||||||
(mc/remove ~coll-arg)
|
|
||||||
(f#)
|
|
||||||
(mc/remove ~coll-arg))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(def factories (atom {}))
|
|
||||||
(def defaults (atom {}))
|
|
||||||
|
|
||||||
(defn defaults-for
|
|
||||||
[f-group & { :as attributes }]
|
|
||||||
(swap! defaults (fn [v]
|
|
||||||
(assoc v (name f-group) attributes))))
|
|
||||||
|
|
||||||
(defn factory
|
|
||||||
[f-group f-name & { :as attributes }]
|
|
||||||
(swap! factories (fn [a]
|
|
||||||
(assoc-in a [(name f-group) (name f-name)] attributes))))
|
|
||||||
|
|
||||||
|
|
||||||
(declare build seed)
|
|
||||||
(defn- expand-associate-for-building
|
|
||||||
[f]
|
|
||||||
(let [mt (meta f)
|
|
||||||
[f-group f-name] (f)]
|
|
||||||
(:_id (build f-group f-name))))
|
|
||||||
|
|
||||||
(defn- expand-for-building
|
|
||||||
"Expands functions, treating those with association metadata (see `parent-id` for example) specially"
|
|
||||||
[f]
|
|
||||||
(let [mt (meta f)]
|
|
||||||
(if (:associate-gen mt)
|
|
||||||
(expand-associate-for-building f)
|
|
||||||
(f))))
|
|
||||||
|
|
||||||
(defn- expand-associate-for-seeding
|
|
||||||
[f]
|
|
||||||
(let [mt (meta f)
|
|
||||||
[f-group f-name] (f)]
|
|
||||||
(:_id (seed f-group f-name))))
|
|
||||||
|
|
||||||
(defn- expand-for-seeding
|
|
||||||
"Expands functions, treating those with association metadata (see `parent-id` for example) specially,
|
|
||||||
making sure parent documents are persisted first"
|
|
||||||
[f]
|
|
||||||
(let [mt (meta f)]
|
|
||||||
(if (:associate-gen mt)
|
|
||||||
(expand-associate-for-seeding f)
|
|
||||||
(f))))
|
|
||||||
|
|
||||||
(defn build
|
|
||||||
"Generates a new document and returns it"
|
|
||||||
[f-group f-name & { :as overrides }]
|
|
||||||
(let [d (@defaults (name f-group))
|
|
||||||
attributes (get-in @factories [(name f-group) (name f-name)])
|
|
||||||
merged (merge { :_id (ObjectId.) } d attributes overrides)]
|
|
||||||
(expand-all-with merged expand-for-building)))
|
|
||||||
|
|
||||||
(defn seed
|
|
||||||
"Generates and inserts a new document, then returns it"
|
|
||||||
[f-group f-name & { :as overrides }]
|
|
||||||
(io!
|
|
||||||
(let [d (@defaults (name f-group))
|
|
||||||
attributes (get-in @factories [(name f-group) (name f-name)])
|
|
||||||
merged (merge { :_id (ObjectId.) } d attributes overrides)
|
|
||||||
expanded (expand-all-with merged expand-for-seeding)]
|
|
||||||
(assert (mr/ok? (mc/insert f-group expanded)))
|
|
||||||
expanded)))
|
|
||||||
|
|
||||||
(defn embedded-doc
|
|
||||||
[f-group f-name & { :as overrides }]
|
|
||||||
(fn []
|
|
||||||
(apply build f-group f-name (flatten (vec overrides)))))
|
|
||||||
|
|
||||||
(defn parent-id
|
|
||||||
[f-group f-name]
|
|
||||||
(with-meta (fn []
|
|
||||||
[f-group f-name]) { :associate-gen true :parent-gen true }))
|
|
||||||
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
;; Copyright (c) 2011-2012 Michael S. Klishin
|
|
||||||
;;
|
|
||||||
;; The use and distribution terms for this software are covered by the
|
|
||||||
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
||||||
;; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
;; the terms of this license.
|
|
||||||
;; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns monger.util
|
|
||||||
(:import (java.security SecureRandom) (java.math BigInteger) (org.bson.types ObjectId) (com.mongodb DBObject) (clojure.lang IPersistentMap) (java.util Map)))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; API
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn ^String random-uuid
|
|
||||||
"Generates a secure random UUID string"
|
|
||||||
[]
|
|
||||||
(.toString (java.util.UUID/randomUUID)))
|
|
||||||
|
|
||||||
(defn ^String random-str
|
|
||||||
"Generates a secure random string"
|
|
||||||
[^long n, ^long num-base]
|
|
||||||
(.toString (new BigInteger n (SecureRandom.)) num-base))
|
|
||||||
|
|
||||||
(defn ^ObjectId object-id
|
|
||||||
"Returns a new BSON object id"
|
|
||||||
[]
|
|
||||||
(ObjectId.))
|
|
||||||
|
|
||||||
(defprotocol GetDocumentId
|
|
||||||
(get-id [input] "Returns document id"))
|
|
||||||
|
|
||||||
(extend-protocol GetDocumentId
|
|
||||||
DBObject
|
|
||||||
(get-id
|
|
||||||
[^DBObject object]
|
|
||||||
(.get object "_id"))
|
|
||||||
|
|
||||||
IPersistentMap
|
|
||||||
(get-id
|
|
||||||
[^IPersistentMap object]
|
|
||||||
(or (:_id object) (object "_id"))))
|
|
||||||
136
test/monger/test/aggregation_framework_test.clj
Normal file
136
test/monger/test/aggregation_framework_test.clj
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
(ns monger.test.aggregation-framework-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")
|
||||||
|
coll "docs"]
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/purge-many db [coll])
|
||||||
|
(f)
|
||||||
|
(mc/purge-many db [coll]))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
(deftest test-basic-single-stage-$project-aggregation-no-keywordize
|
||||||
|
(let [batch [{"state" "CA" "quantity" 1 "price" 199.00}
|
||||||
|
{"state" "NY" "quantity" 2 "price" 199.00}
|
||||||
|
{"state" "NY" "quantity" 1 "price" 299.00}
|
||||||
|
{"state" "IL" "quantity" 2 "price" 11.50 }
|
||||||
|
{"state" "CA" "quantity" 2 "price" 2.95 }
|
||||||
|
{"state" "IL" "quantity" 3 "price" 5.50 }]
|
||||||
|
expected #{{"quantity" 1 "state" "CA"}
|
||||||
|
{"quantity" 2 "state" "NY"}
|
||||||
|
{"quantity" 1 "state" "NY"}
|
||||||
|
{"quantity" 2 "state" "IL"}
|
||||||
|
{"quantity" 2 "state" "CA"}
|
||||||
|
{"quantity" 3 "state" "IL"}}]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(is (= 6 (mc/count db coll)))
|
||||||
|
(let [result (->>
|
||||||
|
(mc/aggregate db coll [{$project {"state" 1 "quantity" 1}}] :keywordize false)
|
||||||
|
(map #(select-keys % ["state" "quantity"]))
|
||||||
|
(set))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
(deftest test-basic-single-stage-$project-aggregation
|
||||||
|
(let [batch [{:state "CA" :quantity 1 :price 199.00}
|
||||||
|
{:state "NY" :quantity 2 :price 199.00}
|
||||||
|
{:state "NY" :quantity 1 :price 299.00}
|
||||||
|
{:state "IL" :quantity 2 :price 11.50 }
|
||||||
|
{:state "CA" :quantity 2 :price 2.95 }
|
||||||
|
{:state "IL" :quantity 3 :price 5.50 }]
|
||||||
|
expected #{{:quantity 1 :state "CA"}
|
||||||
|
{:quantity 2 :state "NY"}
|
||||||
|
{:quantity 1 :state "NY"}
|
||||||
|
{:quantity 2 :state "IL"}
|
||||||
|
{:quantity 2 :state "CA"}
|
||||||
|
{:quantity 3 :state "IL"}}]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(is (= 6 (mc/count db coll)))
|
||||||
|
(let [result (set (map #(select-keys % [:state :quantity])
|
||||||
|
(mc/aggregate db coll [{$project {:state 1 :quantity 1}}])))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-basic-projection-with-multiplication
|
||||||
|
(let [batch [{:state "CA" :quantity 1 :price 199.00}
|
||||||
|
{:state "NY" :quantity 2 :price 199.00}
|
||||||
|
{:state "NY" :quantity 1 :price 299.00}
|
||||||
|
{:state "IL" :quantity 2 :price 11.50 }
|
||||||
|
{:state "CA" :quantity 2 :price 2.95 }
|
||||||
|
{:state "IL" :quantity 3 :price 5.50 }]
|
||||||
|
expected #{{:_id "NY" :subtotal 398.0}
|
||||||
|
{:_id "NY" :subtotal 299.0}
|
||||||
|
{:_id "IL" :subtotal 23.0}
|
||||||
|
{:_id "CA" :subtotal 5.9}
|
||||||
|
{:_id "IL" :subtotal 16.5}
|
||||||
|
{:_id "CA" :subtotal 199.0}}]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(let [result (set (mc/aggregate db coll [{$project {:subtotal {$multiply ["$quantity", "$price"]}
|
||||||
|
:_id "$state"}}]))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-basic-total-aggregation
|
||||||
|
(let [batch [{:state "CA" :quantity 1 :price 199.00}
|
||||||
|
{:state "NY" :quantity 2 :price 199.00}
|
||||||
|
{:state "NY" :quantity 1 :price 299.00}
|
||||||
|
{:state "IL" :quantity 2 :price 11.50 }
|
||||||
|
{:state "CA" :quantity 2 :price 2.95 }
|
||||||
|
{:state "IL" :quantity 3 :price 5.50 }]
|
||||||
|
expected #{{:_id "CA" :total 204.9} {:_id "IL" :total 39.5} {:_id "NY" :total 697.0}}]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(let [result (set (mc/aggregate db coll [{$project {:subtotal {$multiply ["$quantity", "$price"]}
|
||||||
|
:_id 1
|
||||||
|
:state 1}}
|
||||||
|
{$group {:_id "$state"
|
||||||
|
:total {$sum "$subtotal"}}}]))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-$first-aggregation-operator
|
||||||
|
(let [batch [{:state "CA"}
|
||||||
|
{:state "IL"}]
|
||||||
|
expected "CA"]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(let [result (:state (first (mc/aggregate db coll [{$group {:_id 1 :state {$first "$state"}}}])))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
(deftest test-$last-aggregation-operator
|
||||||
|
(let [batch [{:state "CA"}
|
||||||
|
{:state "IL"}]
|
||||||
|
expected "IL"]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(let [result (:state (first (mc/aggregate db coll [{$group {:_id 1 :state {$last "$state"}}}])))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
(deftest test-cursor-aggregation
|
||||||
|
(let [batch [{:state "CA" :quantity 1 :price 199.00}
|
||||||
|
{:state "NY" :quantity 2 :price 199.00}
|
||||||
|
{:state "NY" :quantity 1 :price 299.00}
|
||||||
|
{:state "IL" :quantity 2 :price 11.50 }
|
||||||
|
{:state "CA" :quantity 2 :price 2.95 }
|
||||||
|
{:state "IL" :quantity 3 :price 5.50 }]
|
||||||
|
expected #{{:quantity 1 :state "CA"}
|
||||||
|
{:quantity 2 :state "NY"}
|
||||||
|
{:quantity 1 :state "NY"}
|
||||||
|
{:quantity 2 :state "IL"}
|
||||||
|
{:quantity 2 :state "CA"}
|
||||||
|
{:quantity 3 :state "IL"}}]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(is (= 6 (mc/count db coll)))
|
||||||
|
(let [result (set (map #(select-keys % [:state :quantity])
|
||||||
|
(mc/aggregate db coll [{$project {:state 1 :quantity 1}}] :cursor {:batch-size 10})))]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
||||||
|
(deftest test-explain-aggregate
|
||||||
|
(let [batch [{:state "CA" :price 100}
|
||||||
|
{:state "CA" :price 10}
|
||||||
|
{:state "IL" :price 50}]]
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(let [result (mc/explain-aggregate db coll [{$match {:state "CA"}}])]
|
||||||
|
(is (:ok result))))))
|
||||||
|
|
@ -1,293 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.atomic-modifiers
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.operators]
|
|
||||||
[monger.test.fixtures]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-docs purge-things purge-scores)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $inc
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest increment-a-single-existing-field-using-$inc-modifier
|
|
||||||
(let [coll "scores"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :username "l33r0y" :score 100 })
|
|
||||||
(mgcol/update coll { :_id oid } { $inc { :score 20 } })
|
|
||||||
(is (= 120 (:score (mgcol/find-map-by-id coll oid))))))
|
|
||||||
|
|
||||||
(deftest set-a-single-non-existing-field-using-$inc-modifier
|
|
||||||
(let [coll "scores"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :username "l33r0y" })
|
|
||||||
(mgcol/update coll { :_id oid } { $inc { :score 30 } })
|
|
||||||
(is (= 30 (:score (mgcol/find-map-by-id coll oid))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest increment-multiple-existing-fields-using-$inc-modifier
|
|
||||||
(let [coll "scores"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :username "l33r0y" :score 100 :bonus 0 })
|
|
||||||
(mgcol/update coll { :_id oid } {$inc { :score 20 :bonus 10 } })
|
|
||||||
(is (= { :_id oid :score 120 :bonus 10 :username "l33r0y" } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest increment-and-set-multiple-existing-fields-using-$inc-modifier
|
|
||||||
(let [coll "scores"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :username "l33r0y" :score 100 })
|
|
||||||
(mgcol/update coll { :_id oid } { $inc { :score 20 :bonus 10 } })
|
|
||||||
(is (= { :_id oid :score 120 :bonus 10 :username "l33r0y" } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $set
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest update-a-single-existing-field-using-$set-modifier
|
|
||||||
(let [coll "things"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :weight 10.0 })
|
|
||||||
(mgcol/update coll { :_id oid } { $set { :weight 20.5 } })
|
|
||||||
(is (= 20.5 (:weight (mgcol/find-map-by-id coll oid [:weight]))))))
|
|
||||||
|
|
||||||
(deftest set-a-single-non-existing-field-using-$set-modifier
|
|
||||||
(let [coll "things"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :weight 10.0 })
|
|
||||||
(mgcol/update coll { :_id oid } { $set { :height 17.2 } })
|
|
||||||
(is (= 17.2 (:height (mgcol/find-map-by-id coll oid [:height]))))))
|
|
||||||
|
|
||||||
(deftest update-multiple-existing-fields-using-$set-modifier
|
|
||||||
(let [coll "things"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :weight 10.0 :height 15.2 })
|
|
||||||
(mgcol/update coll { :_id oid } { $set { :weight 20.5 :height 25.6 } })
|
|
||||||
(is (= { :_id oid :weight 20.5 :height 25.6 } (mgcol/find-map-by-id coll oid [:weight :height])))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest update-and-set-multiple-fields-using-$set-modifier
|
|
||||||
(let [coll "things"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :weight 10.0 })
|
|
||||||
(mgcol/update coll { :_id oid } {$set { :weight 20.5 :height 25.6 } })
|
|
||||||
(is (= { :_id oid :weight 20.5 :height 25.6 } (mgcol/find-map-by-id coll oid [:weight :height])))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $unset
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest unset-a-single-existing-field-using-$unset-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :title "Document 1" :published true })
|
|
||||||
(mgcol/update coll { :_id oid } { $unset { :published 1 } })
|
|
||||||
(is (= { :_id oid :title "Document 1" } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest unset-multiple-existing-fields-using-$unset-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :title "Document 1" :published true :featured true })
|
|
||||||
(mgcol/update coll { :_id oid } { $unset { :published 1 :featured true } })
|
|
||||||
(is (= { :_id oid :title "Document 1" } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest unsetting-an-unexisting-field-using-$unset-modifier-is-not-considered-an-issue
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert coll { :_id oid :title "Document 1" :published true })
|
|
||||||
(is (mgres/ok? (mgcol/update coll { :_id oid } { $unset { :published 1 :featured true } })))
|
|
||||||
(is (= { :_id oid :title "Document 1" } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $push
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest initialize-an-array-using-$push-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$push modifier appends value to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title })
|
|
||||||
(mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
|
|
||||||
(is (= { :_id oid :title title :tags ["modifiers"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
(deftest add-value-to-an-existing-array-using-$push-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$push modifier appends value to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;; this is a common mistake, I leave it here to demonstrate it. You almost never
|
|
||||||
;; actually want to do this! What you really want is to use $pushAll instead of $push. MK.
|
|
||||||
(deftest add-array-value-to-an-existing-array-using-$push-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$push modifier appends value to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $push { :tags ["modifiers" "operators"] } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" ["modifiers" "operators"]] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest double-add-value-to-an-existing-array-using-$push-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$push modifier appends value to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
|
|
||||||
(mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "modifiers" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $pushAll
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest initialize-an-array-using-$pushAll-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pushAll modifier appends multiple values to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title })
|
|
||||||
(mgcol/update coll { :_id oid } { $pushAll { :tags ["mongodb" "docs"] } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "docs"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
(deftest add-value-to-an-existing-array-using-$pushAll-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pushAll modifier appends multiple values to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pushAll { :tags ["modifiers" "docs"] } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "modifiers" "docs"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest double-add-value-to-an-existing-array-using-$pushAll-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pushAll modifier appends multiple values to field"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb" "docs"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pushAll { :tags ["modifiers" "docs"] } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "docs" "modifiers" "docs"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $addToSet
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest initialize-an-array-using-$addToSet-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$addToSet modifier appends value to field unless it is already there"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title })
|
|
||||||
(mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
|
|
||||||
(is (= { :_id oid :title title :tags ["modifiers"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
(deftest add-value-to-an-existing-array-using-$addToSet-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$addToSet modifier appends value to field unless it is already there"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest double-add-value-to-an-existing-array-using-$addToSet-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$addToSet modifier appends value to field unless it is already there"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
|
|
||||||
(mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
|
|
||||||
(is (= { :_id oid :title title :tags ["mongodb" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $pop
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest pop-last-value-in-the-array-using-$pop-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pop modifier removes last or first value in the array"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["products" "apple" "reviews"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pop { :tags 1 } })
|
|
||||||
(is (= { :_id oid :title title :tags ["products" "apple"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
(deftest unshift-first-value-in-the-array-using-$pop-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pop modifier removes last or first value in the array"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["products" "apple" "reviews"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pop { :tags -1 } })
|
|
||||||
(is (= { :_id oid :title title :tags ["apple" "reviews"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
(deftest pop-last-values-from-multiple-arrays-using-$pop-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pop modifier removes last or first value in the array"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :tags ["products" "apple" "reviews"] :categories ["apple" "reviews" "drafts"] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pop { :tags 1 :categories 1 } })
|
|
||||||
(is (= { :_id oid :title title :tags ["products" "apple"] :categories ["apple" "reviews"] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $pull
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest remove-all-value-entries-from-array-using-$pull-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pull modifier removes all value entries in the array"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pull { :measurements 1.2 } })
|
|
||||||
(is (= { :_id oid :title title :measurements [1.0 1.1 1.1 1.3 1.0] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
(deftest remove-all-value-entries-from-array-using-$pull-modifier-based-on-a-condition
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pull modifier removes all value entries in the array"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pull { :measurements { $gte 1.2 } } })
|
|
||||||
(is (= { :_id oid :title title :measurements [1.0 1.1 1.1 1.0] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
;;
|
|
||||||
;; $pullAll
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest remove-all-value-entries-from-array-using-$pullAll-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$pullAll modifier removes entries of multiple values in the array"]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0] })
|
|
||||||
(mgcol/update coll { :_id oid } { $pullAll { :measurements [1.0 1.1 1.2] } })
|
|
||||||
(is (= { :_id oid :title title :measurements [1.3] } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $rename
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest rename-a-single-field-using-$rename-modifier
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
title "$rename renames fields"
|
|
||||||
v [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]]
|
|
||||||
(mgcol/insert coll { :_id oid :title title :measurements v })
|
|
||||||
(mgcol/update coll { :_id oid } { $rename { :measurements "results" } })
|
|
||||||
(is (= { :_id oid :title title :results v } (mgcol/find-map-by-id coll oid)))))
|
|
||||||
461
test/monger/test/atomic_modifiers_test.clj
Normal file
461
test/monger/test/atomic_modifiers_test.clj
Normal file
|
|
@ -0,0 +1,461 @@
|
||||||
|
(ns monger.test.atomic-modifiers-test
|
||||||
|
(:import [com.mongodb WriteResult WriteConcern DBObject]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.result :refer [acknowledged?]]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "scores")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "scores"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $inc
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest increment-a-single-existing-field-using-$inc-modifier
|
||||||
|
(let [coll "scores"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :username "l33r0y" :score 100})
|
||||||
|
(mc/update db coll {:_id oid} {$inc {:score 20}})
|
||||||
|
(is (= 120 (:score (mc/find-map-by-id db coll oid))))))
|
||||||
|
|
||||||
|
(deftest set-a-single-non-existing-field-using-$inc-modifier
|
||||||
|
(let [coll "scores"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :username "l33r0y"})
|
||||||
|
(mc/update db coll {:_id oid} {$inc {:score 30}})
|
||||||
|
(is (= 30 (:score (mc/find-map-by-id db coll oid))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest increment-multiple-existing-fields-using-$inc-modifier
|
||||||
|
(let [coll "scores"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :username "l33r0y" :score 100 :bonus 0})
|
||||||
|
(mc/update db coll {:_id oid} {$inc {:score 20 :bonus 10}})
|
||||||
|
(is (= {:_id oid :score 120 :bonus 10 :username "l33r0y"}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest increment-and-set-multiple-existing-fields-using-$inc-modifier
|
||||||
|
(let [coll "scores"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :username "l33r0y" :score 100})
|
||||||
|
(mc/update db coll {:_id oid} {$inc {:score 20 :bonus 10}})
|
||||||
|
(is (= {:_id oid :score 120 :bonus 10 :username "l33r0y"}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $set
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest update-a-single-existing-field-using-$set-modifier
|
||||||
|
(let [coll "things"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :weight 10.0})
|
||||||
|
(mc/update db coll {:_id oid} {$set {:weight 20.5}})
|
||||||
|
(is (= 20.5 (:weight (mc/find-map-by-id db coll oid [:weight]))))))
|
||||||
|
|
||||||
|
(deftest set-a-single-non-existing-field-using-$set-modifier
|
||||||
|
(let [coll "things"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :weight 10.0})
|
||||||
|
(mc/update db coll {:_id oid} {$set {:height 17.2}})
|
||||||
|
(is (= 17.2 (:height (mc/find-map-by-id db coll oid [:height]))))))
|
||||||
|
|
||||||
|
(deftest update-multiple-existing-fields-using-$set-modifier
|
||||||
|
(let [coll "things"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :weight 10.0 :height 15.2})
|
||||||
|
(mc/update db coll {:_id oid} {$set {:weight 20.5 :height 25.6}})
|
||||||
|
(is (= {:_id oid :weight 20.5 :height 25.6}
|
||||||
|
(mc/find-map-by-id db coll oid [:weight :height])))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest update-and-set-multiple-fields-using-$set-modifier
|
||||||
|
(let [coll "things"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :weight 10.0})
|
||||||
|
(mc/update db coll {:_id oid} {$set {:weight 20.5 :height 25.6}})
|
||||||
|
(is (= {:_id oid :weight 20.5 :height 25.6}
|
||||||
|
(mc/find-map-by-id db coll oid [:weight :height])))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $unset
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest unset-a-single-existing-field-using-$unset-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :title "Document 1" :published true})
|
||||||
|
(mc/update db coll {:_id oid} {$unset {:published 1}})
|
||||||
|
(is (= {:_id oid :title "Document 1"}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest unset-multiple-existing-fields-using-$unset-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :title "Document 1" :published true :featured true})
|
||||||
|
(mc/update db coll {:_id oid} {$unset {:published 1 :featured true}})
|
||||||
|
(is (= {:_id oid :title "Document 1"}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest unsetting-an-unexisting-field-using-$unset-modifier-is-not-considered-an-issue
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :title "Document 1" :published true})
|
||||||
|
(is (acknowledged? (mc/update db coll {:_id oid} {$unset {:published 1 :featured true}})))
|
||||||
|
(is (= {:_id oid :title "Document 1"}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $setOnInsert
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest setOnInsert-in-upsert-for-non-existing-document
|
||||||
|
(let [coll "docs"
|
||||||
|
now 456
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/find-and-modify db coll {:_id oid} {$set {:lastseen now} $setOnInsert {:firstseen now}} {:upsert true})
|
||||||
|
(is (= {:_id oid :lastseen now :firstseen now}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest setOnInsert-in-upsert-for-existing-document
|
||||||
|
(let [coll "docs"
|
||||||
|
before 123
|
||||||
|
now 456
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert db coll {:_id oid :firstseen before :lastseen before})
|
||||||
|
(mc/find-and-modify db coll {:_id oid} {$set {:lastseen now} $setOnInsert {:firstseen now}} {:upsert true})
|
||||||
|
(is (= {:_id oid :lastseen now :firstseen before}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $push
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest initialize-an-array-using-$push-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push modifier appends value to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}})
|
||||||
|
(is (= {:_id oid :title title :tags ["modifiers"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest add-value-to-an-existing-array-using-$push-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push modifier appends value to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
;; this is a common mistake, I leave it here to demonstrate it. You almost never
|
||||||
|
;; actually want to do this! What you really want is to use $push with $each instead of $push. MK.
|
||||||
|
(deftest add-array-value-to-an-existing-array-using-$push-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push modifier appends value to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags ["modifiers" "operators"]}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" ["modifiers" "operators"]]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(deftest double-add-value-to-an-existing-array-using-$push-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push modifier appends value to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "modifiers"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $push $each
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest initialize-an-array-using-$push-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push with $each modifier appends multiple values to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags {$each ["mongodb" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest add-values-to-an-existing-array-using-$push-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push with $each modifier appends multiple values to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest double-add-value-to-an-existing-array-using-$push-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$push with $each modifier appends multiple values to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb" "docs"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "docs" "modifiers" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $push + $each (formerly $pushAll)
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest initialize-an-array-using-$push-and-$each-modifiers
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pushAll modifier appends multiple values to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags {$each ["mongodb" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest add-value-to-an-existing-array-using-$push-and-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pushAll modifier appends multiple values to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest double-add-value-to-an-existing-array-using-$push-and-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pushAll modifier appends multiple values to field"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb" "docs"]})
|
||||||
|
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "docs" "modifiers" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $addToSet
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest initialize-an-array-using-$addToSet-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$addToSet modifier appends value to field unless it is already there"]
|
||||||
|
(mc/insert db coll {:_id oid :title title})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}})
|
||||||
|
(is (= {:_id oid :title title :tags ["modifiers"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest add-value-to-an-existing-array-using-$addToSet-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$addToSet modifier appends value to field unless it is already there"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest double-add-value-to-an-existing-array-using-$addToSet-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$addToSet modifier appends value to field unless it is already there"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $addToSet $each
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest initialize-an-array-using-$addToSet-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$addToSet with $each modifier appends multiple values to field unless they are already there"]
|
||||||
|
(mc/insert db coll {:_id oid :title title})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags {$each ["mongodb" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest add-values-to-an-existing-array-using-$addToSet-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$addToSet with $each modifier appends multiple values to field unless they are already there"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags {$each ["modifiers" "docs"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "docs"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest double-add-value-to-an-existing-array-using-$addToSet-$each-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$addToSet with $each modifier appends multiple values to field unless they are already there"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["mongodb" "docs"]})
|
||||||
|
(mc/update db coll {:_id oid} {$addToSet {:tags {$each ["modifiers" "docs" "operators"]}}})
|
||||||
|
(is (= {:_id oid :title title :tags ["mongodb" "docs" "modifiers" "operators"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $pop
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest pop-last-value-in-the-array-using-$pop-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pop modifier removes last or first value in the array"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["products" "apple" "reviews"]})
|
||||||
|
(mc/update db coll {:_id oid} {$pop {:tags 1}})
|
||||||
|
(is (= {:_id oid :title title :tags ["products" "apple"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest unshift-first-value-in-the-array-using-$pop-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pop modifier removes last or first value in the array"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["products" "apple" "reviews"]})
|
||||||
|
(mc/update db coll {:_id oid} {$pop {:tags -1}})
|
||||||
|
(is (= {:_id oid :title title :tags ["apple" "reviews"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest pop-last-values-from-multiple-arrays-using-$pop-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pop modifier removes last or first value in the array"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :tags ["products" "apple" "reviews"] :categories ["apple" "reviews" "drafts"]})
|
||||||
|
(mc/update db coll {:_id oid} {$pop {:tags 1 :categories 1}})
|
||||||
|
(is (= {:_id oid :title title :tags ["products" "apple"] :categories ["apple" "reviews"]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $pull
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest remove-all-value-entries-from-array-using-$pull-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pull modifier removes all value entries in the array"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]})
|
||||||
|
(mc/update db coll {:_id oid} {$pull {:measurements 1.2}})
|
||||||
|
(is (= {:_id oid :title title :measurements [1.0 1.1 1.1 1.3 1.0]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
(deftest remove-all-value-entries-from-array-using-$pull-modifier-based-on-a-condition
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pull modifier removes all value entries in the array"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]})
|
||||||
|
(mc/update db coll {:_id oid} {$pull {:measurements {$gte 1.2}}})
|
||||||
|
(is (= {:_id oid :title title :measurements [1.0 1.1 1.1 1.0]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
;;
|
||||||
|
;; $pullAll
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest remove-all-value-entries-from-array-using-$pullAll-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$pullAll modifier removes entries of multiple values in the array"]
|
||||||
|
(mc/insert db coll {:_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]})
|
||||||
|
(mc/update db coll {:_id oid} {$pullAll {:measurements [1.0 1.1 1.2]}})
|
||||||
|
(is (= {:_id oid :title title :measurements [1.3]}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $rename
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest rename-a-single-field-using-$rename-modifier
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
title "$rename renames fields"
|
||||||
|
v [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]]
|
||||||
|
(mc/insert db coll {:_id oid :title title :measurements v})
|
||||||
|
(mc/update db coll {:_id oid} {$rename {:measurements "results"}})
|
||||||
|
(is (= {:_id oid :title title :results v}
|
||||||
|
(mc/find-map-by-id db coll oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; find-and-modify
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-and-modify-a-single-document
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
doc {:_id oid :name "Sophie Bangs" :level 42}
|
||||||
|
conditions {:name "Sophie Bangs"}
|
||||||
|
update {$inc {:level 1}}]
|
||||||
|
(mc/insert db coll doc)
|
||||||
|
(let [res (mc/find-and-modify db coll conditions update {:return-new true})]
|
||||||
|
(is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 43})))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-and-modify-remove-a-document
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
doc {:_id oid :name "Sophie Bangs" :level 42}
|
||||||
|
conditions {:name "Sophie Bangs"}]
|
||||||
|
(mc/insert db coll doc)
|
||||||
|
(let [res (mc/find-and-modify db coll conditions {} {:remove true})]
|
||||||
|
(is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 42}))
|
||||||
|
(is (empty? (mc/find-maps db coll conditions))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-and-modify-upsert-a-document
|
||||||
|
(testing "case 1"
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
doc {:_id oid :name "Sophie Bangs" :level 42}]
|
||||||
|
(let [res (mc/find-and-modify db coll doc doc {:upsert true})]
|
||||||
|
(is (empty? res))
|
||||||
|
(is (select-keys (mc/find-map-by-id db coll oid) [:name :level]) (dissoc doc :_id)))))
|
||||||
|
(testing "case 2"
|
||||||
|
(let [coll "docs"
|
||||||
|
query {:name "Sophie Bangs"}
|
||||||
|
doc (merge query {:level 42})]
|
||||||
|
(let [res (mc/find-and-modify db coll query doc {:upsert true :return-new true})]
|
||||||
|
(is (:_id res))
|
||||||
|
(is (select-keys (mc/find-map-by-id db coll (:_id res)) [:name :level]) doc)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-and-modify-after-sort
|
||||||
|
(let [coll "docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
oid2 (ObjectId.)
|
||||||
|
doc {:name "Sophie Bangs"}
|
||||||
|
doc1 (assoc doc :_id oid :level 42)
|
||||||
|
doc2 (assoc doc :_id oid2 :level 0)]
|
||||||
|
(mc/insert-batch db coll [doc1 doc2])
|
||||||
|
(let [res (mc/find-and-modify db coll doc {$inc {:level 1}} {:sort {:level -1}})]
|
||||||
|
(is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 42}))))))
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
(ns monger.test.authentication
|
|
||||||
(:require [monger core util db]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-authentication-with-valid-credentials
|
|
||||||
;; see ./bin/ci/before_script.sh. MK.
|
|
||||||
(let [username "clojurewerkz/monger"
|
|
||||||
pwd "monger"]
|
|
||||||
(is (monger.core/authenticate "monger-test" username (.toCharArray pwd)))))
|
|
||||||
|
|
||||||
(deftest test-authentication-with-invalid-credentials
|
|
||||||
(let [username "monger"
|
|
||||||
^String pwd (monger.util/random-str 128 32)]
|
|
||||||
(is (not (monger.core/authenticate "monger-test2" username (.toCharArray pwd))))))
|
|
||||||
42
test/monger/test/authentication_test.clj
Normal file
42
test/monger/test/authentication_test.clj
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
(ns monger.test.authentication-test
|
||||||
|
(:require [monger util db]
|
||||||
|
[monger.credentials :as mcr]
|
||||||
|
[monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Connection via URI
|
||||||
|
;;
|
||||||
|
|
||||||
|
(when-not (System/getenv "CI")
|
||||||
|
(deftest ^{:authentication true} connect-to-mongo-via-uri-without-credentials
|
||||||
|
(let [{:keys [conn db]} (mg/connect-via-uri "mongodb://127.0.0.1/monger-test4")]
|
||||||
|
(is (-> conn .getAddress (.sameHost "127.0.0.1")))))
|
||||||
|
|
||||||
|
(deftest ^{:authentication true} connect-to-mongo-via-uri-with-valid-credentials
|
||||||
|
(let [{:keys [conn db]} (mg/connect-via-uri "mongodb://clojurewerkz%2Fmonger:monger@127.0.0.1/monger-test4")]
|
||||||
|
(is (= "monger-test4" (.getName db)))
|
||||||
|
(is (-> conn .getAddress (.sameHost "127.0.0.1")))
|
||||||
|
(mc/remove db "documents")
|
||||||
|
;; make sure that the database is selected
|
||||||
|
;; and operations get through.
|
||||||
|
(mc/insert db "documents" {:field "value"})
|
||||||
|
(is (= 1 (mc/count db "documents" {}))))))
|
||||||
|
|
||||||
|
(if-let [uri (System/getenv "MONGOHQ_URL")]
|
||||||
|
(deftest ^{:external true :authentication true} connect-to-mongo-via-uri-with-valid-credentials
|
||||||
|
(let [{:keys [conn db]} (mg/connect-via-uri uri)]
|
||||||
|
(is (-> conn .getAddress (.sameHost "127.0.0.1"))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Regular connecton
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest ^{:authentication true} test-authentication-with-valid-credentials
|
||||||
|
;; see ./bin/ci/before_script.sh. MK.
|
||||||
|
(doseq [s ["monger-test" "monger-test2" "monger-test3" "monger-test4"]]
|
||||||
|
(let [creds (mcr/create "clojurewerkz/monger" "monger-test" "monger")
|
||||||
|
conn (mg/connect-with-credentials "127.0.0.1" creds)]
|
||||||
|
(mc/remove (mg/get-db conn "monger-test") "documents"))))
|
||||||
25
test/monger/test/capped_collections_test.clj
Normal file
25
test/monger/test/capped_collections_test.clj
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
(ns monger.test.capped-collections-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]))
|
||||||
|
|
||||||
|
(defn- megabytes
|
||||||
|
[^long n]
|
||||||
|
(* n 1024 1024))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(deftest test-inserting-into-capped-collection
|
||||||
|
(let [n 1000
|
||||||
|
cname "cached"
|
||||||
|
_ (mc/drop db cname)
|
||||||
|
coll (mc/create db cname {:capped true :size (-> 16 megabytes) :max n})]
|
||||||
|
(is (= cname (.getName coll)))
|
||||||
|
(mc/insert-batch db cname (for [i (range 0 (+ n 100))] {:i i}))
|
||||||
|
(is (= n (mc/count db cname)))
|
||||||
|
;; older elements get replaced by newer ones
|
||||||
|
(is (not (mc/any? db cname {:i 1})))
|
||||||
|
(is (not (mc/any? db cname {:i 5})))
|
||||||
|
(is (not (mc/any? db cname {:i 9})))
|
||||||
|
(is (mc/any? db cname {:i (+ n 80)})))))
|
||||||
|
|
@ -1,212 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.collection
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure MapReduceOutput MapReduceCommand MapReduceCommand$OutputType]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[clojure stacktrace]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.conversion :as mgcnv]
|
|
||||||
[monger.js :as js]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.operators]
|
|
||||||
[monger.test.fixtures]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; count, remove
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest get-collection-size
|
|
||||||
(let [collection "things"]
|
|
||||||
(is (= 0 (mgcol/count collection)))
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }] )
|
|
||||||
(is (= 4 (mgcol/count collection)))
|
|
||||||
(is (mgcol/any? collection))
|
|
||||||
(is (= 3 (mgcol/count monger.core/*mongodb-database* collection { :language "Clojure" })))
|
|
||||||
(is (mgcol/any? monger.core/*mongodb-database* collection { :language "Clojure" }))
|
|
||||||
(is (= 1 (mgcol/count collection { :language "Scala" })))
|
|
||||||
(is (mgcol/any? collection { :language "Scala" }))
|
|
||||||
(is (= 0 (mgcol/count monger.core/*mongodb-database* collection { :language "Python" })))
|
|
||||||
(is (not (mgcol/any? monger.core/*mongodb-database* collection { :language "Python" })))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest remove-all-documents-from-collection
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(is (= 4 (mgcol/count collection)))
|
|
||||||
(mgcol/remove collection)
|
|
||||||
(is (= 0 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest remove-some-documents-from-collection
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(is (= 4 (mgcol/count collection)))
|
|
||||||
(mgcol/remove collection { :language "Clojure" })
|
|
||||||
(is (= 1 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
(deftest remove-a-single-document-from-collection
|
|
||||||
(let [collection "libraries"
|
|
||||||
oid (ObjectId.)]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure" :name "monger" :_id oid }])
|
|
||||||
(mgcol/remove-by-id collection oid)
|
|
||||||
(is (= 0 (mgcol/count collection)))
|
|
||||||
(is (nil? (mgcol/find-by-id collection oid)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; indexes
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest index-operations
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/drop-indexes collection)
|
|
||||||
(is (= "_id_"
|
|
||||||
(:name (first (mgcol/indexes-on collection)))))
|
|
||||||
(is (nil? (second (mgcol/indexes-on collection))))
|
|
||||||
(mgcol/create-index collection { "language" 1 })
|
|
||||||
(is (= "language_1"
|
|
||||||
(:name (second (mgcol/indexes-on collection)))))
|
|
||||||
(mgcol/drop-index collection "language_1")
|
|
||||||
(is (nil? (second (mgcol/indexes-on collection))))
|
|
||||||
(mgcol/ensure-index collection { "language" 1 })
|
|
||||||
(is (= "language_1"
|
|
||||||
(:name (second (mgcol/indexes-on collection)))))
|
|
||||||
(mgcol/ensure-index collection { "language" 1 })))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; exists?, drop, create
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest checking-for-collection-existence-when-it-does-not-exist
|
|
||||||
(let [collection "widgets"]
|
|
||||||
(mgcol/drop collection)
|
|
||||||
(is (false? (mgcol/exists? collection)))))
|
|
||||||
|
|
||||||
(deftest checking-for-collection-existence-when-it-does-exist
|
|
||||||
(let [collection "widgets"]
|
|
||||||
(mgcol/drop collection)
|
|
||||||
(mgcol/insert-batch collection [{ :name "widget1" }
|
|
||||||
{ :name "widget2" }])
|
|
||||||
(is (mgcol/exists? collection))
|
|
||||||
(mgcol/drop collection)
|
|
||||||
(is (false? (mgcol/exists? collection)))
|
|
||||||
(mgcol/create "widgets" { :capped true :size 100000 :max 10 })
|
|
||||||
(is (mgcol/exists? collection))
|
|
||||||
(mgcol/rename collection "gadgets")
|
|
||||||
(is (not (mgcol/exists? collection)))
|
|
||||||
(is (mgcol/exists? "gadgets"))
|
|
||||||
(mgcol/drop "gadgets")))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Map/Reduce
|
|
||||||
;;
|
|
||||||
|
|
||||||
(let [collection "widgets"
|
|
||||||
mapper (js/load-resource "resources/mongo/js/mapfun1.js")
|
|
||||||
reducer "function(key, values) {
|
|
||||||
var result = 0;
|
|
||||||
values.forEach(function(v) { result += v });
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}"
|
|
||||||
batch [{ :state "CA" :quantity 1 :price 199.00 }
|
|
||||||
{ :state "NY" :quantity 2 :price 199.00 }
|
|
||||||
{ :state "NY" :quantity 1 :price 299.00 }
|
|
||||||
{ :state "IL" :quantity 2 :price 11.50 }
|
|
||||||
{ :state "CA" :quantity 2 :price 2.95 }
|
|
||||||
{ :state "IL" :quantity 3 :price 5.50 }]
|
|
||||||
expected [{:_id "CA", :value 204.9} {:_id "IL", :value 39.5} {:_id "NY", :value 697.0}]]
|
|
||||||
(deftest basic-inline-map-reduce-example
|
|
||||||
(mgcol/remove monger.core/*mongodb-database* collection {})
|
|
||||||
(is (mgres/ok? (mgcol/insert-batch collection batch)))
|
|
||||||
(let [output (mgcol/map-reduce collection mapper reducer nil MapReduceCommand$OutputType/INLINE {})
|
|
||||||
results (mgcnv/from-db-object ^DBObject (.results ^MapReduceOutput output) true)]
|
|
||||||
(mgres/ok? output)
|
|
||||||
(is (= expected results))))
|
|
||||||
|
|
||||||
(deftest basic-map-reduce-example-that-replaces-named-collection
|
|
||||||
(mgcol/remove monger.core/*mongodb-database* collection {})
|
|
||||||
(is (mgres/ok? (mgcol/insert-batch collection batch)))
|
|
||||||
(let [output (mgcol/map-reduce collection mapper reducer "mr_outputs" {})
|
|
||||||
results (mgcnv/from-db-object ^DBObject (.results ^MapReduceOutput output) true)]
|
|
||||||
(mgres/ok? output)
|
|
||||||
(is (= 3 (monger.core/count results)))
|
|
||||||
(is (= expected
|
|
||||||
(map #(mgcnv/from-db-object % true) (seq results))))
|
|
||||||
(is (= expected
|
|
||||||
(map #(mgcnv/from-db-object % true) (mgcol/find "mr_outputs"))))
|
|
||||||
(.drop ^MapReduceOutput output)))
|
|
||||||
|
|
||||||
(deftest basic-map-reduce-example-that-merged-results-into-named-collection
|
|
||||||
(mgcol/remove monger.core/*mongodb-database* collection {})
|
|
||||||
(is (mgres/ok? (mgcol/insert-batch collection batch)))
|
|
||||||
(mgcol/map-reduce collection mapper reducer "merged_mr_outputs" MapReduceCommand$OutputType/MERGE {})
|
|
||||||
(is (mgres/ok? (mgcol/insert collection { :state "OR" :price 17.95 :quantity 4 })))
|
|
||||||
(let [output (mgcol/map-reduce collection mapper reducer "merged_mr_outputs" MapReduceCommand$OutputType/MERGE {})]
|
|
||||||
(mgres/ok? output)
|
|
||||||
(is (= 4 (monger.core/count (.results ^MapReduceOutput output))))
|
|
||||||
(is (= ["CA" "IL" "NY" "OR"]
|
|
||||||
(map :_id (mgcol/find-maps "merged_mr_outputs"))))
|
|
||||||
(.drop ^MapReduceOutput output))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; distinct
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest distinct-values
|
|
||||||
(let [collection "widgets"
|
|
||||||
batch [{ :state "CA" :quantity 1 :price 199.00 }
|
|
||||||
{ :state "NY" :quantity 2 :price 199.00 }
|
|
||||||
{ :state "NY" :quantity 1 :price 299.00 }
|
|
||||||
{ :state "IL" :quantity 2 :price 11.50 }
|
|
||||||
{ :state "CA" :quantity 2 :price 2.95 }
|
|
||||||
{ :state "IL" :quantity 3 :price 5.50 }]]
|
|
||||||
(mgcol/insert-batch collection batch)
|
|
||||||
(is (= ["CA" "IL" "NY"] (sort (mgcol/distinct monger.core/*mongodb-database* collection :state {}))))
|
|
||||||
(is (= ["CA" "NY"] (sort (mgcol/distinct collection :state { :price { $gt 100.00 } }))))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; any?, empty?
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest any-on-empty-collection
|
|
||||||
(let [collection "things"]
|
|
||||||
(is (not (mgcol/any? collection)))))
|
|
||||||
|
|
||||||
(deftest any-on-non-empty-collection
|
|
||||||
(let [collection "things"
|
|
||||||
_ (mgcol/insert collection { :language "Clojure", :name "langohr" })]
|
|
||||||
(is (mgcol/any? "things"))
|
|
||||||
(is (mgcol/any? monger.core/*mongodb-database* "things" {:language "Clojure"}))))
|
|
||||||
|
|
||||||
(deftest empty-on-empty-collection
|
|
||||||
(let [collection "things"]
|
|
||||||
(is (mgcol/empty? collection))
|
|
||||||
(is (mgcol/empty? monger.core/*mongodb-database* collection))))
|
|
||||||
|
|
||||||
(deftest empty-on-non-empty-collection
|
|
||||||
(let [collection "things"
|
|
||||||
_ (mgcol/insert collection { :language "Clojure", :name "langohr" })]
|
|
||||||
(is (not (mgcol/empty? "things")))))
|
|
||||||
193
test/monger/test/collection_test.clj
Normal file
193
test/monger/test/collection_test.clj
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
(ns monger.test.collection-test
|
||||||
|
(:import org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "libraries")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "libraries"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; count, remove
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest get-collection-size
|
||||||
|
(let [collection "things"]
|
||||||
|
(is (= 0 (mc/count db collection)))
|
||||||
|
(mc/insert-batch db collection [{:language "Clojure" :name "langohr"}
|
||||||
|
{:language "Clojure" :name "monger"}
|
||||||
|
{:language "Clojure" :name "incanter"}
|
||||||
|
{:language "Scala" :name "akka"}])
|
||||||
|
(is (= 4 (mc/count db collection)))
|
||||||
|
(is (mc/any? db collection))
|
||||||
|
(is (= 3 (mc/count db collection {:language "Clojure"})))
|
||||||
|
(is (mc/any? db collection {:language "Clojure"}))
|
||||||
|
(is (= 1 (mc/count db collection {:language "Scala" })))
|
||||||
|
(is (mc/any? db collection {:language "Scala"}))
|
||||||
|
(is (= 0 (mc/count db collection {:language "Python" })))
|
||||||
|
(is (not (mc/any? db collection {:language "Python"})))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest remove-all-documents-from-collection
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Clojure" :name "monger"}
|
||||||
|
{:language "Clojure" :name "langohr"}
|
||||||
|
{:language "Clojure" :name "incanter"}
|
||||||
|
{:language "Scala" :name "akka"}])
|
||||||
|
(is (= 4 (mc/count db collection)))
|
||||||
|
(mc/remove db collection)
|
||||||
|
(is (= 0 (mc/count db collection)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest remove-some-documents-from-collection
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Clojure" :name "monger"}
|
||||||
|
{:language "Clojure" :name "langohr"}
|
||||||
|
{:language "Clojure" :name "incanter"}
|
||||||
|
{:language "Scala" :name "akka"}])
|
||||||
|
(is (= 4 (mc/count db collection)))
|
||||||
|
(mc/remove db collection {:language "Clojure"})
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest remove-a-single-document-from-collection
|
||||||
|
(let [collection "libraries"
|
||||||
|
oid (ObjectId.)]
|
||||||
|
(mc/insert-batch db collection [{:language "Clojure" :name "monger" :_id oid}])
|
||||||
|
(mc/remove-by-id db collection oid)
|
||||||
|
(is (= 0 (mc/count db collection)))
|
||||||
|
(is (nil? (mc/find-by-id db collection oid)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; exists?, drop, create
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest checking-for-collection-existence-when-it-does-not-exist
|
||||||
|
(let [collection "widgets"]
|
||||||
|
(mc/drop db collection)
|
||||||
|
(is (false? (mc/exists? db collection)))))
|
||||||
|
|
||||||
|
(deftest checking-for-collection-existence-when-it-does-exist
|
||||||
|
(let [collection "widgets"]
|
||||||
|
(mc/drop db collection)
|
||||||
|
(mc/insert-batch db collection [{:name "widget1"}
|
||||||
|
{:name "widget2"}])
|
||||||
|
(is (mc/exists? db collection))
|
||||||
|
(mc/drop db collection)
|
||||||
|
(is (false? (mc/exists? db collection)))
|
||||||
|
(mc/create db "widgets" {:capped true :size 100000 :max 10})
|
||||||
|
(is (mc/exists? db collection))
|
||||||
|
(mc/rename db collection "gadgets")
|
||||||
|
(is (not (mc/exists? db collection)))
|
||||||
|
(is (mc/exists? db "gadgets"))
|
||||||
|
(mc/drop db "gadgets")))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; any?, empty?
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest test-any-on-empty-collection
|
||||||
|
(let [collection "things"]
|
||||||
|
(is (not (mc/any? db collection)))))
|
||||||
|
|
||||||
|
(deftest test-any-on-non-empty-collection
|
||||||
|
(let [collection "things"
|
||||||
|
_ (mc/insert db collection {:language "Clojure" :name "langohr"})]
|
||||||
|
(is (mc/any? db "things" {:language "Clojure"}))))
|
||||||
|
|
||||||
|
(deftest test-empty-on-empty-collection
|
||||||
|
(let [collection "things"]
|
||||||
|
(is (mc/empty? db collection))))
|
||||||
|
|
||||||
|
(deftest test-empty-on-non-empty-collection
|
||||||
|
(let [collection "things"
|
||||||
|
_ (mc/insert db collection {:language "Clojure" :name "langohr"})]
|
||||||
|
(is (not (mc/empty? db "things")))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; distinct
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest test-distinct-values
|
||||||
|
(let [collection "widgets"
|
||||||
|
batch [{:state "CA" :quantity 1 :price 199.00}
|
||||||
|
{:state "NY" :quantity 2 :price 199.00}
|
||||||
|
{:state "NY" :quantity 1 :price 299.00}
|
||||||
|
{:state "IL" :quantity 2 :price 11.50 }
|
||||||
|
{:state "CA" :quantity 2 :price 2.95 }
|
||||||
|
{:state "IL" :quantity 3 :price 5.50 }]]
|
||||||
|
(mc/insert-batch db collection batch)
|
||||||
|
(is (= ["CA" "IL" "NY"] (sort (mc/distinct db collection :state))))
|
||||||
|
(is (= ["CA" "IL" "NY"] (sort (mc/distinct db collection :state {}))))
|
||||||
|
(is (= ["CA" "NY"] (sort (mc/distinct db collection :state {:price {$gt 100.00}}))))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; update
|
||||||
|
;;
|
||||||
|
|
||||||
|
(let [coll "things"
|
||||||
|
batch [{:_id 1 :type "rock" :size "small"}
|
||||||
|
{:_id 2 :type "bed" :size "bed-sized"}
|
||||||
|
{:_id 3 :type "bottle" :size "1.5 liters"}]]
|
||||||
|
|
||||||
|
(deftest test-update
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(is (= "small" (:size (mc/find-one-as-map db coll {:type "rock"}))))
|
||||||
|
(mc/update db coll {:type "rock"} {"$set" {:size "huge"}})
|
||||||
|
(is (= "huge" (:size (mc/find-one-as-map db coll {:type "rock"})))))
|
||||||
|
|
||||||
|
(deftest test-upsert
|
||||||
|
(is (mc/empty? db coll))
|
||||||
|
(mc/upsert db coll {:_id 4} {"$set" {:size "tiny"}})
|
||||||
|
(is (not (mc/empty? db coll)))
|
||||||
|
(mc/upsert db coll {:_id 4} {"$set" {:size "big"}})
|
||||||
|
(is (= [{:_id 4 :size "big"}] (mc/find-maps db coll {:_id 4}))))
|
||||||
|
|
||||||
|
(deftest test-update-by-id
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(is (= "bed" (:type (mc/find-one-as-map db coll {:_id 2}))))
|
||||||
|
(mc/update-by-id db coll 2 {"$set" {:type "living room"}})
|
||||||
|
(is (= "living room" (:type (mc/find-one-as-map db coll {:_id 2})))))
|
||||||
|
|
||||||
|
(deftest test-update-by-ids
|
||||||
|
(mc/insert-batch db coll batch)
|
||||||
|
(is (= "bed" (:type (mc/find-one-as-map db coll {:_id 2}))))
|
||||||
|
(is (= "bottle" (:type (mc/find-one-as-map db coll {:_id 3}))))
|
||||||
|
(mc/update-by-ids db coll [2 3] {"$set" {:type "dog"}})
|
||||||
|
(is (= "dog" (:type (mc/find-one-as-map db coll {:_id 2}))))
|
||||||
|
(is (= "dog" (:type (mc/find-one-as-map db coll {:_id 3}))))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; miscellenous
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest test-system-collection-predicate
|
||||||
|
(are [name] (is (mc/system-collection? name))
|
||||||
|
"system.indexes"
|
||||||
|
"system"
|
||||||
|
;; we treat default GridFS collections as system ones,
|
||||||
|
;; possibly this is a bad idea, time will tell. MK.
|
||||||
|
"fs.chunks"
|
||||||
|
"fs.files")
|
||||||
|
(are [name] (is (not (mc/system-collection? name)))
|
||||||
|
"events"
|
||||||
|
"accounts"
|
||||||
|
"megacorp_account"
|
||||||
|
"myapp_development")))
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
(ns monger.test.command
|
|
||||||
(:require [monger core command]
|
|
||||||
[monger.test.helper :as helper]
|
|
||||||
[monger.collection :as mgcol])
|
|
||||||
(:import (com.mongodb Mongo DB CommandResult))
|
|
||||||
(:use [clojure.test]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-db-stats
|
|
||||||
(let [stats (monger.command/db-stats)]
|
|
||||||
(is (monger.result/ok? stats))
|
|
||||||
(is (= "monger-test" (get stats "db")))))
|
|
||||||
|
|
||||||
(deftest test-collection-stats
|
|
||||||
(let [collection "stat_test"
|
|
||||||
_ (mgcol/insert collection { :name "Clojure" })
|
|
||||||
check (mgcol/count collection)
|
|
||||||
stats (monger.command/collection-stats collection)]
|
|
||||||
(is (monger.result/ok? stats))
|
|
||||||
(is (= "monger-test.stat_test" (get stats "ns")))
|
|
||||||
(is (= check (get stats "count")))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-reindex-collection
|
|
||||||
(let [_ (mgcol/insert "test" { :name "Clojure" })
|
|
||||||
result (monger.command/reindex-collection "test")]
|
|
||||||
(is (monger.result/ok? result))
|
|
||||||
(is (get result "indexes"))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-server-status
|
|
||||||
(let [status (monger.command/server-status)]
|
|
||||||
(is (monger.result/ok? status))
|
|
||||||
(is (not-empty status))
|
|
||||||
(is (get status "serverUsed"))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-top
|
|
||||||
(let [result (monger.command/top)]
|
|
||||||
(is (monger.result/ok? result))
|
|
||||||
(is (not-empty result))
|
|
||||||
(is (get result "serverUsed"))))
|
|
||||||
29
test/monger/test/command_test.clj
Normal file
29
test/monger/test/command_test.clj
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
(ns monger.test.command-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.command :as mcom]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.result :refer [acknowledged?]]
|
||||||
|
[monger.conversion :refer [from-db-object]]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(deftest ^{:command true} test-reindex-collection
|
||||||
|
(let [_ (mc/insert db "test" {:name "Clojure"})
|
||||||
|
result (mcom/reindex-collection db "test")]
|
||||||
|
(is (acknowledged? result))))
|
||||||
|
|
||||||
|
(deftest ^{:command true} test-server-status
|
||||||
|
(let [status (mcom/server-status db)]
|
||||||
|
(is (acknowledged? status))
|
||||||
|
(is (not-empty status))))
|
||||||
|
|
||||||
|
(deftest ^{:command true} test-top
|
||||||
|
(let [result (mcom/top conn)]
|
||||||
|
(is (acknowledged? result))
|
||||||
|
(is (not-empty result))))
|
||||||
|
|
||||||
|
(deftest ^{:command true} test-running-is-master-as-an-arbitrary-command
|
||||||
|
(let [raw (mg/command db {:isMaster 1})
|
||||||
|
result (from-db-object raw true)]
|
||||||
|
(is (acknowledged? raw)))))
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
(ns monger.test.conversion
|
(ns monger.test.conversion-test
|
||||||
(:require [monger core collection]
|
(:require [monger core collection]
|
||||||
[monger.conversion :as cnv])
|
[clojure.test :refer :all]
|
||||||
|
[monger.conversion :refer :all])
|
||||||
(:import [com.mongodb DBObject BasicDBObject BasicDBList]
|
(:import [com.mongodb DBObject BasicDBObject BasicDBList]
|
||||||
[java.util Date Calendar List ArrayList]
|
[java.util Date Calendar List ArrayList]
|
||||||
[org.bson.types ObjectId])
|
org.bson.types.ObjectId
|
||||||
(:use [clojure.test]))
|
(org.bson.types Decimal128)))
|
||||||
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
@ -13,36 +14,53 @@
|
||||||
|
|
||||||
(deftest convert-nil-to-dbobject
|
(deftest convert-nil-to-dbobject
|
||||||
(let [input nil
|
(let [input nil
|
||||||
output (cnv/to-db-object input)]
|
output (to-db-object input)]
|
||||||
(is (nil? output))))
|
(is (nil? output))))
|
||||||
|
|
||||||
(deftest convert-integer-to-dbobject
|
(deftest convert-integer-to-dbobject
|
||||||
(let [input 1
|
(let [input 1
|
||||||
output (cnv/to-db-object input)]
|
output (to-db-object input)]
|
||||||
(is (= input output))))
|
(is (= input output))))
|
||||||
|
|
||||||
(deftest convert-float-to-dbobject
|
(deftest convert-float-to-dbobject
|
||||||
(let [input 11.12
|
(let [input 11.12
|
||||||
output (cnv/to-db-object input)]
|
output (to-db-object input)]
|
||||||
(is (= input output))))
|
(is (= input output))))
|
||||||
|
|
||||||
|
(deftest convert-rationale-to-dbobject
|
||||||
|
(let [input 11/2
|
||||||
|
output (to-db-object input)]
|
||||||
|
(is (= 5.5 output))))
|
||||||
|
|
||||||
(deftest convert-string-to-dbobject
|
(deftest convert-string-to-dbobject
|
||||||
(let [input "MongoDB"
|
(let [input "MongoDB"
|
||||||
output (cnv/to-db-object input)]
|
output (to-db-object input)]
|
||||||
|
(is (= input output))))
|
||||||
|
|
||||||
|
(deftest convert-boolean-true-to-dbobject
|
||||||
|
(let [input true
|
||||||
|
output (to-db-object input)]
|
||||||
|
(is (= input output))))
|
||||||
|
|
||||||
|
(deftest convert-boolean-false-to-dbobject
|
||||||
|
(let [input false
|
||||||
|
output (to-db-object input)]
|
||||||
(is (= input output))))
|
(is (= input output))))
|
||||||
|
|
||||||
|
|
||||||
(deftest convert-map-to-dbobject
|
(deftest convert-map-to-dbobject
|
||||||
(let [input { :int 1, :string "Mongo", :float 22.23 }
|
(let [input { :int 1 :string "Mongo" :float 22.23 :true true :false false }
|
||||||
output ^DBObject (cnv/to-db-object input)]
|
output ^DBObject (to-db-object input)]
|
||||||
(is (= 1 (.get output "int")))
|
(is (= 1 (.get output "int")))
|
||||||
(is (= "Mongo" (.get output "string")))
|
(is (= "Mongo" (.get output "string")))
|
||||||
(is (= 22.23 (.get output "float")))))
|
(is (= 22.23 (.get output "float")))
|
||||||
|
(is (true? (.get output "true")))
|
||||||
|
(is (false? (.get output "false")))))
|
||||||
|
|
||||||
|
|
||||||
(deftest convert-nested-map-to-dbobject
|
(deftest convert-nested-map-to-dbobject
|
||||||
(let [input { :int 1, :string "Mongo", :float 22.23, :map { :int 10, :string "Clojure", :float 11.9, :list '(1 "a" :b), :map { :key "value" } } }
|
(let [input { :int 1, :string "Mongo", :float 22.23, :map { :int 10, :string "Clojure", :float 11.9, :list '(1 "a" :b), :map { :key "value" } } }
|
||||||
output ^DBObject (cnv/to-db-object input)
|
output ^DBObject (to-db-object input)
|
||||||
inner ^DBObject (.get output "map")]
|
inner ^DBObject (.get output "map")]
|
||||||
(is (= 10 (.get inner "int")))
|
(is (= 10 (.get inner "int")))
|
||||||
(is (= "Clojure" (.get inner "string")))
|
(is (= "Clojure" (.get inner "string")))
|
||||||
|
|
@ -54,19 +72,19 @@
|
||||||
;; to obtain _id that was generated. MK.
|
;; to obtain _id that was generated. MK.
|
||||||
(deftest convert-dbobject-to-dbobject
|
(deftest convert-dbobject-to-dbobject
|
||||||
(let [input (BasicDBObject.)
|
(let [input (BasicDBObject.)
|
||||||
output (cnv/to-db-object input)]
|
output (to-db-object input)]
|
||||||
(is (= input output))))
|
(is (= input output))))
|
||||||
|
|
||||||
(deftest convert-java-date-to-dbobject
|
(deftest convert-java-date-to-dbobject
|
||||||
(let [date (Date.)
|
(let [date (Date.)
|
||||||
input { :int 1, :string "Mongo", :date date }
|
input { :int 1, :string "Mongo", :date date }
|
||||||
output ^DBObject (cnv/to-db-object input)]
|
output ^DBObject (to-db-object input)]
|
||||||
(is (= date (.get output "date")))))
|
(is (= date (.get output "date")))))
|
||||||
|
|
||||||
(deftest convert-java-calendar-instance-to-dbobject
|
(deftest convert-java-calendar-instance-to-dbobject
|
||||||
(let [date (Calendar/getInstance)
|
(let [date (Calendar/getInstance)
|
||||||
input { :int 1, :string "Mongo", :date date }
|
input { :int 1, :string "Mongo", :date date }
|
||||||
output ^DBObject (cnv/to-db-object input)]
|
output ^DBObject (to-db-object input)]
|
||||||
(is (= date (.get output "date")))))
|
(is (= date (.get output "date")))))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -77,16 +95,23 @@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(deftest convert-nil-from-db-object
|
(deftest convert-nil-from-db-object
|
||||||
(is (nil? (cnv/from-db-object nil false)))
|
(is (nil? (from-db-object nil false)))
|
||||||
(is (nil? (cnv/from-db-object nil true))))
|
(is (nil? (from-db-object nil true))))
|
||||||
|
|
||||||
(deftest convert-integer-from-dbobject
|
(deftest convert-integer-from-dbobject
|
||||||
(is (= 2 (cnv/from-db-object 2 false)))
|
(is (= 2 (from-db-object 2 false)))
|
||||||
(is (= 2 (cnv/from-db-object 2 true))))
|
(is (= 2 (from-db-object 2 true))))
|
||||||
|
|
||||||
|
(deftest convert-decimal-from-dbobject
|
||||||
|
(is (= 2.3M (from-db-object (Decimal128. 2.3M) false)))
|
||||||
|
(is (= 2.3M (from-db-object (Decimal128. 2.3M) true)))
|
||||||
|
(is (= 2.3M (from-db-object (Decimal128/parse "2.3") true)))
|
||||||
|
(is (not= 2.32M (from-db-object (Decimal128/parse "2.3") true)))
|
||||||
|
)
|
||||||
|
|
||||||
(deftest convert-float-from-dbobject
|
(deftest convert-float-from-dbobject
|
||||||
(is (= 3.3 (cnv/from-db-object 3.3 false)))
|
(is (= 3.3 (from-db-object 3.3 false)))
|
||||||
(is (= 3.3 (cnv/from-db-object 3.3 true))))
|
(is (= 3.3 (from-db-object 3.3 true))))
|
||||||
|
|
||||||
(deftest convert-flat-db-object-to-map-without-keywordizing
|
(deftest convert-flat-db-object-to-map-without-keywordizing
|
||||||
(let [name "Michael"
|
(let [name "Michael"
|
||||||
|
|
@ -94,21 +119,21 @@
|
||||||
input (doto (BasicDBObject.)
|
input (doto (BasicDBObject.)
|
||||||
(.put "name" name)
|
(.put "name" name)
|
||||||
(.put "age" age))
|
(.put "age" age))
|
||||||
output (cnv/from-db-object input false)]
|
output (from-db-object input false)]
|
||||||
(is (= (output { "name" name, "age" age })))
|
(is (= output { "name" name, "age" age }))
|
||||||
(is (= (output "name") name))
|
(is (= (output "name") name))
|
||||||
(is (nil? (output :name)))
|
(is (nil? (output :name)))
|
||||||
(is (= (output "age") age))
|
(is (= (output "age") age))
|
||||||
(is (nil? (output "points")))))
|
(is (nil? (output "points")))))
|
||||||
|
|
||||||
(deftest convert-flat-db-object-to-map-without-keywordizing
|
(deftest convert-flat-db-object-to-map-with-keywordizing
|
||||||
(let [name "Michael"
|
(let [name "Michael"
|
||||||
age 26
|
age 26
|
||||||
input (doto (BasicDBObject.)
|
input (doto (BasicDBObject.)
|
||||||
(.put "name" name)
|
(.put "name" name)
|
||||||
(.put "age" age))
|
(.put "age" age))
|
||||||
output (cnv/from-db-object input true)]
|
output (from-db-object input true)]
|
||||||
(is (= (output { :name name, :age age })))
|
(is (= output { :name name, :age age }))
|
||||||
(is (= (output :name) name))
|
(is (= (output :name) name))
|
||||||
(is (nil? (output "name")))
|
(is (nil? (output "name")))
|
||||||
(is (= (output :age) age))
|
(is (= (output :age) age))
|
||||||
|
|
@ -126,7 +151,7 @@
|
||||||
input (doto (BasicDBObject.)
|
input (doto (BasicDBObject.)
|
||||||
(.put "_id" did)
|
(.put "_id" did)
|
||||||
(.put "nested" nested))
|
(.put "nested" nested))
|
||||||
output (cnv/from-db-object input false)]
|
output (from-db-object input false)]
|
||||||
(is (= (output "_id") did))
|
(is (= (output "_id") did))
|
||||||
(is (= (-> output (get "nested") (get "int")) 101))
|
(is (= (-> output (get "nested") (get "int")) 101))
|
||||||
(is (= (-> output (get "nested") (get "list")) ["red" "green" "blue"]))
|
(is (= (-> output (get "nested") (get "list")) ["red" "green" "blue"]))
|
||||||
|
|
@ -140,5 +165,18 @@
|
||||||
|
|
||||||
(deftest test-conversion-to-object-id
|
(deftest test-conversion-to-object-id
|
||||||
(let [output (ObjectId. "4efb39370364238a81020502")]
|
(let [output (ObjectId. "4efb39370364238a81020502")]
|
||||||
(is (= output (cnv/to-object-id "4efb39370364238a81020502")))
|
(is (= output (to-object-id "4efb39370364238a81020502")))
|
||||||
(is (= output (cnv/to-object-id output)))))
|
(is (= output (to-object-id output)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Field selector coercion
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest test-field-selector-coercion
|
||||||
|
(are [i o] (is (= (from-db-object (as-field-selector i) true) o))
|
||||||
|
[:a :b :c] {:a 1 :b 1 :c 1}
|
||||||
|
'(:a :b :c) {:a 1 :b 1 :c 1}
|
||||||
|
{:a 1 :b 1 :c 1} {:a 1 :b 1 :c 1}
|
||||||
|
{"a" 1 "b" 1 "c" 1} {:a 1 :b 1 :c 1}
|
||||||
|
{:comments 0} {:comments 0}))
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
(ns monger.test.core
|
|
||||||
(:require [monger core collection util result]
|
|
||||||
[monger.test.helper :as helper]
|
|
||||||
[monger.collection :as mgcol])
|
|
||||||
(:import [com.mongodb Mongo DB WriteConcern MongoOptions ServerAddress])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.core :only [server-address mongo-options]]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(deftest connect-to-mongo-with-default-host-and-port
|
|
||||||
(let [connection (monger.core/connect)]
|
|
||||||
(is (instance? com.mongodb.Mongo connection))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest connect-to-mongo-with-default-host-and-explicit-port
|
|
||||||
(let [connection (monger.core/connect { :port 27017 })]
|
|
||||||
(is (instance? com.mongodb.Mongo connection))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest connect-to-mongo-with-default-port-and-explicit-host
|
|
||||||
(let [connection (monger.core/connect { :host "127.0.0.1" })]
|
|
||||||
(is (instance? com.mongodb.Mongo connection))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-mongo-options-builder
|
|
||||||
(let [max-wait-time (* 1000 60 2)
|
|
||||||
^MongoOptions result (monger.core/mongo-options :connections-per-host 3 :threads-allowed-to-block-for-connection-multiplier 50
|
|
||||||
:max-wait-time max-wait-time :connect-timeout 10 :socket-timeout 10 :socket-keep-alive true
|
|
||||||
:auto-connect-retry true :max-auto-connect-retry-time 0 :safe true
|
|
||||||
:w 1 :w-timeout 20 :fsync true :j true)]
|
|
||||||
(is (= 3 (. result connectionsPerHost)))
|
|
||||||
(is (= 50 (. result threadsAllowedToBlockForConnectionMultiplier)))
|
|
||||||
(is (= max-wait-time (.maxWaitTime result)))
|
|
||||||
(is (= 10 (.connectTimeout result)))
|
|
||||||
(is (= 10 (.socketTimeout result)))
|
|
||||||
(is (.socketKeepAlive result))
|
|
||||||
(is (.autoConnectRetry result))
|
|
||||||
(is (= 0 (.maxAutoConnectRetryTime result)))
|
|
||||||
(is (.safe result))
|
|
||||||
(is (= 1 (.w result)))
|
|
||||||
(is (= 20 (.wtimeout result)))
|
|
||||||
(is (.fsync result))
|
|
||||||
(is (.j result))))
|
|
||||||
|
|
||||||
(deftest test-server-address
|
|
||||||
(let [host "127.0.0.1"
|
|
||||||
port 7878
|
|
||||||
^ServerAddress sa (server-address host port)]
|
|
||||||
(is (= host (.getHost sa)))
|
|
||||||
(is (= port (.getPort sa)))))
|
|
||||||
|
|
||||||
(deftest use-existing-mongo-connection
|
|
||||||
(let [^MongoOptions opts (mongo-options :threads-allowed-to-block-for-connection-multiplier 300)
|
|
||||||
connection (Mongo. "127.0.0.1" opts)]
|
|
||||||
(monger.core/set-connection! connection)
|
|
||||||
(is (= monger.core/*mongodb-connection* connection))))
|
|
||||||
|
|
||||||
(deftest connect-to-mongo-with-extra-options
|
|
||||||
(let [^MongoOptions opts (mongo-options :threads-allowed-to-block-for-connection-multiplier 300)
|
|
||||||
^ServerAddress sa (server-address "127.0.0.1" 27017)]
|
|
||||||
(monger.core/connect! sa opts)))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest get-database
|
|
||||||
(let [connection (monger.core/connect)
|
|
||||||
db (monger.core/get-db connection "monger-test")]
|
|
||||||
(is (instance? com.mongodb.DB db))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-get-db-names
|
|
||||||
(let [dbs (monger.core/get-db-names)]
|
|
||||||
(is (not (empty? dbs)))
|
|
||||||
(is (dbs "monger-test"))))
|
|
||||||
|
|
||||||
(deftest issuing-a-command
|
|
||||||
"Some commands require administrative priviledges or complex data / checks or heavily depend on DB version. They will be ommited here."
|
|
||||||
(let [collection "things"]
|
|
||||||
(doseq [c [{ :profile 1 }
|
|
||||||
{ :listCommands 1 }
|
|
||||||
{ :dbStats 1 }
|
|
||||||
{ :collstats "things" :scale (* 1024 1024) }
|
|
||||||
{ :getLastError 1 }]]
|
|
||||||
(is (monger.result/ok? (monger.core/command c))))))
|
|
||||||
|
|
||||||
(deftest get-last-error
|
|
||||||
(let [connection (monger.core/connect)
|
|
||||||
db (monger.core/get-db connection "monger-test")]
|
|
||||||
(is (monger.result/ok? (monger.core/get-last-error)))
|
|
||||||
(is (monger.result/ok? (monger.core/get-last-error db)))
|
|
||||||
(is (monger.result/ok? (monger.core/get-last-error db WriteConcern/NORMAL)))
|
|
||||||
(is (monger.result/ok? (monger.core/get-last-error db 1 100 true)))))
|
|
||||||
89
test/monger/test/core_test.clj
Normal file
89
test/monger/test/core_test.clj
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
(ns monger.test.core-test
|
||||||
|
(:require [monger util result]
|
||||||
|
[monger.core :as mg :refer [server-address mongo-options]]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all])
|
||||||
|
(:import [com.mongodb MongoClient DB WriteConcern MongoClientOptions ServerAddress]))
|
||||||
|
|
||||||
|
(println (str "Using Clojure version " *clojure-version*))
|
||||||
|
|
||||||
|
(deftest connect-to-mongo-with-default-host-and-port
|
||||||
|
(let [connection (mg/connect)]
|
||||||
|
(is (instance? com.mongodb.MongoClient connection))))
|
||||||
|
|
||||||
|
(deftest connect-and-disconnect
|
||||||
|
(let [conn (mg/connect)]
|
||||||
|
(mg/disconnect conn)))
|
||||||
|
|
||||||
|
(deftest connect-to-mongo-with-default-host-and-explicit-port
|
||||||
|
(let [connection (mg/connect {:port 27017})]
|
||||||
|
(is (instance? com.mongodb.MongoClient connection))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest connect-to-mongo-with-default-port-and-explicit-host
|
||||||
|
(let [connection (mg/connect {:host "127.0.0.1"})]
|
||||||
|
(is (instance? com.mongodb.MongoClient connection))))
|
||||||
|
|
||||||
|
(deftest test-server-address
|
||||||
|
(let [host "127.0.0.1"
|
||||||
|
port 7878
|
||||||
|
^ServerAddress sa (server-address host port)]
|
||||||
|
(is (= host (.getHost sa)))
|
||||||
|
(is (= port (.getPort sa)))))
|
||||||
|
|
||||||
|
(deftest use-existing-mongo-connection
|
||||||
|
(let [^MongoClientOptions opts (mongo-options {:threads-allowed-to-block-for-connection-multiplier 300})
|
||||||
|
connection (MongoClient. "127.0.0.1" opts)
|
||||||
|
db (mg/get-db connection "monger-test")]
|
||||||
|
(mg/disconnect connection)))
|
||||||
|
|
||||||
|
(deftest connect-to-mongo-with-extra-options
|
||||||
|
(let [^MongoClientOptions opts (mongo-options {:threads-allowed-to-block-for-connection-multiplier 300})
|
||||||
|
^ServerAddress sa (server-address "127.0.0.1" 27017)
|
||||||
|
conn (mg/connect sa opts)]
|
||||||
|
(mg/disconnect conn)))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest get-database
|
||||||
|
(let [connection (mg/connect)
|
||||||
|
db (mg/get-db connection "monger-test")]
|
||||||
|
(is (instance? com.mongodb.DB db))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-get-db-names
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
dbs (mg/get-db-names conn)]
|
||||||
|
(is (not (empty? dbs)))
|
||||||
|
(is (dbs "monger-test"))))
|
||||||
|
|
||||||
|
(deftest monger-options-test
|
||||||
|
(let [opts {:always-use-mbeans true
|
||||||
|
:application-name "app"
|
||||||
|
:connect-timeout 1
|
||||||
|
:connections-per-host 1
|
||||||
|
:cursor-finalizer-enabled true
|
||||||
|
:description "Description"
|
||||||
|
:heartbeat-connect-timeout 1
|
||||||
|
:heartbeat-frequency 1
|
||||||
|
:heartbeat-socket-timeout 1
|
||||||
|
:local-threshold 1
|
||||||
|
:max-connection-idle-time 1
|
||||||
|
:max-connection-life-time 1
|
||||||
|
:max-wait-time 1
|
||||||
|
:min-connections-per-host 1
|
||||||
|
:min-heartbeat-frequency 1
|
||||||
|
:required-replica-set-name "rs"
|
||||||
|
:retry-writes true
|
||||||
|
:server-selection-timeout 1
|
||||||
|
:socket-keep-alive true
|
||||||
|
:socket-timeout 1
|
||||||
|
:ssl-enabled true
|
||||||
|
:ssl-invalid-host-name-allowed true
|
||||||
|
:threads-allowed-to-block-for-connection-multiplier 1
|
||||||
|
:uuid-representation org.bson.UuidRepresentation/STANDARD
|
||||||
|
:write-concern com.mongodb.WriteConcern/JOURNAL_SAFE}]
|
||||||
|
(is (instance? com.mongodb.MongoClientOptions$Builder (mg/mongo-options-builder opts)))))
|
||||||
|
|
||||||
|
(deftest connect-to-uri-without-db-name
|
||||||
|
(let [uri "mongodb://localhost:27017"]
|
||||||
|
(is (thrown? IllegalArgumentException (mg/connect-via-uri uri)))))
|
||||||
107
test/monger/test/cursor_test.clj
Normal file
107
test/monger/test/cursor_test.clj
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
(ns monger.test.cursor-test
|
||||||
|
(:import [com.mongodb DBCursor DBObject Bytes]
|
||||||
|
[java.util List Map])
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.cursor :refer :all]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(deftest make-db-cursor-for-collection
|
||||||
|
(is (= DBCursor
|
||||||
|
(class (make-db-cursor db :docs)))))
|
||||||
|
|
||||||
|
(deftest getting-cursor-options-value
|
||||||
|
(let [db-cur (make-db-cursor db :docs)
|
||||||
|
opts (get-options db-cur)]
|
||||||
|
(is (= true (isa? (class opts) Map)))
|
||||||
|
(is (= 0 (.getOptions db-cur))) ;;test default value
|
||||||
|
(is (= false (:notimeout opts)))
|
||||||
|
(is (= false (:partial opts)))
|
||||||
|
(is (= false (:awaitdata opts)))
|
||||||
|
(is (= false (:oplogreplay opts)))
|
||||||
|
(is (= false (:slaveok opts)))
|
||||||
|
(is (= false (:tailable opts)))))
|
||||||
|
|
||||||
|
(deftest adding-option-to-cursor
|
||||||
|
(let [db-cur (make-db-cursor db :docs)]
|
||||||
|
(add-option! db-cur :notimeout)
|
||||||
|
(is (= (:notimeout cursor-options)
|
||||||
|
(.getOptions db-cur)))
|
||||||
|
(add-option! db-cur :tailable)
|
||||||
|
(is (= (.getOptions db-cur)
|
||||||
|
(bit-or (:notimeout cursor-options)
|
||||||
|
(:tailable cursor-options))))))
|
||||||
|
|
||||||
|
(deftest remove-option-from-cursor
|
||||||
|
(let [db-cur (make-db-cursor db :docs)]
|
||||||
|
(add-option! db-cur :partial)
|
||||||
|
(add-option! db-cur :awaitdata)
|
||||||
|
;; removing not-set option should not affect result
|
||||||
|
(remove-option! db-cur :notimeout)
|
||||||
|
(is (= (.getOptions db-cur)
|
||||||
|
(bit-or (:partial cursor-options)
|
||||||
|
(:awaitdata cursor-options))))
|
||||||
|
;; removing active option should remove correct value
|
||||||
|
(remove-option! db-cur :awaitdata)
|
||||||
|
(is (= (.getOptions db-cur)
|
||||||
|
(:partial cursor-options)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-reset-options
|
||||||
|
(let [db-cur (make-db-cursor db :docs)]
|
||||||
|
(add-option! db-cur :partial)
|
||||||
|
(is (= (.getOptions db-cur)
|
||||||
|
(:partial cursor-options)))
|
||||||
|
(is (= 0
|
||||||
|
(int (.getOptions (reset-options db-cur)))))))
|
||||||
|
|
||||||
|
(deftest add-options-with-hashmap
|
||||||
|
(let [db-cur (make-db-cursor db :docs)
|
||||||
|
_ (add-options db-cur {:notimeout true :slaveok true})
|
||||||
|
opts (get-options db-cur)]
|
||||||
|
(is (= true (:notimeout opts)))
|
||||||
|
(is (= true (:slaveok opts)))
|
||||||
|
(is (= false (:tailable opts)))
|
||||||
|
(is (= false (:oplogreplay opts)))))
|
||||||
|
|
||||||
|
(deftest add-options-with-hashmap-and-remove-option
|
||||||
|
(let [db-cur (make-db-cursor db :docs)
|
||||||
|
_ (add-options db-cur {:notimeout true :slaveok true})
|
||||||
|
opts (get-options db-cur)]
|
||||||
|
(is (= true (:notimeout opts)))
|
||||||
|
(is (= true (:slaveok opts)))
|
||||||
|
;;remove key and add another option
|
||||||
|
(add-options db-cur {:partial true :slaveok false})
|
||||||
|
(let [opts (get-options db-cur)]
|
||||||
|
(is (= true (:notimeout opts)))
|
||||||
|
(is (= true (:partial opts)))
|
||||||
|
(is (= false (:slaveok opts)))
|
||||||
|
(is (= false (:tailable opts))))))
|
||||||
|
|
||||||
|
(deftest add-options-with-list
|
||||||
|
(let [db-cur (make-db-cursor db :docs)
|
||||||
|
_ (add-options db-cur [:notimeout :slaveok])
|
||||||
|
opts (get-options db-cur)]
|
||||||
|
(is (= true (:notimeout opts)))
|
||||||
|
(is (= true (:slaveok opts)))
|
||||||
|
(is (= false (:tailable opts)))
|
||||||
|
(is (= false (:oplogreplay opts)))))
|
||||||
|
|
||||||
|
(deftest add-options-with-Bytes
|
||||||
|
(let [db-cur (make-db-cursor db :docs)
|
||||||
|
_ (add-options db-cur Bytes/QUERYOPTION_NOTIMEOUT)
|
||||||
|
opts (get-options db-cur)]
|
||||||
|
(is (= true (:notimeout opts)))
|
||||||
|
(is (= false (:slaveok opts)))
|
||||||
|
(is (= false (:tailable opts)))
|
||||||
|
(is (= false (:oplogreplay opts)))))
|
||||||
|
|
||||||
|
(deftest add-options-with-one-keyword
|
||||||
|
(let [db-cur (make-db-cursor db :docs)
|
||||||
|
_ (add-options db-cur :notimeout)
|
||||||
|
opts (get-options db-cur)]
|
||||||
|
(is (= true (:notimeout opts)))
|
||||||
|
(is (= false (:slaveok opts)))
|
||||||
|
(is (= false (:tailable opts)))
|
||||||
|
(is (= false (:oplogreplay opts))))))
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
(ns monger.test.db
|
|
||||||
(:require [monger core db]
|
|
||||||
[monger.test.helper :as helper]
|
|
||||||
[monger.collection :as mgcol])
|
|
||||||
(:import [com.mongodb Mongo DB]
|
|
||||||
[java.util Set])
|
|
||||||
(:use [clojure.test]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-add-user
|
|
||||||
(let [username "clojurewerkz/monger!"
|
|
||||||
pwd (.toCharArray "monger!")
|
|
||||||
db-name "monger-test4"]
|
|
||||||
;; use a secondary database here. MK.
|
|
||||||
(monger.core/with-db (monger.core/get-db db-name)
|
|
||||||
(monger.db/add-user username pwd)
|
|
||||||
(is (monger.core/authenticate db-name username pwd)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-drop-database
|
|
||||||
;; drop a secondary database here. MK.
|
|
||||||
(monger.core/with-db (monger.core/get-db "monger-test3")
|
|
||||||
(let [collection "test"
|
|
||||||
_ (mgcol/insert collection { :name "Clojure" })
|
|
||||||
check (mgcol/count collection)
|
|
||||||
_ (monger.db/drop-db)]
|
|
||||||
(is (= 1 check))
|
|
||||||
(is (not (mgcol/exists? collection)))
|
|
||||||
(is (= 0 (mgcol/count collection))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-get-collection-names
|
|
||||||
(mgcol/insert "test-1" { :name "Clojure" })
|
|
||||||
(mgcol/insert "test-2" { :name "Clojure" })
|
|
||||||
(let [^Set collections (monger.db/get-collection-names)]
|
|
||||||
(is (.contains collections "test-1"))
|
|
||||||
(is (.contains collections "test-2"))))
|
|
||||||
31
test/monger/test/db_test.clj
Normal file
31
test/monger/test/db_test.clj
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
(ns monger.test.db-test
|
||||||
|
(:require [monger.db :as mdb]
|
||||||
|
[monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all])
|
||||||
|
(:import [com.mongodb Mongo DB]
|
||||||
|
java.util.Set))
|
||||||
|
|
||||||
|
|
||||||
|
;; do not run this test for CI, it complicates matters by messing up
|
||||||
|
;; authentication for some other tests :( MK.
|
||||||
|
(let [conn (mg/connect)]
|
||||||
|
(when-not (System/getenv "CI")
|
||||||
|
(deftest test-drop-database
|
||||||
|
;; drop a secondary database here. MK.
|
||||||
|
(let [db (mg/get-db conn "monger-test3")
|
||||||
|
collection "test"
|
||||||
|
_ (mc/insert db collection {:name "Clojure"})
|
||||||
|
check (mc/count db collection)
|
||||||
|
_ (mdb/drop-db db)]
|
||||||
|
(is (= 1 check))
|
||||||
|
(is (not (mc/exists? db collection)))
|
||||||
|
(is (= 0 (mc/count db collection))))))
|
||||||
|
|
||||||
|
(deftest test-get-collection-names
|
||||||
|
(let [db (mg/get-db conn "monger-test")]
|
||||||
|
(mc/insert db "test-1" {:name "Clojure"})
|
||||||
|
(mc/insert db "test-2" {:name "Clojure"})
|
||||||
|
(let [^Set xs (mdb/get-collection-names db)]
|
||||||
|
(is (.contains xs "test-1"))
|
||||||
|
(is (.contains xs "test-2"))))))
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
(ns monger.test.factory-dsl
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger testing joda-time]
|
|
||||||
[monger.test.fixtures]
|
|
||||||
[clj-time.core :only [days ago weeks now]])
|
|
||||||
(:require [monger.collection :as mc]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:import [org.bson.types ObjectId]
|
|
||||||
[org.joda.time DateTime]))
|
|
||||||
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-domains purge-pages)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defaults-for "domains"
|
|
||||||
:ipv6-enabled false)
|
|
||||||
|
|
||||||
(factory "domains" "clojure"
|
|
||||||
:name "clojure.org"
|
|
||||||
:created-at (-> 2 days ago)
|
|
||||||
:embedded [(embedded-doc "pages" "http://clojure.org/lisp")
|
|
||||||
(embedded-doc "pages" "http://clojure.org/jvm_hosted")
|
|
||||||
(embedded-doc "pages" "http://clojure.org/runtime_polymorphism")])
|
|
||||||
|
|
||||||
(factory "domains" "elixir"
|
|
||||||
:name "elixir-lang.org"
|
|
||||||
:created-at (fn [] (now))
|
|
||||||
:topics (fn [] ["programming" "erlang" "beam" "ruby"])
|
|
||||||
:related {
|
|
||||||
:terms (fn [] ["erlang" "python" "ruby"])
|
|
||||||
})
|
|
||||||
|
|
||||||
(factory "pages" "http://clojure.org/rationale"
|
|
||||||
:name "/rationale"
|
|
||||||
:domain-id (parent-id "domains" "clojure"))
|
|
||||||
(factory "pages" "http://clojure.org/jvm_hosted"
|
|
||||||
:name "/jvm_hosted")
|
|
||||||
(factory "pages" "http://clojure.org/runtime_polymorphism"
|
|
||||||
:name "/runtime_polymorphism")
|
|
||||||
(factory "pages" "http://clojure.org/lisp"
|
|
||||||
:name "/lisp")
|
|
||||||
|
|
||||||
(deftest test-building-documents-from-a-factory-case-1
|
|
||||||
(let [t (-> 2 weeks ago)
|
|
||||||
doc (build "domains" "clojure" :created-at t)]
|
|
||||||
(is (:_id doc))
|
|
||||||
(is (= t (:created-at doc)))
|
|
||||||
(is (= "clojure.org" (:name doc)))
|
|
||||||
(is (false? (:ipv6-enabled doc)))))
|
|
||||||
|
|
||||||
(deftest test-building-documents-from-a-factory-case-2
|
|
||||||
(let [oid (ObjectId.)
|
|
||||||
doc (build "domains" "clojure" :_id oid)]
|
|
||||||
(is (= oid (:_id doc)))
|
|
||||||
(is (= "clojure.org" (:name doc)))
|
|
||||||
(is (false? (:ipv6-enabled doc)))))
|
|
||||||
|
|
||||||
(deftest test-building-documents-from-a-factory-case-3
|
|
||||||
(let [oid (ObjectId.)
|
|
||||||
t (-> 3 weeks ago)
|
|
||||||
doc (build "domains" "clojure" :_id oid :created-at t :name "clojurewerkz.org" :ipv6-enabled true)]
|
|
||||||
(is (= oid (:_id doc)))
|
|
||||||
(is (= t (:created-at doc)))
|
|
||||||
(is (= "clojurewerkz.org" (:name doc)))
|
|
||||||
(is (:ipv6-enabled doc))
|
|
||||||
(is (= ["/lisp" "/jvm_hosted" "/runtime_polymorphism"]
|
|
||||||
(vec (map :name (:embedded doc)))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-building-documents-from-a-factory-case-4
|
|
||||||
(let [doc (build "domains" "elixir")]
|
|
||||||
(is (:_id doc))
|
|
||||||
(is (instance? DateTime (:created-at doc)))
|
|
||||||
(is (= ["erlang" "python" "ruby"] (get-in doc [:related :terms])))
|
|
||||||
(is (= "elixir-lang.org" (:name doc)))
|
|
||||||
(is (not (:ipv6-enabled doc)))))
|
|
||||||
|
|
||||||
(deftest test-building-child-documents-with-a-parent-ref-case-1
|
|
||||||
(let [doc (build "pages" "http://clojure.org/rationale")]
|
|
||||||
(is (:domain-id doc))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-seeding-documents-using-a-factory-case-1
|
|
||||||
(is (mc/empty? "domains"))
|
|
||||||
(let [t (-> 2 weeks ago)
|
|
||||||
doc (seed "domains" "clojure" :created-at t)]
|
|
||||||
(is (= 1 (mc/count "domains")))
|
|
||||||
(is (:_id doc))
|
|
||||||
(is (= t (:created-at doc)))
|
|
||||||
(is (= "clojure.org" (:name doc)))
|
|
||||||
(is (false? (:ipv6-enabled doc)))))
|
|
||||||
|
|
||||||
(deftest test-seeding-documents-using-a-factory-case-2
|
|
||||||
(is (mc/empty? "domains"))
|
|
||||||
(let [doc (seed "domains" "elixir")
|
|
||||||
loaded (first (mc/find-maps "domains"))]
|
|
||||||
(is (= 1 (mc/count "domains")))
|
|
||||||
(is (:_id doc))
|
|
||||||
(is (= (:_id doc) (:_id loaded)))
|
|
||||||
(is (instance? DateTime (:created-at loaded)))
|
|
||||||
(is (= ["erlang" "python" "ruby"] (get-in loaded [:related :terms])))
|
|
||||||
(is (= "elixir-lang.org" (:name loaded)))
|
|
||||||
(is (not (:ipv6-enabled loaded)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-seeding-child-documents-with-a-parent-ref-case-1
|
|
||||||
(is (mc/empty? "domains"))
|
|
||||||
(is (mc/empty? "pages"))
|
|
||||||
(let [page (seed "pages" "http://clojure.org/rationale")
|
|
||||||
domain (mc/find-map-by-id "domains" (:domain-id page))]
|
|
||||||
(is (= 1 (mc/count "domains")))
|
|
||||||
(is (= 1 (mc/count "pages")))
|
|
||||||
(is domain)
|
|
||||||
(is (:domain-id page))
|
|
||||||
(is (= "clojure.org" (:name domain)))
|
|
||||||
(is (= "/rationale" (:name page)))))
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
(ns monger.test.fixtures
|
|
||||||
(:require [monger.collection :as mgcol])
|
|
||||||
(:use [monger.testing]))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; fixture functions
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defcleaner people "people")
|
|
||||||
(defcleaner docs "docs")
|
|
||||||
(defcleaner things "things")
|
|
||||||
(defcleaner libraries "libraries")
|
|
||||||
(defcleaner scores "scores")
|
|
||||||
(defcleaner locations "locations")
|
|
||||||
(defcleaner domains "domains")
|
|
||||||
(defcleaner pages "pages")
|
|
||||||
28
test/monger/test/full_text_search_test.clj
Normal file
28
test/monger/test/full_text_search_test.clj
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
(ns monger.test.full-text-search-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.command :as cmd]
|
||||||
|
[monger.operators :refer :all]
|
||||||
|
[clojure.test :refer [deftest is use-fixtures]]
|
||||||
|
[monger.result :refer [acknowledged?]])
|
||||||
|
(:import com.mongodb.BasicDBObjectBuilder))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")
|
||||||
|
coll "search-docs"]
|
||||||
|
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/purge-many db [coll])
|
||||||
|
(f)
|
||||||
|
(mc/purge-many db [coll]))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
(deftest ^{:search true} test-basic-full-text-search-query
|
||||||
|
(mc/ensure-index db coll (array-map :subject "text" :content "text"))
|
||||||
|
(mc/insert db coll {:subject "hello there" :content "this should be searchable"})
|
||||||
|
(mc/insert db coll {:subject "untitled" :content "this is just noize"})
|
||||||
|
(let [xs (mc/find-maps db coll {$text {$search "hello"}})]
|
||||||
|
(is (= 1 (count xs)))
|
||||||
|
(is (= "hello there" (-> xs first :subject))))))
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
(ns monger.test.gridfs
|
|
||||||
(:refer-clojure :exclude [count remove find])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.core :only [count]]
|
|
||||||
[monger.test.fixtures]
|
|
||||||
[monger operators conversion]
|
|
||||||
[monger.gridfs :only (store make-input-file)])
|
|
||||||
(:require [monger.gridfs :as gridfs]
|
|
||||||
[monger.test.helper :as helper]
|
|
||||||
[clojure.java.io :as io])
|
|
||||||
(:import [java.io InputStream File FileInputStream]
|
|
||||||
[com.mongodb.gridfs GridFS GridFSInputFile GridFSDBFile]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn purge-gridfs
|
|
||||||
[f]
|
|
||||||
(gridfs/remove-all)
|
|
||||||
(f)
|
|
||||||
(gridfs/remove-all))
|
|
||||||
|
|
||||||
(use-fixtures :each purge-gridfs)
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-storing-files-to-gridfs-using-relative-fs-paths
|
|
||||||
(let [input "./test/resources/mongo/js/mapfun1.js"]
|
|
||||||
(is (= 0 (count (gridfs/all-files))))
|
|
||||||
(store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file1")
|
|
||||||
(.setContentType "application/octet-stream"))
|
|
||||||
(is (= 1 (count (gridfs/all-files))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-storing-files-to-gridfs-using-file-instances
|
|
||||||
(let [input (io/as-file "./test/resources/mongo/js/mapfun1.js")]
|
|
||||||
(is (= 0 (count (gridfs/all-files))))
|
|
||||||
(store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file2")
|
|
||||||
(.setContentType "application/octet-stream"))
|
|
||||||
(is (= 1 (count (gridfs/all-files))))))
|
|
||||||
|
|
||||||
(deftest test-storing-bytes-to-gridfs
|
|
||||||
(let [input (.getBytes "A string")]
|
|
||||||
(is (= 0 (count (gridfs/all-files))))
|
|
||||||
(store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file3")
|
|
||||||
(.setContentType "application/octet-stream"))
|
|
||||||
(is (= 1 (count (gridfs/all-files))))))
|
|
||||||
|
|
||||||
(deftest test-storing-files-to-gridfs-using-absolute-fs-paths
|
|
||||||
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-absolute-fs-paths")
|
|
||||||
_ (spit tmp-file "Some content")
|
|
||||||
input (.getAbsolutePath tmp-file)]
|
|
||||||
(is (= 0 (count (gridfs/all-files))))
|
|
||||||
(store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file4")
|
|
||||||
(.setContentType "application/octet-stream"))
|
|
||||||
(is (= 1 (count (gridfs/all-files))))))
|
|
||||||
|
|
||||||
(deftest test-storing-files-to-gridfs-using-input-stream
|
|
||||||
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-input-stream")
|
|
||||||
_ (spit tmp-file "Some other content")]
|
|
||||||
(is (= 0 (count (gridfs/all-files))))
|
|
||||||
(store (make-input-file (FileInputStream. tmp-file))
|
|
||||||
(.setFilename "monger.test.gridfs.file4b")
|
|
||||||
(.setContentType "application/octet-stream"))
|
|
||||||
(is (= 1 (count (gridfs/all-files))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-finding-individual-files-on-gridfs
|
|
||||||
(let [input "./test/resources/mongo/js/mapfun1.js"
|
|
||||||
ct "binary/octet-stream"
|
|
||||||
filename "monger.test.gridfs.file5"
|
|
||||||
md5 "14a09deabb50925a3381315149017bbd"
|
|
||||||
stored (store (make-input-file input)
|
|
||||||
(.setFilename filename)
|
|
||||||
(.setContentType ct))]
|
|
||||||
(is (= 1 (count (gridfs/all-files))))
|
|
||||||
(is (:_id stored))
|
|
||||||
(is (:uploadDate stored))
|
|
||||||
(is (= 62 (:length stored)))
|
|
||||||
(is (= md5 (:md5 stored)))
|
|
||||||
(is (= filename (:filename stored)))
|
|
||||||
(is (= ct (:contentType stored)))
|
|
||||||
(are [a b] (is (= a (:md5 (from-db-object (gridfs/find-one b) true))))
|
|
||||||
md5 (:_id stored)
|
|
||||||
md5 filename
|
|
||||||
md5 (to-db-object { :md5 md5 }))))
|
|
||||||
|
|
||||||
(deftest test-finding-multiple-files-on-gridfs
|
|
||||||
(let [input "./test/resources/mongo/js/mapfun1.js"
|
|
||||||
ct "binary/octet-stream"
|
|
||||||
md5 "14a09deabb50925a3381315149017bbd"
|
|
||||||
stored1 (store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file6")
|
|
||||||
(.setContentType ct))
|
|
||||||
stored2 (store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file7")
|
|
||||||
(.setContentType ct))
|
|
||||||
list1 (gridfs/find "monger.test.gridfs.file6")
|
|
||||||
list2 (gridfs/find "monger.test.gridfs.file7")
|
|
||||||
list3 (gridfs/find "888000___.monger.test.gridfs.file")
|
|
||||||
list4 (gridfs/find { :md5 md5 })]
|
|
||||||
(is (= 2 (count (gridfs/all-files))))
|
|
||||||
(are [a b] (is (= (map #(.get ^GridFSDBFile % "_id") a)
|
|
||||||
(map :_id b)))
|
|
||||||
list1 [stored1]
|
|
||||||
list2 [stored2]
|
|
||||||
list3 []
|
|
||||||
list4 [stored1 stored2])))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-removing-multiple-files-from-gridfs
|
|
||||||
(let [input "./test/resources/mongo/js/mapfun1.js"
|
|
||||||
ct "binary/octet-stream"
|
|
||||||
md5 "14a09deabb50925a3381315149017bbd"
|
|
||||||
stored1 (store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file8")
|
|
||||||
(.setContentType ct))
|
|
||||||
stored2 (store (make-input-file input)
|
|
||||||
(.setFilename "monger.test.gridfs.file9")
|
|
||||||
(.setContentType ct))]
|
|
||||||
(is (= 2 (count (gridfs/all-files))))
|
|
||||||
(gridfs/remove { :filename "monger.test.gridfs.file8" })
|
|
||||||
(is (= 1 (count (gridfs/all-files))))
|
|
||||||
(gridfs/remove { :md5 md5 })
|
|
||||||
(is (= 0 (count (gridfs/all-files))))))
|
|
||||||
212
test/monger/test/gridfs_test.clj
Normal file
212
test/monger/test/gridfs_test.clj
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
(ns monger.test.gridfs-test
|
||||||
|
(:refer-clojure :exclude [count remove find])
|
||||||
|
(:require [monger.gridfs :as gridfs]
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.core :as mg :refer [count]]
|
||||||
|
[monger.operators :refer :all]
|
||||||
|
[monger.conversion :refer :all]
|
||||||
|
[monger.gridfs :refer [store make-input-file store-file filename content-type metadata]])
|
||||||
|
(:import [java.io InputStream File FileInputStream]
|
||||||
|
[com.mongodb.gridfs GridFS GridFSInputFile GridFSDBFile]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")
|
||||||
|
fs (mg/get-gridfs conn "monger-test")]
|
||||||
|
(defn purge-gridfs*
|
||||||
|
[]
|
||||||
|
(gridfs/remove-all fs))
|
||||||
|
|
||||||
|
(defn purge-gridfs
|
||||||
|
[f]
|
||||||
|
(gridfs/remove-all fs)
|
||||||
|
(f)
|
||||||
|
(gridfs/remove-all fs))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-gridfs)
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-relative-fs-paths
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"]
|
||||||
|
(is (= 0 (count (gridfs/all-files fs))))
|
||||||
|
(store (make-input-file fs input)
|
||||||
|
(.setFilename "monger.test.gridfs.file1")
|
||||||
|
(.setContentType "application/octet-stream"))
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-file-instances
|
||||||
|
(let [input (io/as-file "./test/resources/mongo/js/mapfun1.js")]
|
||||||
|
(is (= 0 (count (gridfs/all-files fs))))
|
||||||
|
(store-file (make-input-file fs input)
|
||||||
|
(filename "monger.test.gridfs.file2")
|
||||||
|
(content-type "application/octet-stream"))
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))))
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-storing-bytes-to-gridfs
|
||||||
|
(let [input (.getBytes "A string")
|
||||||
|
md {:format "raw" :source "AwesomeCamera D95"}
|
||||||
|
fname "monger.test.gridfs.file3"
|
||||||
|
ct "application/octet-stream"]
|
||||||
|
(is (= 0 (count (gridfs/all-files fs))))
|
||||||
|
(store-file (make-input-file fs input)
|
||||||
|
(filename fname)
|
||||||
|
(metadata md)
|
||||||
|
(content-type "application/octet-stream"))
|
||||||
|
(let [f (first (gridfs/files-as-maps fs))]
|
||||||
|
(is (= ct (:contentType f)))
|
||||||
|
(is (= fname (:filename f)))
|
||||||
|
(is (= md (:metadata f))))
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))))
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-absolute-fs-paths
|
||||||
|
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-absolute-fs-paths")
|
||||||
|
_ (spit tmp-file "Some content")
|
||||||
|
input (.getAbsolutePath tmp-file)]
|
||||||
|
(is (= 0 (count (gridfs/all-files fs))))
|
||||||
|
(store-file (make-input-file fs input)
|
||||||
|
(filename "monger.test.gridfs.file4")
|
||||||
|
(content-type "application/octet-stream"))
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))))
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-input-stream
|
||||||
|
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-input-stream")
|
||||||
|
_ (spit tmp-file "Some other content")]
|
||||||
|
(is (= 0 (count (gridfs/all-files fs))))
|
||||||
|
(store-file fs
|
||||||
|
(make-input-file (FileInputStream. tmp-file))
|
||||||
|
(filename "monger.test.gridfs.file4b")
|
||||||
|
(content-type "application/octet-stream"))
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))))
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-deleting-file-instance-on-disk-after-storing
|
||||||
|
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-deleting-file-instance-on-disk-after-storing")
|
||||||
|
_ (spit tmp-file "to be deleted")]
|
||||||
|
(is (= 0 (count (gridfs/all-files fs))))
|
||||||
|
(store-file (make-input-file fs tmp-file)
|
||||||
|
(filename "test-deleting-file-instance-on-disk-after-storing")
|
||||||
|
(content-type "application/octet-stream"))
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))
|
||||||
|
(is (.delete tmp-file))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-finding-individual-files-on-gridfs
|
||||||
|
(testing "gridfs/find-one"
|
||||||
|
(purge-gridfs*)
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"
|
||||||
|
ct "binary/octet-stream"
|
||||||
|
fname "monger.test.gridfs.file5"
|
||||||
|
md5 "14a09deabb50925a3381315149017bbd"
|
||||||
|
stored (store-file (make-input-file fs input)
|
||||||
|
(filename fname)
|
||||||
|
(content-type ct))]
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))
|
||||||
|
(is (:_id stored))
|
||||||
|
(is (:uploadDate stored))
|
||||||
|
(is (= 62 (:length stored)))
|
||||||
|
(is (= md5 (:md5 stored)))
|
||||||
|
(is (= fname (:filename stored)))
|
||||||
|
(is (= ct (:contentType stored)))
|
||||||
|
(are [a b] (is (= a (:md5 (from-db-object (gridfs/find-one fs b) true))))
|
||||||
|
md5 {:_id (:_id stored)}
|
||||||
|
md5 (to-db-object {:md5 md5}))))
|
||||||
|
(testing "gridfs/find-one-as-map"
|
||||||
|
(purge-gridfs*)
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"
|
||||||
|
ct "binary/octet-stream"
|
||||||
|
fname "monger.test.gridfs.file6"
|
||||||
|
md5 "14a09deabb50925a3381315149017bbd"
|
||||||
|
stored (store-file (make-input-file fs input)
|
||||||
|
(filename fname)
|
||||||
|
(metadata (to-db-object {:meta "data"}))
|
||||||
|
(content-type ct))]
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))
|
||||||
|
(is (:_id stored))
|
||||||
|
(is (:uploadDate stored))
|
||||||
|
(is (= 62 (:length stored)))
|
||||||
|
(is (= md5 (:md5 stored)))
|
||||||
|
(is (= fname (:filename stored)))
|
||||||
|
(is (= ct (:contentType stored)))
|
||||||
|
(let [m (gridfs/find-one-as-map fs {:filename fname})]
|
||||||
|
(is (= {:meta "data"} (:metadata m))))
|
||||||
|
(are [a query] (is (= a (:md5 (gridfs/find-one-as-map fs query))))
|
||||||
|
md5 {:_id (:_id stored)}
|
||||||
|
md5 {:md5 md5})))
|
||||||
|
(testing "gridfs/find-by-id"
|
||||||
|
(purge-gridfs*)
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"
|
||||||
|
ct "binary/octet-stream"
|
||||||
|
fname "monger.test.gridfs.file5"
|
||||||
|
md5 "14a09deabb50925a3381315149017bbd"
|
||||||
|
stored (store-file (make-input-file fs input)
|
||||||
|
(filename fname)
|
||||||
|
(content-type ct))]
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))
|
||||||
|
(is (:_id stored))
|
||||||
|
(is (:uploadDate stored))
|
||||||
|
(is (= 62 (:length stored)))
|
||||||
|
(is (= md5 (:md5 stored)))
|
||||||
|
(is (= fname (:filename stored)))
|
||||||
|
(is (= ct (:contentType stored)))
|
||||||
|
(are [a id] (is (= a (:md5 (from-db-object (gridfs/find-by-id fs id) true))))
|
||||||
|
md5 (:_id stored))))
|
||||||
|
(testing "gridfs/find-map-by-id"
|
||||||
|
(purge-gridfs*)
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"
|
||||||
|
ct "binary/octet-stream"
|
||||||
|
fname "monger.test.gridfs.file6"
|
||||||
|
md5 "14a09deabb50925a3381315149017bbd"
|
||||||
|
stored (store-file (make-input-file fs input)
|
||||||
|
(filename fname)
|
||||||
|
(metadata (to-db-object {:meta "data"}))
|
||||||
|
(content-type ct))]
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))
|
||||||
|
(is (:_id stored))
|
||||||
|
(is (:uploadDate stored))
|
||||||
|
(is (= 62 (:length stored)))
|
||||||
|
(is (= md5 (:md5 stored)))
|
||||||
|
(is (= fname (:filename stored)))
|
||||||
|
(is (= ct (:contentType stored)))
|
||||||
|
(let [m (gridfs/find-map-by-id fs (:_id stored))]
|
||||||
|
(is (= {:meta "data"} (:metadata m))))
|
||||||
|
(are [a id] (is (= a (:md5 (gridfs/find-map-by-id fs id))))
|
||||||
|
md5 (:_id stored)))))
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-finding-multiple-files-on-gridfs
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"
|
||||||
|
ct "binary/octet-stream"
|
||||||
|
md5 "14a09deabb50925a3381315149017bbd"
|
||||||
|
stored1 (store-file (make-input-file fs input)
|
||||||
|
(filename "monger.test.gridfs.file6")
|
||||||
|
(content-type ct))
|
||||||
|
stored2 (store-file (make-input-file fs input)
|
||||||
|
(filename "monger.test.gridfs.file7")
|
||||||
|
(content-type ct))
|
||||||
|
list1 (gridfs/find-by-filename fs "monger.test.gridfs.file6")
|
||||||
|
list2 (gridfs/find-by-filename fs "monger.test.gridfs.file7")
|
||||||
|
list3 (gridfs/find-by-filename fs "888000___.monger.test.gridfs.file")
|
||||||
|
list4 (gridfs/find-by-md5 fs md5)]
|
||||||
|
(is (= 2 (count (gridfs/all-files fs))))
|
||||||
|
(are [a b] (is (= (map #(.get ^GridFSDBFile % "_id") a)
|
||||||
|
(map :_id b)))
|
||||||
|
list1 [stored1]
|
||||||
|
list2 [stored2]
|
||||||
|
list3 []
|
||||||
|
list4 [stored1 stored2])))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:gridfs true} test-removing-multiple-files-from-gridfs
|
||||||
|
(let [input "./test/resources/mongo/js/mapfun1.js"
|
||||||
|
ct "binary/octet-stream"
|
||||||
|
md5 "14a09deabb50925a3381315149017bbd"
|
||||||
|
stored1 (store-file (make-input-file fs input)
|
||||||
|
(filename "monger.test.gridfs.file8")
|
||||||
|
(content-type ct))
|
||||||
|
stored2 (store-file (make-input-file fs input)
|
||||||
|
(filename "monger.test.gridfs.file9")
|
||||||
|
(content-type ct))]
|
||||||
|
(is (= 2 (count (gridfs/all-files fs))))
|
||||||
|
(gridfs/remove fs { :filename "monger.test.gridfs.file8" })
|
||||||
|
(is (= 1 (count (gridfs/all-files fs))))
|
||||||
|
(gridfs/remove fs { :md5 md5 })
|
||||||
|
(is (= 0 (count (gridfs/all-files fs)))))))
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
(ns monger.test.helper
|
|
||||||
(:require [monger core util])
|
|
||||||
(:import [com.mongodb WriteConcern]))
|
|
||||||
|
|
||||||
(def connected (atom false))
|
|
||||||
(defn connected?
|
|
||||||
[]
|
|
||||||
@connected)
|
|
||||||
|
|
||||||
(defn connect!
|
|
||||||
[]
|
|
||||||
(when-not (connected?)
|
|
||||||
(do
|
|
||||||
(monger.core/connect!)
|
|
||||||
(monger.core/set-db! (monger.core/get-db "monger-test"))
|
|
||||||
(monger.core/set-default-write-concern! WriteConcern/SAFE)
|
|
||||||
(reset! connected true))))
|
|
||||||
49
test/monger/test/indexing_test.clj
Normal file
49
test/monger/test/indexing_test.clj
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
(ns monger.test.indexing-test
|
||||||
|
(:import org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
monger.joda-time
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[clj-time.core :refer [now seconds ago from-now]]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(deftest ^{:indexing true} test-creating-and-dropping-indexes
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/drop-indexes db collection)
|
||||||
|
(mc/create-index db collection {"language" 1})
|
||||||
|
(is (= "language_1"
|
||||||
|
(:name (second (mc/indexes-on db collection)))))
|
||||||
|
(mc/drop-indexes db collection)
|
||||||
|
(is (nil? (second (mc/indexes-on db collection))))
|
||||||
|
(mc/ensure-index db collection (array-map "language" 1) {:unique true})
|
||||||
|
(is (= "language_1"
|
||||||
|
(:name (second (mc/indexes-on db collection)))))
|
||||||
|
(mc/drop-indexes db collection)
|
||||||
|
(mc/ensure-index db collection (array-map "language" 1))
|
||||||
|
(mc/drop-indexes db collection)
|
||||||
|
(mc/ensure-index db collection (array-map "language" 1) {:unique true})
|
||||||
|
(mc/drop-indexes db collection)
|
||||||
|
(mc/ensure-index db collection (array-map "language" 1) "index-name" true)
|
||||||
|
(mc/drop-indexes db collection)))
|
||||||
|
|
||||||
|
(deftest ^{:indexing true :time-consuming true} test-ttl-collections
|
||||||
|
(let [coll "recent_events"
|
||||||
|
ttl 15
|
||||||
|
sleep 65]
|
||||||
|
(mc/remove db coll)
|
||||||
|
(mc/drop-indexes db coll)
|
||||||
|
(mc/ensure-index db coll (array-map :created-at 1) {:expireAfterSeconds ttl})
|
||||||
|
(dotimes [i 100]
|
||||||
|
(mc/insert db coll {:type "signup" :created-at (-> i seconds ago) :i i}))
|
||||||
|
(dotimes [i 100]
|
||||||
|
(mc/insert db coll {:type "signup" :created-at (-> i seconds from-now) :i i}))
|
||||||
|
(is (= 200 (mc/count db coll {:type "signup"})))
|
||||||
|
;; sleep for > 60 seconds. MongoDB seems to run TTLMonitor once per minute, according to
|
||||||
|
;; the log.
|
||||||
|
(println (format "Now sleeping for %d seconds to test TTL collections!" sleep))
|
||||||
|
(Thread/sleep (* sleep 1000))
|
||||||
|
(println (format "Documents in the TTL collection: %d" (mc/count db coll {:type "signup"})))
|
||||||
|
(is (< (mc/count db coll {:type "signup"}) 100))
|
||||||
|
(mc/remove db coll))))
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.inserting
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.conversion :as mgcnv]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.operators]
|
|
||||||
[monger.test.fixtures]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; insert
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest insert-a-basic-document-without-id-and-with-default-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
doc { :name "Joe", :age 30 }]
|
|
||||||
(is (monger.result/ok? (mgcol/insert "people" doc)))
|
|
||||||
(is (= 1 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
(deftest insert-a-basic-document-with-explicitly-passed-database-without-id-and-with-default-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
doc { :name "Joe", :age 30 }]
|
|
||||||
(dotimes [n 5]
|
|
||||||
(is (monger.result/ok? (mgcol/insert monger.core/*mongodb-database* "people" doc WriteConcern/SAFE))))
|
|
||||||
(is (= 5 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
(deftest insert-a-basic-document-without-id-and-with-explicit-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
doc { :name "Joe", :age 30 }]
|
|
||||||
(is (monger.result/ok? (mgcol/insert "people" doc WriteConcern/SAFE)))
|
|
||||||
(is (= 1 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
(deftest insert-a-basic-db-object-without-id-and-with-default-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
doc (mgcnv/to-db-object { :name "Joe", :age 30 })]
|
|
||||||
(is (nil? (.get ^DBObject doc "_id")))
|
|
||||||
(mgcol/insert "people" doc)
|
|
||||||
(is (not (nil? (monger.util/get-id doc))))))
|
|
||||||
|
|
||||||
(deftest insert-a-map-with-id-and-with-default-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
id (ObjectId.)
|
|
||||||
doc { :name "Joe", :age 30 "_id" id }
|
|
||||||
result (mgcol/insert "people" doc)]
|
|
||||||
(is (= id (monger.util/get-id doc)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; insert-batch
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-default-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
docs [{ :name "Joe", :age 30 }, { :name "Paul", :age 27 }]]
|
|
||||||
(is (monger.result/ok? (mgcol/insert-batch "people" docs)))
|
|
||||||
(is (= 2 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-explicit-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
docs [{ :name "Joe", :age 30 }, { :name "Paul", :age 27 }]]
|
|
||||||
(is (monger.result/ok? (mgcol/insert-batch "people" docs WriteConcern/NORMAL)))
|
|
||||||
(is (= 2 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
(deftest insert-a-batch-of-basic-documents-with-explicit-database-without-ids-and-with-explicit-write-concern
|
|
||||||
(let [collection "people"
|
|
||||||
docs [{ :name "Joe", :age 30 }, { :name "Paul", :age 27 }]]
|
|
||||||
(dotimes [n 44]
|
|
||||||
(is (monger.result/ok? (mgcol/insert-batch monger.core/*mongodb-database* "people" docs WriteConcern/NORMAL))))
|
|
||||||
(is (= 88 (mgcol/count collection)))))
|
|
||||||
180
test/monger/test/inserting_test.clj
Normal file
180
test/monger/test/inserting_test.clj
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
(ns monger.test.inserting-test
|
||||||
|
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject DBRef]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.util :as mu]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]
|
||||||
|
[monger.conversion :refer :all]))
|
||||||
|
|
||||||
|
(defrecord Metrics
|
||||||
|
[rps eps])
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "widgets")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "widgets"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; insert
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest insert-a-basic-document-without-id-and-with-default-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
doc {:name "Joe" :age 30}]
|
||||||
|
(is (mc/insert db collection doc))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-a-basic-document-with-explicitly-passed-database-without-id-and-with-default-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
doc {:name "Joe" :age 30}]
|
||||||
|
(dotimes [n 5]
|
||||||
|
(mc/insert db collection doc WriteConcern/SAFE))
|
||||||
|
(is (= 5 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-a-basic-document-without-id-and-with-explicit-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
doc {:name "Joe" :age 30}]
|
||||||
|
(is (mc/insert db collection doc WriteConcern/SAFE))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-a-basic-db-object-without-id-and-with-default-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
doc (to-db-object {:name "Joe" :age 30})]
|
||||||
|
(is (nil? (.get ^DBObject doc "_id")))
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (not (nil? (monger.util/get-id doc))))))
|
||||||
|
|
||||||
|
(deftest insert-a-map-with-id-and-with-default-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
id (ObjectId.)
|
||||||
|
doc {:name "Joe" :age 30 "_id" id}
|
||||||
|
result (mc/insert db collection doc)]
|
||||||
|
(is (= id (monger.util/get-id doc)))))
|
||||||
|
|
||||||
|
(deftest insert-a-document-with-clojure-ratio-in-it
|
||||||
|
(let [collection "widgets"
|
||||||
|
id (ObjectId.)
|
||||||
|
doc {:ratio 11/2 "_id" id}
|
||||||
|
result (mc/insert db collection doc)]
|
||||||
|
(is (= 5.5 (:ratio (mc/find-map-by-id db collection id))))))
|
||||||
|
|
||||||
|
(deftest insert-a-document-with-clojure-keyword-in-it
|
||||||
|
(let [collection "widgets"
|
||||||
|
id (ObjectId.)
|
||||||
|
doc {:keyword :kwd "_id" id}
|
||||||
|
result (mc/insert db collection doc)]
|
||||||
|
(is (= (name :kwd) (:keyword (mc/find-map-by-id db collection id))))))
|
||||||
|
|
||||||
|
(deftest insert-a-document-with-clojure-keyword-in-a-set-in-it
|
||||||
|
(let [collection "widgets"
|
||||||
|
id (ObjectId.)
|
||||||
|
doc {:keyword1 {:keyword2 #{:kw1 :kw2}} "_id" id}
|
||||||
|
result (mc/insert db collection doc)]
|
||||||
|
(is (= (sort ["kw1" "kw2"])
|
||||||
|
(sort (get-in (mc/find-map-by-id db collection id) [:keyword1 :keyword2]))))))
|
||||||
|
|
||||||
|
(deftest insert-a-document-with-clojure-record-in-it
|
||||||
|
(let [collection "widgets"
|
||||||
|
id (ObjectId.)
|
||||||
|
doc {:record (Metrics. 10 20) "_id" id}
|
||||||
|
result (mc/insert db collection doc)]
|
||||||
|
(is (= {:rps 10 :eps 20} (:record (mc/find-map-by-id db collection id))))))
|
||||||
|
|
||||||
|
;; TODO: disabled until we figure out how to implement dereferencing of DBRefs
|
||||||
|
;; in 3.0 in a compatible way (and if that's possible at all). MK.
|
||||||
|
#_ (deftest test-insert-a-document-with-dbref
|
||||||
|
(mc/remove db "widgets")
|
||||||
|
(mc/remove db "owners")
|
||||||
|
(let [coll1 "widgets"
|
||||||
|
coll2 "owners"
|
||||||
|
oid (ObjectId.)
|
||||||
|
joe (mc/insert db coll2 {:name "Joe" :_id oid})
|
||||||
|
dbref (DBRef. coll2 oid)]
|
||||||
|
(mc/insert db coll1 {:type "pentagon" :owner dbref})
|
||||||
|
(let [fetched (mc/find-one-as-map db coll1 {:type "pentagon"})
|
||||||
|
fo (:owner fetched)]
|
||||||
|
(is (= {:_id oid :name "Joe"} (from-db-object @fo true))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; insert-and-return
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest insert-and-return-a-basic-document-without-id-and-with-default-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
doc {:name "Joe" :age 30}
|
||||||
|
result (mc/insert-and-return db collection doc)]
|
||||||
|
(is (= (:name doc)
|
||||||
|
(:name result)))
|
||||||
|
(is (= (:age doc)
|
||||||
|
(:age result)))
|
||||||
|
(is (:_id result))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-and-return-a-basic-document-without-id-but-with-a-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
doc {:name "Joe" :age 30 :ratio 3/4}
|
||||||
|
result (mc/insert-and-return db collection doc WriteConcern/FSYNC_SAFE)]
|
||||||
|
(is (= (:name doc)
|
||||||
|
(:name result)))
|
||||||
|
(is (= (:age doc)
|
||||||
|
(:age result)))
|
||||||
|
(is (= (:ratio doc)
|
||||||
|
(:ratio result)))
|
||||||
|
(is (:_id result))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-and-return-with-a-provided-id
|
||||||
|
(let [collection "people"
|
||||||
|
oid (ObjectId.)
|
||||||
|
doc {:name "Joe" :age 30 :_id oid}
|
||||||
|
result (mc/insert-and-return db collection doc)]
|
||||||
|
(is (= (:_id result) (:_id doc) oid))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; insert-batch
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-default-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
|
||||||
|
(is (mc/insert-batch db collection docs))
|
||||||
|
(is (= 2 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-explicit-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
|
||||||
|
(is (mc/insert-batch db collection docs WriteConcern/FSYNCED))
|
||||||
|
(is (= 2 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-a-batch-of-basic-documents-with-explicit-database-without-ids-and-with-explicit-write-concern
|
||||||
|
(let [collection "people"
|
||||||
|
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
|
||||||
|
(dotimes [n 44]
|
||||||
|
(is (mc/insert-batch db collection docs WriteConcern/FSYNCED)))
|
||||||
|
(is (= 88 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest insert-a-batch-of-basic-documents-from-a-lazy-sequence
|
||||||
|
(let [collection "people"
|
||||||
|
numbers (range 0 1000)]
|
||||||
|
(is (mc/insert-batch db collection (map (fn [^long l]
|
||||||
|
{:n l})
|
||||||
|
numbers)))
|
||||||
|
(is (= (count numbers) (mc/count db collection))))))
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
(ns monger.test.internal.fn
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.internal.fn]))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-expand-all
|
|
||||||
(are [i o] (is (= (expand-all i) o))
|
|
||||||
{ :int (fn [] 1) :str "Clojure" :float (Float/valueOf 11.0) } { :int 1 :str "Clojure" :float (Float/valueOf 11.0 )}
|
|
||||||
{ :long (fn [] (Long/valueOf 11)) } { :long (Long/valueOf 11) }
|
|
||||||
{
|
|
||||||
:i 1
|
|
||||||
:l (Long/valueOf 1111)
|
|
||||||
:s "Clojure"
|
|
||||||
:d (Double/valueOf 11.1)
|
|
||||||
:f (Float/valueOf 2.5)
|
|
||||||
:v [1 2 3]
|
|
||||||
:dyn-v [(fn [] 10) (fn [] 20) (fn [] 30)]
|
|
||||||
:dyn-i (fn [] 1)
|
|
||||||
:dyn-s (fn [] "Clojure (expanded)")
|
|
||||||
:m { :nested "String" }
|
|
||||||
:dyn-m { :abc (fn [] :abc) :nested { :a { :b { :c (fn [] "d") } } } }
|
|
||||||
}
|
|
||||||
{
|
|
||||||
:i 1
|
|
||||||
:l (Long/valueOf 1111)
|
|
||||||
:s "Clojure"
|
|
||||||
:d (Double/valueOf 11.1)
|
|
||||||
:f (Float/valueOf 2.5)
|
|
||||||
:v [1 2 3]
|
|
||||||
:dyn-v [10 20 30]
|
|
||||||
:dyn-i 1
|
|
||||||
:dyn-s "Clojure (expanded)"
|
|
||||||
:m { :nested "String" }
|
|
||||||
:dyn-m {
|
|
||||||
:abc :abc
|
|
||||||
:nested { :a { :b { :c "d" } } }
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
(deftest test-expand-all-with
|
|
||||||
(let [expander-fn (fn [f]
|
|
||||||
(* 3 (f)))]
|
|
||||||
(are [i o] (is (= (expand-all-with i expander-fn) o))
|
|
||||||
{ :a 1 :int (fn [] 3) } { :a 1 :int 9 }
|
|
||||||
{ :v [(fn [] 1) (fn [] 11)] :m { :inner (fn [] 3) } :s "Clojure" } { :v [3 33] :m { :inner 9 } :s "Clojure" })))
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(ns monger.test.internal.pagination
|
(ns monger.test.internal.pagination-test
|
||||||
(:use [clojure.test]
|
(:require [clojure.test :refer :all]
|
||||||
[monger.internal.pagination]))
|
[monger.internal.pagination :refer :all]))
|
||||||
|
|
||||||
(deftest test-pagination-offset
|
(deftest test-pagination-offset
|
||||||
(are [a b] (= a b)
|
(are [a b] (= a b)
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
(ns monger.test.js
|
(ns monger.test.js-test
|
||||||
(:require [monger js]
|
(:require monger.js
|
||||||
[monger.test.helper :as helper])
|
[clojure.test :refer :all]))
|
||||||
(:use [clojure.test]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(deftest load-js-resource-using-path-on-the-classpath
|
(deftest load-js-resource-using-path-on-the-classpath
|
||||||
(are [c path] (= c (count (monger.js/load-resource path)))
|
(are [c path] (= c (count (monger.js/load-resource path)))
|
||||||
16
test/monger/test/json_cheshire_test.clj
Normal file
16
test/monger/test/json_cheshire_test.clj
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
(ns monger.test.json-cheshire-test
|
||||||
|
(:require [clojure.test :refer :all]
|
||||||
|
[monger.json]
|
||||||
|
[cheshire.core :refer :all])
|
||||||
|
(:import org.bson.types.ObjectId
|
||||||
|
org.bson.types.BSONTimestamp))
|
||||||
|
|
||||||
|
(deftest convert-dbobject-to-json
|
||||||
|
(let [input (ObjectId.)
|
||||||
|
output (generate-string input)]
|
||||||
|
(is (= (str "\"" input "\"") output))))
|
||||||
|
|
||||||
|
(deftest convert-bson-timestamp-to-json
|
||||||
|
(let [input (BSONTimestamp. 123 4)
|
||||||
|
output (generate-string input)]
|
||||||
|
(is (= "{\"time\":123,\"inc\":4}" output))))
|
||||||
16
test/monger/test/json_test.clj
Normal file
16
test/monger/test/json_test.clj
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
(ns monger.test.json-test
|
||||||
|
(:require [clojure.test :refer :all]
|
||||||
|
[monger.json]
|
||||||
|
[clojure.data.json :as json])
|
||||||
|
(:import org.bson.types.ObjectId
|
||||||
|
org.bson.types.BSONTimestamp))
|
||||||
|
|
||||||
|
(deftest convert-dbobject-to-json
|
||||||
|
(let [input (ObjectId.)
|
||||||
|
output (json/write-str input)]
|
||||||
|
(is (= (str "\"" input "\"") output))))
|
||||||
|
|
||||||
|
(deftest convert-bson-timestamp-to-json
|
||||||
|
(let [input (BSONTimestamp. 123 4)
|
||||||
|
output (json/write-str input)]
|
||||||
|
(is (= "{\"time\":123,\"inc\":4}" output))))
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
(ns monger.test.lib-integration
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.json]
|
|
||||||
[monger.joda-time]
|
|
||||||
[monger.conversion])
|
|
||||||
(:import [org.joda.time DateTime ReadableInstant]
|
|
||||||
[org.joda.time.format ISODateTimeFormat]
|
|
||||||
[java.io StringWriter PrintWriter]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[com.mongodb DBObject])
|
|
||||||
(:require [clojure.data.json :as json]
|
|
||||||
[clj-time.core :as t]))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest serialization-of-joda-datetime-to-json-with-clojure-data-json
|
|
||||||
(is (= "\"2011-10-13T23:55:00.000Z\"" (json/json-str (t/date-time 2011 10 13 23 55 0)))))
|
|
||||||
|
|
||||||
(deftest serialization-of-object-id-to-json-with-clojure-data-json
|
|
||||||
(is (= "\"4ec2d1a6b55634a935ea4ac8\"" (json/json-str (ObjectId. "4ec2d1a6b55634a935ea4ac8")))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest conversion-of-joda-datetime-to-db-object
|
|
||||||
(let [d (to-db-object (t/date-time 2011 10 13 23 55 0))]
|
|
||||||
(is (instance? java.util.Date d))
|
|
||||||
(is (= 1318550100000 (.getTime ^java.util.Date d)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest conversion-of-java-util-date-to-joda-datetime
|
|
||||||
(let [input (.toDate ^DateTime (t/date-time 2011 10 13 23 55 0))
|
|
||||||
output (from-db-object input false)]
|
|
||||||
(is (instance? org.joda.time.DateTime output))
|
|
||||||
(is (= input (.toDate ^DateTime output)))))
|
|
||||||
55
test/monger/test/lib_integration_test.clj
Normal file
55
test/monger/test/lib_integration_test.clj
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
(ns monger.test.lib-integration-test
|
||||||
|
(:import [org.joda.time DateTime DateMidnight LocalDate]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
com.mongodb.DBObject)
|
||||||
|
(:require monger.json
|
||||||
|
monger.joda-time
|
||||||
|
[clj-time.core :as t]
|
||||||
|
[cheshire.core :as json]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.conversion :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:integration true} serialization-of-joda-datetime-to-json
|
||||||
|
(let [dt (t/date-time 2011 10 13 23 55 0)]
|
||||||
|
(is (= "\"2011-10-13T23:55:00.000Z\""
|
||||||
|
(json/encode dt)))))
|
||||||
|
|
||||||
|
(deftest ^{:integration true} serialization-of-joda-date-to-json
|
||||||
|
(let [d (.toDate (t/date-time 2011 10 13 23 55 0))]
|
||||||
|
(is (= "\"2011-10-13T23:55:00Z\""
|
||||||
|
(json/encode d)))))
|
||||||
|
|
||||||
|
(deftest ^{:integration true} conversion-of-joda-datetime-to-db-object
|
||||||
|
(let [d (to-db-object (t/date-time 2011 10 13 23 55 0))]
|
||||||
|
(is (instance? java.util.Date d))
|
||||||
|
(is (= 1318550100000 (.getTime ^java.util.Date d)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:integration true} conversion-of-joda-datemidnight-to-db-object
|
||||||
|
(let [d (to-db-object (DateMidnight. (t/date-time 2011 10 13)))]
|
||||||
|
(is (instance? java.util.Date d))
|
||||||
|
(is (= 1318464000000 (.getTime ^java.util.Date d)))))
|
||||||
|
|
||||||
|
(deftest ^{:integration true} conversion-of-joda-localdate-to-db-object
|
||||||
|
(let [d (to-db-object (LocalDate. 2011 10 13))]
|
||||||
|
(is (instance? java.util.Date d))
|
||||||
|
(is (= 111 (.getYear ^java.util.Date d))) ;; how many years since 1900
|
||||||
|
(is (= 9 (.getMonth ^java.util.Date d))) ;; java.util.Date counts from 0
|
||||||
|
(is (= 13 (.getDate ^java.util.Date d)))))
|
||||||
|
|
||||||
|
(deftest ^{:integration true} conversion-of-java-util-date-to-joda-datetime
|
||||||
|
(let [input (.toDate ^DateTime (t/date-time 2011 10 13 23 55 0))
|
||||||
|
output (from-db-object input false)]
|
||||||
|
(is (instance? org.joda.time.DateTime output))
|
||||||
|
(is (= input (.toDate ^DateTime output)))))
|
||||||
|
|
||||||
|
(deftest ^{:integration true} test-reader-extensions
|
||||||
|
(let [^DateTime d (t/date-time 2011 10 13 23 55 0)]
|
||||||
|
(binding [*print-dup* true]
|
||||||
|
(pr-str d))))
|
||||||
|
|
||||||
|
(deftest ^{:integration true} test-reader-extensions-for-localdate
|
||||||
|
(let [^DateTime d (t/today)]
|
||||||
|
(binding [*print-dup* true]
|
||||||
|
(pr-str d))))
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.query-operators
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure MapReduceOutput MapReduceCommand MapReduceCommand$OutputType]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[clojure stacktrace]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.conversion :as mgcnv]
|
|
||||||
[monger.js :as js]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.operators]
|
|
||||||
[monger.test.fixtures]))
|
|
||||||
|
|
||||||
(monger.core/connect!)
|
|
||||||
(monger.core/set-db! (monger.core/get-db "monger-test"))
|
|
||||||
|
|
||||||
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $gt, $gte, $lt, lte
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-with-conditional-operators-comparison
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" :users 1}
|
|
||||||
{ :language "Clojure", :name "langohr" :users 5 }
|
|
||||||
{ :language "Clojure", :name "incanter" :users 15 }
|
|
||||||
{ :language "Scala", :name "akka" :users 150}])
|
|
||||||
(are [a b] (= a (.count (mgcol/find collection b)))
|
|
||||||
2 { :users { $gt 10 }}
|
|
||||||
3 { :users { $gte 5 }}
|
|
||||||
2 { :users { $lt 10 }}
|
|
||||||
2 { :users { $lte 5 }}
|
|
||||||
1 { :users { $gt 10 $lt 150 }})))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $ne
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-with-and-or-operators
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Ruby", :name "mongoid" :users 1}
|
|
||||||
{ :language "Clojure", :name "langohr" :users 5 }
|
|
||||||
{ :language "Clojure", :name "incanter" :users 15 }
|
|
||||||
{ :language "Scala", :name "akka" :users 150}])
|
|
||||||
(is (= 2 (.count (mgcol/find collection {$ne { :language "Clojure" }}))))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $and, $or, $nor
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-with-and-or-operators
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Ruby", :name "mongoid" :users 1}
|
|
||||||
{ :language "Clojure", :name "langohr" :users 5 }
|
|
||||||
{ :language "Clojure", :name "incanter" :users 15 }
|
|
||||||
{ :language "Scala", :name "akka" :users 150}])
|
|
||||||
(is (= 1 (.count (mgcol/find collection {$and [{ :language "Clojure" }
|
|
||||||
{ :users { $gt 10 } }]}))))
|
|
||||||
(is (= 3 (.count (mgcol/find collection {$or [{ :language "Clojure" }
|
|
||||||
{:users { $gt 10 } } ]}))))
|
|
||||||
(is (= 1 (.count (mgcol/find collection {$nor [{ :language "Clojure" }
|
|
||||||
{:users { $gt 10 } } ]}))))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; $all, $in, $nin
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-on-embedded-arrays
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :tags [ "functional" ] }
|
|
||||||
{ :language "Scala", :tags [ "functional" "object-oriented" ] }
|
|
||||||
{ :language "Ruby", :tags [ "object-oriented" "dynamic" ] }])
|
|
||||||
|
|
||||||
(is (= "Scala" (:language (first (mgcol/find-maps collection { :tags { $all [ "functional" "object-oriented" ] } } )))))
|
|
||||||
(is (= 3 (.count (mgcol/find-maps collection { :tags { $in [ "functional" "object-oriented" ] } } ))))
|
|
||||||
(is (= 2 (.count (mgcol/find-maps collection { :language { $in [ "Scala" "Ruby" ] } } ))))
|
|
||||||
(is (= 1 (.count (mgcol/find-maps collection { :tags { $nin [ "dynamic", "object-oriented" ] } } ))))
|
|
||||||
(is (= 3 (.count (mgcol/find-maps collection { :language { $nin [ "C#" ] } } ))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-with-conditional-operators-on-embedded-documents
|
|
||||||
(let [collection "people"]
|
|
||||||
(mgcol/insert-batch collection [{ :name "Bob", :comments [ { :text "Nice!" :rating 1 }
|
|
||||||
{ :text "Love it" :rating 4 }
|
|
||||||
{ :text "What?":rating -5 } ] }
|
|
||||||
{ :name "Alice", :comments [ { :text "Yeah" :rating 2 }
|
|
||||||
{ :text "Doh" :rating 1 }
|
|
||||||
{ :text "Agreed" :rating 3 }
|
|
||||||
] } ])
|
|
||||||
(are [a b] (= a (.count (mgcol/find collection b)))
|
|
||||||
1 { :comments { $elemMatch { :text "Nice!" :rating { $gte 1 } } } }
|
|
||||||
2 { "comments.rating" 1 }
|
|
||||||
1 { "comments.rating" { $gt 3 } })))
|
|
||||||
146
test/monger/test/query_operators_test.clj
Normal file
146
test/monger/test/query_operators_test.clj
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
(ns monger.test.query-operators-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.js :as js]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[clojure.set :refer [difference]]
|
||||||
|
[monger.operators :refer :all])
|
||||||
|
(:import [com.mongodb QueryOperators]))
|
||||||
|
|
||||||
|
;; (use-fixtures :each purge-people purge-docs purge-things purge-libraries)
|
||||||
|
|
||||||
|
(deftest every-query-operator-is-defined
|
||||||
|
(let [driver-query-operators (->> (.getDeclaredFields QueryOperators) (map #(.get % nil)) set)
|
||||||
|
monger-query-operators (->> (ns-publics 'monger.operators) (map (comp name first)) set)
|
||||||
|
; $within is deprecated and replaced by $geoWithin since v2.4.
|
||||||
|
; $uniqueDocs is deprecated since v2.6.
|
||||||
|
deprecated-query-operators #{"$within" "$uniqueDocs"}
|
||||||
|
; Query modifier operators that are deprecated in the mongo shell since v3.2
|
||||||
|
deprecated-meta-operators #{"$comment" "$explain" "$hint" "$maxScan"
|
||||||
|
"$maxTimeMS" "$max" "$min" "$orderby"
|
||||||
|
"$returnKey" "$showDiskLoc" "$snapshot" "$query"}
|
||||||
|
undefined-non-deprecated-operators (difference driver-query-operators
|
||||||
|
deprecated-query-operators
|
||||||
|
deprecated-meta-operators
|
||||||
|
monger-query-operators)]
|
||||||
|
(is (= #{} undefined-non-deprecated-operators))))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "libraries")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "libraries"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $gt, $gte, $lt, lte
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-with-conditional-operators-comparison
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Clojure" :name "monger" :users 1}
|
||||||
|
{:language "Clojure" :name "langohr" :users 5}
|
||||||
|
{:language "Clojure" :name "incanter" :users 15}
|
||||||
|
{:language "Scala" :name "akka" :users 150}])
|
||||||
|
(are [a b] (= a (.count (mc/find db collection b)))
|
||||||
|
2 {:users {$gt 10}}
|
||||||
|
3 {:users {$gte 5}}
|
||||||
|
2 {:users {$lt 10}}
|
||||||
|
2 {:users {$lte 5}}
|
||||||
|
1 {:users {$gt 10 $lt 150}})))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $eq
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-with-eq-operator
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Ruby" :name "mongoid" :users 1 :displayName nil}
|
||||||
|
{:language "Clojure" :name "langohr" :users 5}
|
||||||
|
{:language "Clojure" :name "incanter" :users 15}
|
||||||
|
{:language "Scala" :name "akka" :users 150}])
|
||||||
|
(is (= 2 (.count (mc/find db collection {:language {$eq "Clojure"}}))))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $ne
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-with-ne-operator
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Ruby" :name "mongoid" :users 1}
|
||||||
|
{:language "Clojure" :name "langohr" :users 5}
|
||||||
|
{:language "Clojure" :name "incanter" :users 15}
|
||||||
|
{:language "Scala" :name "akka" :users 150}])
|
||||||
|
(is (= 2 (.count (mc/find db collection {:language {$ne "Clojure"}}))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $and, $or, $nor
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-with-and-or-operators
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Ruby" :name "mongoid" :users 1}
|
||||||
|
{:language "Clojure" :name "langohr" :users 5}
|
||||||
|
{:language "Clojure" :name "incanter" :users 15}
|
||||||
|
{:language "Scala" :name "akka" :users 150}])
|
||||||
|
(is (= 1 (.count (mc/find db collection {$and [{:language "Clojure"}
|
||||||
|
{:users {$gt 10}}]}))))
|
||||||
|
(is (= 3 (.count (mc/find db collection {$or [{:language "Clojure"}
|
||||||
|
{:users {$gt 10}} ]}))))
|
||||||
|
(is (= 1 (.count (mc/find db collection {$nor [{:language "Clojure"}
|
||||||
|
{:users {$gt 10}} ]}))))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; $all, $in, $nin
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-on-embedded-arrays
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Clojure" :tags [ "functional" ]}
|
||||||
|
{:language "Scala" :tags [ "functional" "object-oriented" ]}
|
||||||
|
{:language "Ruby" :tags [ "object-oriented" "dynamic" ]}])
|
||||||
|
|
||||||
|
(is (= "Scala" (:language (first (mc/find-maps db collection {:tags {$all [ "functional" "object-oriented" ]}} )))))
|
||||||
|
(is (= 3 (.count (mc/find-maps db collection {:tags {$in [ "functional" "object-oriented" ]}} ))))
|
||||||
|
(is (= 2 (.count (mc/find-maps db collection {:language {$in [ "Scala" "Ruby" ]}} ))))
|
||||||
|
(is (= 1 (.count (mc/find-maps db collection {:tags {$nin [ "dynamic" "object-oriented" ]}} ))))
|
||||||
|
(is (= 3 (.count (mc/find-maps db collection {:language {$nin [ "C#" ]}} ))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-with-conditional-operators-on-embedded-documents
|
||||||
|
(let [collection "people"]
|
||||||
|
(mc/insert-batch db collection [{:name "Bob" :comments [{:text "Nice!" :rating 1}
|
||||||
|
{:text "Love it" :rating 4}
|
||||||
|
{:text "What?":rating -5} ]}
|
||||||
|
{:name "Alice" :comments [{:text "Yeah" :rating 2}
|
||||||
|
{:text "Doh" :rating 1}
|
||||||
|
{:text "Agreed" :rating 3}]}])
|
||||||
|
(are [a b] (= a (.count (mc/find db collection b)))
|
||||||
|
1 {:comments {$elemMatch {:text "Nice!" :rating {$gte 1}}}}
|
||||||
|
2 {"comments.rating" 1}
|
||||||
|
1 {"comments.rating" {$gt 3}})))
|
||||||
|
|
||||||
|
(deftest find-with-regex-operator
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{:language "Ruby" :name "Mongoid" :users 1}
|
||||||
|
{:language "Clojure" :name "Langohr" :users 5}
|
||||||
|
{:language "Clojure" :name "Incanter" :users 15}
|
||||||
|
{:language "Scala" :name "Akka" :users 150}])
|
||||||
|
(are [query results] (is (= results (.count (mc/find db collection query))))
|
||||||
|
{:language {$regex "Clo.*"}} 2
|
||||||
|
{:language {$regex "clo.*" $options "i"}} 2
|
||||||
|
{:name {$regex "aK.*" $options "i"}} 1
|
||||||
|
{:language {$regex ".*by"}} 1
|
||||||
|
{:language {$regex ".*ala.*"}} 1)))
|
||||||
|
|
||||||
|
(deftest find-with-js-expression
|
||||||
|
(let [collection "people"]
|
||||||
|
(mc/insert-batch db collection [{:name "Bob" :placeOfBirth "New York" :address {:city "New York"}}
|
||||||
|
{:name "Alice" :placeOfBirth "New York" :address {:city "Los Angeles"}}])
|
||||||
|
(is (= 1 (.count (mc/find db collection {$where "this.placeOfBirth === this.address.city"})))))))
|
||||||
|
|
@ -1,282 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.querying
|
|
||||||
(:refer-clojure :exclude [select find sort])
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure ReadPreference]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.test.fixtures]
|
|
||||||
[monger conversion query operators joda-time]
|
|
||||||
[clj-time.core :only [date-time]]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-docs purge-things purge-locations)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.collection/* finders ("low-level API")
|
|
||||||
;;
|
|
||||||
|
|
||||||
;; by ObjectId
|
|
||||||
|
|
||||||
(deftest query-full-document-by-object-id
|
|
||||||
(let [coll "docs"
|
|
||||||
oid (ObjectId.)
|
|
||||||
doc { :_id oid :title "Introducing Monger" }]
|
|
||||||
(mgcol/insert coll doc)
|
|
||||||
(is (= doc (mgcol/find-map-by-id coll oid)))
|
|
||||||
(is (= doc (mgcol/find-one-as-map coll { :_id oid })))))
|
|
||||||
|
|
||||||
|
|
||||||
;; exact match over string field
|
|
||||||
|
|
||||||
(deftest query-full-document-using-exact-matching-over-string-field
|
|
||||||
(let [coll "docs"
|
|
||||||
doc { :title "monger" :language "Clojure" :_id (ObjectId.) }]
|
|
||||||
(mgcol/insert coll doc)
|
|
||||||
(is (= [doc] (mgcol/find-maps coll { :title "monger" })))
|
|
||||||
(is (= doc (from-db-object (first (mgcol/find coll { :title "monger" })) true)))))
|
|
||||||
|
|
||||||
|
|
||||||
;; exact match over string field with limit
|
|
||||||
|
|
||||||
(deftest query-full-document-using-exact-matching-over-string-with-field-with-limit
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :title "monger" :language "Clojure" :_id (ObjectId.) }
|
|
||||||
doc2 { :title "langohr" :language "Clojure" :_id (ObjectId.) }
|
|
||||||
doc3 { :title "netty" :language "Java" :_id (ObjectId.) }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])
|
|
||||||
result (with-collection coll
|
|
||||||
(find { :title "monger" })
|
|
||||||
(fields [:title, :language, :_id])
|
|
||||||
(skip 0)
|
|
||||||
(limit 1))]
|
|
||||||
(is (= 1 (count result)))
|
|
||||||
(is (= [doc1] result))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest query-full-document-using-exact-matching-over-string-field-with-limit-and-offset
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :title "lucene" :language "Java" :_id (ObjectId.) }
|
|
||||||
doc2 { :title "joda-time" :language "Java" :_id (ObjectId.) }
|
|
||||||
doc3 { :title "netty" :language "Java" :_id (ObjectId.) }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])
|
|
||||||
result (with-collection coll
|
|
||||||
(find { :language "Java" })
|
|
||||||
(skip 1)
|
|
||||||
(limit 2)
|
|
||||||
(sort { :title 1 }))]
|
|
||||||
(is (= 2 (count result)))
|
|
||||||
(is (= [doc1 doc3] result))))
|
|
||||||
|
|
||||||
|
|
||||||
;; < ($lt), <= ($lte), > ($gt), >= ($gte)
|
|
||||||
|
|
||||||
(deftest query-using-dsl-and-$lt-operator-with-integers
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
|
||||||
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
|
||||||
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
|
||||||
lt-result (with-collection "docs"
|
|
||||||
(find { :inception_year { $lt 2000 } })
|
|
||||||
(limit 2))]
|
|
||||||
(is (= [doc2] (vec lt-result)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest query-using-dsl-and-$lt-operator-with-dates
|
|
||||||
(let [coll "docs"
|
|
||||||
;; these rely on monger.joda-time being loaded. MK.
|
|
||||||
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) }
|
|
||||||
doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) }
|
|
||||||
doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
|
||||||
lt-result (with-collection "docs"
|
|
||||||
(find { :inception_year { $lt (date-time 2000 1 2) } })
|
|
||||||
(limit 2))]
|
|
||||||
(is (= (map :_id [doc2])
|
|
||||||
(map :_id (vec lt-result))))))
|
|
||||||
|
|
||||||
(deftest query-using-both-$lte-and-$gte-operators-with-dates
|
|
||||||
(let [coll "docs"
|
|
||||||
;; these rely on monger.joda-time being loaded. MK.
|
|
||||||
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) }
|
|
||||||
doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) }
|
|
||||||
doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])
|
|
||||||
lt-result (with-collection "docs"
|
|
||||||
(find { :inception_year { $gt (date-time 2000 1 2) $lte (date-time 2007 2 2) } })
|
|
||||||
(sort { :inception_year 1 }))]
|
|
||||||
(is (= (map :_id [doc3 doc1])
|
|
||||||
(map :_id (vec lt-result))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest query-using-$gt-$lt-$gte-$lte-operators-as-strings
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
|
||||||
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
|
||||||
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])]
|
|
||||||
(are [doc, result]
|
|
||||||
(= doc, result)
|
|
||||||
(doc2 (with-collection coll
|
|
||||||
(find { :inception_year { "$lt" 2000 } })))
|
|
||||||
(doc2 (with-collection coll
|
|
||||||
(find { :inception_year { "$lte" 1992 } })))
|
|
||||||
(doc1 (with-collection coll
|
|
||||||
(find { :inception_year { "$gt" 2002 } })
|
|
||||||
(limit 1)
|
|
||||||
(sort { :inception_year -1 })))
|
|
||||||
(doc1 (with-collection coll
|
|
||||||
(find { :inception_year { "$gte" 2006 } }))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest query-using-$gt-$lt-$gte-$lte-operators-using-dsl-composition
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
|
||||||
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
|
||||||
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
|
||||||
srt (-> {}
|
|
||||||
(limit 1)
|
|
||||||
(sort { :inception_year -1 }))
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])]
|
|
||||||
(is (= [doc1] (with-collection coll
|
|
||||||
(find { :inception_year { "$gt" 2002 } })
|
|
||||||
(merge srt))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; $all
|
|
||||||
|
|
||||||
(deftest query-with-using-$all
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
|
|
||||||
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
|
|
||||||
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
|
|
||||||
- (mgcol/insert-batch coll [doc1 doc2 doc3])
|
|
||||||
result1 (with-collection coll
|
|
||||||
(find { :tags { "$all" ["functional" "jvm" "homoiconic"] } }))
|
|
||||||
result2 (with-collection coll
|
|
||||||
(find { :tags { "$all" ["functional" "native" "homoiconic"] } }))
|
|
||||||
result3 (with-collection coll
|
|
||||||
(find { :tags { "$all" ["functional" "jvm" "dsls"] } })
|
|
||||||
(sort { :title 1 }))]
|
|
||||||
(is (= [doc1] result1))
|
|
||||||
(is (empty? result2))
|
|
||||||
(is (= 2 (count result3)))
|
|
||||||
(is (= doc1 (first result3)))))
|
|
||||||
|
|
||||||
|
|
||||||
;; $exists
|
|
||||||
|
|
||||||
(deftest query-with-find-one-as-map-using-$exists
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :_id (ObjectId.) :published-by "Jill The Blogger" :draft false :title "X announces another Y" }
|
|
||||||
doc2 { :_id (ObjectId.) :draft true :title "Z announces a Y competitor" }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
|
||||||
result1 (mgcol/find-one-as-map coll { :published-by { "$exists" true } })
|
|
||||||
result2 (mgcol/find-one-as-map coll { :published-by { "$exists" false } })]
|
|
||||||
(is (= doc1 result1))
|
|
||||||
(is (= doc2 result2))))
|
|
||||||
|
|
||||||
;; $mod
|
|
||||||
|
|
||||||
(deftest query-with-find-one-as-map-using-$mod
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :_id (ObjectId.) :counter 25 }
|
|
||||||
doc2 { :_id (ObjectId.) :counter 32 }
|
|
||||||
doc3 { :_id (ObjectId.) :counter 63 }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])
|
|
||||||
result1 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 5] } })
|
|
||||||
result2 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 2] } })
|
|
||||||
result3 (mgcol/find-one-as-map coll { :counter { "$mod" [11, 1] } })]
|
|
||||||
(is (= doc1 result1))
|
|
||||||
(is (= doc2 result2))
|
|
||||||
(is (empty? result3))))
|
|
||||||
|
|
||||||
|
|
||||||
;; $ne
|
|
||||||
|
|
||||||
(deftest query-with-find-one-as-map-using-$ne
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :_id (ObjectId.) :counter 25 }
|
|
||||||
doc2 { :_id (ObjectId.) :counter 32 }
|
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
|
||||||
result1 (mgcol/find-one-as-map coll { :counter { "$ne" 25 } })
|
|
||||||
result2 (mgcol/find-one-as-map coll { :counter { "$ne" 32 } })]
|
|
||||||
(is (= doc2 result1))
|
|
||||||
(is (= doc1 result2))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; monger.query DSL features
|
|
||||||
;;
|
|
||||||
|
|
||||||
;; pagination
|
|
||||||
(deftest query-using-pagination-dsl
|
|
||||||
(let [coll "docs"
|
|
||||||
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
|
|
||||||
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
|
|
||||||
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
|
|
||||||
doc4 { :_id (ObjectId.) :title "Ruby" :tags ["dynamic" "object-oriented" "dsls" "jvm"] }
|
|
||||||
doc5 { :_id (ObjectId.) :title "Groovy" :tags ["dynamic" "object-oriented" "dsls" "jvm"] }
|
|
||||||
doc6 { :_id (ObjectId.) :title "OCaml" :tags ["functional" "static" "dsls"] }
|
|
||||||
doc7 { :_id (ObjectId.) :title "Haskell" :tags ["functional" "static" "dsls" "concurrency features"] }
|
|
||||||
- (mgcol/insert-batch coll [doc1 doc2 doc3 doc4 doc5 doc6 doc7])
|
|
||||||
result1 (with-collection coll
|
|
||||||
(find {})
|
|
||||||
(paginate :page 1 :per-page 3)
|
|
||||||
(sort { :title 1 })
|
|
||||||
(read-preference ReadPreference/PRIMARY))
|
|
||||||
result2 (with-collection coll
|
|
||||||
(find {})
|
|
||||||
(paginate :page 2 :per-page 3)
|
|
||||||
(sort { :title 1 }))
|
|
||||||
result3 (with-collection coll
|
|
||||||
(find {})
|
|
||||||
(paginate :page 3 :per-page 3)
|
|
||||||
(sort { :title 1 }))]
|
|
||||||
(is (= [doc1 doc5 doc7] result1))
|
|
||||||
(is (= [doc2 doc6 doc4] result2))
|
|
||||||
(is (= [doc3] result3))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest combined-querying-dsl-example1
|
|
||||||
(let [coll "docs"
|
|
||||||
ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" }
|
|
||||||
de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" }
|
|
||||||
ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" }
|
|
||||||
ca-doc { :_id (ObjectId.) :name "California" :iso "CA" :population 37253956 :joined_in 1850 :capital "Sacramento" }
|
|
||||||
tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" }
|
|
||||||
top3 (partial-query (limit 3))
|
|
||||||
by-population-desc (partial-query (sort { :population -1 }))
|
|
||||||
_ (mgcol/insert-batch coll [ma-doc de-doc ny-doc ca-doc tx-doc])
|
|
||||||
result (with-collection coll
|
|
||||||
(find {})
|
|
||||||
(merge top3)
|
|
||||||
(merge by-population-desc))]
|
|
||||||
(is (= result [ca-doc tx-doc ny-doc]))))
|
|
||||||
|
|
||||||
(deftest combined-querying-dsl-example2
|
|
||||||
(let [coll "docs"
|
|
||||||
ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" }
|
|
||||||
de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" }
|
|
||||||
ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" }
|
|
||||||
ca-doc { :_id (ObjectId.) :name "California" :iso "CA" :population 37253956 :joined_in 1850 :capital "Sacramento" }
|
|
||||||
tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" }
|
|
||||||
top3 (partial-query (limit 3))
|
|
||||||
by-population-desc (partial-query (sort { :population -1 }))
|
|
||||||
_ (mgcol/insert-batch coll [ma-doc de-doc ny-doc ca-doc tx-doc])
|
|
||||||
result (with-collection coll
|
|
||||||
(find {})
|
|
||||||
(merge top3)
|
|
||||||
(merge by-population-desc)
|
|
||||||
(keywordize-fields false))]
|
|
||||||
;; documents have fields as strings,
|
|
||||||
;; not keywords
|
|
||||||
(is (= (map #(% "name") result)
|
|
||||||
(map #(% :name) [ca-doc tx-doc ny-doc])))))
|
|
||||||
325
test/monger/test/querying_test.clj
Normal file
325
test/monger/test/querying_test.clj
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
(ns monger.test.querying-test
|
||||||
|
(:refer-clojure :exclude [select find sort])
|
||||||
|
(:import [com.mongodb WriteResult WriteConcern DBObject ReadPreference]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
monger.joda-time
|
||||||
|
[monger.result :as mgres]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.conversion :refer :all]
|
||||||
|
[monger.query :refer :all]
|
||||||
|
[monger.operators :refer :all]
|
||||||
|
[clj-time.core :refer [date-time]]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "locations")
|
||||||
|
(mc/remove db "querying_docs")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "locations")
|
||||||
|
(mc/remove db "querying_docs"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.collection/* finders ("low-level API")
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; by ObjectId
|
||||||
|
|
||||||
|
(deftest query-full-document-by-object-id
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
oid (ObjectId.)
|
||||||
|
doc { :_id oid :title "Introducing Monger" }]
|
||||||
|
(mc/insert db coll doc)
|
||||||
|
(is (= doc (mc/find-map-by-id db coll oid)))
|
||||||
|
(is (= doc (mc/find-one-as-map db coll { :_id oid })))))
|
||||||
|
|
||||||
|
|
||||||
|
;; exact match over string field
|
||||||
|
|
||||||
|
(deftest query-full-document-using-exact-matching-over-string-field
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc { :title "monger" :language "Clojure" :_id (ObjectId.) }]
|
||||||
|
(mc/insert db coll doc)
|
||||||
|
(is (= [doc] (mc/find-maps db coll { :title "monger" })))
|
||||||
|
(is (= doc (from-db-object (first (mc/find db coll { :title "monger" })) true)))))
|
||||||
|
|
||||||
|
|
||||||
|
;; exact match over string field with limit
|
||||||
|
|
||||||
|
(deftest query-full-document-using-exact-matching-over-string-with-field-with-limit
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :title "monger" :language "Clojure" :_id (ObjectId.) }
|
||||||
|
doc2 { :title "langohr" :language "Clojure" :_id (ObjectId.) }
|
||||||
|
doc3 { :title "netty" :language "Java" :_id (ObjectId.) }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3])
|
||||||
|
result (with-collection db coll
|
||||||
|
(find { :title "monger" })
|
||||||
|
(fields [:title, :language, :_id])
|
||||||
|
(skip 0)
|
||||||
|
(limit 1))]
|
||||||
|
(is (= 1 (count result)))
|
||||||
|
(is (= [doc1] result))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest query-full-document-using-exact-matching-over-string-field-with-limit-and-offset
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :title "lucene" :language "Java" :_id (ObjectId.) }
|
||||||
|
doc2 { :title "joda-time" :language "Java" :_id (ObjectId.) }
|
||||||
|
doc3 { :title "netty" :language "Java" :_id (ObjectId.) }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3])
|
||||||
|
result (with-collection db coll
|
||||||
|
(find { :language "Java" })
|
||||||
|
(skip 1)
|
||||||
|
(limit 2)
|
||||||
|
(sort { :title 1 }))]
|
||||||
|
(is (= 2 (count result)))
|
||||||
|
(is (= [doc1 doc3] result))))
|
||||||
|
|
||||||
|
(deftest query-with-sorting-on-multiple-fields
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :a 1 :b 2 :c 3 :text "Whatever" :_id (ObjectId.) }
|
||||||
|
doc2 { :a 1 :b 1 :c 4 :text "Blah " :_id (ObjectId.) }
|
||||||
|
doc3 { :a 10 :b 3 :c 1 :text "Abc" :_id (ObjectId.) }
|
||||||
|
doc4 { :a 10 :b 3 :c 3 :text "Abc" :_id (ObjectId.) }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3 doc4])
|
||||||
|
result1 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(limit 2)
|
||||||
|
(fields [:a :b :c :text])
|
||||||
|
(sort (sorted-map :a 1 :b 1 :text -1)))
|
||||||
|
result2 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(limit 2)
|
||||||
|
(fields [:a :b :c :text])
|
||||||
|
(sort (array-map :c 1 :text -1)))
|
||||||
|
result3 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(limit 2)
|
||||||
|
(fields [:a :b :c :text])
|
||||||
|
(sort (array-map :c -1 :text 1)))]
|
||||||
|
(is (= [doc2 doc1] result1))
|
||||||
|
(is (= [doc3 doc1] result2))
|
||||||
|
(is (= [doc2 doc4] result3))))
|
||||||
|
|
||||||
|
|
||||||
|
;; < ($lt), <= ($lte), > ($gt), >= ($gte)
|
||||||
|
|
||||||
|
(deftest query-using-dsl-and-$lt-operator-with-integers
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
||||||
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
||||||
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2])
|
||||||
|
lt-result (with-collection db coll
|
||||||
|
(find { :inception_year { $lt 2000 } })
|
||||||
|
(limit 2))]
|
||||||
|
(is (= [doc2] (vec lt-result)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest query-using-dsl-and-$lt-operator-with-dates
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
;; these rely on monger.joda-time being loaded. MK.
|
||||||
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) }
|
||||||
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) }
|
||||||
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2])
|
||||||
|
lt-result (with-collection db coll
|
||||||
|
(find { :inception_year { $lt (date-time 2000 1 2) } })
|
||||||
|
(limit 2))]
|
||||||
|
(is (= (map :_id [doc2])
|
||||||
|
(map :_id (vec lt-result))))))
|
||||||
|
|
||||||
|
(deftest query-using-both-$lte-and-$gte-operators-with-dates
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
;; these rely on monger.joda-time being loaded. MK.
|
||||||
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) }
|
||||||
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) }
|
||||||
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3])
|
||||||
|
lt-result (with-collection db coll
|
||||||
|
(find { :inception_year { $gt (date-time 2000 1 2) $lte (date-time 2007 2 2) } })
|
||||||
|
(sort { :inception_year 1 }))]
|
||||||
|
(is (= (map :_id [doc3 doc1])
|
||||||
|
(map :_id (vec lt-result))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest query-using-$gt-$lt-$gte-$lte-operators-as-strings
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
||||||
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
||||||
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3])]
|
||||||
|
(are [doc, result]
|
||||||
|
(= doc, result)
|
||||||
|
(doc2 (with-collection db coll
|
||||||
|
(find { :inception_year { "$lt" 2000 } })))
|
||||||
|
(doc2 (with-collection db coll
|
||||||
|
(find { :inception_year { "$lte" 1992 } })))
|
||||||
|
(doc1 (with-collection db coll
|
||||||
|
(find { :inception_year { "$gt" 2002 } })
|
||||||
|
(limit 1)
|
||||||
|
(sort { :inception_year -1 })))
|
||||||
|
(doc1 (with-collection db coll
|
||||||
|
(find { :inception_year { "$gte" 2006 } }))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest query-using-$gt-$lt-$gte-$lte-operators-using-dsl-composition
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
||||||
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
||||||
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
||||||
|
srt (-> {}
|
||||||
|
(limit 1)
|
||||||
|
(sort { :inception_year -1 }))
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3])]
|
||||||
|
(is (= [doc1] (with-collection db coll
|
||||||
|
(find { :inception_year { "$gt" 2002 } })
|
||||||
|
(merge srt))))))
|
||||||
|
|
||||||
|
|
||||||
|
;; $all
|
||||||
|
|
||||||
|
(deftest query-with-using-$all
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
|
||||||
|
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
|
||||||
|
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
|
||||||
|
- (mc/insert-batch db coll [doc1 doc2 doc3])
|
||||||
|
result1 (with-collection db coll
|
||||||
|
(find { :tags { "$all" ["functional" "jvm" "homoiconic"] } }))
|
||||||
|
result2 (with-collection db coll
|
||||||
|
(find { :tags { "$all" ["functional" "native" "homoiconic"] } }))
|
||||||
|
result3 (with-collection db coll
|
||||||
|
(find { :tags { "$all" ["functional" "jvm" "dsls"] } })
|
||||||
|
(sort { :title 1 }))]
|
||||||
|
(is (= [doc1] result1))
|
||||||
|
(is (empty? result2))
|
||||||
|
(is (= 2 (count result3)))
|
||||||
|
(is (= doc1 (first result3)))))
|
||||||
|
|
||||||
|
|
||||||
|
;; $exists
|
||||||
|
|
||||||
|
(deftest query-with-find-one-as-map-using-$exists
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :_id (ObjectId.) :published-by "Jill The Blogger" :draft false :title "X announces another Y" }
|
||||||
|
doc2 { :_id (ObjectId.) :draft true :title "Z announces a Y competitor" }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2])
|
||||||
|
result1 (mc/find-one-as-map db coll { :published-by { "$exists" true } })
|
||||||
|
result2 (mc/find-one-as-map db coll { :published-by { "$exists" false } })]
|
||||||
|
(is (= doc1 result1))
|
||||||
|
(is (= doc2 result2))))
|
||||||
|
|
||||||
|
;; $mod
|
||||||
|
|
||||||
|
(deftest query-with-find-one-as-map-using-$mod
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :_id (ObjectId.) :counter 25 }
|
||||||
|
doc2 { :_id (ObjectId.) :counter 32 }
|
||||||
|
doc3 { :_id (ObjectId.) :counter 63 }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2 doc3])
|
||||||
|
result1 (mc/find-one-as-map db coll { :counter { "$mod" [10, 5] } })
|
||||||
|
result2 (mc/find-one-as-map db coll { :counter { "$mod" [10, 2] } })
|
||||||
|
result3 (mc/find-one-as-map db coll { :counter { "$mod" [11, 1] } })]
|
||||||
|
(is (= doc1 result1))
|
||||||
|
(is (= doc2 result2))
|
||||||
|
(is (empty? result3))))
|
||||||
|
|
||||||
|
|
||||||
|
;; $ne
|
||||||
|
|
||||||
|
(deftest query-with-find-one-as-map-using-$ne
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :_id (ObjectId.) :counter 25 }
|
||||||
|
doc2 { :_id (ObjectId.) :counter 32 }
|
||||||
|
_ (mc/insert-batch db coll [doc1 doc2])
|
||||||
|
result1 (mc/find-one-as-map db coll { :counter { "$ne" 25 } })
|
||||||
|
result2 (mc/find-one-as-map db coll { :counter { "$ne" 32 } })]
|
||||||
|
(is (= doc2 result1))
|
||||||
|
(is (= doc1 result2))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; monger.query DSL features
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; pagination
|
||||||
|
(deftest query-using-pagination-dsl
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
|
||||||
|
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
|
||||||
|
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
|
||||||
|
doc4 { :_id (ObjectId.) :title "Ruby" :tags ["dynamic" "object-oriented" "dsls" "jvm"] }
|
||||||
|
doc5 { :_id (ObjectId.) :title "Groovy" :tags ["dynamic" "object-oriented" "dsls" "jvm"] }
|
||||||
|
doc6 { :_id (ObjectId.) :title "OCaml" :tags ["functional" "static" "dsls"] }
|
||||||
|
doc7 { :_id (ObjectId.) :title "Haskell" :tags ["functional" "static" "dsls" "concurrency features"] }
|
||||||
|
- (mc/insert-batch db coll [doc1 doc2 doc3 doc4 doc5 doc6 doc7])
|
||||||
|
result1 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(paginate :page 1 :per-page 3)
|
||||||
|
(sort { :title 1 })
|
||||||
|
(read-preference (ReadPreference/primary))
|
||||||
|
(options com.mongodb.Bytes/QUERYOPTION_NOTIMEOUT))
|
||||||
|
result2 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(paginate :page 2 :per-page 3)
|
||||||
|
(sort { :title 1 }))
|
||||||
|
result3 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(paginate :page 3 :per-page 3)
|
||||||
|
(sort { :title 1 }))
|
||||||
|
result4 (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(paginate :page 10 :per-page 3)
|
||||||
|
(sort { :title 1 }))]
|
||||||
|
(is (= [doc1 doc5 doc7] result1))
|
||||||
|
(is (= [doc2 doc6 doc4] result2))
|
||||||
|
(is (= [doc3] result3))
|
||||||
|
(is (empty? result4))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest combined-querying-dsl-example1
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" }
|
||||||
|
de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" }
|
||||||
|
ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" }
|
||||||
|
ca-doc { :_id (ObjectId.) :name "California" :iso "CA" :population 37253956 :joined_in 1850 :capital "Sacramento" }
|
||||||
|
tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" }
|
||||||
|
top3 (partial-query (limit 3))
|
||||||
|
by-population-desc (partial-query (sort { :population -1 }))
|
||||||
|
_ (mc/insert-batch db coll [ma-doc de-doc ny-doc ca-doc tx-doc])
|
||||||
|
result (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(merge top3)
|
||||||
|
(merge by-population-desc))]
|
||||||
|
(is (= result [ca-doc tx-doc ny-doc]))))
|
||||||
|
|
||||||
|
(deftest combined-querying-dsl-example2
|
||||||
|
(let [coll "querying_docs"
|
||||||
|
ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" }
|
||||||
|
de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" }
|
||||||
|
ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" }
|
||||||
|
ca-doc { :_id (ObjectId.) :name "California" :iso "CA" :population 37253956 :joined_in 1850 :capital "Sacramento" }
|
||||||
|
tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" }
|
||||||
|
top3 (partial-query (limit 3))
|
||||||
|
by-population-desc (partial-query (sort { :population -1 }))
|
||||||
|
_ (mc/insert-batch db coll [ma-doc de-doc ny-doc ca-doc tx-doc])
|
||||||
|
result (with-collection db coll
|
||||||
|
(find {})
|
||||||
|
(merge top3)
|
||||||
|
(merge by-population-desc)
|
||||||
|
(keywordize-fields false))]
|
||||||
|
;; documents have fields as strings,
|
||||||
|
;; not keywords
|
||||||
|
(is (= (map #(% "name") result)
|
||||||
|
(map #(% :name) [ca-doc tx-doc ny-doc]))))))
|
||||||
55
test/monger/test/ragtime_test.clj
Normal file
55
test/monger/test/ragtime_test.clj
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
(ns monger.test.ragtime-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
monger.ragtime
|
||||||
|
[ragtime.protocols :refer :all]
|
||||||
|
[clojure.test :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "meta.migrations")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "meta.migrations"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
(when-not (get (System/getenv) "CI")
|
||||||
|
(deftest test-add-migration-id
|
||||||
|
(let [coll "meta.migrations"
|
||||||
|
key "1"]
|
||||||
|
(mc/remove db coll {})
|
||||||
|
(is (not (mc/any? db coll {:_id key})))
|
||||||
|
(is (not (some #{key} (applied-migration-ids db))))
|
||||||
|
(add-migration-id db key)
|
||||||
|
(is (mc/any? db coll {:_id key}))
|
||||||
|
(is (some #{key} (applied-migration-ids db)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-remove-migration-id
|
||||||
|
(let [coll "meta.migrations"
|
||||||
|
key "1"]
|
||||||
|
(mc/remove db coll {})
|
||||||
|
(add-migration-id db key)
|
||||||
|
(is (mc/any? db coll {:_id key}))
|
||||||
|
(is (some #{key} (applied-migration-ids db)))
|
||||||
|
(remove-migration-id db key)
|
||||||
|
(is (not (some #{key} (applied-migration-ids db))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-migrations-ordering
|
||||||
|
(let [coll "meta.migrations"
|
||||||
|
all-keys [ "9" "4" "7" "1" "5" "3" "6" "2" "8"]]
|
||||||
|
(mc/remove db coll {})
|
||||||
|
|
||||||
|
(doseq [key all-keys]
|
||||||
|
(add-migration-id db key))
|
||||||
|
|
||||||
|
(doseq [key all-keys]
|
||||||
|
(is (mc/any? db coll {:_id key}))
|
||||||
|
(is (some #{key} (applied-migration-ids db))))
|
||||||
|
|
||||||
|
(testing "Applied migrations must come out in creation order"
|
||||||
|
(is (= all-keys (applied-migration-ids db))))))))
|
||||||
|
|
@ -1,266 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.regular-finders
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.conversion :as mgcnv]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.operators]
|
|
||||||
[monger.test.fixtures]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; find-one
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-one-full-document-when-collection-is-empty
|
|
||||||
(let [collection "docs"]
|
|
||||||
(is (nil? (mgcol/find-one collection {})))))
|
|
||||||
|
|
||||||
(deftest find-one-full-document-as-map-when-collection-is-empty
|
|
||||||
(let [collection "docs"]
|
|
||||||
(is (nil? (mgcol/find-one-as-map collection {})))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-one-full-document-when-collection-has-matches
|
|
||||||
(let [collection "docs"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
_ (mgcol/insert collection doc)
|
|
||||||
found-one (mgcol/find-one collection { :language "Clojure" })]
|
|
||||||
(is (= (:_id doc) (monger.util/get-id found-one)))
|
|
||||||
(is (= (mgcnv/from-db-object found-one true) doc))
|
|
||||||
(is (= (mgcnv/to-db-object doc) found-one))))
|
|
||||||
|
|
||||||
(deftest find-one-full-document-as-map-when-collection-has-matches
|
|
||||||
(let [collection "docs"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= doc (mgcol/find-one-as-map collection { :language "Clojure" })))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-one-partial-document-when-collection-has-matches
|
|
||||||
(let [collection "docs"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
fields [:language]
|
|
||||||
_ (mgcol/insert collection doc)
|
|
||||||
loaded (mgcol/find-one collection { :language "Clojure" } fields)]
|
|
||||||
(is (nil? (.get ^DBObject loaded "data-store")))
|
|
||||||
(is (= doc-id (monger.util/get-id loaded)))
|
|
||||||
(is (= "Clojure" (.get ^DBObject loaded "language")))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-one-partial-document-as-map-when-collection-has-matches
|
|
||||||
(let [collection "docs"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
fields [:data-store]]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= { :data-store "MongoDB", :_id doc-id } (mgcol/find-one-as-map collection { :language "Clojure" } fields)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize
|
|
||||||
(let [collection "docs"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
fields [:data-store]
|
|
||||||
_id (mgcol/insert collection doc)
|
|
||||||
loaded (mgcol/find-one-as-map collection { :language "Clojure" } fields true)
|
|
||||||
]
|
|
||||||
(is (= { :data-store "MongoDB", :_id doc-id } loaded ))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize-false
|
|
||||||
(let [collection "docs"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
fields [:data-store]
|
|
||||||
_id (mgcol/insert collection doc)
|
|
||||||
loaded (mgcol/find-one-as-map collection { :language "Clojure" } fields false)
|
|
||||||
]
|
|
||||||
(is (= { "_id" doc-id, "data-store" "MongoDB" } loaded ))))
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; find-by-id
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-full-document-by-string-id-when-that-document-does-not-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)]
|
|
||||||
(is (nil? (mgcol/find-by-id collection doc-id)))))
|
|
||||||
|
|
||||||
(deftest find-full-document-by-string-id-when-id-is-nil
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id nil]
|
|
||||||
(is (thrown? IllegalArgumentException (mgcol/find-by-id collection doc-id)))))
|
|
||||||
|
|
||||||
(deftest find-full-document-by-object-id-when-that-document-does-not-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (ObjectId.)]
|
|
||||||
(is (nil? (mgcol/find-by-id collection doc-id)))))
|
|
||||||
|
|
||||||
(deftest find-full-document-by-id-as-map-when-that-document-does-not-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)]
|
|
||||||
(is (nil? (mgcol/find-map-by-id collection doc-id)))))
|
|
||||||
|
|
||||||
(deftest find-full-document-by-id-as-map-when-id-is-nil
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id nil]
|
|
||||||
(is (thrown? IllegalArgumentException
|
|
||||||
(mgcol/find-map-by-id collection doc-id)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-full-document-by-string-id-when-document-does-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= (doc (mgcol/find-by-id collection doc-id))))))
|
|
||||||
|
|
||||||
(deftest find-full-document-by-object-id-when-document-does-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (ObjectId.)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= (doc (mgcol/find-by-id collection doc-id))))))
|
|
||||||
|
|
||||||
(deftest find-full-document-map-by-string-id-when-document-does-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= (doc (mgcol/find-map-by-id collection doc-id))))))
|
|
||||||
|
|
||||||
(deftest find-full-document-map-by-object-id-when-document-does-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (ObjectId.)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= (doc (mgcol/find-map-by-id collection doc-id))))))
|
|
||||||
|
|
||||||
(deftest find-partial-document-by-id-when-document-does-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= ({ :language "Clojure" } (mgcol/find-by-id collection doc-id [ :language ]))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-partial-document-as-map-by-id-when-document-does-exist
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
fields [:data-store]
|
|
||||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
_ (mgcol/insert collection doc)
|
|
||||||
loaded (mgcol/find-map-by-id collection doc-id [ :language ])]
|
|
||||||
(is (= { :language "Clojure", :_id doc-id } loaded ))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; find
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest find-full-document-when-collection-is-empty
|
|
||||||
(let [collection "docs"
|
|
||||||
cursor (mgcol/find collection)]
|
|
||||||
(is (empty? (iterator-seq cursor)))))
|
|
||||||
|
|
||||||
(deftest find-document-seq-when-collection-is-empty
|
|
||||||
(let [collection "docs"]
|
|
||||||
(is (empty? (mgcol/find-seq collection)))))
|
|
||||||
|
|
||||||
(deftest find-multiple-documents-when-collection-is-empty
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(is (empty? (mgcol/find collection { :language "Scala" })))))
|
|
||||||
|
|
||||||
(deftest find-multiple-maps-when-collection-is-empty
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(is (empty? (mgcol/find-maps collection { :language "Scala" })))))
|
|
||||||
|
|
||||||
(deftest find-multiple-documents-by-regex
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Java", :name "nhibernate" }
|
|
||||||
{ :language "JavaScript", :name "sprout-core" }])
|
|
||||||
(is (= 2 (monger.core/count (mgcol/find collection { :language #"Java*" }))))))
|
|
||||||
|
|
||||||
(deftest find-multiple-documents
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(is (= 1 (monger.core/count (mgcol/find collection { :language "Scala" }))))
|
|
||||||
(is (= 3 (.count (mgcol/find collection { :language "Clojure" }))))
|
|
||||||
(is (empty? (mgcol/find collection { :language "Java" })))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-document-specify-fields
|
|
||||||
(let [collection "libraries"
|
|
||||||
_ (mgcol/insert collection { :language "Clojure", :name "monger" })
|
|
||||||
result (mgcol/find collection { :language "Clojure"} [:language])]
|
|
||||||
(is (= (seq [:_id :language]) (keys (mgcnv/from-db-object (.next result) true))))))
|
|
||||||
|
|
||||||
(deftest find-and-iterate-over-multiple-documents-the-hard-way
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(doseq [doc (take 3 (map (fn [dbo]
|
|
||||||
(mgcnv/from-db-object dbo true))
|
|
||||||
(mgcol/find-seq collection { :language "Clojure" })))]
|
|
||||||
(is (= "Clojure" (:language doc))))))
|
|
||||||
|
|
||||||
(deftest find-and-iterate-over-multiple-documents
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(doseq [doc (take 3 (mgcol/find-maps collection { :language "Clojure" }))]
|
|
||||||
(is (= "Clojure" (:language doc))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-multiple-maps
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(is (= 1 (clojure.core/count (mgcol/find-maps collection { :language "Scala" }))))
|
|
||||||
(is (= 3 (.count (mgcol/find-maps collection { :language "Clojure" }))))
|
|
||||||
(is (empty? (mgcol/find-maps collection { :language "Java" })))
|
|
||||||
(is (empty? (mgcol/find-maps monger.core/*mongodb-database* collection { :language "Java" } [:language :name])))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest find-multiple-partial-documents
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
|
|
||||||
{ :language "Clojure", :name "langohr" }
|
|
||||||
{ :language "Clojure", :name "incanter" }
|
|
||||||
{ :language "Scala", :name "akka" }])
|
|
||||||
(let [scala-libs (mgcol/find collection { :language "Scala" } [:name])
|
|
||||||
clojure-libs (mgcol/find collection { :language "Clojure"} [:language])]
|
|
||||||
(is (= 1 (.count scala-libs)))
|
|
||||||
(is (= 3 (.count clojure-libs)))
|
|
||||||
(doseq [i clojure-libs]
|
|
||||||
(let [doc (mgcnv/from-db-object i true)]
|
|
||||||
(is (= (:language doc) "Clojure"))))
|
|
||||||
(is (empty? (mgcol/find collection { :language "Erlang" } [:name]))))))
|
|
||||||
293
test/monger/test/regular_finders_test.clj
Normal file
293
test/monger/test/regular_finders_test.clj
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
(ns monger.test.regular-finders-test
|
||||||
|
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.util :as mu]
|
||||||
|
[monger.result :as mgres]
|
||||||
|
[monger.conversion :as mgcnv]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]
|
||||||
|
[monger.conversion :refer [to-db-object]]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(use-fixtures :each (fn [f]
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "regular_finders_docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "libraries")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "regular_finders_docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "libraries")))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; find-one
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-one-full-document-when-collection-is-empty
|
||||||
|
(let [collection "regular_finders_docs"]
|
||||||
|
(is (nil? (mc/find-one db collection {})))))
|
||||||
|
|
||||||
|
(deftest find-one-full-document-as-map-when-collection-is-empty
|
||||||
|
(let [collection "regular_finders_docs"]
|
||||||
|
(mc/remove db collection)
|
||||||
|
(is (nil? (mc/find-one-as-map db collection {})))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-one-full-document-when-collection-has-matches
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
_ (mc/insert db collection doc)
|
||||||
|
found-one (mc/find-one db collection { :language "Clojure" })]
|
||||||
|
(is found-one)
|
||||||
|
(is (= (:_id doc) (mu/get-id found-one)))
|
||||||
|
(is (= (mgcnv/from-db-object found-one true) doc))
|
||||||
|
(is (= (mgcnv/to-db-object doc) found-one))))
|
||||||
|
|
||||||
|
(deftest find-one-full-document-as-map-when-collection-has-matches
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= doc (mc/find-one-as-map db collection { :language "Clojure" })))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-one-partial-document-when-collection-has-matches
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
_ (mc/insert db collection doc)
|
||||||
|
loaded (mc/find-one db collection { :language "Clojure" } [:language])]
|
||||||
|
(is (nil? (.get ^DBObject loaded "data-store")))
|
||||||
|
(is (= doc-id (mu/get-id loaded)))
|
||||||
|
(is (= "Clojure" (.get ^DBObject loaded "language")))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-one-partial-document-using-field-negation-when-collection-has-matches
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
_ (mc/insert db collection doc)
|
||||||
|
^DBObject loaded (mc/find-one db collection { :language "Clojure" } {:data-store 0 :_id 0})]
|
||||||
|
(is (nil? (.get loaded "data-store")))
|
||||||
|
(is (nil? (.get loaded "_id")))
|
||||||
|
(is (nil? (mu/get-id loaded)))
|
||||||
|
(is (= "Clojure" (.get loaded "language")))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-one-partial-document-as-map-when-collection-has-matches
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= { :data-store "MongoDB", :_id doc-id }
|
||||||
|
(mc/find-one-as-map db collection { :language "Clojure" } [:data-store])))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
fields [:data-store]
|
||||||
|
_id (mc/insert db collection doc)
|
||||||
|
loaded (mc/find-one-as-map db collection { :language "Clojure" } fields true)
|
||||||
|
]
|
||||||
|
(is (= { :data-store "MongoDB", :_id doc-id } loaded ))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize-false
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
fields [:data-store]
|
||||||
|
_id (mc/insert db collection doc)
|
||||||
|
loaded (mc/find-one-as-map db collection { :language "Clojure" } fields false)]
|
||||||
|
(is (= { "_id" doc-id, "data-store" "MongoDB" } loaded ))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; find-by-id
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-full-document-by-string-id-when-that-document-does-not-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)]
|
||||||
|
(is (nil? (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-by-string-id-when-id-is-nil
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id nil]
|
||||||
|
(is (thrown? IllegalArgumentException (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-by-object-id-when-that-document-does-not-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (ObjectId.)]
|
||||||
|
(is (nil? (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-by-id-as-map-when-that-document-does-not-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)]
|
||||||
|
(is (nil? (mc/find-map-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-by-id-as-map-when-id-is-nil
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id nil]
|
||||||
|
(is (thrown? IllegalArgumentException
|
||||||
|
(mc/find-map-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-full-document-by-string-id-when-document-does-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-by-object-id-when-document-does-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (ObjectId.)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-map-by-string-id-when-document-does-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= doc (mc/find-map-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-full-document-map-by-object-id-when-document-does-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (ObjectId.)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= doc (mc/find-map-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest find-partial-document-by-id-when-document-does-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= (to-db-object { :_id doc-id :language "Clojure" })
|
||||||
|
(mc/find-by-id db collection doc-id [ :language ])))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-partial-document-as-map-by-id-when-document-does-exist
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
fields [:data-store]
|
||||||
|
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
_ (mc/insert db collection doc)
|
||||||
|
loaded (mc/find-map-by-id db collection doc-id [ :language ])]
|
||||||
|
(is (= { :language "Clojure", :_id doc-id } loaded ))))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; find
|
||||||
|
;;
|
||||||
|
|
||||||
|
(deftest find-full-document-when-collection-is-empty
|
||||||
|
(let [collection "regular_finders_docs"
|
||||||
|
cursor (mc/find db collection)]
|
||||||
|
(is (empty? (iterator-seq cursor)))))
|
||||||
|
|
||||||
|
(deftest find-document-seq-when-collection-is-empty
|
||||||
|
(let [collection "regular_finders_docs"]
|
||||||
|
(is (empty? (mc/find-seq db collection)))))
|
||||||
|
|
||||||
|
(deftest find-multiple-documents-when-collection-is-empty
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(is (empty? (mc/find db collection { :language "Scala" })))))
|
||||||
|
|
||||||
|
(deftest find-multiple-maps-when-collection-is-empty
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(is (empty? (mc/find-maps db collection { :language "Scala" })))))
|
||||||
|
|
||||||
|
(deftest find-multiple-documents-by-regex
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Java", :name "nhibernate" }
|
||||||
|
{ :language "JavaScript", :name "sprout-core" }])
|
||||||
|
(is (= 2 (monger.core/count (mc/find db collection { :language #"Java*" }))))))
|
||||||
|
|
||||||
|
(deftest find-multiple-documents
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }
|
||||||
|
{ :language "Clojure", :name "incanter" }
|
||||||
|
{ :language "Scala", :name "akka" }])
|
||||||
|
(is (= 1 (monger.core/count (mc/find db collection { :language "Scala" }))))
|
||||||
|
(is (= 3 (.count (mc/find db collection { :language "Clojure" }))))
|
||||||
|
(is (empty? (mc/find db collection { :language "Java" })))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-document-specify-fields
|
||||||
|
(let [collection "libraries"
|
||||||
|
_ (mc/insert db collection { :language "Clojure", :name "monger" })
|
||||||
|
result (mc/find db collection { :language "Clojure"} [:language])]
|
||||||
|
(is (= (set [:_id :language]) (-> (mgcnv/from-db-object (.next result) true) keys set)))))
|
||||||
|
|
||||||
|
(deftest find-and-iterate-over-multiple-documents-the-hard-way
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }
|
||||||
|
{ :language "Clojure", :name "incanter" }
|
||||||
|
{ :language "Scala", :name "akka" }])
|
||||||
|
(doseq [doc (take 3 (map (fn [dbo]
|
||||||
|
(mgcnv/from-db-object dbo true))
|
||||||
|
(mc/find-seq db collection { :language "Clojure" })))]
|
||||||
|
(is (= "Clojure" (:language doc))))))
|
||||||
|
|
||||||
|
(deftest find-and-iterate-over-multiple-documents
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }
|
||||||
|
{ :language "Clojure", :name "incanter" }
|
||||||
|
{ :language "Scala", :name "akka" }])
|
||||||
|
(doseq [doc (take 3 (mc/find-maps db collection { :language "Clojure" }))]
|
||||||
|
(is (= "Clojure" (:language doc))))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-multiple-maps
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }
|
||||||
|
{ :language "Clojure", :name "incanter" }
|
||||||
|
{ :language "Scala", :name "akka" }])
|
||||||
|
(is (= 1 (clojure.core/count (mc/find-maps db collection { :language "Scala" }))))
|
||||||
|
(is (= 3 (.count (mc/find-maps db collection { :language "Clojure" }))))
|
||||||
|
(is (empty? (mc/find-maps db collection { :language "Java" })))
|
||||||
|
(is (empty? (mc/find-maps db collection { :language "Java" } [:language :name])))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(deftest find-multiple-partial-documents
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }
|
||||||
|
{ :language "Clojure", :name "incanter" }
|
||||||
|
{ :language "Scala", :name "akka" }])
|
||||||
|
(let [scala-libs (mc/find db collection { :language "Scala" } [:name])
|
||||||
|
clojure-libs (mc/find db collection { :language "Clojure"} [:language])]
|
||||||
|
(is (= 1 (.count scala-libs)))
|
||||||
|
(is (= 3 (.count clojure-libs)))
|
||||||
|
(doseq [i clojure-libs]
|
||||||
|
(let [doc (mgcnv/from-db-object i true)]
|
||||||
|
(is (= (:language doc) "Clojure"))))
|
||||||
|
(is (empty? (mc/find db collection { :language "Erlang" } [:name]))))))
|
||||||
|
|
||||||
|
(deftest find-maps-with-keywordize-false
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }])
|
||||||
|
(let [results (mc/find-maps db collection {:name "langohr"} [] false)]
|
||||||
|
(is (= 1 (.count results)))
|
||||||
|
(is (= (get (first results) "language") "Clojure"))))))
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
(ns monger.test.result
|
|
||||||
(:import (com.mongodb BasicDBObject WriteResult WriteConcern) (java.util Date))
|
|
||||||
(:require [monger core collection conversion]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; MongoCommandResult
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-ok?
|
|
||||||
(let [result-that-is-not-ok-1 (doto (BasicDBObject.) (.put "ok" 0))
|
|
||||||
result-that-is-not-ok-2 (doto (BasicDBObject.) (.put "ok" "false"))
|
|
||||||
result-that-is-ok-1 (doto (BasicDBObject.) (.put "ok" 1))
|
|
||||||
result-that-is-ok-2 (doto (BasicDBObject.) (.put "ok" "true"))
|
|
||||||
result-that-is-ok-3 (doto (BasicDBObject.) (.put "ok" 1.0))]
|
|
||||||
(is (not (monger.result/ok? result-that-is-not-ok-1)))
|
|
||||||
(is (not (monger.result/ok? result-that-is-not-ok-2)))
|
|
||||||
(is (monger.result/ok? result-that-is-ok-1))
|
|
||||||
(is (monger.result/ok? result-that-is-ok-2))
|
|
||||||
(is (monger.result/ok? result-that-is-ok-3))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-has-error?
|
|
||||||
(let [result-that-has-no-error1 (doto (BasicDBObject.) (.put "ok" 0))
|
|
||||||
result-that-has-no-error2 (doto (BasicDBObject.) (.put "err" ""))
|
|
||||||
result-that-has-error1 (doto (BasicDBObject.) (.put "err" (BasicDBObject.)))]
|
|
||||||
(is (not (monger.result/has-error? result-that-has-no-error1)))
|
|
||||||
(is (not (monger.result/has-error? result-that-has-no-error2)))
|
|
||||||
(is (monger.result/has-error? result-that-has-error1))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-updated-existing?-with-db-object
|
|
||||||
(let [input1 (doto (BasicDBObject.) (.put "updatedExisting" true))
|
|
||||||
input2 (doto (BasicDBObject.) (.put "updatedExisting" false))
|
|
||||||
input3 (BasicDBObject.)]
|
|
||||||
(is (monger.result/updated-existing? input1))
|
|
||||||
(is (not (monger.result/updated-existing? input2)))
|
|
||||||
(is (not (monger.result/updated-existing? input3)))))
|
|
||||||
|
|
||||||
(deftest test-updated-existing?-with-write-result
|
|
||||||
(monger.collection/remove "libraries")
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
date (Date.)
|
|
||||||
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
|
||||||
(is (not (monger.result/updated-existing? (monger.collection/update collection { :language "Clojure" } doc :upsert true))))
|
|
||||||
(is (monger.result/updated-existing? (monger.collection/update collection { :language "Clojure" } doc :upsert true)))
|
|
||||||
(monger.result/updated-existing? (monger.collection/update collection { :language "Clojure" } modified-doc :multi false :upsert true))
|
|
||||||
(monger.collection/remove collection)))
|
|
||||||
25
test/monger/test/result_test.clj
Normal file
25
test/monger/test/result_test.clj
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
(ns monger.test.result-test
|
||||||
|
(:import [com.mongodb BasicDBObject WriteResult WriteConcern] java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.result :as mgres]
|
||||||
|
monger.util
|
||||||
|
[clojure.test :refer :all]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(deftest test-updated-existing?-with-write-result
|
||||||
|
(mc/remove db "libraries")
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (monger.util/random-uuid)
|
||||||
|
date (Date.)
|
||||||
|
doc { :created-at date :data-store "MongoDB" :language "Clojure" :_id doc-id }
|
||||||
|
modified-doc { :created-at date :data-store "MongoDB" :language "Erlang" :_id doc-id }]
|
||||||
|
(let [result (mc/update db collection { :language "Clojure" } doc {:upsert true})]
|
||||||
|
(is (not (mgres/updated-existing? result)))
|
||||||
|
(is (= 1 (mgres/affected-count result))))
|
||||||
|
(is (mgres/updated-existing? (mc/update db collection { :language "Clojure" } doc {:upsert true})))
|
||||||
|
(is (mgres/updated-existing? (mc/update db collection { :language "Clojure" } modified-doc {:multi false :upsert true})))
|
||||||
|
(is (= 1 (mgres/affected-count (mc/remove db collection { :_id doc-id }))))
|
||||||
|
(mc/remove db collection)
|
||||||
|
(mg/disconnect conn))))
|
||||||
50
test/monger/test/ring/clojure_session_store_test.clj
Normal file
50
test/monger/test/ring/clojure_session_store_test.clj
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
(ns monger.test.ring.clojure-session-store-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[ring.middleware.session.store :refer :all]
|
||||||
|
[monger.ring.session-store :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-sessions
|
||||||
|
[f]
|
||||||
|
(mc/remove db "sessions")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "sessions"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-sessions)
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-reading-a-session-that-does-not-exist
|
||||||
|
(let [store (session-store db "sessions")]
|
||||||
|
(is (= {} (read-session store "a-missing-key-1228277")))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-reading-a-session-that-does-exist
|
||||||
|
(let [store (session-store db "sessions")
|
||||||
|
sk (write-session store nil {:library "Monger"})
|
||||||
|
m (read-session store sk)]
|
||||||
|
(is sk)
|
||||||
|
(is (and (:_id m)))
|
||||||
|
(is (= (dissoc m :_id)
|
||||||
|
{:library "Monger"}))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest test-updating-a-session
|
||||||
|
(let [store (session-store db "sessions")
|
||||||
|
sk1 (write-session store nil {:library "Monger"})
|
||||||
|
sk2 (write-session store sk1 {:library "Ring"})
|
||||||
|
m (read-session store sk2)]
|
||||||
|
(is (and sk1 sk2))
|
||||||
|
(is (and (:_id m)))
|
||||||
|
(is (= sk1 sk2))
|
||||||
|
(is (= (dissoc m :_id)
|
||||||
|
{:library "Ring"}))))
|
||||||
|
|
||||||
|
(deftest test-deleting-a-session
|
||||||
|
(let [store (session-store db "sessions")
|
||||||
|
sk (write-session store nil {:library "Monger"})]
|
||||||
|
(is (nil? (delete-session store sk)))
|
||||||
|
(is (= {} (read-session store sk))))))
|
||||||
54
test/monger/test/ring/session_store_test.clj
Normal file
54
test/monger/test/ring/session_store_test.clj
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
(ns monger.test.ring.session-store-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[ring.middleware.session.store :refer :all]
|
||||||
|
[monger.ring.session-store :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-sessions
|
||||||
|
[f]
|
||||||
|
(mc/remove db "sessions")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "sessions"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-sessions)
|
||||||
|
|
||||||
|
(deftest test-reading-a-session-that-does-not-exist
|
||||||
|
(let [store (monger-store db "sessions")]
|
||||||
|
(is (= {} (read-session store "a-missing-key-1228277")))))
|
||||||
|
|
||||||
|
(deftest test-reading-a-session-that-does-exist
|
||||||
|
(let [store (monger-store db "sessions")
|
||||||
|
sk (write-session store nil {:library "Monger"})
|
||||||
|
m (read-session store sk)]
|
||||||
|
(is sk)
|
||||||
|
(is (and (:_id m) (:date m)))
|
||||||
|
(is (= (dissoc m :_id :date)
|
||||||
|
{:library "Monger"}))))
|
||||||
|
|
||||||
|
(deftest test-updating-a-session
|
||||||
|
(let [store (monger-store db "sessions")
|
||||||
|
sk1 (write-session store nil {:library "Monger"})
|
||||||
|
sk2 (write-session store sk1 {:library "Ring"})
|
||||||
|
m (read-session store sk2)]
|
||||||
|
(is (and sk1 sk2))
|
||||||
|
(is (and (:_id m) (:date m)))
|
||||||
|
(is (= sk1 sk2))
|
||||||
|
(is (= (dissoc m :_id :date)
|
||||||
|
{:library "Ring"}))))
|
||||||
|
|
||||||
|
(deftest test-deleting-a-session
|
||||||
|
(let [store (monger-store db "sessions")
|
||||||
|
sk (write-session store nil {:library "Monger"})]
|
||||||
|
(is (nil? (delete-session store sk)))
|
||||||
|
(is (= {} (read-session store sk)))))
|
||||||
|
|
||||||
|
(deftest test-reader-extensions
|
||||||
|
(let [d (java.util.Date.)
|
||||||
|
oid (org.bson.types.ObjectId.)]
|
||||||
|
(binding [*print-dup* true]
|
||||||
|
(pr-str d)
|
||||||
|
(pr-str oid)))))
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
(ns monger.test.stress
|
|
||||||
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Fixture functions
|
|
||||||
;;
|
|
||||||
|
|
||||||
(defn purge-collection
|
|
||||||
[collection-name, f]
|
|
||||||
(monger.collection/remove collection-name)
|
|
||||||
(f)
|
|
||||||
(monger.collection/remove collection-name))
|
|
||||||
|
|
||||||
(defn purge-things-collection
|
|
||||||
[f]
|
|
||||||
(purge-collection "things" f))
|
|
||||||
|
|
||||||
(use-fixtures :each purge-things-collection)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Tests
|
|
||||||
;;
|
|
||||||
|
|
||||||
(monger.core/set-default-write-concern! WriteConcern/NORMAL)
|
|
||||||
|
|
||||||
(deftest insert-large-batches-of-documents-without-object-ids
|
|
||||||
(doseq [n [1000 10000 100000]]
|
|
||||||
(let [collection "things"
|
|
||||||
docs (map (fn [i]
|
|
||||||
(monger.conversion/to-db-object { :title "Untitled" :created-at (Date.) :number i }))
|
|
||||||
(take n (iterate inc 1)))]
|
|
||||||
(monger.collection/remove collection)
|
|
||||||
(println "Inserting " n " documents...")
|
|
||||||
(time (monger.collection/insert-batch collection docs))
|
|
||||||
(is (= n (monger.collection/count collection))))))
|
|
||||||
40
test/monger/test/stress_test.clj
Normal file
40
test/monger/test/stress_test.clj
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
(ns monger.test.stress-test
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.conversion :refer [to-db-object from-db-object]]
|
||||||
|
[clojure.test :refer :all])
|
||||||
|
(:import [com.mongodb WriteConcern]
|
||||||
|
java.util.Date))
|
||||||
|
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-collection
|
||||||
|
[coll f]
|
||||||
|
(mc/remove db coll)
|
||||||
|
(f)
|
||||||
|
(mc/remove db coll))
|
||||||
|
|
||||||
|
(defn purge-things-collection
|
||||||
|
[f]
|
||||||
|
(purge-collection "things" f))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-things-collection)
|
||||||
|
|
||||||
|
(deftest ^{:performance true} insert-large-batches-of-documents-without-object-ids
|
||||||
|
(doseq [n [10 100 1000 10000 20000]]
|
||||||
|
(let [collection "things"
|
||||||
|
docs (map (fn [i]
|
||||||
|
(to-db-object { :title "Untitled" :created-at (Date.) :number i }))
|
||||||
|
(take n (iterate inc 1)))]
|
||||||
|
(mc/remove db collection)
|
||||||
|
(println "Inserting " n " documents...")
|
||||||
|
(time (mc/insert-batch db collection docs))
|
||||||
|
(is (= n (mc/count db collection))))))
|
||||||
|
|
||||||
|
(deftest ^{:performance true} convert-large-number-of-dbojects-to-maps
|
||||||
|
(doseq [n [10 100 1000 20000 40000]]
|
||||||
|
(let [docs (map (fn [i]
|
||||||
|
(to-db-object {:title "Untitled" :created-at (Date.) :number i}))
|
||||||
|
(take n (iterate inc 1)))]
|
||||||
|
(time (doall (map (fn [x] (from-db-object x true)) docs)))))))
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
(set! *warn-on-reflection* true)
|
|
||||||
|
|
||||||
(ns monger.test.updating
|
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject]
|
|
||||||
[org.bson.types ObjectId]
|
|
||||||
[java.util Date])
|
|
||||||
(:require [monger core util]
|
|
||||||
[monger.collection :as mgcol]
|
|
||||||
[monger.result :as mgres]
|
|
||||||
[monger.conversion :as mgcnv]
|
|
||||||
[monger.test.helper :as helper])
|
|
||||||
(:use [clojure.test]
|
|
||||||
[monger.operators]
|
|
||||||
[monger.test.fixtures]))
|
|
||||||
|
|
||||||
(helper/connect!)
|
|
||||||
|
|
||||||
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; update, save
|
|
||||||
;;
|
|
||||||
|
|
||||||
(deftest update-document-by-id-without-upsert
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
date (Date.)
|
|
||||||
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= (doc (mgcol/find-by-id collection doc-id))))
|
|
||||||
(mgcol/update collection { :_id doc-id } { :language "Erlang" })
|
|
||||||
(is (= (modified-doc (mgcol/find-by-id collection doc-id))))))
|
|
||||||
|
|
||||||
(deftest update-document-by-id-without-upsert-using-update-by-id
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
date (Date.)
|
|
||||||
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
|
||||||
(mgcol/insert collection doc)
|
|
||||||
(is (= (doc (mgcol/find-by-id collection doc-id))))
|
|
||||||
(mgcol/update-by-id collection doc-id { :language "Erlang" })
|
|
||||||
(is (= (modified-doc (mgcol/find-by-id collection doc-id))))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest update-multiple-documents
|
|
||||||
(let [collection "libraries"]
|
|
||||||
(mgcol/insert collection { :language "Clojure", :name "monger" })
|
|
||||||
(mgcol/insert collection { :language "Clojure", :name "langohr" })
|
|
||||||
(mgcol/insert collection { :language "Clojure", :name "incanter" })
|
|
||||||
(mgcol/insert collection { :language "Scala", :name "akka" })
|
|
||||||
(is (= 3 (mgcol/count collection { :language "Clojure" })))
|
|
||||||
(is (= 1 (mgcol/count collection { :language "Scala" })))
|
|
||||||
(is (= 0 (mgcol/count collection { :language "Python" })))
|
|
||||||
(mgcol/update collection { :language "Clojure" } { $set { :language "Python" } } :multi true)
|
|
||||||
(is (= 0 (mgcol/count collection { :language "Clojure" })))
|
|
||||||
(is (= 1 (mgcol/count collection { :language "Scala" })))
|
|
||||||
(is (= 3 (mgcol/count collection { :language "Python" })))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest save-a-new-document
|
|
||||||
(let [collection "people"
|
|
||||||
document { :name "Joe", :age 30 }]
|
|
||||||
(is (monger.result/ok? (mgcol/save "people" document)))
|
|
||||||
(is (= 1 (mgcol/count collection)))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest save-a-new-basic-db-object
|
|
||||||
(let [collection "people"
|
|
||||||
doc (mgcnv/to-db-object { :name "Joe", :age 30 })]
|
|
||||||
(is (nil? (monger.util/get-id doc)))
|
|
||||||
(mgcol/save monger.core/*mongodb-database* "people" doc WriteConcern/SAFE)
|
|
||||||
(is (not (nil? (monger.util/get-id doc))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest update-an-existing-document-using-save
|
|
||||||
(let [collection "people"
|
|
||||||
doc-id "people-1"
|
|
||||||
document { :_id doc-id, :name "Joe", :age 30 }]
|
|
||||||
(is (monger.result/ok? (mgcol/insert "people" document)))
|
|
||||||
(is (= 1 (mgcol/count collection)))
|
|
||||||
(mgcol/save collection { :_id doc-id, :name "Alan", :age 40 })
|
|
||||||
(is (= 1 (mgcol/count collection { :name "Alan", :age 40 })))))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest set-an-attribute-on-existing-document-using-update
|
|
||||||
(let [collection "people"
|
|
||||||
doc-id (monger.util/object-id)
|
|
||||||
document { :_id doc-id, :name "Joe", :age 30 }]
|
|
||||||
(is (monger.result/ok? (mgcol/insert "people" document)))
|
|
||||||
(is (= 1 (mgcol/count collection)))
|
|
||||||
(is (= 0 (mgcol/count collection { :has_kids true })))
|
|
||||||
(mgcol/update collection { :_id doc-id } { $set { :has_kids true } })
|
|
||||||
(is (= 1 (mgcol/count collection { :has_kids true })))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest upsert-a-document
|
|
||||||
(let [collection "libraries"
|
|
||||||
doc-id (monger.util/random-uuid)
|
|
||||||
date (Date.)
|
|
||||||
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
|
||||||
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
|
||||||
(is (not (monger.result/updated-existing? (mgcol/update collection { :language "Clojure" } doc :upsert true))))
|
|
||||||
(is (= 1 (mgcol/count collection)))
|
|
||||||
(is (monger.result/updated-existing? (mgcol/update collection { :language "Clojure" } modified-doc :multi false :upsert true)))
|
|
||||||
(is (= 1 (mgcol/count collection)))
|
|
||||||
(is (= (modified-doc (mgcol/find-by-id collection doc-id))))
|
|
||||||
(mgcol/remove collection)))
|
|
||||||
169
test/monger/test/updating_test.clj
Normal file
169
test/monger/test/updating_test.clj
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
(ns monger.test.updating-test
|
||||||
|
(:import [com.mongodb WriteResult WriteConcern DBObject]
|
||||||
|
org.bson.types.ObjectId
|
||||||
|
java.util.Date)
|
||||||
|
(:require [monger.core :as mg]
|
||||||
|
[monger.collection :as mc]
|
||||||
|
[monger.util :as mu]
|
||||||
|
[monger.result :as mr]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[monger.operators :refer :all]
|
||||||
|
[monger.conversion :refer [to-db-object]]))
|
||||||
|
|
||||||
|
(let [conn (mg/connect)
|
||||||
|
db (mg/get-db conn "monger-test")]
|
||||||
|
(defn purge-collections
|
||||||
|
[f]
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "libraries")
|
||||||
|
(f)
|
||||||
|
(mc/remove db "people")
|
||||||
|
(mc/remove db "docs")
|
||||||
|
(mc/remove db "things")
|
||||||
|
(mc/remove db "libraries"))
|
||||||
|
|
||||||
|
(use-fixtures :each purge-collections)
|
||||||
|
|
||||||
|
(deftest ^{:updating true} update-document-by-id-without-upsert
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
date (Date.)
|
||||||
|
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id)))
|
||||||
|
(mc/update db collection { :_id doc-id } { $set { :language "Erlang" } })
|
||||||
|
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest ^{:updating true} update-document-by-id-without-upsert-using-update-by-id
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
date (Date.)
|
||||||
|
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id)))
|
||||||
|
(mc/update-by-id db collection doc-id { $set { :language "Erlang" } })
|
||||||
|
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
(deftest ^{:updating true} update-nested-document-fields-without-upsert-using-update-by-id
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (ObjectId.)
|
||||||
|
date (Date.)
|
||||||
|
doc { :created-at date :data-store "MongoDB" :language { :primary "Clojure" } :_id doc-id }
|
||||||
|
modified-doc { :created-at date :data-store "MongoDB" :language { :primary "Erlang" } :_id doc-id }]
|
||||||
|
(mc/insert db collection doc)
|
||||||
|
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id)))
|
||||||
|
(mc/update-by-id db collection doc-id { $set { "language.primary" "Erlang" }})
|
||||||
|
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} update-multiple-documents
|
||||||
|
(let [collection "libraries"]
|
||||||
|
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
|
||||||
|
{ :language "Clojure", :name "langohr" }
|
||||||
|
{ :language "Clojure", :name "incanter" }
|
||||||
|
{ :language "Scala", :name "akka" }])
|
||||||
|
(is (= 3 (mc/count db collection { :language "Clojure" })))
|
||||||
|
(is (= 1 (mc/count db collection { :language "Scala" })))
|
||||||
|
(is (= 0 (mc/count db collection { :language "Python" })))
|
||||||
|
(mc/update db collection { :language "Clojure" } { $set { :language "Python" } } {:multi true})
|
||||||
|
(is (= 0 (mc/count db collection { :language "Clojure" })))
|
||||||
|
(is (= 1 (mc/count db collection { :language "Scala" })))
|
||||||
|
(is (= 3 (mc/count db collection { :language "Python" })))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} save-a-new-document
|
||||||
|
(let [collection "people"
|
||||||
|
document {:name "Joe" :age 30}]
|
||||||
|
(is (mc/save db "people" document))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
(deftest ^{:updating true} save-and-return-a-new-document
|
||||||
|
(let [collection "people"
|
||||||
|
document {:name "Joe" :age 30}
|
||||||
|
returned (mc/save-and-return db "people" document)]
|
||||||
|
(is (:_id returned))
|
||||||
|
(is (= document (dissoc returned :_id)))
|
||||||
|
(is (= 1 (mc/count db collection)))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} save-a-new-basic-db-object
|
||||||
|
(let [collection "people"
|
||||||
|
doc (to-db-object {:name "Joe" :age 30})]
|
||||||
|
(is (nil? (mu/get-id doc)))
|
||||||
|
(mc/save db "people" doc WriteConcern/SAFE)
|
||||||
|
(is (not (nil? (mu/get-id doc))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} update-an-existing-document-using-save
|
||||||
|
(let [collection "people"
|
||||||
|
doc-id "people-1"
|
||||||
|
document { :_id doc-id, :name "Joe", :age 30 }]
|
||||||
|
(is (mc/insert db collection document))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(mc/save db collection { :_id doc-id, :name "Alan", :age 40 })
|
||||||
|
(is (= 1 (mc/count db collection { :name "Alan", :age 40 })))))
|
||||||
|
|
||||||
|
(deftest ^{:updating true} update-an-existing-document-using-save-and-return
|
||||||
|
(let [collection "people"
|
||||||
|
document (mc/insert-and-return db collection {:name "Joe" :age 30})
|
||||||
|
doc-id (:_id document)
|
||||||
|
updated (mc/save-and-return db collection {:_id doc-id :name "Alan" :age 40})]
|
||||||
|
(is (= {:_id doc-id :name "Alan" :age 40} updated))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(is (= 1 (mc/count db collection {:name "Alan" :age 40})))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} set-an-attribute-on-existing-document-using-update
|
||||||
|
(let [collection "people"
|
||||||
|
doc-id (mu/object-id)
|
||||||
|
document { :_id doc-id, :name "Joe", :age 30 }]
|
||||||
|
(is (mc/insert db collection document))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(is (= 0 (mc/count db collection { :has_kids true })))
|
||||||
|
(mc/update db collection { :_id doc-id } { $set { :has_kids true } })
|
||||||
|
(is (= 1 (mc/count db collection { :has_kids true })))))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} increment-multiple-fields-using-exists-operator-and-update
|
||||||
|
(let [collection "matches"
|
||||||
|
doc-id (mu/object-id)
|
||||||
|
document { :_id doc-id :abc 0 :def 10 }]
|
||||||
|
(mc/remove db collection)
|
||||||
|
(is (mc/insert db collection document))
|
||||||
|
(is (= 1 (mc/count db collection {:abc {$exists true} :def {$exists true}})))
|
||||||
|
(mc/update db collection {:abc {$exists true} :def {$exists true}} {$inc {:abc 1 :def 0}})
|
||||||
|
(is (= 1 (mc/count db collection { :abc 1 })))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(deftest ^{:updating true} upsert-a-document-using-update
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
date (Date.)
|
||||||
|
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||||
|
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
|
||||||
|
(is (not (mr/updated-existing? (mc/update db collection { :language "Clojure" } doc {:upsert true}))))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(is (mr/updated-existing? (mc/update db collection { :language "Clojure" } modified-doc {:multi false :upsert true})))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id)))
|
||||||
|
(mc/remove db collection)))
|
||||||
|
|
||||||
|
(deftest ^{:updating true} upsert-a-document-using-upsert
|
||||||
|
(let [collection "libraries"
|
||||||
|
doc-id (mu/random-uuid)
|
||||||
|
date (Date.)
|
||||||
|
doc {:created-at date :data-store "MongoDB" :language "Clojure" :_id doc-id}
|
||||||
|
modified-doc {:created-at date :data-store "MongoDB" :language "Erlang" :_id doc-id}]
|
||||||
|
(mc/remove db collection)
|
||||||
|
(is (not (mr/updated-existing? (mc/upsert db collection {:language "Clojure"} doc))))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(is (mr/updated-existing? (mc/upsert db collection {:language "Clojure"} modified-doc {:multi false})))
|
||||||
|
(is (= 1 (mc/count db collection)))
|
||||||
|
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id)))
|
||||||
|
(mc/remove db collection))))
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
(ns monger.test.util
|
(ns monger.test.util-test
|
||||||
(:import (com.mongodb DBObject))
|
(:import com.mongodb.DBObject)
|
||||||
(:require [monger util conversion])
|
(:require [monger util conversion]
|
||||||
(:use [clojure.test]))
|
[clojure.test :refer :all]))
|
||||||
|
|
||||||
|
|
||||||
(deftest get-object-id
|
(deftest get-object-id
|
||||||
11
test/resources/logback.xml
Normal file
11
test/resources/logback.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<logger name="org.mongodb" level="WARN"/>
|
||||||
|
<root level="DEBUG">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
Loading…
Reference in a new issue