From 98cdcb6d59dace02da3fd7ddc51179b317b6bf8f Mon Sep 17 00:00:00 2001 From: Josh Mock Date: Fri, 30 Jun 2023 16:16:03 -0500 Subject: [PATCH 1/2] Generated serverless client: take 1 --- .ci/Dockerfile | 25 + .ci/make.sh | 184 + .../014b788c879e4aaa1020672e45e25473.asciidoc | 13 + .../015294a400986295039e52ebc62033be.asciidoc | 11 + .../025b54db0edc50c24ea48a2bd94366ad.asciidoc | 9 + .../028f6d6ac2594e20b78b8a8f8cbad49d.asciidoc | 31 + .../033778305d52746f5ce0a2a922c8e521.asciidoc | 20 + .../047266b0d20fdb62ebc72d51952c8f6d.asciidoc | 18 + .../048d8abd42d094bbdcf4452a58ccb35b.asciidoc | 15 + .../04f5dd677c777bcb15d7d5fa63275fc8.asciidoc | 7 + .../04fe1e3a0047b0cdb10987b79fc3f3f3.asciidoc | 22 + .../06afce2955f9094d96d27067ebca32e8.asciidoc | 23 + .../073539a7e38be3cdf13008330b6a536a.asciidoc | 7 + .../0989cc65d8924f666ce3eb0820d2d244.asciidoc | 14 + .../09cdd5ae8114c49886026fef8d00a19c.asciidoc | 10 + .../09d617863a103c82fb4101e6165ea7fe.asciidoc | 7 + .../09dbd90c5e22ea4a17b4cf9aa72e08ae.asciidoc | 10 + .../09ecba5814d71e4c44468575eada9878.asciidoc | 20 + .../0a958e486ede3f519d48431ab689eded.asciidoc | 18 + .../0ac9916f47a2483b89c1416684af322a.asciidoc | 19 + .../0afaf1cad692e6201aa574c8feb6e622.asciidoc | 19 + .../0ba0b2db24852abccb7c0fc1098d566e.asciidoc | 12 + .../0bbd30b9be3e54ff3028b9f4459634d2.asciidoc | 12 + .../0bd3923424a20a4ba860b0774b9991b1.asciidoc | 37 + .../0be2c28ee65384774b1e479b47dc3d92.asciidoc | 9 + .../0c4ad860a485fe53d8140ad3ccd11dcf.asciidoc | 13 + .../0cc991e3f7f8511a34730e154b3c5edc.asciidoc | 12 + .../0ce3606f1dba490eef83c4317b315b62.asciidoc | 9 + .../0d664883151008b1051ef2c9ab2d0373.asciidoc | 22 + .../0e118857b815b62118a30c042f079db1.asciidoc | 17 + .../1027ab1ca767ac1428176ef4f84bfbcf.asciidoc | 24 + .../1216f8f7367df3aa823012cef310c08a.asciidoc | 15 + .../12433d2b637d002e8d5c9a1adce69d3b.asciidoc | 7 + .../1252fa45847edba5ec2b2f33da70ec5b.asciidoc | 7 + .../138ccd89f72aa7502dd9578403dcc589.asciidoc | 7 + .../14701dcc0cca9665fce2aace0cb62af7.asciidoc | 12 + .../1577e6e806b3283c9e99f1596d310754.asciidoc | 12 + .../15dad5338065baaaa7d475abe85f4c22.asciidoc | 20 + .../162b5b693b713f0bfab1209d59443c46.asciidoc | 13 + .../179f0a3e84ff4bbac18787a018eabf89.asciidoc | 18 + .../17de0020b228df961ad3c6b06233c948.asciidoc | 12 + .../189a921df2f5b1fe580937210ce9c1c2.asciidoc | 9 + .../18ddb7e7a4bcafd449df956e828ed7a8.asciidoc | 7 + .../1aa91d3d48140d6367b6cabca8737b8f.asciidoc | 16 + .../1b542e3ea87a742f95641d64dcfb1bdb.asciidoc | 7 + .../1b8655e6ba99fe39933c6eafe78728b7.asciidoc | 20 + .../1b8caf0a6741126c6d0ad83b56fce290.asciidoc | 21 + .../1bc731a4df952228af6dfa6b48627332.asciidoc | 18 + .../1c23507edd7a3c18538b68223378e4ab.asciidoc | 7 + .../1d65cb6d055c46a1bde809687d835b71.asciidoc | 7 + .../1da77e114459e0b77d78a3dcc8fae429.asciidoc | 16 + .../1dbb8cf17fbc45c87c7d2f75f15f9778.asciidoc | 7 + .../1e18a67caf8f06ff2710ec4a8b30f625.asciidoc | 12 + .../1e49eba5b9042c1900a608fe5105ba43.asciidoc | 22 + .../1e50d993bd6517e6c381e82d09f0389e.asciidoc | 13 + .../1f336ecc62480c1d56351cc2f82d0d08.asciidoc | 15 + .../1f6fe6833686e38c3711c6f2aa00a078.asciidoc | 16 + .../210cf5c76bff517f48e80fa1c2d63907.asciidoc | 7 + .../213ab768f1b6a895e09403a0880e259a.asciidoc | 22 + .../216848930c2d344fe0bed0daa70c35b9.asciidoc | 7 + .../22334f4b24bb8977d3e1bf2ffdc29d3f.asciidoc | 46 + .../231aa0bb39c35fe199d28fe0e4a62b2e.asciidoc | 10 + .../23ab0f1023b1b2cd5cdf2a8f9ccfd57b.asciidoc | 10 + .../2468ab381257d759d8a88af1141f6f9c.asciidoc | 7 + .../251ea12c1248385ab409906ac64d9ee9.asciidoc | 19 + .../2533e4b36ae837eaecda08407ecb6383.asciidoc | 17 + .../2891aa10ee9d474780adf94d5607f2db.asciidoc | 10 + .../28aad2c5942bfb221c2bf1bbdc01658e.asciidoc | 16 + .../2a1de18774f9c68cafa169847832b2bc.asciidoc | 10 + .../2bb2339ac055337abf753bddb7771659.asciidoc | 17 + .../2fd69fb0538e4f36ac69a8b8f8bf5ae8.asciidoc | 13 + .../2fe28d9a91b3081a9ec4601af8fb7b1c.asciidoc | 40 + .../311c4b632a29b9ead63b02d01f10096b.asciidoc | 7 + .../3342c69b2c2303247217532956fcce85.asciidoc | 7 + .../33f148e3d8676de6cc52f58749898a13.asciidoc | 18 + .../34efeade38445b2834749ced59782e25.asciidoc | 21 + .../35e8da9410b8432cf4095f2541ad7b1d.asciidoc | 19 + .../3653567181f43a5f64c74f934aa821c2.asciidoc | 9 + .../36818c6d9f434d387819c30bd9addb14.asciidoc | 14 + .../36b2778f23d0955255f52c075c4d213d.asciidoc | 20 + .../3722cb3705b6bc7f486969deace3dd83.asciidoc | 17 + .../381fced1882ca8337143e6bb180a5715.asciidoc | 9 + .../38c1d0f6668e9563c0827f839f9fa505.asciidoc | 9 + .../39a6a038c4b551022afe83de0523634e.asciidoc | 21 + .../3a700f836d8d5da1b656a876554028aa.asciidoc | 14 + .../3ae03ba3b56e5e287953094050766738.asciidoc | 12 + .../3b04cc894e6a47d57983484010feac0c.asciidoc | 10 + .../3cd50a789b8e1f0ebbbc53a8d7ecf656.asciidoc | 31 + .../3d1ff6097e2359f927c88c2ccdb36252.asciidoc | 7 + .../3e573bfabe00f8bfb8bb69aa5820768e.asciidoc | 15 + .../3f3b3e207f79303ce6f86e03e928e062.asciidoc | 7 + .../400e89eb46ead8e9c9e40f123fd5e590.asciidoc | 12 + .../42744a175125df5be0ef77413bf8f608.asciidoc | 9 + .../427f6b5c5376cbf0f71f242a60ca3d9e.asciidoc | 7 + .../43af86de5e49aa06070092fffc138208.asciidoc | 9 + .../4646764bf09911fee7d58630c72d3137.asciidoc | 20 + .../46658f00edc4865dfe472a392374cd0f.asciidoc | 9 + .../46c4b0dfb674825f9579203d41e7f404.asciidoc | 10 + .../47b5ff897f26e9c943cee5c06034181d.asciidoc | 7 + .../47bb632c6091ad0cd94bc660bdd309a5.asciidoc | 17 + .../4acf902c2598b2558f34f20c1744c433.asciidoc | 12 + .../4b90feb9d5d3dbfce424dac0341320b7.asciidoc | 15 + .../4cd246e5c4c035a2cd4081ae9a3d54e5.asciidoc | 17 + .../4d46dbb96125b27f46299547de9d8709.asciidoc | 10 + .../4d56b179242fed59e3d6476f817b6055.asciidoc | 18 + .../4d6997c70a1851f9151443c0d38b532e.asciidoc | 34 + .../5043b83a89091fa00edb341ddf7ba370.asciidoc | 18 + .../506844befdc5691d835771bcbb1c1a60.asciidoc | 10 + .../5271f4ff29bb48838396e5a674664ee0.asciidoc | 35 + .../527324766814561b75aaee853ede49a7.asciidoc | 11 + .../5275842787967b6db876025f4a1c6942.asciidoc | 15 + .../52a87b81e4e0b6b11e23e85db1602a63.asciidoc | 11 + .../52b2bfbdd78f8283b6f4891c48013237.asciidoc | 13 + .../52c7e4172a446c394210a07c464c57d2.asciidoc | 9 + .../53b908c3432118c5a6e460f74d32006b.asciidoc | 16 + .../53d938c754f36a912fcbe6473abb463f.asciidoc | 9 + .../54092c8c646133f5dbbc047990dd458d.asciidoc | 28 + .../54a770f053f3225ea0d1e34334232411.asciidoc | 7 + .../58b5003c0a53a39bf509aa3797aad471.asciidoc | 16 + .../58df61acbfb15b8ef0aaa18b81ae98a6.asciidoc | 11 + .../5925c23a173a63bdb30b458248d1df76.asciidoc | 7 + .../5be23858b35043fcb7b50fe36b873e6e.asciidoc | 9 + .../5c2f486c27bd5346e512265f93375d16.asciidoc | 19 + .../5d32279dcd52b22d9e1178a02a3ad957.asciidoc | 15 + .../5d9d7b84e2fec7ecd832145cbb951cf1.asciidoc | 22 + .../5da6efd5b038ada64c9e853c88c1ec47.asciidoc | 18 + .../5dd695679b5141d9142d3d30ba8d300a.asciidoc | 11 + .../5eabcdbf61bfcb484dc694f25c2bba36.asciidoc | 9 + .../5f210f74725ea0c9265190346edfa246.asciidoc | 13 + .../5f3549ac7fee94682ca0d7439eebdd2a.asciidoc | 10 + .../5f3a3eefeefe6fa85ec49d499212d245.asciidoc | 17 + .../609260ad1d5998be2ca09ff1fe237efa.asciidoc | 7 + .../60ee33f3acfdd0fe6f288ac77312c780.asciidoc | 17 + .../6138d6919f3cbaaf61e1092f817d295c.asciidoc | 15 + .../618d5f3d35921d8cb7e9ccfbe9a4c3e3.asciidoc | 20 + .../621665fdbd7fc103c09bfeed28b67b1a.asciidoc | 7 + .../625dc94df1f9affb49a082fd99d41620.asciidoc | 15 + .../626f8c4b3e2cd3d9beaa63a7f5799d7a.asciidoc | 18 + .../645136747d37368a14ab34de8bd046c6.asciidoc | 26 + .../645796e8047967ca4a7635a22a876f4c.asciidoc | 21 + .../645c4c6e209719d3a4d25b1a629cb23b.asciidoc | 15 + .../6464124d1677f4552ddddd95a340ca3a.asciidoc | 30 + .../64b9baa6d7556b960b29698f3383aa31.asciidoc | 17 + .../6799d132c1c7ca3970763acde2337ef9.asciidoc | 13 + .../67bba546d835bca8f31df13e3587c348.asciidoc | 7 + .../67ceac4bf2d9ac7cc500390544cdcb41.asciidoc | 13 + .../68721288dc9ad8aa1b55099b4d303051.asciidoc | 17 + .../68738b4fd0dda177022be45be95b4c84.asciidoc | 9 + .../69a7be47f85138b10437113ab2f0d72d.asciidoc | 12 + .../6a1702dd50690cae833572e48a0ddf25.asciidoc | 16 + .../6a4679531e64c492fce16dc12de6dcb0.asciidoc | 15 + .../6a81d00f0d73bc5985e76b3cadab645e.asciidoc | 24 + .../6bbc613bd4f9aec1bbdbabf5db021d28.asciidoc | 19 + .../6be70810d6ebd6f09d8a49f9df847765.asciidoc | 25 + .../6bf63f2ec6ba55fcaf1092f48212bf25.asciidoc | 14 + .../6d1e75312a28a5ba23837abf768f2510.asciidoc | 9 + .../6f097c298a7abf4c032c4314920c49c8.asciidoc | 12 + .../6f21a878fee3b43c5332b81aaddbeac7.asciidoc | 18 + .../6faf10a73f7d5fffbcb037bdb2cbaff8.asciidoc | 22 + .../70f0aa5853697e265ef3b1df72940951.asciidoc | 36 + .../710c7871f20f176d51209b1574b0d61b.asciidoc | 9 + .../71b5b2ba9557d0f296ff2de91727d2f6.asciidoc | 21 + .../71ba9033107882f61cdc3b32fc73568d.asciidoc | 12 + .../72231b7debac60c95b9869a97dafda3a.asciidoc | 18 + .../72beebe779a258c225dee7b023e60c52.asciidoc | 7 + .../734c2e2a1e45b84f1e4e65b51356fcd7.asciidoc | 10 + .../73e5c88ad1488b213fb278ee1cb42289.asciidoc | 20 + .../745f9b8cdb8e91073f6e520e1d9f8c05.asciidoc | 7 + .../7477671958734843dd67cf0b8e6c7515.asciidoc | 10 + .../75330ec1305d2beb0e2f34d2195464e2.asciidoc | 7 + .../764f9884b370cbdc82a1c5c42ed40ff3.asciidoc | 15 + .../77243bbf92f2a55e0fca6c2a349a1c15.asciidoc | 20 + .../774d715155cd13713e6e327adf6ce328.asciidoc | 13 + .../78c96113ae4ed0054e581b17542528a7.asciidoc | 15 + .../7b908b1189f076942de8cd497ff1fa59.asciidoc | 17 + .../7cac05cb589f1614fd5b8589153bef06.asciidoc | 11 + .../7cf71671859be7c1ecf673396db377cd.asciidoc | 19 + .../7df191cc7f814e410a4ac7261065e6ef.asciidoc | 7 + .../7e52bec09624cf6c0de5d13f2bfad5a5.asciidoc | 11 + .../7f28f8ae8fcdbd807dadde0b5b007a6d.asciidoc | 18 + .../7f465b7e8ed42df6c42251b4481e699e.asciidoc | 16 + .../7f56755fb6c42f7e6203339a6d0cb6e6.asciidoc | 18 + .../7f697eb436dfa3c30dfe610d8c32d132.asciidoc | 20 + .../804a97ff4d0613e6568e4efb19c52021.asciidoc | 23 + .../81c9aa2678d6166a9662ddf2c011a6a5.asciidoc | 7 + .../83f95657beca9bf5d8264c80c7fb463f.asciidoc | 9 + .../8653e76676de5d327201b77512afa3a0.asciidoc | 9 + .../873fbbc6ab81409058591385fd602736.asciidoc | 35 + .../8871b8fcb6de4f0c7dff22798fb10fb7.asciidoc | 16 + .../899eef71a67a1b2aa11a2166ec7f48f1.asciidoc | 12 + .../89a8ac1509936acc272fc2d72907bc45.asciidoc | 7 + .../8a355eb25d2a01ba62dc1a22dd46f46f.asciidoc | 21 + .../8acc1d67b152e7027e0f0e1a8b4b2431.asciidoc | 20 + .../8baccd8688a6bad1749b8935f9601ea4.asciidoc | 17 + .../8c5977410335d58217e0626618ce6641.asciidoc | 7 + .../8cd00a3aba7c3c158277bc032aac2830.asciidoc | 45 + .../8d9a63d7c31f08bd27d92ece3de1649c.asciidoc | 22 + .../8de3206f80e18185a5ad6481f4c2ee07.asciidoc | 21 + .../8e6bfb4441ffa15c86d5dc20fa083571.asciidoc | 9 + .../8eaf4d5dd4ab1335deefa7749fdbbcc3.asciidoc | 20 + .../8f0511f8a5cb176ff2afdd4311799a33.asciidoc | 17 + .../8fdf2344c4fb3de6902ad7c5735270df.asciidoc | 12 + .../913770050ebbf3b9b549a899bc11060a.asciidoc | 17 + .../9166cf38427d5cde5d2ec12a2012b669.asciidoc | 15 + .../93f1bdd72e79827dcf9a34efa02fd977.asciidoc | 15 + .../9524a9b7373fa4eb2905183b0e806962.asciidoc | 22 + .../96de5703ba0bd43fd4ac239ec5408542.asciidoc | 17 + .../973a3ff47fc4ce036ecd9bd363fef9f7.asciidoc | 16 + .../978088f989d45dd09339582e9cbc60e0.asciidoc | 10 + .../979d25dff2d8987119410291ad47b0d1.asciidoc | 20 + .../97babc8d19ef0866774576716eb6d19e.asciidoc | 16 + .../98234499cfec70487cec5d013e976a84.asciidoc | 7 + .../98aeb275f829b5f7b8eb2147701565ff.asciidoc | 17 + .../98b121bf47cebd85671a2cb519688d28.asciidoc | 18 + .../98f14fddddea54a7d6149ab7b92e099d.asciidoc | 7 + .../99a52be903945b17e734a1d02a57e958.asciidoc | 9 + .../9a4d5e41c52c20635d1fd9c6e13f6c7a.asciidoc | 20 + .../9a8995fd31351045d99c78e40444c8ea.asciidoc | 9 + .../9e56d79ad9a02b642c361f0b85dd95d7.asciidoc | 10 + .../a116949e446f34dc25ae57d4b703d0c1.asciidoc | 9 + .../a1db5c822745fe167e9ef854dca3d129.asciidoc | 20 + .../a2a25aad1fea9a541b52ac613c78fb64.asciidoc | 17 + .../a34d70d7022eb4ba48909d440c80390f.asciidoc | 10 + .../a42f33e15b0995bb4b6058659bfdea85.asciidoc | 18 + .../a49169b4622918992411fab4ec48191b.asciidoc | 21 + .../a4a396cd07657b3977713fb3a742c41b.asciidoc | 7 + .../a5a7050fb9dcb9574e081957ade28617.asciidoc | 12 + .../a6f8636b03cc5f677b7d89e750328612.asciidoc | 7 + .../a71c438cc4df1cafe3109ccff475afdb.asciidoc | 18 + .../a7c15fe6b5779c84ce9a34bf4b2a7ab7.asciidoc | 10 + .../a80f5db4357bb25b8704d374c18318ed.asciidoc | 11 + .../a8fba09a46b2c3524428aa3259b7124f.asciidoc | 7 + .../aa6bfe54e2436eb668091fe31c2fbf4d.asciidoc | 37 + .../abd4fc3ce7784413a56fe2dcfe2809b5.asciidoc | 11 + .../abf329ebefaf58acd4ee30e685731499.asciidoc | 10 + .../ac544eb247a29ca42aab13826ca88561.asciidoc | 17 + .../ad0dcbc7fc619e952c8825b8f307b7b2.asciidoc | 17 + .../ad6ea0c1e46712aa1fd6d3bfa0ec979e.asciidoc | 16 + .../ad79228630684d950fe9792a768d24c5.asciidoc | 26 + .../ae9b5fbd42af2386ffbf56ad4a697e51.asciidoc | 19 + .../ae9ccfaa146731ab9176df90670db1c2.asciidoc | 17 + .../af3fb9fa5691a7b37a6dc2a69ff66e64.asciidoc | 14 + .../afc29b61c532cf683f749baf013e7bfe.asciidoc | 14 + .../b02e4907c9936c1adc16ccce9d49900d.asciidoc | 7 + .../b0d64d0a554549e5b2808002a0725493.asciidoc | 11 + .../b0eaf67e5cce24ef8889bf20951ccec1.asciidoc | 19 + .../b0ec418bf416c62bed602b0a32a6d5f5.asciidoc | 7 + .../b1efa1c51a34dd5ab5511b71a399f5b1.asciidoc | 12 + .../b214942b938e47f2c486e523546cb574.asciidoc | 19 + .../b41dce56b0e640d32b1cf452f87cec17.asciidoc | 12 + .../b4392116f2cc57ce8064ccbad30318d5.asciidoc | 9 + .../b4a0d0ed512dffc10ee53bca2feca49b.asciidoc | 28 + .../b5f95bc097a201b29c7200fc8d3d31c1.asciidoc | 26 + .../b68c85fe1b0d2f264dc0d1cbf530f319.asciidoc | 21 + .../b789292f9cf63ce912e058c46d90ce20.asciidoc | 20 + .../b918d6b798da673a33e49b94f61dcdc0.asciidoc | 16 + .../b919f88e6f47a40d5793479440a90ba6.asciidoc | 66 + .../b93ed4ef309819734f0eeea82e8b0f1f.asciidoc | 18 + .../b94cee0f74f57742b3948f9b784dfdd4.asciidoc | 12 + .../b997885974522ef439d5e345924cc5ba.asciidoc | 20 + .../b9a153725b28fdd0a5aabd7f17a8c2d7.asciidoc | 7 + .../b9c5d7ca6ca9c6f747201f45337a4abf.asciidoc | 10 + .../ba0b4081c98f3387f76b77847c52ee9a.asciidoc | 22 + .../bb143628fd04070683eeeadc9406d9cc.asciidoc | 15 + .../bc1ad5cc6d3eab98e3ce01f209ba7094.asciidoc | 13 + .../bd5918ab903c0889bb1f09c8c2466e43.asciidoc | 10 + .../bdb30dd52d32f50994008f4f9c0da5f0.asciidoc | 9 + .../be1bd47393646ac6bbee177d1cdb7738.asciidoc | 17 + .../be3a6431d01846950dc1a39a7a6a1faa.asciidoc | 7 + .../be8f28f31207b173de61be032fcf239c.asciidoc | 7 + .../bfcd65ab85d684d36a8550080032958d.asciidoc | 7 + .../bfdad8a928ea30d7cf60d0a0a6bc6e2e.asciidoc | 17 + .../c22b72c4a52ee098331b3f252c22860d.asciidoc | 9 + .../c2c21e2824fbf6b7198ede30419da82b.asciidoc | 7 + .../c32a3f8071d87f0a3f5a78e07fe7a669.asciidoc | 11 + .../c48264ec5d9b9679fddd72e5c44425b9.asciidoc | 7 + .../c4b278ba293abd0d02a0b5ad1a99f84a.asciidoc | 16 + .../c5e5873783246c7b1c01d8464fed72c4.asciidoc | 7 + .../c612d93e7f682a0d731e385edf9f5d56.asciidoc | 10 + .../c849c6c8f8659dbb93e1c14356f74e37.asciidoc | 10 + .../cb01106bf524df5e0501d4c655c1aa7b.asciidoc | 14 + .../cd247f267968aa0927bfdad56852f8f5.asciidoc | 9 + .../cd5bc5bf7cd58d7b1492c9c298b345f6.asciidoc | 22 + .../cde4dddae5c06e7f1d38c9d933dbc7ac.asciidoc | 7 + .../cdedd5f33f7e5f7acde561e97bff61de.asciidoc | 11 + .../cf02e3d8b371bd59f0224967c36330da.asciidoc | 7 + .../cfbaea6f0df045c5d940bbb6a9c69cd8.asciidoc | 18 + .../cfc37446bd892d1ac42a3c8e8b204e6c.asciidoc | 7 + .../d0a8a938a2fa913b6fdbc871079a59dd.asciidoc | 9 + .../d17269bb80fb63ec0bf37d219e003dcb.asciidoc | 23 + .../d1b3b7d2bb2ab90d15fd10318abd24db.asciidoc | 19 + .../d1bcf2eb63a462bfdcf01a68e68d5b4a.asciidoc | 17 + .../d222c6a6ec7a3beca6c97011b0874512.asciidoc | 12 + .../d3016e4e8025362ad9a05ee86bb2061f.asciidoc | 9 + .../d3088d5fa59b3ab110f64fb4f9b0065c.asciidoc | 9 + .../d31062ff8c015387889fed4ad86fd914.asciidoc | 19 + .../d4b4cefba4318caeba7480187faf2b13.asciidoc | 9 + .../d50a3c64890f88af32c6d4ef4899d82a.asciidoc | 20 + .../d5dcddc6398b473b6ad9bce5c6adf986.asciidoc | 7 + .../d718b63cf1b6591a1d59a0cf4fd995eb.asciidoc | 16 + .../d8b115341da772a628a024e7d1644e73.asciidoc | 7 + .../d8b2a88b5eca99d3691ad3cd40266736.asciidoc | 18 + .../d90a84a24a407731dfc1929ac8327746.asciidoc | 7 + .../d9474f66970c6955e24b17c7447e7b5f.asciidoc | 16 + .../db6cba451ba562abe953d09ad80cc15c.asciidoc | 13 + .../dc15e2373e5ecbe09b4ea0858eb63d47.asciidoc | 28 + .../de139866a220124360e5e27d1a736ea4.asciidoc | 23 + .../de176bc4788ea286fff9e92418a43ea8.asciidoc | 20 + .../df17f920b0deab3529b98df88b781f55.asciidoc | 27 + .../dfac8d098b50aa0181161bcd17b38ef4.asciidoc | 9 + .../dfb1fe96d806a644214d06f9b4b87878.asciidoc | 11 + .../dfef545b1e2c247bafd1347e8e807ac1.asciidoc | 13 + .../e0d6e02b998bdea99c9c08dcc3630c5e.asciidoc | 9 + .../e17e8852ec3f31781e1364f4dffeb6d0.asciidoc | 15 + .../e21e1c26dc8687e7bf7bd2bf019a6698.asciidoc | 11 + .../e270f3f721a5712cd11a5ca03554f5b0.asciidoc | 18 + .../e2a042c629429855c3bcaefffb26b7fa.asciidoc | 19 + .../e30ea6e3823a139d7693d8cce1920a06.asciidoc | 16 + .../e4be53736bcc02b03068fd72fdbfe271.asciidoc | 9 + .../e567e6dbf86300142573c73789c8fce4.asciidoc | 9 + .../e5d2172b524332196cac0f031c043659.asciidoc | 14 + .../e5f50b31f165462d883ecbff45f74985.asciidoc | 23 + .../e8e451bc8c45bcf16df43804c4fc8329.asciidoc | 17 + .../e9c2e15b36372d5281c879d336322b6c.asciidoc | 12 + .../e9fe608f105d7e3268a15e409e2cb9ab.asciidoc | 41 + .../ea02de2dbe05091fcb0dac72c8ba5f83.asciidoc | 12 + .../eb30ba547e4a7b8f54f33ab259aca523.asciidoc | 11 + .../ebb6b59fbc9325c17e45f524602d6be2.asciidoc | 10 + .../ec27afee074001b0e4e393611010842b.asciidoc | 22 + .../ec473de07fe89bcbac1f8e278617fe46.asciidoc | 20 + .../ef0f4fa4272c47ff62fb7b422cf975e7.asciidoc | 12 + .../ef9111c1648d7820925f12e07d1346c5.asciidoc | 19 + .../f085fb032dae56a3b104ab874eaea2ad.asciidoc | 11 + .../f0e21e03a07c8fa0209b0aafdb3791e6.asciidoc | 14 + .../f29a28fffa7ec604a33a838f48f7ea79.asciidoc | 22 + .../f2d68493abd3ca430bd03a7f7f8d18f9.asciidoc | 16 + .../f32f0c19b42de3b87dd764fe4ca17e7c.asciidoc | 17 + .../f4a1008b3f9baa67bb03ce9ef5ab4cb4.asciidoc | 10 + .../f6b5032bf27c2445d28845be0d413970.asciidoc | 10 + .../f6d6889667f56b8f49d2858070571a6b.asciidoc | 20 + .../f70a54cd9a9f4811bf962e469f2ca2ea.asciidoc | 9 + .../f8cc4b331a19ff4df8e4a490f906ee69.asciidoc | 7 + .../f9636d7ef1a45be4f36418c875cf6bef.asciidoc | 24 + .../fa0f4485cd48f986b7ae8cbb24e331c4.asciidoc | 20 + .../fa2fe60f570bd930d2891778c6efbfe6.asciidoc | 9 + .../fa88f6f5a7d728ec4f1d05244228cb09.asciidoc | 16 + .../fabe14480624a99e8ee42c7338672058.asciidoc | 7 + .../fbcf5078a6a9e09790553804054c36b3.asciidoc | 7 + .../fc8097bdfb6f3a4017bf4186ccca8a84.asciidoc | 45 + .../fdcaba9547180439ff4b6275034a5170.asciidoc | 14 + .../fdd38f0d248385a444c777e7acd97846.asciidoc | 17 + .../fe5763d32955e8b65eb3048e97b1580c.asciidoc | 7 + .../feefeb68144002fd1fff57b77b95b85e.asciidoc | 13 + docs/getting-started.mdx | 78 - docs/guide/configuration.asciidoc | 440 ++ docs/guide/connecting.asciidoc | 439 ++ docs/guide/examples.asciidoc | 111 + docs/guide/getting-started.asciidoc | 153 + docs/guide/helpers.asciidoc | 90 + docs/guide/images/create-api-key.png | Bin 0 -> 80572 bytes docs/guide/images/es-endpoint.jpg | Bin 0 -> 369643 bytes docs/guide/index-custom-title-page.html | 185 + docs/guide/index.asciidoc | 25 + docs/guide/installation.asciidoc | 20 + docs/guide/integrations.asciidoc | 55 + docs/guide/overview.asciidoc | 88 + docs/guide/release-notes.asciidoc | 492 ++ docs/images/copy-endpoint.gif | Bin 335063 -> 0 bytes docs/images/setup-api-key.gif | Bin 357690 -> 0 bytes docs/landing-page.mdx | 22 - docs/questions-and-assumptions.md | 31 - docs/sphinx/Makefile | 177 + docs/sphinx/api.rst | 223 + docs/sphinx/async.rst | 225 + docs/sphinx/conf.py | 60 + docs/sphinx/exceptions.rst | 36 + docs/sphinx/helpers.rst | 135 + docs/sphinx/index.rst | 124 + elasticsearch_serverless/__init__.py | 91 + elasticsearch_serverless/_async/__init__.py | 16 + .../_async/client/__init__.py | 4058 +++++++++++++++++ .../_async/client/_base.py | 391 ++ .../_async/client/async_search.py | 603 +++ .../_async/client/autoscaling.py | 175 + elasticsearch_serverless/_async/client/cat.py | 1161 +++++ elasticsearch_serverless/_async/client/ccr.py | 749 +++ .../_async/client/cluster.py | 539 +++ .../_async/client/dangling_indices.py | 162 + .../_async/client/enrich.py | 222 + elasticsearch_serverless/_async/client/eql.py | 298 ++ .../_async/client/features.py | 88 + .../_async/client/fleet.py | 631 +++ .../_async/client/graph.py | 96 + elasticsearch_serverless/_async/client/ilm.py | 543 +++ .../_async/client/indices.py | 2795 ++++++++++++ .../_async/client/ingest.py | 295 ++ .../_async/client/license.py | 294 ++ .../_async/client/logstash.py | 143 + .../_async/client/migration.py | 127 + elasticsearch_serverless/_async/client/ml.py | 3306 ++++++++++++++ .../_async/client/monitoring.py | 87 + .../_async/client/nodes.py | 483 ++ .../_async/client/rollup.py | 440 ++ .../_async/client/search_application.py | 348 ++ .../_async/client/searchable_snapshots.py | 265 ++ .../_async/client/security.py | 453 ++ .../_async/client/shutdown.py | 229 + elasticsearch_serverless/_async/client/slm.py | 377 ++ .../_async/client/snapshot.py | 773 ++++ elasticsearch_serverless/_async/client/sql.py | 373 ++ elasticsearch_serverless/_async/client/ssl.py | 57 + .../_async/client/tasks.py | 208 + .../_async/client/text_structure.py | 158 + .../_async/client/transform.py | 690 +++ .../_async/client/utils.py | 44 + .../_async/client/watcher.py | 607 +++ .../_async/client/xpack.py | 111 + elasticsearch_serverless/_async/helpers.py | 588 +++ elasticsearch_serverless/_sync/__init__.py | 16 + .../_sync/client/__init__.py | 4056 ++++++++++++++++ .../_sync/client/_base.py | 391 ++ .../_sync/client/async_search.py | 603 +++ .../_sync/client/autoscaling.py | 175 + elasticsearch_serverless/_sync/client/cat.py | 1161 +++++ elasticsearch_serverless/_sync/client/ccr.py | 749 +++ .../_sync/client/cluster.py | 539 +++ .../_sync/client/dangling_indices.py | 162 + .../_sync/client/enrich.py | 222 + elasticsearch_serverless/_sync/client/eql.py | 298 ++ .../_sync/client/features.py | 88 + .../_sync/client/fleet.py | 631 +++ .../_sync/client/graph.py | 96 + elasticsearch_serverless/_sync/client/ilm.py | 543 +++ .../_sync/client/indices.py | 2795 ++++++++++++ .../_sync/client/ingest.py | 295 ++ .../_sync/client/license.py | 294 ++ .../_sync/client/logstash.py | 143 + .../_sync/client/migration.py | 127 + elasticsearch_serverless/_sync/client/ml.py | 3306 ++++++++++++++ .../_sync/client/monitoring.py | 87 + .../_sync/client/nodes.py | 483 ++ .../_sync/client/rollup.py | 440 ++ .../_sync/client/search_application.py | 348 ++ .../_sync/client/searchable_snapshots.py | 265 ++ .../_sync/client/security.py | 453 ++ .../_sync/client/shutdown.py | 229 + elasticsearch_serverless/_sync/client/slm.py | 377 ++ .../_sync/client/snapshot.py | 773 ++++ elasticsearch_serverless/_sync/client/sql.py | 373 ++ elasticsearch_serverless/_sync/client/ssl.py | 57 + .../_sync/client/tasks.py | 208 + .../_sync/client/text_structure.py | 158 + .../_sync/client/transform.py | 690 +++ .../_sync/client/utils.py | 443 ++ .../_sync/client/watcher.py | 607 +++ .../_sync/client/xpack.py | 111 + elasticsearch_serverless/_utils.py | 34 + elasticsearch_serverless/_version.py | 18 + elasticsearch_serverless/client.py | 112 + elasticsearch_serverless/compat.py | 79 + elasticsearch_serverless/exceptions.py | 129 + elasticsearch_serverless/helpers/__init__.py | 41 + elasticsearch_serverless/helpers/actions.py | 845 ++++ elasticsearch_serverless/helpers/errors.py | 32 + elasticsearch_serverless/py.typed | 0 elasticsearch_serverless/serializer.py | 203 + elasticsearch_serverless/transport.py | 57 + noxfile.py | 4 +- setup.py | 2 +- .../test_types/README.md | 6 + .../test_types/aliased_types.py | 169 + .../test_types/async_types.py | 105 + .../test_types/sync_types.py | 98 + utils/build-dists.py | 296 ++ utils/bump-version.py | 85 + utils/generate-examples.py | 181 + utils/license-headers.py | 123 + utils/run-black.py | 88 + utils/run-unasync.py | 58 + 479 files changed, 57175 insertions(+), 133 deletions(-) create mode 100644 .ci/Dockerfile create mode 100755 .ci/make.sh create mode 100644 docs/examples/014b788c879e4aaa1020672e45e25473.asciidoc create mode 100644 docs/examples/015294a400986295039e52ebc62033be.asciidoc create mode 100644 docs/examples/025b54db0edc50c24ea48a2bd94366ad.asciidoc create mode 100644 docs/examples/028f6d6ac2594e20b78b8a8f8cbad49d.asciidoc create mode 100644 docs/examples/033778305d52746f5ce0a2a922c8e521.asciidoc create mode 100644 docs/examples/047266b0d20fdb62ebc72d51952c8f6d.asciidoc create mode 100644 docs/examples/048d8abd42d094bbdcf4452a58ccb35b.asciidoc create mode 100644 docs/examples/04f5dd677c777bcb15d7d5fa63275fc8.asciidoc create mode 100644 docs/examples/04fe1e3a0047b0cdb10987b79fc3f3f3.asciidoc create mode 100644 docs/examples/06afce2955f9094d96d27067ebca32e8.asciidoc create mode 100644 docs/examples/073539a7e38be3cdf13008330b6a536a.asciidoc create mode 100644 docs/examples/0989cc65d8924f666ce3eb0820d2d244.asciidoc create mode 100644 docs/examples/09cdd5ae8114c49886026fef8d00a19c.asciidoc create mode 100644 docs/examples/09d617863a103c82fb4101e6165ea7fe.asciidoc create mode 100644 docs/examples/09dbd90c5e22ea4a17b4cf9aa72e08ae.asciidoc create mode 100644 docs/examples/09ecba5814d71e4c44468575eada9878.asciidoc create mode 100644 docs/examples/0a958e486ede3f519d48431ab689eded.asciidoc create mode 100644 docs/examples/0ac9916f47a2483b89c1416684af322a.asciidoc create mode 100644 docs/examples/0afaf1cad692e6201aa574c8feb6e622.asciidoc create mode 100644 docs/examples/0ba0b2db24852abccb7c0fc1098d566e.asciidoc create mode 100644 docs/examples/0bbd30b9be3e54ff3028b9f4459634d2.asciidoc create mode 100644 docs/examples/0bd3923424a20a4ba860b0774b9991b1.asciidoc create mode 100644 docs/examples/0be2c28ee65384774b1e479b47dc3d92.asciidoc create mode 100644 docs/examples/0c4ad860a485fe53d8140ad3ccd11dcf.asciidoc create mode 100644 docs/examples/0cc991e3f7f8511a34730e154b3c5edc.asciidoc create mode 100644 docs/examples/0ce3606f1dba490eef83c4317b315b62.asciidoc create mode 100644 docs/examples/0d664883151008b1051ef2c9ab2d0373.asciidoc create mode 100644 docs/examples/0e118857b815b62118a30c042f079db1.asciidoc create mode 100644 docs/examples/1027ab1ca767ac1428176ef4f84bfbcf.asciidoc create mode 100644 docs/examples/1216f8f7367df3aa823012cef310c08a.asciidoc create mode 100644 docs/examples/12433d2b637d002e8d5c9a1adce69d3b.asciidoc create mode 100644 docs/examples/1252fa45847edba5ec2b2f33da70ec5b.asciidoc create mode 100644 docs/examples/138ccd89f72aa7502dd9578403dcc589.asciidoc create mode 100644 docs/examples/14701dcc0cca9665fce2aace0cb62af7.asciidoc create mode 100644 docs/examples/1577e6e806b3283c9e99f1596d310754.asciidoc create mode 100644 docs/examples/15dad5338065baaaa7d475abe85f4c22.asciidoc create mode 100644 docs/examples/162b5b693b713f0bfab1209d59443c46.asciidoc create mode 100644 docs/examples/179f0a3e84ff4bbac18787a018eabf89.asciidoc create mode 100644 docs/examples/17de0020b228df961ad3c6b06233c948.asciidoc create mode 100644 docs/examples/189a921df2f5b1fe580937210ce9c1c2.asciidoc create mode 100644 docs/examples/18ddb7e7a4bcafd449df956e828ed7a8.asciidoc create mode 100644 docs/examples/1aa91d3d48140d6367b6cabca8737b8f.asciidoc create mode 100644 docs/examples/1b542e3ea87a742f95641d64dcfb1bdb.asciidoc create mode 100644 docs/examples/1b8655e6ba99fe39933c6eafe78728b7.asciidoc create mode 100644 docs/examples/1b8caf0a6741126c6d0ad83b56fce290.asciidoc create mode 100644 docs/examples/1bc731a4df952228af6dfa6b48627332.asciidoc create mode 100644 docs/examples/1c23507edd7a3c18538b68223378e4ab.asciidoc create mode 100644 docs/examples/1d65cb6d055c46a1bde809687d835b71.asciidoc create mode 100644 docs/examples/1da77e114459e0b77d78a3dcc8fae429.asciidoc create mode 100644 docs/examples/1dbb8cf17fbc45c87c7d2f75f15f9778.asciidoc create mode 100644 docs/examples/1e18a67caf8f06ff2710ec4a8b30f625.asciidoc create mode 100644 docs/examples/1e49eba5b9042c1900a608fe5105ba43.asciidoc create mode 100644 docs/examples/1e50d993bd6517e6c381e82d09f0389e.asciidoc create mode 100644 docs/examples/1f336ecc62480c1d56351cc2f82d0d08.asciidoc create mode 100644 docs/examples/1f6fe6833686e38c3711c6f2aa00a078.asciidoc create mode 100644 docs/examples/210cf5c76bff517f48e80fa1c2d63907.asciidoc create mode 100644 docs/examples/213ab768f1b6a895e09403a0880e259a.asciidoc create mode 100644 docs/examples/216848930c2d344fe0bed0daa70c35b9.asciidoc create mode 100644 docs/examples/22334f4b24bb8977d3e1bf2ffdc29d3f.asciidoc create mode 100644 docs/examples/231aa0bb39c35fe199d28fe0e4a62b2e.asciidoc create mode 100644 docs/examples/23ab0f1023b1b2cd5cdf2a8f9ccfd57b.asciidoc create mode 100644 docs/examples/2468ab381257d759d8a88af1141f6f9c.asciidoc create mode 100644 docs/examples/251ea12c1248385ab409906ac64d9ee9.asciidoc create mode 100644 docs/examples/2533e4b36ae837eaecda08407ecb6383.asciidoc create mode 100644 docs/examples/2891aa10ee9d474780adf94d5607f2db.asciidoc create mode 100644 docs/examples/28aad2c5942bfb221c2bf1bbdc01658e.asciidoc create mode 100644 docs/examples/2a1de18774f9c68cafa169847832b2bc.asciidoc create mode 100644 docs/examples/2bb2339ac055337abf753bddb7771659.asciidoc create mode 100644 docs/examples/2fd69fb0538e4f36ac69a8b8f8bf5ae8.asciidoc create mode 100644 docs/examples/2fe28d9a91b3081a9ec4601af8fb7b1c.asciidoc create mode 100644 docs/examples/311c4b632a29b9ead63b02d01f10096b.asciidoc create mode 100644 docs/examples/3342c69b2c2303247217532956fcce85.asciidoc create mode 100644 docs/examples/33f148e3d8676de6cc52f58749898a13.asciidoc create mode 100644 docs/examples/34efeade38445b2834749ced59782e25.asciidoc create mode 100644 docs/examples/35e8da9410b8432cf4095f2541ad7b1d.asciidoc create mode 100644 docs/examples/3653567181f43a5f64c74f934aa821c2.asciidoc create mode 100644 docs/examples/36818c6d9f434d387819c30bd9addb14.asciidoc create mode 100644 docs/examples/36b2778f23d0955255f52c075c4d213d.asciidoc create mode 100644 docs/examples/3722cb3705b6bc7f486969deace3dd83.asciidoc create mode 100644 docs/examples/381fced1882ca8337143e6bb180a5715.asciidoc create mode 100644 docs/examples/38c1d0f6668e9563c0827f839f9fa505.asciidoc create mode 100644 docs/examples/39a6a038c4b551022afe83de0523634e.asciidoc create mode 100644 docs/examples/3a700f836d8d5da1b656a876554028aa.asciidoc create mode 100644 docs/examples/3ae03ba3b56e5e287953094050766738.asciidoc create mode 100644 docs/examples/3b04cc894e6a47d57983484010feac0c.asciidoc create mode 100644 docs/examples/3cd50a789b8e1f0ebbbc53a8d7ecf656.asciidoc create mode 100644 docs/examples/3d1ff6097e2359f927c88c2ccdb36252.asciidoc create mode 100644 docs/examples/3e573bfabe00f8bfb8bb69aa5820768e.asciidoc create mode 100644 docs/examples/3f3b3e207f79303ce6f86e03e928e062.asciidoc create mode 100644 docs/examples/400e89eb46ead8e9c9e40f123fd5e590.asciidoc create mode 100644 docs/examples/42744a175125df5be0ef77413bf8f608.asciidoc create mode 100644 docs/examples/427f6b5c5376cbf0f71f242a60ca3d9e.asciidoc create mode 100644 docs/examples/43af86de5e49aa06070092fffc138208.asciidoc create mode 100644 docs/examples/4646764bf09911fee7d58630c72d3137.asciidoc create mode 100644 docs/examples/46658f00edc4865dfe472a392374cd0f.asciidoc create mode 100644 docs/examples/46c4b0dfb674825f9579203d41e7f404.asciidoc create mode 100644 docs/examples/47b5ff897f26e9c943cee5c06034181d.asciidoc create mode 100644 docs/examples/47bb632c6091ad0cd94bc660bdd309a5.asciidoc create mode 100644 docs/examples/4acf902c2598b2558f34f20c1744c433.asciidoc create mode 100644 docs/examples/4b90feb9d5d3dbfce424dac0341320b7.asciidoc create mode 100644 docs/examples/4cd246e5c4c035a2cd4081ae9a3d54e5.asciidoc create mode 100644 docs/examples/4d46dbb96125b27f46299547de9d8709.asciidoc create mode 100644 docs/examples/4d56b179242fed59e3d6476f817b6055.asciidoc create mode 100644 docs/examples/4d6997c70a1851f9151443c0d38b532e.asciidoc create mode 100644 docs/examples/5043b83a89091fa00edb341ddf7ba370.asciidoc create mode 100644 docs/examples/506844befdc5691d835771bcbb1c1a60.asciidoc create mode 100644 docs/examples/5271f4ff29bb48838396e5a674664ee0.asciidoc create mode 100644 docs/examples/527324766814561b75aaee853ede49a7.asciidoc create mode 100644 docs/examples/5275842787967b6db876025f4a1c6942.asciidoc create mode 100644 docs/examples/52a87b81e4e0b6b11e23e85db1602a63.asciidoc create mode 100644 docs/examples/52b2bfbdd78f8283b6f4891c48013237.asciidoc create mode 100644 docs/examples/52c7e4172a446c394210a07c464c57d2.asciidoc create mode 100644 docs/examples/53b908c3432118c5a6e460f74d32006b.asciidoc create mode 100644 docs/examples/53d938c754f36a912fcbe6473abb463f.asciidoc create mode 100644 docs/examples/54092c8c646133f5dbbc047990dd458d.asciidoc create mode 100644 docs/examples/54a770f053f3225ea0d1e34334232411.asciidoc create mode 100644 docs/examples/58b5003c0a53a39bf509aa3797aad471.asciidoc create mode 100644 docs/examples/58df61acbfb15b8ef0aaa18b81ae98a6.asciidoc create mode 100644 docs/examples/5925c23a173a63bdb30b458248d1df76.asciidoc create mode 100644 docs/examples/5be23858b35043fcb7b50fe36b873e6e.asciidoc create mode 100644 docs/examples/5c2f486c27bd5346e512265f93375d16.asciidoc create mode 100644 docs/examples/5d32279dcd52b22d9e1178a02a3ad957.asciidoc create mode 100644 docs/examples/5d9d7b84e2fec7ecd832145cbb951cf1.asciidoc create mode 100644 docs/examples/5da6efd5b038ada64c9e853c88c1ec47.asciidoc create mode 100644 docs/examples/5dd695679b5141d9142d3d30ba8d300a.asciidoc create mode 100644 docs/examples/5eabcdbf61bfcb484dc694f25c2bba36.asciidoc create mode 100644 docs/examples/5f210f74725ea0c9265190346edfa246.asciidoc create mode 100644 docs/examples/5f3549ac7fee94682ca0d7439eebdd2a.asciidoc create mode 100644 docs/examples/5f3a3eefeefe6fa85ec49d499212d245.asciidoc create mode 100644 docs/examples/609260ad1d5998be2ca09ff1fe237efa.asciidoc create mode 100644 docs/examples/60ee33f3acfdd0fe6f288ac77312c780.asciidoc create mode 100644 docs/examples/6138d6919f3cbaaf61e1092f817d295c.asciidoc create mode 100644 docs/examples/618d5f3d35921d8cb7e9ccfbe9a4c3e3.asciidoc create mode 100644 docs/examples/621665fdbd7fc103c09bfeed28b67b1a.asciidoc create mode 100644 docs/examples/625dc94df1f9affb49a082fd99d41620.asciidoc create mode 100644 docs/examples/626f8c4b3e2cd3d9beaa63a7f5799d7a.asciidoc create mode 100644 docs/examples/645136747d37368a14ab34de8bd046c6.asciidoc create mode 100644 docs/examples/645796e8047967ca4a7635a22a876f4c.asciidoc create mode 100644 docs/examples/645c4c6e209719d3a4d25b1a629cb23b.asciidoc create mode 100644 docs/examples/6464124d1677f4552ddddd95a340ca3a.asciidoc create mode 100644 docs/examples/64b9baa6d7556b960b29698f3383aa31.asciidoc create mode 100644 docs/examples/6799d132c1c7ca3970763acde2337ef9.asciidoc create mode 100644 docs/examples/67bba546d835bca8f31df13e3587c348.asciidoc create mode 100644 docs/examples/67ceac4bf2d9ac7cc500390544cdcb41.asciidoc create mode 100644 docs/examples/68721288dc9ad8aa1b55099b4d303051.asciidoc create mode 100644 docs/examples/68738b4fd0dda177022be45be95b4c84.asciidoc create mode 100644 docs/examples/69a7be47f85138b10437113ab2f0d72d.asciidoc create mode 100644 docs/examples/6a1702dd50690cae833572e48a0ddf25.asciidoc create mode 100644 docs/examples/6a4679531e64c492fce16dc12de6dcb0.asciidoc create mode 100644 docs/examples/6a81d00f0d73bc5985e76b3cadab645e.asciidoc create mode 100644 docs/examples/6bbc613bd4f9aec1bbdbabf5db021d28.asciidoc create mode 100644 docs/examples/6be70810d6ebd6f09d8a49f9df847765.asciidoc create mode 100644 docs/examples/6bf63f2ec6ba55fcaf1092f48212bf25.asciidoc create mode 100644 docs/examples/6d1e75312a28a5ba23837abf768f2510.asciidoc create mode 100644 docs/examples/6f097c298a7abf4c032c4314920c49c8.asciidoc create mode 100644 docs/examples/6f21a878fee3b43c5332b81aaddbeac7.asciidoc create mode 100644 docs/examples/6faf10a73f7d5fffbcb037bdb2cbaff8.asciidoc create mode 100644 docs/examples/70f0aa5853697e265ef3b1df72940951.asciidoc create mode 100644 docs/examples/710c7871f20f176d51209b1574b0d61b.asciidoc create mode 100644 docs/examples/71b5b2ba9557d0f296ff2de91727d2f6.asciidoc create mode 100644 docs/examples/71ba9033107882f61cdc3b32fc73568d.asciidoc create mode 100644 docs/examples/72231b7debac60c95b9869a97dafda3a.asciidoc create mode 100644 docs/examples/72beebe779a258c225dee7b023e60c52.asciidoc create mode 100644 docs/examples/734c2e2a1e45b84f1e4e65b51356fcd7.asciidoc create mode 100644 docs/examples/73e5c88ad1488b213fb278ee1cb42289.asciidoc create mode 100644 docs/examples/745f9b8cdb8e91073f6e520e1d9f8c05.asciidoc create mode 100644 docs/examples/7477671958734843dd67cf0b8e6c7515.asciidoc create mode 100644 docs/examples/75330ec1305d2beb0e2f34d2195464e2.asciidoc create mode 100644 docs/examples/764f9884b370cbdc82a1c5c42ed40ff3.asciidoc create mode 100644 docs/examples/77243bbf92f2a55e0fca6c2a349a1c15.asciidoc create mode 100644 docs/examples/774d715155cd13713e6e327adf6ce328.asciidoc create mode 100644 docs/examples/78c96113ae4ed0054e581b17542528a7.asciidoc create mode 100644 docs/examples/7b908b1189f076942de8cd497ff1fa59.asciidoc create mode 100644 docs/examples/7cac05cb589f1614fd5b8589153bef06.asciidoc create mode 100644 docs/examples/7cf71671859be7c1ecf673396db377cd.asciidoc create mode 100644 docs/examples/7df191cc7f814e410a4ac7261065e6ef.asciidoc create mode 100644 docs/examples/7e52bec09624cf6c0de5d13f2bfad5a5.asciidoc create mode 100644 docs/examples/7f28f8ae8fcdbd807dadde0b5b007a6d.asciidoc create mode 100644 docs/examples/7f465b7e8ed42df6c42251b4481e699e.asciidoc create mode 100644 docs/examples/7f56755fb6c42f7e6203339a6d0cb6e6.asciidoc create mode 100644 docs/examples/7f697eb436dfa3c30dfe610d8c32d132.asciidoc create mode 100644 docs/examples/804a97ff4d0613e6568e4efb19c52021.asciidoc create mode 100644 docs/examples/81c9aa2678d6166a9662ddf2c011a6a5.asciidoc create mode 100644 docs/examples/83f95657beca9bf5d8264c80c7fb463f.asciidoc create mode 100644 docs/examples/8653e76676de5d327201b77512afa3a0.asciidoc create mode 100644 docs/examples/873fbbc6ab81409058591385fd602736.asciidoc create mode 100644 docs/examples/8871b8fcb6de4f0c7dff22798fb10fb7.asciidoc create mode 100644 docs/examples/899eef71a67a1b2aa11a2166ec7f48f1.asciidoc create mode 100644 docs/examples/89a8ac1509936acc272fc2d72907bc45.asciidoc create mode 100644 docs/examples/8a355eb25d2a01ba62dc1a22dd46f46f.asciidoc create mode 100644 docs/examples/8acc1d67b152e7027e0f0e1a8b4b2431.asciidoc create mode 100644 docs/examples/8baccd8688a6bad1749b8935f9601ea4.asciidoc create mode 100644 docs/examples/8c5977410335d58217e0626618ce6641.asciidoc create mode 100644 docs/examples/8cd00a3aba7c3c158277bc032aac2830.asciidoc create mode 100644 docs/examples/8d9a63d7c31f08bd27d92ece3de1649c.asciidoc create mode 100644 docs/examples/8de3206f80e18185a5ad6481f4c2ee07.asciidoc create mode 100644 docs/examples/8e6bfb4441ffa15c86d5dc20fa083571.asciidoc create mode 100644 docs/examples/8eaf4d5dd4ab1335deefa7749fdbbcc3.asciidoc create mode 100644 docs/examples/8f0511f8a5cb176ff2afdd4311799a33.asciidoc create mode 100644 docs/examples/8fdf2344c4fb3de6902ad7c5735270df.asciidoc create mode 100644 docs/examples/913770050ebbf3b9b549a899bc11060a.asciidoc create mode 100644 docs/examples/9166cf38427d5cde5d2ec12a2012b669.asciidoc create mode 100644 docs/examples/93f1bdd72e79827dcf9a34efa02fd977.asciidoc create mode 100644 docs/examples/9524a9b7373fa4eb2905183b0e806962.asciidoc create mode 100644 docs/examples/96de5703ba0bd43fd4ac239ec5408542.asciidoc create mode 100644 docs/examples/973a3ff47fc4ce036ecd9bd363fef9f7.asciidoc create mode 100644 docs/examples/978088f989d45dd09339582e9cbc60e0.asciidoc create mode 100644 docs/examples/979d25dff2d8987119410291ad47b0d1.asciidoc create mode 100644 docs/examples/97babc8d19ef0866774576716eb6d19e.asciidoc create mode 100644 docs/examples/98234499cfec70487cec5d013e976a84.asciidoc create mode 100644 docs/examples/98aeb275f829b5f7b8eb2147701565ff.asciidoc create mode 100644 docs/examples/98b121bf47cebd85671a2cb519688d28.asciidoc create mode 100644 docs/examples/98f14fddddea54a7d6149ab7b92e099d.asciidoc create mode 100644 docs/examples/99a52be903945b17e734a1d02a57e958.asciidoc create mode 100644 docs/examples/9a4d5e41c52c20635d1fd9c6e13f6c7a.asciidoc create mode 100644 docs/examples/9a8995fd31351045d99c78e40444c8ea.asciidoc create mode 100644 docs/examples/9e56d79ad9a02b642c361f0b85dd95d7.asciidoc create mode 100644 docs/examples/a116949e446f34dc25ae57d4b703d0c1.asciidoc create mode 100644 docs/examples/a1db5c822745fe167e9ef854dca3d129.asciidoc create mode 100644 docs/examples/a2a25aad1fea9a541b52ac613c78fb64.asciidoc create mode 100644 docs/examples/a34d70d7022eb4ba48909d440c80390f.asciidoc create mode 100644 docs/examples/a42f33e15b0995bb4b6058659bfdea85.asciidoc create mode 100644 docs/examples/a49169b4622918992411fab4ec48191b.asciidoc create mode 100644 docs/examples/a4a396cd07657b3977713fb3a742c41b.asciidoc create mode 100644 docs/examples/a5a7050fb9dcb9574e081957ade28617.asciidoc create mode 100644 docs/examples/a6f8636b03cc5f677b7d89e750328612.asciidoc create mode 100644 docs/examples/a71c438cc4df1cafe3109ccff475afdb.asciidoc create mode 100644 docs/examples/a7c15fe6b5779c84ce9a34bf4b2a7ab7.asciidoc create mode 100644 docs/examples/a80f5db4357bb25b8704d374c18318ed.asciidoc create mode 100644 docs/examples/a8fba09a46b2c3524428aa3259b7124f.asciidoc create mode 100644 docs/examples/aa6bfe54e2436eb668091fe31c2fbf4d.asciidoc create mode 100644 docs/examples/abd4fc3ce7784413a56fe2dcfe2809b5.asciidoc create mode 100644 docs/examples/abf329ebefaf58acd4ee30e685731499.asciidoc create mode 100644 docs/examples/ac544eb247a29ca42aab13826ca88561.asciidoc create mode 100644 docs/examples/ad0dcbc7fc619e952c8825b8f307b7b2.asciidoc create mode 100644 docs/examples/ad6ea0c1e46712aa1fd6d3bfa0ec979e.asciidoc create mode 100644 docs/examples/ad79228630684d950fe9792a768d24c5.asciidoc create mode 100644 docs/examples/ae9b5fbd42af2386ffbf56ad4a697e51.asciidoc create mode 100644 docs/examples/ae9ccfaa146731ab9176df90670db1c2.asciidoc create mode 100644 docs/examples/af3fb9fa5691a7b37a6dc2a69ff66e64.asciidoc create mode 100644 docs/examples/afc29b61c532cf683f749baf013e7bfe.asciidoc create mode 100644 docs/examples/b02e4907c9936c1adc16ccce9d49900d.asciidoc create mode 100644 docs/examples/b0d64d0a554549e5b2808002a0725493.asciidoc create mode 100644 docs/examples/b0eaf67e5cce24ef8889bf20951ccec1.asciidoc create mode 100644 docs/examples/b0ec418bf416c62bed602b0a32a6d5f5.asciidoc create mode 100644 docs/examples/b1efa1c51a34dd5ab5511b71a399f5b1.asciidoc create mode 100644 docs/examples/b214942b938e47f2c486e523546cb574.asciidoc create mode 100644 docs/examples/b41dce56b0e640d32b1cf452f87cec17.asciidoc create mode 100644 docs/examples/b4392116f2cc57ce8064ccbad30318d5.asciidoc create mode 100644 docs/examples/b4a0d0ed512dffc10ee53bca2feca49b.asciidoc create mode 100644 docs/examples/b5f95bc097a201b29c7200fc8d3d31c1.asciidoc create mode 100644 docs/examples/b68c85fe1b0d2f264dc0d1cbf530f319.asciidoc create mode 100644 docs/examples/b789292f9cf63ce912e058c46d90ce20.asciidoc create mode 100644 docs/examples/b918d6b798da673a33e49b94f61dcdc0.asciidoc create mode 100644 docs/examples/b919f88e6f47a40d5793479440a90ba6.asciidoc create mode 100644 docs/examples/b93ed4ef309819734f0eeea82e8b0f1f.asciidoc create mode 100644 docs/examples/b94cee0f74f57742b3948f9b784dfdd4.asciidoc create mode 100644 docs/examples/b997885974522ef439d5e345924cc5ba.asciidoc create mode 100644 docs/examples/b9a153725b28fdd0a5aabd7f17a8c2d7.asciidoc create mode 100644 docs/examples/b9c5d7ca6ca9c6f747201f45337a4abf.asciidoc create mode 100644 docs/examples/ba0b4081c98f3387f76b77847c52ee9a.asciidoc create mode 100644 docs/examples/bb143628fd04070683eeeadc9406d9cc.asciidoc create mode 100644 docs/examples/bc1ad5cc6d3eab98e3ce01f209ba7094.asciidoc create mode 100644 docs/examples/bd5918ab903c0889bb1f09c8c2466e43.asciidoc create mode 100644 docs/examples/bdb30dd52d32f50994008f4f9c0da5f0.asciidoc create mode 100644 docs/examples/be1bd47393646ac6bbee177d1cdb7738.asciidoc create mode 100644 docs/examples/be3a6431d01846950dc1a39a7a6a1faa.asciidoc create mode 100644 docs/examples/be8f28f31207b173de61be032fcf239c.asciidoc create mode 100644 docs/examples/bfcd65ab85d684d36a8550080032958d.asciidoc create mode 100644 docs/examples/bfdad8a928ea30d7cf60d0a0a6bc6e2e.asciidoc create mode 100644 docs/examples/c22b72c4a52ee098331b3f252c22860d.asciidoc create mode 100644 docs/examples/c2c21e2824fbf6b7198ede30419da82b.asciidoc create mode 100644 docs/examples/c32a3f8071d87f0a3f5a78e07fe7a669.asciidoc create mode 100644 docs/examples/c48264ec5d9b9679fddd72e5c44425b9.asciidoc create mode 100644 docs/examples/c4b278ba293abd0d02a0b5ad1a99f84a.asciidoc create mode 100644 docs/examples/c5e5873783246c7b1c01d8464fed72c4.asciidoc create mode 100644 docs/examples/c612d93e7f682a0d731e385edf9f5d56.asciidoc create mode 100644 docs/examples/c849c6c8f8659dbb93e1c14356f74e37.asciidoc create mode 100644 docs/examples/cb01106bf524df5e0501d4c655c1aa7b.asciidoc create mode 100644 docs/examples/cd247f267968aa0927bfdad56852f8f5.asciidoc create mode 100644 docs/examples/cd5bc5bf7cd58d7b1492c9c298b345f6.asciidoc create mode 100644 docs/examples/cde4dddae5c06e7f1d38c9d933dbc7ac.asciidoc create mode 100644 docs/examples/cdedd5f33f7e5f7acde561e97bff61de.asciidoc create mode 100644 docs/examples/cf02e3d8b371bd59f0224967c36330da.asciidoc create mode 100644 docs/examples/cfbaea6f0df045c5d940bbb6a9c69cd8.asciidoc create mode 100644 docs/examples/cfc37446bd892d1ac42a3c8e8b204e6c.asciidoc create mode 100644 docs/examples/d0a8a938a2fa913b6fdbc871079a59dd.asciidoc create mode 100644 docs/examples/d17269bb80fb63ec0bf37d219e003dcb.asciidoc create mode 100644 docs/examples/d1b3b7d2bb2ab90d15fd10318abd24db.asciidoc create mode 100644 docs/examples/d1bcf2eb63a462bfdcf01a68e68d5b4a.asciidoc create mode 100644 docs/examples/d222c6a6ec7a3beca6c97011b0874512.asciidoc create mode 100644 docs/examples/d3016e4e8025362ad9a05ee86bb2061f.asciidoc create mode 100644 docs/examples/d3088d5fa59b3ab110f64fb4f9b0065c.asciidoc create mode 100644 docs/examples/d31062ff8c015387889fed4ad86fd914.asciidoc create mode 100644 docs/examples/d4b4cefba4318caeba7480187faf2b13.asciidoc create mode 100644 docs/examples/d50a3c64890f88af32c6d4ef4899d82a.asciidoc create mode 100644 docs/examples/d5dcddc6398b473b6ad9bce5c6adf986.asciidoc create mode 100644 docs/examples/d718b63cf1b6591a1d59a0cf4fd995eb.asciidoc create mode 100644 docs/examples/d8b115341da772a628a024e7d1644e73.asciidoc create mode 100644 docs/examples/d8b2a88b5eca99d3691ad3cd40266736.asciidoc create mode 100644 docs/examples/d90a84a24a407731dfc1929ac8327746.asciidoc create mode 100644 docs/examples/d9474f66970c6955e24b17c7447e7b5f.asciidoc create mode 100644 docs/examples/db6cba451ba562abe953d09ad80cc15c.asciidoc create mode 100644 docs/examples/dc15e2373e5ecbe09b4ea0858eb63d47.asciidoc create mode 100644 docs/examples/de139866a220124360e5e27d1a736ea4.asciidoc create mode 100644 docs/examples/de176bc4788ea286fff9e92418a43ea8.asciidoc create mode 100644 docs/examples/df17f920b0deab3529b98df88b781f55.asciidoc create mode 100644 docs/examples/dfac8d098b50aa0181161bcd17b38ef4.asciidoc create mode 100644 docs/examples/dfb1fe96d806a644214d06f9b4b87878.asciidoc create mode 100644 docs/examples/dfef545b1e2c247bafd1347e8e807ac1.asciidoc create mode 100644 docs/examples/e0d6e02b998bdea99c9c08dcc3630c5e.asciidoc create mode 100644 docs/examples/e17e8852ec3f31781e1364f4dffeb6d0.asciidoc create mode 100644 docs/examples/e21e1c26dc8687e7bf7bd2bf019a6698.asciidoc create mode 100644 docs/examples/e270f3f721a5712cd11a5ca03554f5b0.asciidoc create mode 100644 docs/examples/e2a042c629429855c3bcaefffb26b7fa.asciidoc create mode 100644 docs/examples/e30ea6e3823a139d7693d8cce1920a06.asciidoc create mode 100644 docs/examples/e4be53736bcc02b03068fd72fdbfe271.asciidoc create mode 100644 docs/examples/e567e6dbf86300142573c73789c8fce4.asciidoc create mode 100644 docs/examples/e5d2172b524332196cac0f031c043659.asciidoc create mode 100644 docs/examples/e5f50b31f165462d883ecbff45f74985.asciidoc create mode 100644 docs/examples/e8e451bc8c45bcf16df43804c4fc8329.asciidoc create mode 100644 docs/examples/e9c2e15b36372d5281c879d336322b6c.asciidoc create mode 100644 docs/examples/e9fe608f105d7e3268a15e409e2cb9ab.asciidoc create mode 100644 docs/examples/ea02de2dbe05091fcb0dac72c8ba5f83.asciidoc create mode 100644 docs/examples/eb30ba547e4a7b8f54f33ab259aca523.asciidoc create mode 100644 docs/examples/ebb6b59fbc9325c17e45f524602d6be2.asciidoc create mode 100644 docs/examples/ec27afee074001b0e4e393611010842b.asciidoc create mode 100644 docs/examples/ec473de07fe89bcbac1f8e278617fe46.asciidoc create mode 100644 docs/examples/ef0f4fa4272c47ff62fb7b422cf975e7.asciidoc create mode 100644 docs/examples/ef9111c1648d7820925f12e07d1346c5.asciidoc create mode 100644 docs/examples/f085fb032dae56a3b104ab874eaea2ad.asciidoc create mode 100644 docs/examples/f0e21e03a07c8fa0209b0aafdb3791e6.asciidoc create mode 100644 docs/examples/f29a28fffa7ec604a33a838f48f7ea79.asciidoc create mode 100644 docs/examples/f2d68493abd3ca430bd03a7f7f8d18f9.asciidoc create mode 100644 docs/examples/f32f0c19b42de3b87dd764fe4ca17e7c.asciidoc create mode 100644 docs/examples/f4a1008b3f9baa67bb03ce9ef5ab4cb4.asciidoc create mode 100644 docs/examples/f6b5032bf27c2445d28845be0d413970.asciidoc create mode 100644 docs/examples/f6d6889667f56b8f49d2858070571a6b.asciidoc create mode 100644 docs/examples/f70a54cd9a9f4811bf962e469f2ca2ea.asciidoc create mode 100644 docs/examples/f8cc4b331a19ff4df8e4a490f906ee69.asciidoc create mode 100644 docs/examples/f9636d7ef1a45be4f36418c875cf6bef.asciidoc create mode 100644 docs/examples/fa0f4485cd48f986b7ae8cbb24e331c4.asciidoc create mode 100644 docs/examples/fa2fe60f570bd930d2891778c6efbfe6.asciidoc create mode 100644 docs/examples/fa88f6f5a7d728ec4f1d05244228cb09.asciidoc create mode 100644 docs/examples/fabe14480624a99e8ee42c7338672058.asciidoc create mode 100644 docs/examples/fbcf5078a6a9e09790553804054c36b3.asciidoc create mode 100644 docs/examples/fc8097bdfb6f3a4017bf4186ccca8a84.asciidoc create mode 100644 docs/examples/fdcaba9547180439ff4b6275034a5170.asciidoc create mode 100644 docs/examples/fdd38f0d248385a444c777e7acd97846.asciidoc create mode 100644 docs/examples/fe5763d32955e8b65eb3048e97b1580c.asciidoc create mode 100644 docs/examples/feefeb68144002fd1fff57b77b95b85e.asciidoc delete mode 100644 docs/getting-started.mdx create mode 100644 docs/guide/configuration.asciidoc create mode 100644 docs/guide/connecting.asciidoc create mode 100644 docs/guide/examples.asciidoc create mode 100644 docs/guide/getting-started.asciidoc create mode 100644 docs/guide/helpers.asciidoc create mode 100644 docs/guide/images/create-api-key.png create mode 100644 docs/guide/images/es-endpoint.jpg create mode 100644 docs/guide/index-custom-title-page.html create mode 100644 docs/guide/index.asciidoc create mode 100644 docs/guide/installation.asciidoc create mode 100644 docs/guide/integrations.asciidoc create mode 100644 docs/guide/overview.asciidoc create mode 100644 docs/guide/release-notes.asciidoc delete mode 100644 docs/images/copy-endpoint.gif delete mode 100644 docs/images/setup-api-key.gif delete mode 100644 docs/landing-page.mdx delete mode 100644 docs/questions-and-assumptions.md create mode 100644 docs/sphinx/Makefile create mode 100644 docs/sphinx/api.rst create mode 100644 docs/sphinx/async.rst create mode 100644 docs/sphinx/conf.py create mode 100644 docs/sphinx/exceptions.rst create mode 100644 docs/sphinx/helpers.rst create mode 100644 docs/sphinx/index.rst create mode 100644 elasticsearch_serverless/__init__.py create mode 100644 elasticsearch_serverless/_async/__init__.py create mode 100644 elasticsearch_serverless/_async/client/__init__.py create mode 100644 elasticsearch_serverless/_async/client/_base.py create mode 100644 elasticsearch_serverless/_async/client/async_search.py create mode 100644 elasticsearch_serverless/_async/client/autoscaling.py create mode 100644 elasticsearch_serverless/_async/client/cat.py create mode 100644 elasticsearch_serverless/_async/client/ccr.py create mode 100644 elasticsearch_serverless/_async/client/cluster.py create mode 100644 elasticsearch_serverless/_async/client/dangling_indices.py create mode 100644 elasticsearch_serverless/_async/client/enrich.py create mode 100644 elasticsearch_serverless/_async/client/eql.py create mode 100644 elasticsearch_serverless/_async/client/features.py create mode 100644 elasticsearch_serverless/_async/client/fleet.py create mode 100644 elasticsearch_serverless/_async/client/graph.py create mode 100644 elasticsearch_serverless/_async/client/ilm.py create mode 100644 elasticsearch_serverless/_async/client/indices.py create mode 100644 elasticsearch_serverless/_async/client/ingest.py create mode 100644 elasticsearch_serverless/_async/client/license.py create mode 100644 elasticsearch_serverless/_async/client/logstash.py create mode 100644 elasticsearch_serverless/_async/client/migration.py create mode 100644 elasticsearch_serverless/_async/client/ml.py create mode 100644 elasticsearch_serverless/_async/client/monitoring.py create mode 100644 elasticsearch_serverless/_async/client/nodes.py create mode 100644 elasticsearch_serverless/_async/client/rollup.py create mode 100644 elasticsearch_serverless/_async/client/search_application.py create mode 100644 elasticsearch_serverless/_async/client/searchable_snapshots.py create mode 100644 elasticsearch_serverless/_async/client/security.py create mode 100644 elasticsearch_serverless/_async/client/shutdown.py create mode 100644 elasticsearch_serverless/_async/client/slm.py create mode 100644 elasticsearch_serverless/_async/client/snapshot.py create mode 100644 elasticsearch_serverless/_async/client/sql.py create mode 100644 elasticsearch_serverless/_async/client/ssl.py create mode 100644 elasticsearch_serverless/_async/client/tasks.py create mode 100644 elasticsearch_serverless/_async/client/text_structure.py create mode 100644 elasticsearch_serverless/_async/client/transform.py create mode 100644 elasticsearch_serverless/_async/client/utils.py create mode 100644 elasticsearch_serverless/_async/client/watcher.py create mode 100644 elasticsearch_serverless/_async/client/xpack.py create mode 100644 elasticsearch_serverless/_async/helpers.py create mode 100644 elasticsearch_serverless/_sync/__init__.py create mode 100644 elasticsearch_serverless/_sync/client/__init__.py create mode 100644 elasticsearch_serverless/_sync/client/_base.py create mode 100644 elasticsearch_serverless/_sync/client/async_search.py create mode 100644 elasticsearch_serverless/_sync/client/autoscaling.py create mode 100644 elasticsearch_serverless/_sync/client/cat.py create mode 100644 elasticsearch_serverless/_sync/client/ccr.py create mode 100644 elasticsearch_serverless/_sync/client/cluster.py create mode 100644 elasticsearch_serverless/_sync/client/dangling_indices.py create mode 100644 elasticsearch_serverless/_sync/client/enrich.py create mode 100644 elasticsearch_serverless/_sync/client/eql.py create mode 100644 elasticsearch_serverless/_sync/client/features.py create mode 100644 elasticsearch_serverless/_sync/client/fleet.py create mode 100644 elasticsearch_serverless/_sync/client/graph.py create mode 100644 elasticsearch_serverless/_sync/client/ilm.py create mode 100644 elasticsearch_serverless/_sync/client/indices.py create mode 100644 elasticsearch_serverless/_sync/client/ingest.py create mode 100644 elasticsearch_serverless/_sync/client/license.py create mode 100644 elasticsearch_serverless/_sync/client/logstash.py create mode 100644 elasticsearch_serverless/_sync/client/migration.py create mode 100644 elasticsearch_serverless/_sync/client/ml.py create mode 100644 elasticsearch_serverless/_sync/client/monitoring.py create mode 100644 elasticsearch_serverless/_sync/client/nodes.py create mode 100644 elasticsearch_serverless/_sync/client/rollup.py create mode 100644 elasticsearch_serverless/_sync/client/search_application.py create mode 100644 elasticsearch_serverless/_sync/client/searchable_snapshots.py create mode 100644 elasticsearch_serverless/_sync/client/security.py create mode 100644 elasticsearch_serverless/_sync/client/shutdown.py create mode 100644 elasticsearch_serverless/_sync/client/slm.py create mode 100644 elasticsearch_serverless/_sync/client/snapshot.py create mode 100644 elasticsearch_serverless/_sync/client/sql.py create mode 100644 elasticsearch_serverless/_sync/client/ssl.py create mode 100644 elasticsearch_serverless/_sync/client/tasks.py create mode 100644 elasticsearch_serverless/_sync/client/text_structure.py create mode 100644 elasticsearch_serverless/_sync/client/transform.py create mode 100644 elasticsearch_serverless/_sync/client/utils.py create mode 100644 elasticsearch_serverless/_sync/client/watcher.py create mode 100644 elasticsearch_serverless/_sync/client/xpack.py create mode 100644 elasticsearch_serverless/_utils.py create mode 100644 elasticsearch_serverless/_version.py create mode 100644 elasticsearch_serverless/client.py create mode 100644 elasticsearch_serverless/compat.py create mode 100644 elasticsearch_serverless/exceptions.py create mode 100644 elasticsearch_serverless/helpers/__init__.py create mode 100644 elasticsearch_serverless/helpers/actions.py create mode 100644 elasticsearch_serverless/helpers/errors.py create mode 100644 elasticsearch_serverless/py.typed create mode 100644 elasticsearch_serverless/serializer.py create mode 100644 elasticsearch_serverless/transport.py create mode 100644 test_elasticsearch_serverless/test_types/README.md create mode 100644 test_elasticsearch_serverless/test_types/aliased_types.py create mode 100644 test_elasticsearch_serverless/test_types/async_types.py create mode 100644 test_elasticsearch_serverless/test_types/sync_types.py create mode 100644 utils/build-dists.py create mode 100644 utils/bump-version.py create mode 100644 utils/generate-examples.py create mode 100644 utils/license-headers.py create mode 100644 utils/run-black.py create mode 100644 utils/run-unasync.py diff --git a/.ci/Dockerfile b/.ci/Dockerfile new file mode 100644 index 0000000..5dd6687 --- /dev/null +++ b/.ci/Dockerfile @@ -0,0 +1,25 @@ +ARG PYTHON_VERSION=3.8 +FROM python:${PYTHON_VERSION} + +# Default UID/GID to 1000 +# it can be overridden at build time +ARG BUILDER_UID=1000 +ARG BUILDER_GID=1000 +ENV BUILDER_USER elastic +ENV BUILDER_GROUP elastic + +# Create user +RUN groupadd --system -g ${BUILDER_GID} ${BUILDER_GROUP} \ + && useradd --system --shell /bin/bash -u ${BUILDER_UID} -g ${BUILDER_GROUP} -d /var/lib/elastic -m elastic 1>/dev/null 2>/dev/null \ + && mkdir -p /code/elasticsearch-py && mkdir /code/elasticsearch-py/build \ + && chown -R ${BUILDER_USER}:${BUILDER_GROUP} /code/ +COPY --chown=$BUILDER_USER:$BUILDER_GROUP . . +WORKDIR /code/elasticsearch-py +USER ${BUILDER_USER}:${BUILDER_GROUP} +COPY dev-requirements.txt . +RUN python -m pip install \ + -U --no-cache-dir \ + --disable-pip-version-check \ + nox -rdev-requirements.txt +COPY --chown=$BUILDER_USER:$BUILDER_GROUP . . +RUN python -m pip install -U -e . diff --git a/.ci/make.sh b/.ci/make.sh new file mode 100755 index 0000000..7b0d227 --- /dev/null +++ b/.ci/make.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------- # +# +# Skeleton for common build entry script for all elastic +# clients. Needs to be adapted to individual client usage. +# +# Must be called: ./.ci/make.sh +# +# Version: 1.1.0 +# +# Targets: +# --------------------------- +# assemble : build client artefacts with version +# bump : bump client internals to version +# codegen : generate endpoints +# docsgen : generate documentation +# examplegen : generate the doc examples +# clean : clean workspace +# +# ------------------------------------------------------- # + +# ------------------------------------------------------- # +# Bootstrap +# ------------------------------------------------------- # + +script_path=$(dirname "$(realpath -s "$0")") +repo=$(realpath "$script_path/../") + +# shellcheck disable=SC1090 +CMD=$1 +TASK=$1 +TASK_ARGS=() +VERSION=$2 +STACK_VERSION=$VERSION +set -euo pipefail + +product="elastic/elasticsearch-serverless-python" +output_folder=".ci/output" +codegen_folder=".ci/output" +OUTPUT_DIR="$repo/${output_folder}" +REPO_BINDING="${OUTPUT_DIR}:/sln/${output_folder}" +WORKFLOW="${WORKFLOW-staging}" +mkdir -p "$OUTPUT_DIR" + +echo -e "\033[34;1mINFO:\033[0m PRODUCT ${product}\033[0m" +echo -e "\033[34;1mINFO:\033[0m VERSION ${STACK_VERSION}\033[0m" +echo -e "\033[34;1mINFO:\033[0m OUTPUT_DIR ${OUTPUT_DIR}\033[0m" + +# ------------------------------------------------------- # +# Parse Command +# ------------------------------------------------------- # + +case $CMD in + clean) + echo -e "\033[36;1mTARGET: clean workspace $output_folder\033[0m" + rm -rf "$output_folder" + echo -e "\033[32;1mdone.\033[0m" + exit 0 + ;; + assemble) + if [ -v $VERSION ]; then + echo -e "\033[31;1mTARGET: assemble -> missing version parameter\033[0m" + exit 1 + fi + echo -e "\033[36;1mTARGET: assemble artefact $VERSION\033[0m" + TASK=release + TASK_ARGS=("$VERSION" "$output_folder") + ;; + codegen) + VERSION=$(git rev-parse --abbrev-ref HEAD) + echo -e "\033[36;1mTARGET: codegen API $VERSION\033[0m" + TASK=codegen + # VERSION is BRANCH here for now + TASK_ARGS=("$VERSION" "$codegen_folder") + ;; + docsgen) + if [ -v $VERSION ]; then + echo -e "\033[31;1mTARGET: docsgen -> missing version parameter\033[0m" + exit 1 + fi + echo -e "\033[36;1mTARGET: generate docs for $VERSION\033[0m" + TASK=codegen + # VERSION is BRANCH here for now + TASK_ARGS=("$VERSION" "$codegen_folder") + ;; + examplesgen) + echo -e "\033[36;1mTARGET: generate examples\033[0m" + TASK=codegen + # VERSION is BRANCH here for now + TASK_ARGS=("$VERSION" "$codegen_folder") + ;; + bump) + if [ -v $VERSION ]; then + echo -e "\033[31;1mTARGET: bump -> missing version parameter\033[0m" + exit 1 + fi + echo -e "\033[36;1mTARGET: bump to version $VERSION\033[0m" + TASK=bump + # VERSION is BRANCH here for now + TASK_ARGS=("$VERSION") + ;; + *) + echo -e "\nUsage:\n\t $CMD is not supported right now\n" + exit 1 +esac + + +# ------------------------------------------------------- # +# Build Container +# ------------------------------------------------------- # + +echo -e "\033[34;1mINFO: building $product container\033[0m" + +docker build \ + --build-arg BUILDER_UID="$(id -u)" \ + --file $repo/.ci/Dockerfile \ + --tag ${product} \ + . + +# ------------------------------------------------------- # +# Run the Container +# ------------------------------------------------------- # + +echo -e "\033[34;1mINFO: running $product container\033[0m" + +if [[ "$CMD" == "assemble" ]]; then + + # Build dists into .ci/output + docker run \ + -u "$(id -u)" \ + --rm -v $repo/.ci/output:/code/elasticsearch-serverless-python/dist \ + $product \ + /bin/bash -c "python /code/elasticsearch-serverless-python/utils/build-dists.py $VERSION" + + # Verify that there are dists in .ci/output + if compgen -G ".ci/output/*" > /dev/null; then + + # Tarball everything up in .ci/output + if [[ "$WORKFLOW" == 'snapshot' ]]; then + cd $repo/.ci/output && tar -czvf elasticsearch-serverless-python-$VERSION-SNAPSHOT.tar.gz * && cd - + else + cd $repo/.ci/output && tar -czvf elasticsearch-serverless-python-$VERSION.tar.gz * && cd - + fi + + echo -e "\033[32;1mTARGET: successfully assembled client v$VERSION\033[0m" + exit 0 + else + echo -e "\033[31;1mTARGET: assemble failed, empty workspace!\033[0m" + exit 1 + fi +fi + +if [[ "$CMD" == "bump" ]]; then + docker run \ + --rm -v $repo:/code/elasticsearch-serverless-python \ + $product \ + /bin/bash -c "python /code/elasticsearch-serverless-python/utils/bump-version.py $VERSION" + + exit 0 +fi + +if [[ "$CMD" == "codegen" ]]; then + docker run \ + --rm -v $repo:/code/elasticsearch-serverless-python \ + $product \ + /bin/bash -c "cd /code && python -m pip install nox && \ + git clone https://$CLIENTS_GITHUB_TOKEN@github.com/elastic/elastic-client-generator-python.git && \ + cd /code/elastic-client-generator-python && GIT_BRANCH=$VERSION python -m nox -s generate-es-serverless && \ + cd /code/elasticsearch-serverless-python && python -m nox -s format" + + exit 0 +fi + +if [[ "$CMD" == "docsgen" ]]; then + echo "TODO" +fi + +if [[ "$CMD" == "examplesgen" ]]; then + echo "TODO" +fi + +echo "Must be called with '.ci/make.sh [command]" +exit 1 diff --git a/docs/examples/014b788c879e4aaa1020672e45e25473.asciidoc b/docs/examples/014b788c879e4aaa1020672e45e25473.asciidoc new file mode 100644 index 0000000..464b818 --- /dev/null +++ b/docs/examples/014b788c879e4aaa1020672e45e25473.asciidoc @@ -0,0 +1,13 @@ +// search.asciidoc:72 + +[source, python] +---- +resp = client.cluster.put_settings( + body={ + "transient": { + "cluster.routing.use_adaptive_replica_selection": False + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/015294a400986295039e52ebc62033be.asciidoc b/docs/examples/015294a400986295039e52ebc62033be.asciidoc new file mode 100644 index 0000000..ef06b01 --- /dev/null +++ b/docs/examples/015294a400986295039e52ebc62033be.asciidoc @@ -0,0 +1,11 @@ +// docs/update.asciidoc:251 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={"doc": {"name": "new_name"}, "detect_noop": False}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/025b54db0edc50c24ea48a2bd94366ad.asciidoc b/docs/examples/025b54db0edc50c24ea48a2bd94366ad.asciidoc new file mode 100644 index 0000000..c120f43 --- /dev/null +++ b/docs/examples/025b54db0edc50c24ea48a2bd94366ad.asciidoc @@ -0,0 +1,9 @@ +// docs/update-by-query.asciidoc:599 + +[source, python] +---- +resp = client.search( + index="twitter", size="0", q="extra:test", filter_path="hits.total", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/028f6d6ac2594e20b78b8a8f8cbad49d.asciidoc b/docs/examples/028f6d6ac2594e20b78b8a8f8cbad49d.asciidoc new file mode 100644 index 0000000..9f4bd48 --- /dev/null +++ b/docs/examples/028f6d6ac2594e20b78b8a8f8cbad49d.asciidoc @@ -0,0 +1,31 @@ +// aggregations/bucket/terms-aggregation.asciidoc:336 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "countries": { + "terms": { + "field": "artist.country", + "order": [ + {"rock>playback_stats.avg": "desc"}, + {"_count": "desc"}, + ], + }, + "aggs": { + "rock": { + "filter": {"term": {"genre": "rock"}}, + "aggs": { + "playback_stats": { + "stats": {"field": "play_count"} + } + }, + } + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/033778305d52746f5ce0a2a922c8e521.asciidoc b/docs/examples/033778305d52746f5ce0a2a922c8e521.asciidoc new file mode 100644 index 0000000..1eec276 --- /dev/null +++ b/docs/examples/033778305d52746f5ce0a2a922c8e521.asciidoc @@ -0,0 +1,20 @@ +// aggregations/bucket/terms-aggregation.asciidoc:410 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": { + "script": { + "source": "doc['genre'].value", + "lang": "painless", + } + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/047266b0d20fdb62ebc72d51952c8f6d.asciidoc b/docs/examples/047266b0d20fdb62ebc72d51952c8f6d.asciidoc new file mode 100644 index 0000000..6de6245 --- /dev/null +++ b/docs/examples/047266b0d20fdb62ebc72d51952c8f6d.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/multi-match-query.asciidoc:341 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "Will Smith", + "type": "cross_fields", + "fields": ["first_name", "last_name"], + "operator": "and", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/048d8abd42d094bbdcf4452a58ccb35b.asciidoc b/docs/examples/048d8abd42d094bbdcf4452a58ccb35b.asciidoc new file mode 100644 index 0000000..2e99e06 --- /dev/null +++ b/docs/examples/048d8abd42d094bbdcf4452a58ccb35b.asciidoc @@ -0,0 +1,15 @@ +// docs/index_.asciidoc:487 + +[source, python] +---- +resp = client.create( + index="twitter", + id="1", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/04f5dd677c777bcb15d7d5fa63275fc8.asciidoc b/docs/examples/04f5dd677c777bcb15d7d5fa63275fc8.asciidoc new file mode 100644 index 0000000..9637196 --- /dev/null +++ b/docs/examples/04f5dd677c777bcb15d7d5fa63275fc8.asciidoc @@ -0,0 +1,7 @@ +// cluster/health.asciidoc:35 + +[source, python] +---- +resp = client.cluster.health(wait_for_status="yellow", timeout="50s") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/04fe1e3a0047b0cdb10987b79fc3f3f3.asciidoc b/docs/examples/04fe1e3a0047b0cdb10987b79fc3f3f3.asciidoc new file mode 100644 index 0000000..2423bb0 --- /dev/null +++ b/docs/examples/04fe1e3a0047b0cdb10987b79fc3f3f3.asciidoc @@ -0,0 +1,22 @@ +// search/request/sort.asciidoc:568 + +[source, python] +---- +resp = client.search( + body={ + "query": {"term": {"user": "kimchy"}}, + "sort": { + "_script": { + "type": "number", + "script": { + "lang": "painless", + "source": "doc['field_name'].value * params.factor", + "params": {"factor": 1.1}, + }, + "order": "asc", + } + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/06afce2955f9094d96d27067ebca32e8.asciidoc b/docs/examples/06afce2955f9094d96d27067ebca32e8.asciidoc new file mode 100644 index 0000000..6996033 --- /dev/null +++ b/docs/examples/06afce2955f9094d96d27067ebca32e8.asciidoc @@ -0,0 +1,23 @@ +// query-dsl/bool-query.asciidoc:36 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "bool": { + "must": {"term": {"user": "kimchy"}}, + "filter": {"term": {"tag": "tech"}}, + "must_not": {"range": {"age": {"gte": 10, "lte": 20}}}, + "should": [ + {"term": {"tag": "wow"}}, + {"term": {"tag": "elasticsearch"}}, + ], + "minimum_should_match": 1, + "boost": 1, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/073539a7e38be3cdf13008330b6a536a.asciidoc b/docs/examples/073539a7e38be3cdf13008330b6a536a.asciidoc new file mode 100644 index 0000000..84f3e80 --- /dev/null +++ b/docs/examples/073539a7e38be3cdf13008330b6a536a.asciidoc @@ -0,0 +1,7 @@ +// cat/indices.asciidoc:94 + +[source, python] +---- +resp = client.cat.indices(index="twi*", v=True, s="index") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0989cc65d8924f666ce3eb0820d2d244.asciidoc b/docs/examples/0989cc65d8924f666ce3eb0820d2d244.asciidoc new file mode 100644 index 0000000..ab936f0 --- /dev/null +++ b/docs/examples/0989cc65d8924f666ce3eb0820d2d244.asciidoc @@ -0,0 +1,14 @@ +// indices/put-mapping.asciidoc:427 + +[source, python] +---- +resp = client.index( + index="users", refresh="wait_for", body={"user_id": 12345}, +) +print(resp) + +resp = client.index( + index="users", refresh="wait_for", body={"user_id": 12346}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/09cdd5ae8114c49886026fef8d00a19c.asciidoc b/docs/examples/09cdd5ae8114c49886026fef8d00a19c.asciidoc new file mode 100644 index 0000000..57c6b80 --- /dev/null +++ b/docs/examples/09cdd5ae8114c49886026fef8d00a19c.asciidoc @@ -0,0 +1,10 @@ +// indices/get-mapping.asciidoc:70 + +[source, python] +---- +resp = client.indices.get_mapping(index="_all") +print(resp) + +resp = client.indices.get_mapping() +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/09d617863a103c82fb4101e6165ea7fe.asciidoc b/docs/examples/09d617863a103c82fb4101e6165ea7fe.asciidoc new file mode 100644 index 0000000..6455d82 --- /dev/null +++ b/docs/examples/09d617863a103c82fb4101e6165ea7fe.asciidoc @@ -0,0 +1,7 @@ +// query-dsl/match-all-query.asciidoc:11 + +[source, python] +---- +resp = client.search(body={"query": {"match_all": {}}}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/09dbd90c5e22ea4a17b4cf9aa72e08ae.asciidoc b/docs/examples/09dbd90c5e22ea4a17b4cf9aa72e08ae.asciidoc new file mode 100644 index 0000000..91c5524 --- /dev/null +++ b/docs/examples/09dbd90c5e22ea4a17b4cf9aa72e08ae.asciidoc @@ -0,0 +1,10 @@ +// api-conventions.asciidoc:222 + +[source, python] +---- +resp = client.search( + q="elasticsearch", + filter_path=["took", "hits.hits._id", "hits.hits._score"], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/09ecba5814d71e4c44468575eada9878.asciidoc b/docs/examples/09ecba5814d71e4c44468575eada9878.asciidoc new file mode 100644 index 0000000..791bfd6 --- /dev/null +++ b/docs/examples/09ecba5814d71e4c44468575eada9878.asciidoc @@ -0,0 +1,20 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:214 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sales_over_time": { + "date_histogram": { + "field": "date", + "fixed_interval": "30d", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0a958e486ede3f519d48431ab689eded.asciidoc b/docs/examples/0a958e486ede3f519d48431ab689eded.asciidoc new file mode 100644 index 0000000..ba99dbd --- /dev/null +++ b/docs/examples/0a958e486ede3f519d48431ab689eded.asciidoc @@ -0,0 +1,18 @@ +// docs/update.asciidoc:271 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={ + "script": { + "source": "ctx._source.counter += params.count", + "lang": "painless", + "params": {"count": 4}, + }, + "upsert": {"counter": 1}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0ac9916f47a2483b89c1416684af322a.asciidoc b/docs/examples/0ac9916f47a2483b89c1416684af322a.asciidoc new file mode 100644 index 0000000..e4592df --- /dev/null +++ b/docs/examples/0ac9916f47a2483b89c1416684af322a.asciidoc @@ -0,0 +1,19 @@ +// query-dsl/match-query.asciidoc:241 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "match": { + "message": { + "query": "to be or not to be", + "operator": "and", + "zero_terms_query": "all", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0afaf1cad692e6201aa574c8feb6e622.asciidoc b/docs/examples/0afaf1cad692e6201aa574c8feb6e622.asciidoc new file mode 100644 index 0000000..348c9d9 --- /dev/null +++ b/docs/examples/0afaf1cad692e6201aa574c8feb6e622.asciidoc @@ -0,0 +1,19 @@ +// aggregations/bucket/terms-aggregation.asciidoc:492 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "tags": { + "terms": { + "field": "tags", + "include": ".*sport.*", + "exclude": "water_.*", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0ba0b2db24852abccb7c0fc1098d566e.asciidoc b/docs/examples/0ba0b2db24852abccb7c0fc1098d566e.asciidoc new file mode 100644 index 0000000..55d4517 --- /dev/null +++ b/docs/examples/0ba0b2db24852abccb7c0fc1098d566e.asciidoc @@ -0,0 +1,12 @@ +// docs/get.asciidoc:366 + +[source, python] +---- +resp = client.index( + index="twitter", + id="2", + routing="user1", + body={"counter": 1, "tags": ["white"]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0bbd30b9be3e54ff3028b9f4459634d2.asciidoc b/docs/examples/0bbd30b9be3e54ff3028b9f4459634d2.asciidoc new file mode 100644 index 0000000..88d6ae7 --- /dev/null +++ b/docs/examples/0bbd30b9be3e54ff3028b9f4459634d2.asciidoc @@ -0,0 +1,12 @@ +// indices/put-mapping.asciidoc:166 + +[source, python] +---- +resp = client.indices.put_mapping( + index="my_index", + body={ + "properties": {"name": {"properties": {"last": {"type": "text"}}}} + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0bd3923424a20a4ba860b0774b9991b1.asciidoc b/docs/examples/0bd3923424a20a4ba860b0774b9991b1.asciidoc new file mode 100644 index 0000000..67442da --- /dev/null +++ b/docs/examples/0bd3923424a20a4ba860b0774b9991b1.asciidoc @@ -0,0 +1,37 @@ +// query-dsl/nested-query.asciidoc:206 + +[source, python] +---- +resp = client.search( + index="drivers", + body={ + "query": { + "nested": { + "path": "driver", + "query": { + "nested": { + "path": "driver.vehicle", + "query": { + "bool": { + "must": [ + { + "match": { + "driver.vehicle.make": "Powell Motors" + } + }, + { + "match": { + "driver.vehicle.model": "Canyonero" + } + }, + ] + } + }, + } + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0be2c28ee65384774b1e479b47dc3d92.asciidoc b/docs/examples/0be2c28ee65384774b1e479b47dc3d92.asciidoc new file mode 100644 index 0000000..a8ab176 --- /dev/null +++ b/docs/examples/0be2c28ee65384774b1e479b47dc3d92.asciidoc @@ -0,0 +1,9 @@ +// indices/update-settings.asciidoc:114 + +[source, python] +---- +resp = client.indices.put_settings( + index="twitter", body={"index": {"refresh_interval": "1s"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0c4ad860a485fe53d8140ad3ccd11dcf.asciidoc b/docs/examples/0c4ad860a485fe53d8140ad3ccd11dcf.asciidoc new file mode 100644 index 0000000..86ed1a2 --- /dev/null +++ b/docs/examples/0c4ad860a485fe53d8140ad3ccd11dcf.asciidoc @@ -0,0 +1,13 @@ +// query-dsl/terms-query.asciidoc:19 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "terms": {"user": ["kimchy", "elasticsearch"], "boost": 1} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0cc991e3f7f8511a34730e154b3c5edc.asciidoc b/docs/examples/0cc991e3f7f8511a34730e154b3c5edc.asciidoc new file mode 100644 index 0000000..e5433be --- /dev/null +++ b/docs/examples/0cc991e3f7f8511a34730e154b3c5edc.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:24 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "twitter"}, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0ce3606f1dba490eef83c4317b315b62.asciidoc b/docs/examples/0ce3606f1dba490eef83c4317b315b62.asciidoc new file mode 100644 index 0000000..31bad24 --- /dev/null +++ b/docs/examples/0ce3606f1dba490eef83c4317b315b62.asciidoc @@ -0,0 +1,9 @@ +// search/request-body.asciidoc:7 + +[source, python] +---- +resp = client.search( + index="twitter", body={"query": {"term": {"user": "kimchy"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0d664883151008b1051ef2c9ab2d0373.asciidoc b/docs/examples/0d664883151008b1051ef2c9ab2d0373.asciidoc new file mode 100644 index 0000000..0e924f2 --- /dev/null +++ b/docs/examples/0d664883151008b1051ef2c9ab2d0373.asciidoc @@ -0,0 +1,22 @@ +// docs/update-by-query.asciidoc:530 + +[source, python] +---- +resp = client.update_by_query( + index="twitter", + body={ + "slice": {"id": 0, "max": 2}, + "script": {"source": "ctx._source['extra'] = 'test'"}, + }, +) +print(resp) + +resp = client.update_by_query( + index="twitter", + body={ + "slice": {"id": 1, "max": 2}, + "script": {"source": "ctx._source['extra'] = 'test'"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/0e118857b815b62118a30c042f079db1.asciidoc b/docs/examples/0e118857b815b62118a30c042f079db1.asciidoc new file mode 100644 index 0000000..2866142 --- /dev/null +++ b/docs/examples/0e118857b815b62118a30c042f079db1.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/multi-match-query.asciidoc:259 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "quick brown f", + "type": "phrase_prefix", + "fields": ["subject", "message"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1027ab1ca767ac1428176ef4f84bfbcf.asciidoc b/docs/examples/1027ab1ca767ac1428176ef4f84bfbcf.asciidoc new file mode 100644 index 0000000..a06f0fa --- /dev/null +++ b/docs/examples/1027ab1ca767ac1428176ef4f84bfbcf.asciidoc @@ -0,0 +1,24 @@ +// search/request/scroll.asciidoc:206 + +[source, python] +---- +resp = client.search( + index="twitter", + scroll="1m", + body={ + "slice": {"id": 0, "max": 2}, + "query": {"match": {"title": "elasticsearch"}}, + }, +) +print(resp) + +resp = client.search( + index="twitter", + scroll="1m", + body={ + "slice": {"id": 1, "max": 2}, + "query": {"match": {"title": "elasticsearch"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1216f8f7367df3aa823012cef310c08a.asciidoc b/docs/examples/1216f8f7367df3aa823012cef310c08a.asciidoc new file mode 100644 index 0000000..bc853fb --- /dev/null +++ b/docs/examples/1216f8f7367df3aa823012cef310c08a.asciidoc @@ -0,0 +1,15 @@ +// docs/reindex.asciidoc:712 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "test"}, + "dest": {"index": "test2"}, + "script": { + "source": 'ctx._source.tag = ctx._source.remove("flag")' + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/12433d2b637d002e8d5c9a1adce69d3b.asciidoc b/docs/examples/12433d2b637d002e8d5c9a1adce69d3b.asciidoc new file mode 100644 index 0000000..26a4228 --- /dev/null +++ b/docs/examples/12433d2b637d002e8d5c9a1adce69d3b.asciidoc @@ -0,0 +1,7 @@ +// indices/put-mapping.asciidoc:84 + +[source, python] +---- +resp = client.indices.create(index="publications") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1252fa45847edba5ec2b2f33da70ec5b.asciidoc b/docs/examples/1252fa45847edba5ec2b2f33da70ec5b.asciidoc new file mode 100644 index 0000000..e9a2c88 --- /dev/null +++ b/docs/examples/1252fa45847edba5ec2b2f33da70ec5b.asciidoc @@ -0,0 +1,7 @@ +// api-conventions.asciidoc:273 + +[source, python] +---- +resp = client.cluster.state(filter_path="routing_table.indices.**.state") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/138ccd89f72aa7502dd9578403dcc589.asciidoc b/docs/examples/138ccd89f72aa7502dd9578403dcc589.asciidoc new file mode 100644 index 0000000..4eee2be --- /dev/null +++ b/docs/examples/138ccd89f72aa7502dd9578403dcc589.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:53 + +[source, python] +---- +resp = client.get(index="twitter", id="0", _source="false") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/14701dcc0cca9665fce2aace0cb62af7.asciidoc b/docs/examples/14701dcc0cca9665fce2aace0cb62af7.asciidoc new file mode 100644 index 0000000..8046288 --- /dev/null +++ b/docs/examples/14701dcc0cca9665fce2aace0cb62af7.asciidoc @@ -0,0 +1,12 @@ +// docs/delete-by-query.asciidoc:504 + +[source, python] +---- +resp = client.search( + index="twitter", + size="0", + filter_path="hits.total", + body={"query": {"range": {"likes": {"lt": 10}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1577e6e806b3283c9e99f1596d310754.asciidoc b/docs/examples/1577e6e806b3283c9e99f1596d310754.asciidoc new file mode 100644 index 0000000..0661a56 --- /dev/null +++ b/docs/examples/1577e6e806b3283c9e99f1596d310754.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:700 + +[source, python] +---- +resp = client.index( + index="test", + id="1", + refresh=True, + body={"text": "words words", "flag": "foo"}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/15dad5338065baaaa7d475abe85f4c22.asciidoc b/docs/examples/15dad5338065baaaa7d475abe85f4c22.asciidoc new file mode 100644 index 0000000..ef9c2dd --- /dev/null +++ b/docs/examples/15dad5338065baaaa7d475abe85f4c22.asciidoc @@ -0,0 +1,20 @@ +// search/request/sort.asciidoc:515 + +[source, python] +---- +resp = client.search( + body={ + "sort": [ + { + "_geo_distance": { + "pin.location": [-70, 40], + "order": "asc", + "unit": "km", + } + } + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/162b5b693b713f0bfab1209d59443c46.asciidoc b/docs/examples/162b5b693b713f0bfab1209d59443c46.asciidoc new file mode 100644 index 0000000..e8fea70 --- /dev/null +++ b/docs/examples/162b5b693b713f0bfab1209d59443c46.asciidoc @@ -0,0 +1,13 @@ +// query-dsl/bool-query.asciidoc:130 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "constant_score": {"filter": {"term": {"status": "active"}}} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/179f0a3e84ff4bbac18787a018eabf89.asciidoc b/docs/examples/179f0a3e84ff4bbac18787a018eabf89.asciidoc new file mode 100644 index 0000000..cb47e38 --- /dev/null +++ b/docs/examples/179f0a3e84ff4bbac18787a018eabf89.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/multi-match-query.asciidoc:472 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "Jon", + "type": "cross_fields", + "analyzer": "standard", + "fields": ["first", "last", "*.edge"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/17de0020b228df961ad3c6b06233c948.asciidoc b/docs/examples/17de0020b228df961ad3c6b06233c948.asciidoc new file mode 100644 index 0000000..00ffa36 --- /dev/null +++ b/docs/examples/17de0020b228df961ad3c6b06233c948.asciidoc @@ -0,0 +1,12 @@ +// indices/put-mapping.asciidoc:346 + +[source, python] +---- +resp = client.indices.put_mapping( + index="my_index", + body={ + "properties": {"user_id": {"type": "keyword", "ignore_above": 100}} + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/189a921df2f5b1fe580937210ce9c1c2.asciidoc b/docs/examples/189a921df2f5b1fe580937210ce9c1c2.asciidoc new file mode 100644 index 0000000..14df612 --- /dev/null +++ b/docs/examples/189a921df2f5b1fe580937210ce9c1c2.asciidoc @@ -0,0 +1,9 @@ +// search.asciidoc:96 + +[source, python] +---- +resp = client.search( + body={"query": {"match_all": {}}, "stats": ["group1", "group2"]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/18ddb7e7a4bcafd449df956e828ed7a8.asciidoc b/docs/examples/18ddb7e7a4bcafd449df956e828ed7a8.asciidoc new file mode 100644 index 0000000..00e9552 --- /dev/null +++ b/docs/examples/18ddb7e7a4bcafd449df956e828ed7a8.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:491 + +[source, python] +---- +resp = client.tasks.cancel(task_id="r1A2WoRbTwKZ516z6NEs5A:36619") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1aa91d3d48140d6367b6cabca8737b8f.asciidoc b/docs/examples/1aa91d3d48140d6367b6cabca8737b8f.asciidoc new file mode 100644 index 0000000..c214a79 --- /dev/null +++ b/docs/examples/1aa91d3d48140d6367b6cabca8737b8f.asciidoc @@ -0,0 +1,16 @@ +// docs/bulk.asciidoc:555 + +[source, python] +---- +resp = client.bulk( + body=[ + {"update": {"_id": "5", "_index": "index1"}}, + {"doc": {"my_field": "foo"}}, + {"update": {"_id": "6", "_index": "index1"}}, + {"doc": {"my_field": "foo"}}, + {"create": {"_id": "7", "_index": "index1"}}, + {"my_field": "foo"}, + ], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1b542e3ea87a742f95641d64dcfb1bdb.asciidoc b/docs/examples/1b542e3ea87a742f95641d64dcfb1bdb.asciidoc new file mode 100644 index 0000000..8ea77ad --- /dev/null +++ b/docs/examples/1b542e3ea87a742f95641d64dcfb1bdb.asciidoc @@ -0,0 +1,7 @@ +// search/count.asciidoc:7 + +[source, python] +---- +resp = client.count(index="twitter", q="user:kimchy") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1b8655e6ba99fe39933c6eafe78728b7.asciidoc b/docs/examples/1b8655e6ba99fe39933c6eafe78728b7.asciidoc new file mode 100644 index 0000000..5146ad6 --- /dev/null +++ b/docs/examples/1b8655e6ba99fe39933c6eafe78728b7.asciidoc @@ -0,0 +1,20 @@ +// docs/reindex.asciidoc:200 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "twitter", "slice": {"id": 0, "max": 2}}, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) + +resp = client.reindex( + body={ + "source": {"index": "twitter", "slice": {"id": 1, "max": 2}}, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1b8caf0a6741126c6d0ad83b56fce290.asciidoc b/docs/examples/1b8caf0a6741126c6d0ad83b56fce290.asciidoc new file mode 100644 index 0000000..52939f9 --- /dev/null +++ b/docs/examples/1b8caf0a6741126c6d0ad83b56fce290.asciidoc @@ -0,0 +1,21 @@ +// indices/templates.asciidoc:146 + +[source, python] +---- +resp = client.indices.put_template( + name="template_1", + body={ + "index_patterns": ["te*"], + "settings": {"number_of_shards": 1}, + "aliases": { + "alias1": {}, + "alias2": { + "filter": {"term": {"user": "kimchy"}}, + "routing": "kimchy", + }, + "{index}-alias": {}, + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1bc731a4df952228af6dfa6b48627332.asciidoc b/docs/examples/1bc731a4df952228af6dfa6b48627332.asciidoc new file mode 100644 index 0000000..fa7f425 --- /dev/null +++ b/docs/examples/1bc731a4df952228af6dfa6b48627332.asciidoc @@ -0,0 +1,18 @@ +// docs/reindex.asciidoc:815 + +[source, python] +---- +resp = client.reindex( + body={ + "max_docs": 10, + "source": { + "index": "twitter", + "query": { + "function_score": {"random_score": {}, "min_score": 0.9} + }, + }, + "dest": {"index": "random_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1c23507edd7a3c18538b68223378e4ab.asciidoc b/docs/examples/1c23507edd7a3c18538b68223378e4ab.asciidoc new file mode 100644 index 0000000..caddb0f --- /dev/null +++ b/docs/examples/1c23507edd7a3c18538b68223378e4ab.asciidoc @@ -0,0 +1,7 @@ +// indices/create-index.asciidoc:10 + +[source, python] +---- +resp = client.indices.create(index="twitter") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1d65cb6d055c46a1bde809687d835b71.asciidoc b/docs/examples/1d65cb6d055c46a1bde809687d835b71.asciidoc new file mode 100644 index 0000000..ff07bb6 --- /dev/null +++ b/docs/examples/1d65cb6d055c46a1bde809687d835b71.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:86 + +[source, python] +---- +resp = client.get(index="twitter", id="2", routing="user1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1da77e114459e0b77d78a3dcc8fae429.asciidoc b/docs/examples/1da77e114459e0b77d78a3dcc8fae429.asciidoc new file mode 100644 index 0000000..d9aad1a --- /dev/null +++ b/docs/examples/1da77e114459e0b77d78a3dcc8fae429.asciidoc @@ -0,0 +1,16 @@ +// indices/put-mapping.asciidoc:109 + +[source, python] +---- +resp = client.indices.create(index="twitter-1") +print(resp) + +resp = client.indices.create(index="twitter-2") +print(resp) + +resp = client.indices.put_mapping( + index=["twitter-1", "twitter-2"], + body={"properties": {"user_name": {"type": "text"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1dbb8cf17fbc45c87c7d2f75f15f9778.asciidoc b/docs/examples/1dbb8cf17fbc45c87c7d2f75f15f9778.asciidoc new file mode 100644 index 0000000..79f1146 --- /dev/null +++ b/docs/examples/1dbb8cf17fbc45c87c7d2f75f15f9778.asciidoc @@ -0,0 +1,7 @@ +// api-conventions.asciidoc:250 + +[source, python] +---- +resp = client.cluster.state(filter_path="metadata.indices.*.stat*") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1e18a67caf8f06ff2710ec4a8b30f625.asciidoc b/docs/examples/1e18a67caf8f06ff2710ec4a8b30f625.asciidoc new file mode 100644 index 0000000..12a2ee5 --- /dev/null +++ b/docs/examples/1e18a67caf8f06ff2710ec4a8b30f625.asciidoc @@ -0,0 +1,12 @@ +// api-conventions.asciidoc:317 + +[source, python] +---- +resp = client.cluster.state( + filter_path=[ + "metadata.indices.*.state", + "-metadata.indices.logstash-*", + ], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1e49eba5b9042c1900a608fe5105ba43.asciidoc b/docs/examples/1e49eba5b9042c1900a608fe5105ba43.asciidoc new file mode 100644 index 0000000..cba18b9 --- /dev/null +++ b/docs/examples/1e49eba5b9042c1900a608fe5105ba43.asciidoc @@ -0,0 +1,22 @@ +// docs/delete-by-query.asciidoc:414 + +[source, python] +---- +resp = client.delete_by_query( + index="twitter", + body={ + "slice": {"id": 0, "max": 2}, + "query": {"range": {"likes": {"lt": 10}}}, + }, +) +print(resp) + +resp = client.delete_by_query( + index="twitter", + body={ + "slice": {"id": 1, "max": 2}, + "query": {"range": {"likes": {"lt": 10}}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1e50d993bd6517e6c381e82d09f0389e.asciidoc b/docs/examples/1e50d993bd6517e6c381e82d09f0389e.asciidoc new file mode 100644 index 0000000..7080ead --- /dev/null +++ b/docs/examples/1e50d993bd6517e6c381e82d09f0389e.asciidoc @@ -0,0 +1,13 @@ +// search/request/from-size.asciidoc:22 + +[source, python] +---- +resp = client.search( + body={ + "from": 5, + "size": 20, + "query": {"term": {"user.id": "8a4f500d"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1f336ecc62480c1d56351cc2f82d0d08.asciidoc b/docs/examples/1f336ecc62480c1d56351cc2f82d0d08.asciidoc new file mode 100644 index 0000000..2685f59 --- /dev/null +++ b/docs/examples/1f336ecc62480c1d56351cc2f82d0d08.asciidoc @@ -0,0 +1,15 @@ +// docs/index_.asciidoc:396 + +[source, python] +---- +resp = client.index( + index="twitter", + id="1", + version="2", + version_type="external", + body={ + "message": "elasticsearch now has versioning support, double cool!" + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/1f6fe6833686e38c3711c6f2aa00a078.asciidoc b/docs/examples/1f6fe6833686e38c3711c6f2aa00a078.asciidoc new file mode 100644 index 0000000..a760ef1 --- /dev/null +++ b/docs/examples/1f6fe6833686e38c3711c6f2aa00a078.asciidoc @@ -0,0 +1,16 @@ +// indices/put-mapping.asciidoc:327 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "user_id": {"type": "keyword", "ignore_above": 20} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/210cf5c76bff517f48e80fa1c2d63907.asciidoc b/docs/examples/210cf5c76bff517f48e80fa1c2d63907.asciidoc new file mode 100644 index 0000000..a09f609 --- /dev/null +++ b/docs/examples/210cf5c76bff517f48e80fa1c2d63907.asciidoc @@ -0,0 +1,7 @@ +// indices/put-mapping.asciidoc:553 + +[source, python] +---- +resp = client.indices.get_mapping(index="my_index") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/213ab768f1b6a895e09403a0880e259a.asciidoc b/docs/examples/213ab768f1b6a895e09403a0880e259a.asciidoc new file mode 100644 index 0000000..c4ff75a --- /dev/null +++ b/docs/examples/213ab768f1b6a895e09403a0880e259a.asciidoc @@ -0,0 +1,22 @@ +// aggregations/metrics/valuecount-aggregation.asciidoc:65 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "types_count": { + "value_count": { + "script": { + "id": "my_script", + "params": {"field": "type"}, + } + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/216848930c2d344fe0bed0daa70c35b9.asciidoc b/docs/examples/216848930c2d344fe0bed0daa70c35b9.asciidoc new file mode 100644 index 0000000..17f6673 --- /dev/null +++ b/docs/examples/216848930c2d344fe0bed0daa70c35b9.asciidoc @@ -0,0 +1,7 @@ +// docs/delete-by-query.asciidoc:586 + +[source, python] +---- +resp = client.tasks.list(detailed="true", actions="*/delete/byquery") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/22334f4b24bb8977d3e1bf2ffdc29d3f.asciidoc b/docs/examples/22334f4b24bb8977d3e1bf2ffdc29d3f.asciidoc new file mode 100644 index 0000000..240e3b1 --- /dev/null +++ b/docs/examples/22334f4b24bb8977d3e1bf2ffdc29d3f.asciidoc @@ -0,0 +1,46 @@ +// search/request/sort.asciidoc:289 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "nested": { + "path": "parent", + "query": { + "bool": { + "must": {"range": {"parent.age": {"gte": 21}}}, + "filter": { + "nested": { + "path": "parent.child", + "query": { + "match": {"parent.child.name": "matt"} + }, + } + }, + } + }, + } + }, + "sort": [ + { + "parent.child.age": { + "mode": "min", + "order": "asc", + "nested": { + "path": "parent", + "filter": {"range": {"parent.age": {"gte": 21}}}, + "nested": { + "path": "parent.child", + "filter": { + "match": {"parent.child.name": "matt"} + }, + }, + }, + } + } + ], + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/231aa0bb39c35fe199d28fe0e4a62b2e.asciidoc b/docs/examples/231aa0bb39c35fe199d28fe0e4a62b2e.asciidoc new file mode 100644 index 0000000..879ecad --- /dev/null +++ b/docs/examples/231aa0bb39c35fe199d28fe0e4a62b2e.asciidoc @@ -0,0 +1,10 @@ +// getting-started.asciidoc:495 + +[source, python] +---- +resp = client.search( + index="bank", + body={"query": {"match_phrase": {"address": "mill lane"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/23ab0f1023b1b2cd5cdf2a8f9ccfd57b.asciidoc b/docs/examples/23ab0f1023b1b2cd5cdf2a8f9ccfd57b.asciidoc new file mode 100644 index 0000000..7f02f94 --- /dev/null +++ b/docs/examples/23ab0f1023b1b2cd5cdf2a8f9ccfd57b.asciidoc @@ -0,0 +1,10 @@ +// indices/aliases.asciidoc:298 + +[source, python] +---- +resp = client.indices.create( + index="test1", + body={"mappings": {"properties": {"user": {"type": "keyword"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2468ab381257d759d8a88af1141f6f9c.asciidoc b/docs/examples/2468ab381257d759d8a88af1141f6f9c.asciidoc new file mode 100644 index 0000000..ec7947e --- /dev/null +++ b/docs/examples/2468ab381257d759d8a88af1141f6f9c.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:288 + +[source, python] +---- +resp = client.exists_source(index="twitter", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/251ea12c1248385ab409906ac64d9ee9.asciidoc b/docs/examples/251ea12c1248385ab409906ac64d9ee9.asciidoc new file mode 100644 index 0000000..f10c5cb --- /dev/null +++ b/docs/examples/251ea12c1248385ab409906ac64d9ee9.asciidoc @@ -0,0 +1,19 @@ +// getting-started.asciidoc:544 + +[source, python] +---- +resp = client.search( + index="bank", + body={ + "query": { + "bool": { + "must": {"match_all": {}}, + "filter": { + "range": {"balance": {"gte": 20000, "lte": 30000}} + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2533e4b36ae837eaecda08407ecb6383.asciidoc b/docs/examples/2533e4b36ae837eaecda08407ecb6383.asciidoc new file mode 100644 index 0000000..bb15791 --- /dev/null +++ b/docs/examples/2533e4b36ae837eaecda08407ecb6383.asciidoc @@ -0,0 +1,17 @@ +// search/suggesters.asciidoc:51 + +[source, python] +---- +resp = client.search( + body={ + "suggest": { + "my-suggest-1": { + "text": "tring out Elasticsearch", + "term": {"field": "message"}, + }, + "my-suggest-2": {"text": "kmichy", "term": {"field": "user"}}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2891aa10ee9d474780adf94d5607f2db.asciidoc b/docs/examples/2891aa10ee9d474780adf94d5607f2db.asciidoc new file mode 100644 index 0000000..ca98225 --- /dev/null +++ b/docs/examples/2891aa10ee9d474780adf94d5607f2db.asciidoc @@ -0,0 +1,10 @@ +// search/request/sort.asciidoc:153 + +[source, python] +---- +resp = client.search( + index=["index_long", "index_double"], + body={"sort": [{"field": {"numeric_type": "double"}}]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/28aad2c5942bfb221c2bf1bbdc01658e.asciidoc b/docs/examples/28aad2c5942bfb221c2bf1bbdc01658e.asciidoc new file mode 100644 index 0000000..a0e93f1 --- /dev/null +++ b/docs/examples/28aad2c5942bfb221c2bf1bbdc01658e.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/query-string-query.asciidoc:316 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["city.*"], + "query": "this AND that OR thus", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2a1de18774f9c68cafa169847832b2bc.asciidoc b/docs/examples/2a1de18774f9c68cafa169847832b2bc.asciidoc new file mode 100644 index 0000000..697866d --- /dev/null +++ b/docs/examples/2a1de18774f9c68cafa169847832b2bc.asciidoc @@ -0,0 +1,10 @@ +// query-dsl/term-query.asciidoc:94 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"full_text": {"type": "text"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2bb2339ac055337abf753bddb7771659.asciidoc b/docs/examples/2bb2339ac055337abf753bddb7771659.asciidoc new file mode 100644 index 0000000..65f780d --- /dev/null +++ b/docs/examples/2bb2339ac055337abf753bddb7771659.asciidoc @@ -0,0 +1,17 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:232 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sales_over_time": { + "date_histogram": {"field": "date", "fixed_interval": "2w"} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2fd69fb0538e4f36ac69a8b8f8bf5ae8.asciidoc b/docs/examples/2fd69fb0538e4f36ac69a8b8f8bf5ae8.asciidoc new file mode 100644 index 0000000..68a3cad --- /dev/null +++ b/docs/examples/2fd69fb0538e4f36ac69a8b8f8bf5ae8.asciidoc @@ -0,0 +1,13 @@ +// docs/update-by-query.asciidoc:348 + +[source, python] +---- +resp = client.update_by_query( + index="twitter", + body={ + "script": {"source": "ctx._source.likes++", "lang": "painless"}, + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/2fe28d9a91b3081a9ec4601af8fb7b1c.asciidoc b/docs/examples/2fe28d9a91b3081a9ec4601af8fb7b1c.asciidoc new file mode 100644 index 0000000..d875696 --- /dev/null +++ b/docs/examples/2fe28d9a91b3081a9ec4601af8fb7b1c.asciidoc @@ -0,0 +1,40 @@ +// docs/update-by-query.asciidoc:655 + +[source, python] +---- +resp = client.indices.create( + index="test", + body={ + "mappings": { + "dynamic": False, + "properties": {"text": {"type": "text"}}, + } + }, +) +print(resp) + +resp = client.index( + index="test", + refresh=True, + body={"text": "words words", "flag": "bar"}, +) +print(resp) + +resp = client.index( + index="test", + refresh=True, + body={"text": "words words", "flag": "foo"}, +) +print(resp) + +resp = client.indices.put_mapping( + index="test", + body={ + "properties": { + "text": {"type": "text"}, + "flag": {"type": "text", "analyzer": "keyword"}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/311c4b632a29b9ead63b02d01f10096b.asciidoc b/docs/examples/311c4b632a29b9ead63b02d01f10096b.asciidoc new file mode 100644 index 0000000..da29182 --- /dev/null +++ b/docs/examples/311c4b632a29b9ead63b02d01f10096b.asciidoc @@ -0,0 +1,7 @@ +// getting-started.asciidoc:251 + +[source, python] +---- +resp = client.index(index="customer", id="1", body={"name": "John Doe"}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3342c69b2c2303247217532956fcce85.asciidoc b/docs/examples/3342c69b2c2303247217532956fcce85.asciidoc new file mode 100644 index 0000000..8b1e463 --- /dev/null +++ b/docs/examples/3342c69b2c2303247217532956fcce85.asciidoc @@ -0,0 +1,7 @@ +// query-dsl/exists-query.asciidoc:20 + +[source, python] +---- +resp = client.search(body={"query": {"exists": {"field": "user"}}}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/33f148e3d8676de6cc52f58749898a13.asciidoc b/docs/examples/33f148e3d8676de6cc52f58749898a13.asciidoc new file mode 100644 index 0000000..6a9746e --- /dev/null +++ b/docs/examples/33f148e3d8676de6cc52f58749898a13.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/multi-match-query.asciidoc:275 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "dis_max": { + "queries": [ + {"match_phrase_prefix": {"subject": "quick brown f"}}, + {"match_phrase_prefix": {"message": "quick brown f"}}, + ] + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/34efeade38445b2834749ced59782e25.asciidoc b/docs/examples/34efeade38445b2834749ced59782e25.asciidoc new file mode 100644 index 0000000..971f9fe --- /dev/null +++ b/docs/examples/34efeade38445b2834749ced59782e25.asciidoc @@ -0,0 +1,21 @@ +// aggregations/bucket/terms-aggregation.asciidoc:263 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": { + "field": "genre", + "order": {"playback_stats.max": "desc"}, + }, + "aggs": { + "playback_stats": {"stats": {"field": "play_count"}} + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/35e8da9410b8432cf4095f2541ad7b1d.asciidoc b/docs/examples/35e8da9410b8432cf4095f2541ad7b1d.asciidoc new file mode 100644 index 0000000..a2443e5 --- /dev/null +++ b/docs/examples/35e8da9410b8432cf4095f2541ad7b1d.asciidoc @@ -0,0 +1,19 @@ +// aggregations/bucket/terms-aggregation.asciidoc:162 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "products": { + "terms": { + "field": "product", + "size": 5, + "show_term_doc_count_error": True, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3653567181f43a5f64c74f934aa821c2.asciidoc b/docs/examples/3653567181f43a5f64c74f934aa821c2.asciidoc new file mode 100644 index 0000000..3962165 --- /dev/null +++ b/docs/examples/3653567181f43a5f64c74f934aa821c2.asciidoc @@ -0,0 +1,9 @@ +// indices/aliases.asciidoc:182 + +[source, python] +---- +resp = client.indices.update_aliases( + body={"actions": [{"remove": {"index": "test1", "alias": "alias1"}}]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/36818c6d9f434d387819c30bd9addb14.asciidoc b/docs/examples/36818c6d9f434d387819c30bd9addb14.asciidoc new file mode 100644 index 0000000..e71314e --- /dev/null +++ b/docs/examples/36818c6d9f434d387819c30bd9addb14.asciidoc @@ -0,0 +1,14 @@ +// docs/index_.asciidoc:196 + +[source, python] +---- +resp = client.index( + index="twitter", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/36b2778f23d0955255f52c075c4d213d.asciidoc b/docs/examples/36b2778f23d0955255f52c075c4d213d.asciidoc new file mode 100644 index 0000000..f268802 --- /dev/null +++ b/docs/examples/36b2778f23d0955255f52c075c4d213d.asciidoc @@ -0,0 +1,20 @@ +// docs/reindex.asciidoc:901 + +[source, python] +---- +resp = client.reindex( + body={ + "source": { + "remote": { + "host": "http://otherhost:9200", + "username": "user", + "password": "pass", + }, + "index": "source", + "query": {"match": {"test": "data"}}, + }, + "dest": {"index": "dest"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3722cb3705b6bc7f486969deace3dd83.asciidoc b/docs/examples/3722cb3705b6bc7f486969deace3dd83.asciidoc new file mode 100644 index 0000000..a9d2373 --- /dev/null +++ b/docs/examples/3722cb3705b6bc7f486969deace3dd83.asciidoc @@ -0,0 +1,17 @@ +// aggregations/metrics/valuecount-aggregation.asciidoc:46 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "type_count": { + "value_count": {"script": {"source": "doc['type'].value"}} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/381fced1882ca8337143e6bb180a5715.asciidoc b/docs/examples/381fced1882ca8337143e6bb180a5715.asciidoc new file mode 100644 index 0000000..dd4bfd1 --- /dev/null +++ b/docs/examples/381fced1882ca8337143e6bb180a5715.asciidoc @@ -0,0 +1,9 @@ +// docs/update.asciidoc:84 + +[source, python] +---- +resp = client.index( + index="test", id="1", body={"counter": 1, "tags": ["red"]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/38c1d0f6668e9563c0827f839f9fa505.asciidoc b/docs/examples/38c1d0f6668e9563c0827f839f9fa505.asciidoc new file mode 100644 index 0000000..00c80bb --- /dev/null +++ b/docs/examples/38c1d0f6668e9563c0827f839f9fa505.asciidoc @@ -0,0 +1,9 @@ +// docs/update.asciidoc:218 + +[source, python] +---- +resp = client.update( + index="test", id="1", body={"doc": {"name": "new_name"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/39a6a038c4b551022afe83de0523634e.asciidoc b/docs/examples/39a6a038c4b551022afe83de0523634e.asciidoc new file mode 100644 index 0000000..ffc4155 --- /dev/null +++ b/docs/examples/39a6a038c4b551022afe83de0523634e.asciidoc @@ -0,0 +1,21 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:636 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sale_date": { + "date_histogram": { + "field": "date", + "calendar_interval": "year", + "missing": "2000/01/01", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3a700f836d8d5da1b656a876554028aa.asciidoc b/docs/examples/3a700f836d8d5da1b656a876554028aa.asciidoc new file mode 100644 index 0000000..bf786d7 --- /dev/null +++ b/docs/examples/3a700f836d8d5da1b656a876554028aa.asciidoc @@ -0,0 +1,14 @@ +// search/request/scroll.asciidoc:172 + +[source, python] +---- +resp = client.clear_scroll( + body={ + "scroll_id": [ + "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==", + "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB", + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3ae03ba3b56e5e287953094050766738.asciidoc b/docs/examples/3ae03ba3b56e5e287953094050766738.asciidoc new file mode 100644 index 0000000..6ca01ad --- /dev/null +++ b/docs/examples/3ae03ba3b56e5e287953094050766738.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:233 + +[source, python] +---- +resp = client.indices.refresh() +print(resp) + +resp = client.search( + index="new_twitter", size="0", filter_path="hits.total", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3b04cc894e6a47d57983484010feac0c.asciidoc b/docs/examples/3b04cc894e6a47d57983484010feac0c.asciidoc new file mode 100644 index 0000000..74ec0cf --- /dev/null +++ b/docs/examples/3b04cc894e6a47d57983484010feac0c.asciidoc @@ -0,0 +1,10 @@ +// docs/reindex.asciidoc:800 + +[source, python] +---- +resp = client.get(index="metricbeat-2016.05.30-1", id="1") +print(resp) + +resp = client.get(index="metricbeat-2016.05.31-1", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3cd50a789b8e1f0ebbbc53a8d7ecf656.asciidoc b/docs/examples/3cd50a789b8e1f0ebbbc53a8d7ecf656.asciidoc new file mode 100644 index 0000000..216d2ba --- /dev/null +++ b/docs/examples/3cd50a789b8e1f0ebbbc53a8d7ecf656.asciidoc @@ -0,0 +1,31 @@ +// query-dsl/multi-match-query.asciidoc:438 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "bool": { + "should": [ + { + "multi_match": { + "query": "Will Smith", + "type": "cross_fields", + "fields": ["first", "last"], + "minimum_should_match": "50%", + } + }, + { + "multi_match": { + "query": "Will Smith", + "type": "cross_fields", + "fields": ["*.edge"], + } + }, + ] + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3d1ff6097e2359f927c88c2ccdb36252.asciidoc b/docs/examples/3d1ff6097e2359f927c88c2ccdb36252.asciidoc new file mode 100644 index 0000000..d5a7eff --- /dev/null +++ b/docs/examples/3d1ff6097e2359f927c88c2ccdb36252.asciidoc @@ -0,0 +1,7 @@ +// setup/install/check-running.asciidoc:7 + +[source, python] +---- +resp = client.info() +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3e573bfabe00f8bfb8bb69aa5820768e.asciidoc b/docs/examples/3e573bfabe00f8bfb8bb69aa5820768e.asciidoc new file mode 100644 index 0000000..bf9afc9 --- /dev/null +++ b/docs/examples/3e573bfabe00f8bfb8bb69aa5820768e.asciidoc @@ -0,0 +1,15 @@ +// docs/delete-by-query.asciidoc:449 + +[source, python] +---- +resp = client.indices.refresh() +print(resp) + +resp = client.search( + index="twitter", + size="0", + filter_path="hits.total", + body={"query": {"range": {"likes": {"lt": 10}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/3f3b3e207f79303ce6f86e03e928e062.asciidoc b/docs/examples/3f3b3e207f79303ce6f86e03e928e062.asciidoc new file mode 100644 index 0000000..5614de8 --- /dev/null +++ b/docs/examples/3f3b3e207f79303ce6f86e03e928e062.asciidoc @@ -0,0 +1,7 @@ +// getting-started.asciidoc:290 + +[source, python] +---- +resp = client.get(index="customer", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/400e89eb46ead8e9c9e40f123fd5e590.asciidoc b/docs/examples/400e89eb46ead8e9c9e40f123fd5e590.asciidoc new file mode 100644 index 0000000..436d19c --- /dev/null +++ b/docs/examples/400e89eb46ead8e9c9e40f123fd5e590.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:394 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "source", "size": 100}, + "dest": {"index": "dest", "routing": "=cat"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/42744a175125df5be0ef77413bf8f608.asciidoc b/docs/examples/42744a175125df5be0ef77413bf8f608.asciidoc new file mode 100644 index 0000000..648902a --- /dev/null +++ b/docs/examples/42744a175125df5be0ef77413bf8f608.asciidoc @@ -0,0 +1,9 @@ +// indices/update-settings.asciidoc:73 + +[source, python] +---- +resp = client.indices.put_settings( + index="twitter", body={"index": {"refresh_interval": None}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/427f6b5c5376cbf0f71f242a60ca3d9e.asciidoc b/docs/examples/427f6b5c5376cbf0f71f242a60ca3d9e.asciidoc new file mode 100644 index 0000000..f175666 --- /dev/null +++ b/docs/examples/427f6b5c5376cbf0f71f242a60ca3d9e.asciidoc @@ -0,0 +1,7 @@ +// indices/aliases.asciidoc:388 + +[source, python] +---- +resp = client.search(index="alias2", q="user:kimchy", routing=["2", "3"]) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/43af86de5e49aa06070092fffc138208.asciidoc b/docs/examples/43af86de5e49aa06070092fffc138208.asciidoc new file mode 100644 index 0000000..94ee8c4 --- /dev/null +++ b/docs/examples/43af86de5e49aa06070092fffc138208.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/exists-query.asciidoc:56 + +[source, python] +---- +resp = client.search( + body={"query": {"bool": {"must_not": {"exists": {"field": "user"}}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4646764bf09911fee7d58630c72d3137.asciidoc b/docs/examples/4646764bf09911fee7d58630c72d3137.asciidoc new file mode 100644 index 0000000..8ac1228 --- /dev/null +++ b/docs/examples/4646764bf09911fee7d58630c72d3137.asciidoc @@ -0,0 +1,20 @@ +// aggregations/bucket/terms-aggregation.asciidoc:444 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": { + "script": { + "id": "my_script", + "params": {"field": "genre"}, + } + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/46658f00edc4865dfe472a392374cd0f.asciidoc b/docs/examples/46658f00edc4865dfe472a392374cd0f.asciidoc new file mode 100644 index 0000000..fa9b2c8 --- /dev/null +++ b/docs/examples/46658f00edc4865dfe472a392374cd0f.asciidoc @@ -0,0 +1,9 @@ +// indices/templates.asciidoc:249 + +[source, python] +---- +resp = client.indices.get_template( + name="template_1", filter_path="*.version", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/46c4b0dfb674825f9579203d41e7f404.asciidoc b/docs/examples/46c4b0dfb674825f9579203d41e7f404.asciidoc new file mode 100644 index 0000000..73ca516 --- /dev/null +++ b/docs/examples/46c4b0dfb674825f9579203d41e7f404.asciidoc @@ -0,0 +1,10 @@ +// mapping/types/keyword.asciidoc:20 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"tags": {"type": "keyword"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/47b5ff897f26e9c943cee5c06034181d.asciidoc b/docs/examples/47b5ff897f26e9c943cee5c06034181d.asciidoc new file mode 100644 index 0000000..857fa6d --- /dev/null +++ b/docs/examples/47b5ff897f26e9c943cee5c06034181d.asciidoc @@ -0,0 +1,7 @@ +// docs/delete.asciidoc:71 + +[source, python] +---- +resp = client.delete(index="twitter", id="1", routing="kimchy") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/47bb632c6091ad0cd94bc660bdd309a5.asciidoc b/docs/examples/47bb632c6091ad0cd94bc660bdd309a5.asciidoc new file mode 100644 index 0000000..355a3a6 --- /dev/null +++ b/docs/examples/47bb632c6091ad0cd94bc660bdd309a5.asciidoc @@ -0,0 +1,17 @@ +// getting-started.asciidoc:512 + +[source, python] +---- +resp = client.search( + index="bank", + body={ + "query": { + "bool": { + "must": [{"match": {"age": "40"}}], + "must_not": [{"match": {"state": "ID"}}], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4acf902c2598b2558f34f20c1744c433.asciidoc b/docs/examples/4acf902c2598b2558f34f20c1744c433.asciidoc new file mode 100644 index 0000000..410f24d --- /dev/null +++ b/docs/examples/4acf902c2598b2558f34f20c1744c433.asciidoc @@ -0,0 +1,12 @@ +// docs/update-by-query.asciidoc:557 + +[source, python] +---- +resp = client.indices.refresh() +print(resp) + +resp = client.search( + index="twitter", size="0", q="extra:test", filter_path="hits.total", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4b90feb9d5d3dbfce424dac0341320b7.asciidoc b/docs/examples/4b90feb9d5d3dbfce424dac0341320b7.asciidoc new file mode 100644 index 0000000..8305d27 --- /dev/null +++ b/docs/examples/4b90feb9d5d3dbfce424dac0341320b7.asciidoc @@ -0,0 +1,15 @@ +// getting-started.asciidoc:461 + +[source, python] +---- +resp = client.search( + index="bank", + body={ + "query": {"match_all": {}}, + "sort": [{"account_number": "asc"}], + "from": 10, + "size": 10, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4cd246e5c4c035a2cd4081ae9a3d54e5.asciidoc b/docs/examples/4cd246e5c4c035a2cd4081ae9a3d54e5.asciidoc new file mode 100644 index 0000000..d3f129f --- /dev/null +++ b/docs/examples/4cd246e5c4c035a2cd4081ae9a3d54e5.asciidoc @@ -0,0 +1,17 @@ +// docs/update.asciidoc:114 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={ + "script": { + "source": "ctx._source.tags.add(params.tag)", + "lang": "painless", + "params": {"tag": "blue"}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4d46dbb96125b27f46299547de9d8709.asciidoc b/docs/examples/4d46dbb96125b27f46299547de9d8709.asciidoc new file mode 100644 index 0000000..9805f1b --- /dev/null +++ b/docs/examples/4d46dbb96125b27f46299547de9d8709.asciidoc @@ -0,0 +1,10 @@ +// indices/create-index.asciidoc:190 + +[source, python] +---- +resp = client.indices.create( + index="test", + body={"settings": {"index.write.wait_for_active_shards": "2"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4d56b179242fed59e3d6476f817b6055.asciidoc b/docs/examples/4d56b179242fed59e3d6476f817b6055.asciidoc new file mode 100644 index 0000000..a57680e --- /dev/null +++ b/docs/examples/4d56b179242fed59e3d6476f817b6055.asciidoc @@ -0,0 +1,18 @@ +// indices/create-index.asciidoc:143 + +[source, python] +---- +resp = client.indices.create( + index="test", + body={ + "aliases": { + "alias_1": {}, + "alias_2": { + "filter": {"term": {"user": "kimchy"}}, + "routing": "kimchy", + }, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/4d6997c70a1851f9151443c0d38b532e.asciidoc b/docs/examples/4d6997c70a1851f9151443c0d38b532e.asciidoc new file mode 100644 index 0000000..ab54bb7 --- /dev/null +++ b/docs/examples/4d6997c70a1851f9151443c0d38b532e.asciidoc @@ -0,0 +1,34 @@ +// mapping/types/array.asciidoc:42 + +[source, python] +---- +resp = client.index( + index="my_index", + id="1", + body={ + "message": "some arrays in this document...", + "tags": ["elasticsearch", "wow"], + "lists": [ + {"name": "prog_list", "description": "programming list"}, + {"name": "cool_list", "description": "cool stuff list"}, + ], + }, +) +print(resp) + +resp = client.index( + index="my_index", + id="2", + body={ + "message": "no arrays in this document...", + "tags": "elasticsearch", + "lists": {"name": "prog_list", "description": "programming list"}, + }, +) +print(resp) + +resp = client.search( + index="my_index", body={"query": {"match": {"tags": "elasticsearch"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5043b83a89091fa00edb341ddf7ba370.asciidoc b/docs/examples/5043b83a89091fa00edb341ddf7ba370.asciidoc new file mode 100644 index 0000000..d8da41b --- /dev/null +++ b/docs/examples/5043b83a89091fa00edb341ddf7ba370.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/match-query.asciidoc:219 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "match": { + "message": { + "query": "this is a testt", + "fuzziness": "AUTO", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/506844befdc5691d835771bcbb1c1a60.asciidoc b/docs/examples/506844befdc5691d835771bcbb1c1a60.asciidoc new file mode 100644 index 0000000..0082edb --- /dev/null +++ b/docs/examples/506844befdc5691d835771bcbb1c1a60.asciidoc @@ -0,0 +1,10 @@ +// getting-started.asciidoc:392 + +[source, python] +---- +resp = client.search( + index="bank", + body={"query": {"match_all": {}}, "sort": [{"account_number": "asc"}]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5271f4ff29bb48838396e5a674664ee0.asciidoc b/docs/examples/5271f4ff29bb48838396e5a674664ee0.asciidoc new file mode 100644 index 0000000..ad1c722 --- /dev/null +++ b/docs/examples/5271f4ff29bb48838396e5a674664ee0.asciidoc @@ -0,0 +1,35 @@ +// mapping/params/multi-fields.asciidoc:10 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "city": { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + } + } + } + }, +) +print(resp) + +resp = client.index(index="my_index", id="1", body={"city": "New York"}) +print(resp) + +resp = client.index(index="my_index", id="2", body={"city": "York"}) +print(resp) + +resp = client.search( + index="my_index", + body={ + "query": {"match": {"city": "york"}}, + "sort": {"city.raw": "asc"}, + "aggs": {"Cities": {"terms": {"field": "city.raw"}}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/527324766814561b75aaee853ede49a7.asciidoc b/docs/examples/527324766814561b75aaee853ede49a7.asciidoc new file mode 100644 index 0000000..5f10f12 --- /dev/null +++ b/docs/examples/527324766814561b75aaee853ede49a7.asciidoc @@ -0,0 +1,11 @@ +// aggregations/bucket/terms-aggregation.asciidoc:369 + +[source, python] +---- +resp = client.search( + body={ + "aggs": {"tags": {"terms": {"field": "tags", "min_doc_count": 10}}} + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5275842787967b6db876025f4a1c6942.asciidoc b/docs/examples/5275842787967b6db876025f4a1c6942.asciidoc new file mode 100644 index 0000000..35f7bc7 --- /dev/null +++ b/docs/examples/5275842787967b6db876025f4a1c6942.asciidoc @@ -0,0 +1,15 @@ +// search/suggesters.asciidoc:127 + +[source, python] +---- +resp = client.search( + body={ + "suggest": { + "text": "tring out Elasticsearch", + "my-suggest-1": {"term": {"field": "message"}}, + "my-suggest-2": {"term": {"field": "user"}}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/52a87b81e4e0b6b11e23e85db1602a63.asciidoc b/docs/examples/52a87b81e4e0b6b11e23e85db1602a63.asciidoc new file mode 100644 index 0000000..322a7c2 --- /dev/null +++ b/docs/examples/52a87b81e4e0b6b11e23e85db1602a63.asciidoc @@ -0,0 +1,11 @@ +// docs/update-by-query.asciidoc:300 + +[source, python] +---- +resp = client.update_by_query( + index="twitter", + conflicts="proceed", + body={"query": {"term": {"user": "kimchy"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/52b2bfbdd78f8283b6f4891c48013237.asciidoc b/docs/examples/52b2bfbdd78f8283b6f4891c48013237.asciidoc new file mode 100644 index 0000000..7899d2d --- /dev/null +++ b/docs/examples/52b2bfbdd78f8283b6f4891c48013237.asciidoc @@ -0,0 +1,13 @@ +// docs/reindex.asciidoc:631 + +[source, python] +---- +resp = client.reindex( + body={ + "max_docs": 1, + "source": {"index": "twitter"}, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/52c7e4172a446c394210a07c464c57d2.asciidoc b/docs/examples/52c7e4172a446c394210a07c464c57d2.asciidoc new file mode 100644 index 0000000..3e27669 --- /dev/null +++ b/docs/examples/52c7e4172a446c394210a07c464c57d2.asciidoc @@ -0,0 +1,9 @@ +// docs/delete-by-query.asciidoc:572 + +[source, python] +---- +resp = client.delete_by_query_rethrottle( + task_id="r1A2WoRbTwKZ516z6NEs5A:36619", requests_per_second="-1", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/53b908c3432118c5a6e460f74d32006b.asciidoc b/docs/examples/53b908c3432118c5a6e460f74d32006b.asciidoc new file mode 100644 index 0000000..1496312 --- /dev/null +++ b/docs/examples/53b908c3432118c5a6e460f74d32006b.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/multi-match-query.asciidoc:11 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "this is a test", + "fields": ["subject", "message"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/53d938c754f36a912fcbe6473abb463f.asciidoc b/docs/examples/53d938c754f36a912fcbe6473abb463f.asciidoc new file mode 100644 index 0000000..37c1500 --- /dev/null +++ b/docs/examples/53d938c754f36a912fcbe6473abb463f.asciidoc @@ -0,0 +1,9 @@ +// indices/put-mapping.asciidoc:465 + +[source, python] +---- +resp = client.reindex( + body={"source": {"index": "users"}, "dest": {"index": "new_users"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/54092c8c646133f5dbbc047990dd458d.asciidoc b/docs/examples/54092c8c646133f5dbbc047990dd458d.asciidoc new file mode 100644 index 0000000..11cf94e --- /dev/null +++ b/docs/examples/54092c8c646133f5dbbc047990dd458d.asciidoc @@ -0,0 +1,28 @@ +// query-dsl/nested-query.asciidoc:133 + +[source, python] +---- +resp = client.indices.create( + index="drivers", + body={ + "mappings": { + "properties": { + "driver": { + "type": "nested", + "properties": { + "last_name": {"type": "text"}, + "vehicle": { + "type": "nested", + "properties": { + "make": {"type": "text"}, + "model": {"type": "text"}, + }, + }, + }, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/54a770f053f3225ea0d1e34334232411.asciidoc b/docs/examples/54a770f053f3225ea0d1e34334232411.asciidoc new file mode 100644 index 0000000..e9e85ef --- /dev/null +++ b/docs/examples/54a770f053f3225ea0d1e34334232411.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:336 + +[source, python] +---- +resp = client.update_by_query(index="twitter", scroll_size="100") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/58b5003c0a53a39bf509aa3797aad471.asciidoc b/docs/examples/58b5003c0a53a39bf509aa3797aad471.asciidoc new file mode 100644 index 0000000..34589a1 --- /dev/null +++ b/docs/examples/58b5003c0a53a39bf509aa3797aad471.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/query-string-query.asciidoc:352 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["content", "name.*^5"], + "query": "this AND that OR thus", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/58df61acbfb15b8ef0aaa18b81ae98a6.asciidoc b/docs/examples/58df61acbfb15b8ef0aaa18b81ae98a6.asciidoc new file mode 100644 index 0000000..3945211 --- /dev/null +++ b/docs/examples/58df61acbfb15b8ef0aaa18b81ae98a6.asciidoc @@ -0,0 +1,11 @@ +// docs/update.asciidoc:164 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={"script": "ctx._source.remove('new_field')"}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5925c23a173a63bdb30b458248d1df76.asciidoc b/docs/examples/5925c23a173a63bdb30b458248d1df76.asciidoc new file mode 100644 index 0000000..b048b18 --- /dev/null +++ b/docs/examples/5925c23a173a63bdb30b458248d1df76.asciidoc @@ -0,0 +1,7 @@ +// api-conventions.asciidoc:407 + +[source, python] +---- +resp = client.indices.get_settings(index="twitter", flat_settings="false") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5be23858b35043fcb7b50fe36b873e6e.asciidoc b/docs/examples/5be23858b35043fcb7b50fe36b873e6e.asciidoc new file mode 100644 index 0000000..04ca4e8 --- /dev/null +++ b/docs/examples/5be23858b35043fcb7b50fe36b873e6e.asciidoc @@ -0,0 +1,9 @@ +// indices/put-mapping.asciidoc:11 + +[source, python] +---- +resp = client.indices.put_mapping( + index="twitter", body={"properties": {"email": {"type": "keyword"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5c2f486c27bd5346e512265f93375d16.asciidoc b/docs/examples/5c2f486c27bd5346e512265f93375d16.asciidoc new file mode 100644 index 0000000..f112169 --- /dev/null +++ b/docs/examples/5c2f486c27bd5346e512265f93375d16.asciidoc @@ -0,0 +1,19 @@ +// query-dsl/range-query.asciidoc:219 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "range": { + "timestamp": { + "time_zone": "+01:00", + "gte": "2020-01-01T00:00:00", + "lte": "now", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5d32279dcd52b22d9e1178a02a3ad957.asciidoc b/docs/examples/5d32279dcd52b22d9e1178a02a3ad957.asciidoc new file mode 100644 index 0000000..cdfee5c --- /dev/null +++ b/docs/examples/5d32279dcd52b22d9e1178a02a3ad957.asciidoc @@ -0,0 +1,15 @@ +// search.asciidoc:18 + +[source, python] +---- +resp = client.index( + index="twitter", + routing="kimchy", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5d9d7b84e2fec7ecd832145cbb951cf1.asciidoc b/docs/examples/5d9d7b84e2fec7ecd832145cbb951cf1.asciidoc new file mode 100644 index 0000000..7f6f82a --- /dev/null +++ b/docs/examples/5d9d7b84e2fec7ecd832145cbb951cf1.asciidoc @@ -0,0 +1,22 @@ +// aggregations/bucket/terms-aggregation.asciidoc:549 + +[source, python] +---- +resp = client.search( + body={ + "size": 0, + "aggs": { + "expired_sessions": { + "terms": { + "field": "account_id", + "include": {"partition": 0, "num_partitions": 20}, + "size": 10000, + "order": {"last_access": "asc"}, + }, + "aggs": {"last_access": {"max": {"field": "access_date"}}}, + } + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5da6efd5b038ada64c9e853c88c1ec47.asciidoc b/docs/examples/5da6efd5b038ada64c9e853c88c1ec47.asciidoc new file mode 100644 index 0000000..7ce0725 --- /dev/null +++ b/docs/examples/5da6efd5b038ada64c9e853c88c1ec47.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/multi-match-query.asciidoc:113 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "brown fox", + "type": "best_fields", + "fields": ["subject", "message"], + "tie_breaker": 0.3, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5dd695679b5141d9142d3d30ba8d300a.asciidoc b/docs/examples/5dd695679b5141d9142d3d30ba8d300a.asciidoc new file mode 100644 index 0000000..18a82e4 --- /dev/null +++ b/docs/examples/5dd695679b5141d9142d3d30ba8d300a.asciidoc @@ -0,0 +1,11 @@ +// aggregations/metrics/valuecount-aggregation.asciidoc:13 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={"aggs": {"types_count": {"value_count": {"field": "type"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5eabcdbf61bfcb484dc694f25c2bba36.asciidoc b/docs/examples/5eabcdbf61bfcb484dc694f25c2bba36.asciidoc new file mode 100644 index 0000000..7899ed4 --- /dev/null +++ b/docs/examples/5eabcdbf61bfcb484dc694f25c2bba36.asciidoc @@ -0,0 +1,9 @@ +// docs/get.asciidoc:323 + +[source, python] +---- +resp = client.index( + index="twitter", id="1", body={"counter": 1, "tags": ["red"]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5f210f74725ea0c9265190346edfa246.asciidoc b/docs/examples/5f210f74725ea0c9265190346edfa246.asciidoc new file mode 100644 index 0000000..3811261 --- /dev/null +++ b/docs/examples/5f210f74725ea0c9265190346edfa246.asciidoc @@ -0,0 +1,13 @@ +// indices/aliases.asciidoc:232 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + {"add": {"indices": ["test1", "test2"], "alias": "alias1"}} + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5f3549ac7fee94682ca0d7439eebdd2a.asciidoc b/docs/examples/5f3549ac7fee94682ca0d7439eebdd2a.asciidoc new file mode 100644 index 0000000..3b1e8df --- /dev/null +++ b/docs/examples/5f3549ac7fee94682ca0d7439eebdd2a.asciidoc @@ -0,0 +1,10 @@ +// search/request/sort.asciidoc:211 + +[source, python] +---- +resp = client.search( + index=["index_long", "index_double"], + body={"sort": [{"field": {"numeric_type": "date_nanos"}}]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/5f3a3eefeefe6fa85ec49d499212d245.asciidoc b/docs/examples/5f3a3eefeefe6fa85ec49d499212d245.asciidoc new file mode 100644 index 0000000..1ac60e9 --- /dev/null +++ b/docs/examples/5f3a3eefeefe6fa85ec49d499212d245.asciidoc @@ -0,0 +1,17 @@ +// indices/put-mapping.asciidoc:257 + +[source, python] +---- +resp = client.indices.put_mapping( + index="my_index", + body={ + "properties": { + "city": { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/609260ad1d5998be2ca09ff1fe237efa.asciidoc b/docs/examples/609260ad1d5998be2ca09ff1fe237efa.asciidoc new file mode 100644 index 0000000..2b17974 --- /dev/null +++ b/docs/examples/609260ad1d5998be2ca09ff1fe237efa.asciidoc @@ -0,0 +1,7 @@ +// mapping.asciidoc:214 + +[source, python] +---- +resp = client.indices.get_mapping(index="my-index") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/60ee33f3acfdd0fe6f288ac77312c780.asciidoc b/docs/examples/60ee33f3acfdd0fe6f288ac77312c780.asciidoc new file mode 100644 index 0000000..82b0f39 --- /dev/null +++ b/docs/examples/60ee33f3acfdd0fe6f288ac77312c780.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/query-string-query.asciidoc:446 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["title"], + "query": "this that thus", + "minimum_should_match": 2, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6138d6919f3cbaaf61e1092f817d295c.asciidoc b/docs/examples/6138d6919f3cbaaf61e1092f817d295c.asciidoc new file mode 100644 index 0000000..15b23b7 --- /dev/null +++ b/docs/examples/6138d6919f3cbaaf61e1092f817d295c.asciidoc @@ -0,0 +1,15 @@ +// query-dsl/match-query.asciidoc:175 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "match": { + "message": {"query": "this is a test", "operator": "and"} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/618d5f3d35921d8cb7e9ccfbe9a4c3e3.asciidoc b/docs/examples/618d5f3d35921d8cb7e9ccfbe9a4c3e3.asciidoc new file mode 100644 index 0000000..b32aad7 --- /dev/null +++ b/docs/examples/618d5f3d35921d8cb7e9ccfbe9a4c3e3.asciidoc @@ -0,0 +1,20 @@ +// query-dsl/regexp-query.asciidoc:23 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "regexp": { + "user": { + "value": "k.*y", + "flags": "ALL", + "max_determinized_states": 10000, + "rewrite": "constant_score", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/621665fdbd7fc103c09bfeed28b67b1a.asciidoc b/docs/examples/621665fdbd7fc103c09bfeed28b67b1a.asciidoc new file mode 100644 index 0000000..0d266e9 --- /dev/null +++ b/docs/examples/621665fdbd7fc103c09bfeed28b67b1a.asciidoc @@ -0,0 +1,7 @@ +// api-conventions.asciidoc:298 + +[source, python] +---- +resp = client.count(filter_path="-_shards") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/625dc94df1f9affb49a082fd99d41620.asciidoc b/docs/examples/625dc94df1f9affb49a082fd99d41620.asciidoc new file mode 100644 index 0000000..6694914 --- /dev/null +++ b/docs/examples/625dc94df1f9affb49a082fd99d41620.asciidoc @@ -0,0 +1,15 @@ +// docs/index_.asciidoc:245 + +[source, python] +---- +resp = client.index( + index="twitter", + routing="kimchy", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/626f8c4b3e2cd3d9beaa63a7f5799d7a.asciidoc b/docs/examples/626f8c4b3e2cd3d9beaa63a7f5799d7a.asciidoc new file mode 100644 index 0000000..c8ffa47 --- /dev/null +++ b/docs/examples/626f8c4b3e2cd3d9beaa63a7f5799d7a.asciidoc @@ -0,0 +1,18 @@ +// search/suggesters.asciidoc:8 + +[source, python] +---- +resp = client.search( + index="twitter", + body={ + "query": {"match": {"message": "tring out Elasticsearch"}}, + "suggest": { + "my-suggestion": { + "text": "tring out Elasticsearch", + "term": {"field": "message"}, + } + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/645136747d37368a14ab34de8bd046c6.asciidoc b/docs/examples/645136747d37368a14ab34de8bd046c6.asciidoc new file mode 100644 index 0000000..60c30b4 --- /dev/null +++ b/docs/examples/645136747d37368a14ab34de8bd046c6.asciidoc @@ -0,0 +1,26 @@ +// mapping/types/date.asciidoc:35 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"date": {"type": "date"}}}}, +) +print(resp) + +resp = client.index(index="my_index", id="1", body={"date": "2015-01-01"}) +print(resp) + +resp = client.index( + index="my_index", id="2", body={"date": "2015-01-01T12:10:30Z"}, +) +print(resp) + +resp = client.index( + index="my_index", id="3", body={"date": 1420070400001}, +) +print(resp) + +resp = client.search(index="my_index", body={"sort": {"date": "asc"}}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/645796e8047967ca4a7635a22a876f4c.asciidoc b/docs/examples/645796e8047967ca4a7635a22a876f4c.asciidoc new file mode 100644 index 0000000..39c8e8a --- /dev/null +++ b/docs/examples/645796e8047967ca4a7635a22a876f4c.asciidoc @@ -0,0 +1,21 @@ +// getting-started.asciidoc:691 + +[source, python] +---- +resp = client.search( + index="bank", + body={ + "size": 0, + "aggs": { + "group_by_state": { + "terms": { + "field": "state.keyword", + "order": {"average_balance": "desc"}, + }, + "aggs": {"average_balance": {"avg": {"field": "balance"}}}, + } + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/645c4c6e209719d3a4d25b1a629cb23b.asciidoc b/docs/examples/645c4c6e209719d3a4d25b1a629cb23b.asciidoc new file mode 100644 index 0000000..02fee7e --- /dev/null +++ b/docs/examples/645c4c6e209719d3a4d25b1a629cb23b.asciidoc @@ -0,0 +1,15 @@ +// query-dsl/function-score-query.asciidoc:241 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "random_score": {"seed": 10, "field": "_seq_no"} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6464124d1677f4552ddddd95a340ca3a.asciidoc b/docs/examples/6464124d1677f4552ddddd95a340ca3a.asciidoc new file mode 100644 index 0000000..ce49a53 --- /dev/null +++ b/docs/examples/6464124d1677f4552ddddd95a340ca3a.asciidoc @@ -0,0 +1,30 @@ +// api-conventions.asciidoc:344 + +[source, python] +---- +resp = client.index( + index="library", + refresh=True, + body={"title": "Book #1", "rating": 200.1}, +) +print(resp) + +resp = client.index( + index="library", + refresh=True, + body={"title": "Book #2", "rating": 1.7}, +) +print(resp) + +resp = client.index( + index="library", + refresh=True, + body={"title": "Book #3", "rating": 0.1}, +) +print(resp) + +resp = client.search( + filter_path="hits.hits._source", _source="title", sort="rating:desc", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/64b9baa6d7556b960b29698f3383aa31.asciidoc b/docs/examples/64b9baa6d7556b960b29698f3383aa31.asciidoc new file mode 100644 index 0000000..ed43be0 --- /dev/null +++ b/docs/examples/64b9baa6d7556b960b29698f3383aa31.asciidoc @@ -0,0 +1,17 @@ +// docs/reindex.asciidoc:967 + +[source, python] +---- +resp = client.reindex( + body={ + "source": { + "remote": {"host": "http://otherhost:9200"}, + "index": "source", + "size": 10, + "query": {"match": {"test": "data"}}, + }, + "dest": {"index": "dest"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6799d132c1c7ca3970763acde2337ef9.asciidoc b/docs/examples/6799d132c1c7ca3970763acde2337ef9.asciidoc new file mode 100644 index 0000000..2e76ff8 --- /dev/null +++ b/docs/examples/6799d132c1c7ca3970763acde2337ef9.asciidoc @@ -0,0 +1,13 @@ +// indices/aliases.asciidoc:249 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + {"add": {"index": "test*", "alias": "all_test_indices"}} + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/67bba546d835bca8f31df13e3587c348.asciidoc b/docs/examples/67bba546d835bca8f31df13e3587c348.asciidoc new file mode 100644 index 0000000..4c649a4 --- /dev/null +++ b/docs/examples/67bba546d835bca8f31df13e3587c348.asciidoc @@ -0,0 +1,7 @@ +// indices/aliases.asciidoc:447 + +[source, python] +---- +resp = client.get(index="test", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/67ceac4bf2d9ac7cc500390544cdcb41.asciidoc b/docs/examples/67ceac4bf2d9ac7cc500390544cdcb41.asciidoc new file mode 100644 index 0000000..734379b --- /dev/null +++ b/docs/examples/67ceac4bf2d9ac7cc500390544cdcb41.asciidoc @@ -0,0 +1,13 @@ +// query-dsl/range-query.asciidoc:157 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "range": {"timestamp": {"gte": "now-1d/d", "lt": "now/d"}} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/68721288dc9ad8aa1b55099b4d303051.asciidoc b/docs/examples/68721288dc9ad8aa1b55099b4d303051.asciidoc new file mode 100644 index 0000000..eab68a5 --- /dev/null +++ b/docs/examples/68721288dc9ad8aa1b55099b4d303051.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/multi-match-query.asciidoc:524 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "quick brown f", + "type": "bool_prefix", + "fields": ["subject", "message"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/68738b4fd0dda177022be45be95b4c84.asciidoc b/docs/examples/68738b4fd0dda177022be45be95b4c84.asciidoc new file mode 100644 index 0000000..5732d47 --- /dev/null +++ b/docs/examples/68738b4fd0dda177022be45be95b4c84.asciidoc @@ -0,0 +1,9 @@ +// docs/reindex.asciidoc:170 + +[source, python] +---- +resp = client.reindex_rethrottle( + task_id="r1A2WoRbTwKZ516z6NEs5A:36619", requests_per_second="-1", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/69a7be47f85138b10437113ab2f0d72d.asciidoc b/docs/examples/69a7be47f85138b10437113ab2f0d72d.asciidoc new file mode 100644 index 0000000..4a29f69 --- /dev/null +++ b/docs/examples/69a7be47f85138b10437113ab2f0d72d.asciidoc @@ -0,0 +1,12 @@ +// docs/get.asciidoc:376 + +[source, python] +---- +resp = client.get( + index="twitter", + id="2", + routing="user1", + stored_fields=["tags", "counter"], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6a1702dd50690cae833572e48a0ddf25.asciidoc b/docs/examples/6a1702dd50690cae833572e48a0ddf25.asciidoc new file mode 100644 index 0000000..29f0080 --- /dev/null +++ b/docs/examples/6a1702dd50690cae833572e48a0ddf25.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/multi-match-query.asciidoc:33 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "Will Smith", + "fields": ["title", "*_name"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6a4679531e64c492fce16dc12de6dcb0.asciidoc b/docs/examples/6a4679531e64c492fce16dc12de6dcb0.asciidoc new file mode 100644 index 0000000..1e4e7fa --- /dev/null +++ b/docs/examples/6a4679531e64c492fce16dc12de6dcb0.asciidoc @@ -0,0 +1,15 @@ +// aggregations/bucket/terms-aggregation.asciidoc:207 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": {"field": "genre", "order": {"_count": "asc"}} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6a81d00f0d73bc5985e76b3cadab645e.asciidoc b/docs/examples/6a81d00f0d73bc5985e76b3cadab645e.asciidoc new file mode 100644 index 0000000..b4edd71 --- /dev/null +++ b/docs/examples/6a81d00f0d73bc5985e76b3cadab645e.asciidoc @@ -0,0 +1,24 @@ +// mapping/params/fielddata.asciidoc:117 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "tag": { + "type": "text", + "fielddata": True, + "fielddata_frequency_filter": { + "min": 0.001, + "max": 0.1, + "min_segment_size": 500, + }, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6bbc613bd4f9aec1bbdbabf5db021d28.asciidoc b/docs/examples/6bbc613bd4f9aec1bbdbabf5db021d28.asciidoc new file mode 100644 index 0000000..9c42018 --- /dev/null +++ b/docs/examples/6bbc613bd4f9aec1bbdbabf5db021d28.asciidoc @@ -0,0 +1,19 @@ +// query-dsl/multi-match-query.asciidoc:228 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "bool": { + "should": [ + {"match": {"title": "quick brown fox"}}, + {"match": {"title.original": "quick brown fox"}}, + {"match": {"title.shingles": "quick brown fox"}}, + ] + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6be70810d6ebd6f09d8a49f9df847765.asciidoc b/docs/examples/6be70810d6ebd6f09d8a49f9df847765.asciidoc new file mode 100644 index 0000000..1cbc431 --- /dev/null +++ b/docs/examples/6be70810d6ebd6f09d8a49f9df847765.asciidoc @@ -0,0 +1,25 @@ +// query-dsl/nested-query.asciidoc:41 + +[source, python] +---- +resp = client.search( + index="my_index", + body={ + "query": { + "nested": { + "path": "obj1", + "query": { + "bool": { + "must": [ + {"match": {"obj1.name": "blue"}}, + {"range": {"obj1.count": {"gt": 5}}}, + ] + } + }, + "score_mode": "avg", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6bf63f2ec6ba55fcaf1092f48212bf25.asciidoc b/docs/examples/6bf63f2ec6ba55fcaf1092f48212bf25.asciidoc new file mode 100644 index 0000000..6c6b35f --- /dev/null +++ b/docs/examples/6bf63f2ec6ba55fcaf1092f48212bf25.asciidoc @@ -0,0 +1,14 @@ +// indices/put-mapping.asciidoc:519 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": {"user_identifier": {"type": "keyword"}} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6d1e75312a28a5ba23837abf768f2510.asciidoc b/docs/examples/6d1e75312a28a5ba23837abf768f2510.asciidoc new file mode 100644 index 0000000..36c6cc8 --- /dev/null +++ b/docs/examples/6d1e75312a28a5ba23837abf768f2510.asciidoc @@ -0,0 +1,9 @@ +// api-conventions.asciidoc:603 + +[source, python] +---- +resp = client.search( + index="twitter", size="surprise_me", error_trace="true", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6f097c298a7abf4c032c4314920c49c8.asciidoc b/docs/examples/6f097c298a7abf4c032c4314920c49c8.asciidoc new file mode 100644 index 0000000..f864883 --- /dev/null +++ b/docs/examples/6f097c298a7abf4c032c4314920c49c8.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:653 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": ["twitter", "blog"]}, + "dest": {"index": "all_together"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6f21a878fee3b43c5332b81aaddbeac7.asciidoc b/docs/examples/6f21a878fee3b43c5332b81aaddbeac7.asciidoc new file mode 100644 index 0000000..460ca9f --- /dev/null +++ b/docs/examples/6f21a878fee3b43c5332b81aaddbeac7.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/query-string-query.asciidoc:528 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["title", "content"], + "query": "this OR that OR thus", + "type": "cross_fields", + "minimum_should_match": 2, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/6faf10a73f7d5fffbcb037bdb2cbaff8.asciidoc b/docs/examples/6faf10a73f7d5fffbcb037bdb2cbaff8.asciidoc new file mode 100644 index 0000000..7e23735 --- /dev/null +++ b/docs/examples/6faf10a73f7d5fffbcb037bdb2cbaff8.asciidoc @@ -0,0 +1,22 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:669 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "dayOfWeek": { + "terms": { + "script": { + "lang": "painless", + "source": "doc['date'].value.dayOfWeekEnum.value", + } + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/70f0aa5853697e265ef3b1df72940951.asciidoc b/docs/examples/70f0aa5853697e265ef3b1df72940951.asciidoc new file mode 100644 index 0000000..4c740df --- /dev/null +++ b/docs/examples/70f0aa5853697e265ef3b1df72940951.asciidoc @@ -0,0 +1,36 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:380 + +[source, python] +---- +resp = client.index( + index="my_index", + id="1", + refresh=True, + body={"date": "2015-10-01T00:30:00Z"}, +) +print(resp) + +resp = client.index( + index="my_index", + id="2", + refresh=True, + body={"date": "2015-10-01T01:30:00Z"}, +) +print(resp) + +resp = client.search( + index="my_index", + size="0", + body={ + "aggs": { + "by_day": { + "date_histogram": { + "field": "date", + "calendar_interval": "day", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/710c7871f20f176d51209b1574b0d61b.asciidoc b/docs/examples/710c7871f20f176d51209b1574b0d61b.asciidoc new file mode 100644 index 0000000..46c992e --- /dev/null +++ b/docs/examples/710c7871f20f176d51209b1574b0d61b.asciidoc @@ -0,0 +1,9 @@ +// docs/get.asciidoc:335 + +[source, python] +---- +resp = client.get( + index="twitter", id="1", stored_fields=["tags", "counter"], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/71b5b2ba9557d0f296ff2de91727d2f6.asciidoc b/docs/examples/71b5b2ba9557d0f296ff2de91727d2f6.asciidoc new file mode 100644 index 0000000..7f8c568 --- /dev/null +++ b/docs/examples/71b5b2ba9557d0f296ff2de91727d2f6.asciidoc @@ -0,0 +1,21 @@ +// aggregations/bucket/terms-aggregation.asciidoc:243 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": { + "field": "genre", + "order": {"max_play_count": "desc"}, + }, + "aggs": { + "max_play_count": {"max": {"field": "play_count"}} + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/71ba9033107882f61cdc3b32fc73568d.asciidoc b/docs/examples/71ba9033107882f61cdc3b32fc73568d.asciidoc new file mode 100644 index 0000000..2462f46 --- /dev/null +++ b/docs/examples/71ba9033107882f61cdc3b32fc73568d.asciidoc @@ -0,0 +1,12 @@ +// mapping.asciidoc:176 + +[source, python] +---- +resp = client.indices.put_mapping( + index="my-index", + body={ + "properties": {"employee-id": {"type": "keyword", "index": False}} + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/72231b7debac60c95b9869a97dafda3a.asciidoc b/docs/examples/72231b7debac60c95b9869a97dafda3a.asciidoc new file mode 100644 index 0000000..9dd6cd0 --- /dev/null +++ b/docs/examples/72231b7debac60c95b9869a97dafda3a.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/match-phrase-query.asciidoc:30 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "match_phrase": { + "message": { + "query": "this is a test", + "analyzer": "my_analyzer", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/72beebe779a258c225dee7b023e60c52.asciidoc b/docs/examples/72beebe779a258c225dee7b023e60c52.asciidoc new file mode 100644 index 0000000..94e56e0 --- /dev/null +++ b/docs/examples/72beebe779a258c225dee7b023e60c52.asciidoc @@ -0,0 +1,7 @@ +// search/request/scroll.asciidoc:148 + +[source, python] +---- +resp = client.nodes.stats(metric="indices", index_metric="search") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/734c2e2a1e45b84f1e4e65b51356fcd7.asciidoc b/docs/examples/734c2e2a1e45b84f1e4e65b51356fcd7.asciidoc new file mode 100644 index 0000000..901ac94 --- /dev/null +++ b/docs/examples/734c2e2a1e45b84f1e4e65b51356fcd7.asciidoc @@ -0,0 +1,10 @@ +// indices/put-mapping.asciidoc:446 + +[source, python] +---- +resp = client.indices.create( + index="new_users", + body={"mappings": {"properties": {"user_id": {"type": "keyword"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/73e5c88ad1488b213fb278ee1cb42289.asciidoc b/docs/examples/73e5c88ad1488b213fb278ee1cb42289.asciidoc new file mode 100644 index 0000000..635b0c2 --- /dev/null +++ b/docs/examples/73e5c88ad1488b213fb278ee1cb42289.asciidoc @@ -0,0 +1,20 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:138 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sales_over_time": { + "date_histogram": { + "field": "date", + "calendar_interval": "2d", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/745f9b8cdb8e91073f6e520e1d9f8c05.asciidoc b/docs/examples/745f9b8cdb8e91073f6e520e1d9f8c05.asciidoc new file mode 100644 index 0000000..96fc5ae --- /dev/null +++ b/docs/examples/745f9b8cdb8e91073f6e520e1d9f8c05.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:73 + +[source, python] +---- +resp = client.get(index="twitter", id="0", _source=["*.id", "retweeted"]) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7477671958734843dd67cf0b8e6c7515.asciidoc b/docs/examples/7477671958734843dd67cf0b8e6c7515.asciidoc new file mode 100644 index 0000000..2c39ebb --- /dev/null +++ b/docs/examples/7477671958734843dd67cf0b8e6c7515.asciidoc @@ -0,0 +1,10 @@ +// search/request/sort.asciidoc:192 + +[source, python] +---- +resp = client.indices.create( + index="index_long", + body={"mappings": {"properties": {"field": {"type": "date_nanos"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/75330ec1305d2beb0e2f34d2195464e2.asciidoc b/docs/examples/75330ec1305d2beb0e2f34d2195464e2.asciidoc new file mode 100644 index 0000000..1483c7f --- /dev/null +++ b/docs/examples/75330ec1305d2beb0e2f34d2195464e2.asciidoc @@ -0,0 +1,7 @@ +// query-dsl/match-all-query.asciidoc:23 + +[source, python] +---- +resp = client.search(body={"query": {"match_all": {"boost": 1.2}}}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/764f9884b370cbdc82a1c5c42ed40ff3.asciidoc b/docs/examples/764f9884b370cbdc82a1c5c42ed40ff3.asciidoc new file mode 100644 index 0000000..a103b18 --- /dev/null +++ b/docs/examples/764f9884b370cbdc82a1c5c42ed40ff3.asciidoc @@ -0,0 +1,15 @@ +// docs/reindex.asciidoc:605 + +[source, python] +---- +resp = client.reindex( + body={ + "source": { + "index": "twitter", + "query": {"term": {"user": "kimchy"}}, + }, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/77243bbf92f2a55e0fca6c2a349a1c15.asciidoc b/docs/examples/77243bbf92f2a55e0fca6c2a349a1c15.asciidoc new file mode 100644 index 0000000..a7d9f79 --- /dev/null +++ b/docs/examples/77243bbf92f2a55e0fca6c2a349a1c15.asciidoc @@ -0,0 +1,20 @@ +// search/request/sort.asciidoc:539 + +[source, python] +---- +resp = client.search( + body={ + "sort": [ + { + "_geo_distance": { + "pin.location": [[-70, 40], [-71, 42]], + "order": "asc", + "unit": "km", + } + } + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/774d715155cd13713e6e327adf6ce328.asciidoc b/docs/examples/774d715155cd13713e6e327adf6ce328.asciidoc new file mode 100644 index 0000000..c0c20c7 --- /dev/null +++ b/docs/examples/774d715155cd13713e6e327adf6ce328.asciidoc @@ -0,0 +1,13 @@ +// aggregations/bucket/terms-aggregation.asciidoc:723 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "tags": {"terms": {"field": "tags", "execution_hint": "map"}} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/78c96113ae4ed0054e581b17542528a7.asciidoc b/docs/examples/78c96113ae4ed0054e581b17542528a7.asciidoc new file mode 100644 index 0000000..0abf266 --- /dev/null +++ b/docs/examples/78c96113ae4ed0054e581b17542528a7.asciidoc @@ -0,0 +1,15 @@ +// docs/reindex.asciidoc:369 + +[source, python] +---- +resp = client.reindex( + body={ + "source": { + "index": "source", + "query": {"match": {"company": "cat"}}, + }, + "dest": {"index": "dest", "routing": "=cat"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7b908b1189f076942de8cd497ff1fa59.asciidoc b/docs/examples/7b908b1189f076942de8cd497ff1fa59.asciidoc new file mode 100644 index 0000000..a404dd3 --- /dev/null +++ b/docs/examples/7b908b1189f076942de8cd497ff1fa59.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/multi-match-query.asciidoc:212 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "quick brown fox", + "type": "most_fields", + "fields": ["title", "title.original", "title.shingles"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7cac05cb589f1614fd5b8589153bef06.asciidoc b/docs/examples/7cac05cb589f1614fd5b8589153bef06.asciidoc new file mode 100644 index 0000000..6e86787 --- /dev/null +++ b/docs/examples/7cac05cb589f1614fd5b8589153bef06.asciidoc @@ -0,0 +1,11 @@ +// docs/update.asciidoc:325 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={"doc": {"name": "new_name"}, "doc_as_upsert": True}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7cf71671859be7c1ecf673396db377cd.asciidoc b/docs/examples/7cf71671859be7c1ecf673396db377cd.asciidoc new file mode 100644 index 0000000..41de4a9 --- /dev/null +++ b/docs/examples/7cf71671859be7c1ecf673396db377cd.asciidoc @@ -0,0 +1,19 @@ +// indices/aliases.asciidoc:314 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + { + "add": { + "index": "test1", + "alias": "alias2", + "filter": {"term": {"user": "kimchy"}}, + } + } + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7df191cc7f814e410a4ac7261065e6ef.asciidoc b/docs/examples/7df191cc7f814e410a4ac7261065e6ef.asciidoc new file mode 100644 index 0000000..fe112d4 --- /dev/null +++ b/docs/examples/7df191cc7f814e410a4ac7261065e6ef.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:413 + +[source, python] +---- +resp = client.tasks.list(detailed="true", actions="*byquery") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7e52bec09624cf6c0de5d13f2bfad5a5.asciidoc b/docs/examples/7e52bec09624cf6c0de5d13f2bfad5a5.asciidoc new file mode 100644 index 0000000..6b53bdc --- /dev/null +++ b/docs/examples/7e52bec09624cf6c0de5d13f2bfad5a5.asciidoc @@ -0,0 +1,11 @@ +// search/request/scroll.asciidoc:45 + +[source, python] +---- +resp = client.search( + index="twitter", + scroll="1m", + body={"size": 100, "query": {"match": {"title": "elasticsearch"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7f28f8ae8fcdbd807dadde0b5b007a6d.asciidoc b/docs/examples/7f28f8ae8fcdbd807dadde0b5b007a6d.asciidoc new file mode 100644 index 0000000..86f9341 --- /dev/null +++ b/docs/examples/7f28f8ae8fcdbd807dadde0b5b007a6d.asciidoc @@ -0,0 +1,18 @@ +// aggregations/bucket/terms-aggregation.asciidoc:641 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "actors": { + "terms": {"field": "actors", "size": 10}, + "aggs": { + "costars": {"terms": {"field": "actors", "size": 5}} + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7f465b7e8ed42df6c42251b4481e699e.asciidoc b/docs/examples/7f465b7e8ed42df6c42251b4481e699e.asciidoc new file mode 100644 index 0000000..c883dea --- /dev/null +++ b/docs/examples/7f465b7e8ed42df6c42251b4481e699e.asciidoc @@ -0,0 +1,16 @@ +// mapping/params/format.asciidoc:13 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "date": {"type": "date", "format": "yyyy-MM-dd"} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7f56755fb6c42f7e6203339a6d0cb6e6.asciidoc b/docs/examples/7f56755fb6c42f7e6203339a6d0cb6e6.asciidoc new file mode 100644 index 0000000..8b27b28 --- /dev/null +++ b/docs/examples/7f56755fb6c42f7e6203339a6d0cb6e6.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/match-query.asciidoc:268 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "match": { + "message": { + "query": "ny city", + "auto_generate_synonyms_phrase_query": False, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/7f697eb436dfa3c30dfe610d8c32d132.asciidoc b/docs/examples/7f697eb436dfa3c30dfe610d8c32d132.asciidoc new file mode 100644 index 0000000..25833c0 --- /dev/null +++ b/docs/examples/7f697eb436dfa3c30dfe610d8c32d132.asciidoc @@ -0,0 +1,20 @@ +// docs/reindex.asciidoc:998 + +[source, python] +---- +resp = client.reindex( + body={ + "source": { + "remote": { + "host": "http://otherhost:9200", + "socket_timeout": "1m", + "connect_timeout": "10s", + }, + "index": "source", + "query": {"match": {"test": "data"}}, + }, + "dest": {"index": "dest"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/804a97ff4d0613e6568e4efb19c52021.asciidoc b/docs/examples/804a97ff4d0613e6568e4efb19c52021.asciidoc new file mode 100644 index 0000000..06f456f --- /dev/null +++ b/docs/examples/804a97ff4d0613e6568e4efb19c52021.asciidoc @@ -0,0 +1,23 @@ +// docs/index_.asciidoc:147 + +[source, python] +---- +resp = client.cluster.put_settings( + body={ + "persistent": { + "action.auto_create_index": "twitter,index10,-index1*,+ind*" + } + }, +) +print(resp) + +resp = client.cluster.put_settings( + body={"persistent": {"action.auto_create_index": "false"}}, +) +print(resp) + +resp = client.cluster.put_settings( + body={"persistent": {"action.auto_create_index": "true"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/81c9aa2678d6166a9662ddf2c011a6a5.asciidoc b/docs/examples/81c9aa2678d6166a9662ddf2c011a6a5.asciidoc new file mode 100644 index 0000000..79c62c7 --- /dev/null +++ b/docs/examples/81c9aa2678d6166a9662ddf2c011a6a5.asciidoc @@ -0,0 +1,7 @@ +// query-dsl/match-all-query.asciidoc:39 + +[source, python] +---- +resp = client.search(body={"query": {"match_none": {}}}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/83f95657beca9bf5d8264c80c7fb463f.asciidoc b/docs/examples/83f95657beca9bf5d8264c80c7fb463f.asciidoc new file mode 100644 index 0000000..2f3d26a --- /dev/null +++ b/docs/examples/83f95657beca9bf5d8264c80c7fb463f.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/match-phrase-query.asciidoc:11 + +[source, python] +---- +resp = client.search( + body={"query": {"match_phrase": {"message": "this is a test"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8653e76676de5d327201b77512afa3a0.asciidoc b/docs/examples/8653e76676de5d327201b77512afa3a0.asciidoc new file mode 100644 index 0000000..74d56c3 --- /dev/null +++ b/docs/examples/8653e76676de5d327201b77512afa3a0.asciidoc @@ -0,0 +1,9 @@ +// indices/update-settings.asciidoc:10 + +[source, python] +---- +resp = client.indices.put_settings( + index="twitter", body={"index": {"number_of_replicas": 2}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/873fbbc6ab81409058591385fd602736.asciidoc b/docs/examples/873fbbc6ab81409058591385fd602736.asciidoc new file mode 100644 index 0000000..58ee708 --- /dev/null +++ b/docs/examples/873fbbc6ab81409058591385fd602736.asciidoc @@ -0,0 +1,35 @@ +// query-dsl/nested-query.asciidoc:165 + +[source, python] +---- +resp = client.index( + index="drivers", + id="1", + body={ + "driver": { + "last_name": "McQueen", + "vehicle": [ + {"make": "Powell Motors", "model": "Canyonero"}, + {"make": "Miller-Meteor", "model": "Ecto-1"}, + ], + } + }, +) +print(resp) + +resp = client.index( + index="drivers", + id="2", + refresh=True, + body={ + "driver": { + "last_name": "Hudson", + "vehicle": [ + {"make": "Mifune", "model": "Mach Five"}, + {"make": "Miller-Meteor", "model": "Ecto-1"}, + ], + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8871b8fcb6de4f0c7dff22798fb10fb7.asciidoc b/docs/examples/8871b8fcb6de4f0c7dff22798fb10fb7.asciidoc new file mode 100644 index 0000000..7dae163 --- /dev/null +++ b/docs/examples/8871b8fcb6de4f0c7dff22798fb10fb7.asciidoc @@ -0,0 +1,16 @@ +// docs/reindex.asciidoc:846 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "twitter"}, + "dest": {"index": "new_twitter", "version_type": "external"}, + "script": { + "source": "if (ctx._source.foo == 'bar') {ctx._version++; ctx._source.remove('foo')}", + "lang": "painless", + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/899eef71a67a1b2aa11a2166ec7f48f1.asciidoc b/docs/examples/899eef71a67a1b2aa11a2166ec7f48f1.asciidoc new file mode 100644 index 0000000..d33a831 --- /dev/null +++ b/docs/examples/899eef71a67a1b2aa11a2166ec7f48f1.asciidoc @@ -0,0 +1,12 @@ +// search/request/sort.asciidoc:369 + +[source, python] +---- +resp = client.search( + body={ + "sort": [{"price": {"unmapped_type": "long"}}], + "query": {"term": {"product": "chocolate"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/89a8ac1509936acc272fc2d72907bc45.asciidoc b/docs/examples/89a8ac1509936acc272fc2d72907bc45.asciidoc new file mode 100644 index 0000000..32d365a --- /dev/null +++ b/docs/examples/89a8ac1509936acc272fc2d72907bc45.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:269 + +[source, python] +---- +resp = client.get_source(index="twitter", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8a355eb25d2a01ba62dc1a22dd46f46f.asciidoc b/docs/examples/8a355eb25d2a01ba62dc1a22dd46f46f.asciidoc new file mode 100644 index 0000000..a72df59 --- /dev/null +++ b/docs/examples/8a355eb25d2a01ba62dc1a22dd46f46f.asciidoc @@ -0,0 +1,21 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:303 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sales_over_time": { + "date_histogram": { + "field": "date", + "calendar_interval": "1M", + "format": "yyyy-MM-dd", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8acc1d67b152e7027e0f0e1a8b4b2431.asciidoc b/docs/examples/8acc1d67b152e7027e0f0e1a8b4b2431.asciidoc new file mode 100644 index 0000000..5e5b375 --- /dev/null +++ b/docs/examples/8acc1d67b152e7027e0f0e1a8b4b2431.asciidoc @@ -0,0 +1,20 @@ +// search.asciidoc:32 + +[source, python] +---- +resp = client.search( + index="twitter", + routing="kimchy", + body={ + "query": { + "bool": { + "must": { + "query_string": {"query": "some query string here"} + }, + "filter": {"term": {"user": "kimchy"}}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8baccd8688a6bad1749b8935f9601ea4.asciidoc b/docs/examples/8baccd8688a6bad1749b8935f9601ea4.asciidoc new file mode 100644 index 0000000..fbd92d2 --- /dev/null +++ b/docs/examples/8baccd8688a6bad1749b8935f9601ea4.asciidoc @@ -0,0 +1,17 @@ +// mapping/types/nested.asciidoc:22 + +[source, python] +---- +resp = client.index( + index="my_index", + id="1", + body={ + "group": "fans", + "user": [ + {"first": "John", "last": "Smith"}, + {"first": "Alice", "last": "White"}, + ], + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8c5977410335d58217e0626618ce6641.asciidoc b/docs/examples/8c5977410335d58217e0626618ce6641.asciidoc new file mode 100644 index 0000000..3353f83 --- /dev/null +++ b/docs/examples/8c5977410335d58217e0626618ce6641.asciidoc @@ -0,0 +1,7 @@ +// query-dsl/terms-query.asciidoc:160 + +[source, python] +---- +resp = client.index(index="my_index", id="2", body={"color": "blue"}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8cd00a3aba7c3c158277bc032aac2830.asciidoc b/docs/examples/8cd00a3aba7c3c158277bc032aac2830.asciidoc new file mode 100644 index 0000000..f69dee2 --- /dev/null +++ b/docs/examples/8cd00a3aba7c3c158277bc032aac2830.asciidoc @@ -0,0 +1,45 @@ +// docs/bulk.asciidoc:533 + +[source, python] +---- +resp = client.bulk( + body=[ + { + "update": { + "_id": "1", + "_index": "index1", + "retry_on_conflict": 3, + } + }, + {"doc": {"field": "value"}}, + { + "update": { + "_id": "0", + "_index": "index1", + "retry_on_conflict": 3, + } + }, + { + "script": { + "source": "ctx._source.counter += params.param1", + "lang": "painless", + "params": {"param1": 1}, + }, + "upsert": {"counter": 1}, + }, + { + "update": { + "_id": "2", + "_index": "index1", + "retry_on_conflict": 3, + } + }, + {"doc": {"field": "value"}, "doc_as_upsert": True}, + {"update": {"_id": "3", "_index": "index1", "_source": True}}, + {"doc": {"field": "value"}}, + {"update": {"_id": "4", "_index": "index1"}}, + {"doc": {"field": "value"}, "_source": True}, + ], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8d9a63d7c31f08bd27d92ece3de1649c.asciidoc b/docs/examples/8d9a63d7c31f08bd27d92ece3de1649c.asciidoc new file mode 100644 index 0000000..14e554e --- /dev/null +++ b/docs/examples/8d9a63d7c31f08bd27d92ece3de1649c.asciidoc @@ -0,0 +1,22 @@ +// mapping/fields/id-field.asciidoc:12 + +[source, python] +---- +resp = client.index( + index="my_index", id="1", body={"text": "Document with ID 1"}, +) +print(resp) + +resp = client.index( + index="my_index", + id="2", + refresh="true", + body={"text": "Document with ID 2"}, +) +print(resp) + +resp = client.search( + index="my_index", body={"query": {"terms": {"_id": ["1", "2"]}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8de3206f80e18185a5ad6481f4c2ee07.asciidoc b/docs/examples/8de3206f80e18185a5ad6481f4c2ee07.asciidoc new file mode 100644 index 0000000..75fc7c7 --- /dev/null +++ b/docs/examples/8de3206f80e18185a5ad6481f4c2ee07.asciidoc @@ -0,0 +1,21 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:431 + +[source, python] +---- +resp = client.search( + index="my_index", + size="0", + body={ + "aggs": { + "by_day": { + "date_histogram": { + "field": "date", + "calendar_interval": "day", + "time_zone": "-01:00", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8e6bfb4441ffa15c86d5dc20fa083571.asciidoc b/docs/examples/8e6bfb4441ffa15c86d5dc20fa083571.asciidoc new file mode 100644 index 0000000..fe02688 --- /dev/null +++ b/docs/examples/8e6bfb4441ffa15c86d5dc20fa083571.asciidoc @@ -0,0 +1,9 @@ +// setup/logging-config.asciidoc:155 + +[source, python] +---- +resp = client.cluster.put_settings( + body={"transient": {"logger.org.elasticsearch.transport": "trace"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8eaf4d5dd4ab1335deefa7749fdbbcc3.asciidoc b/docs/examples/8eaf4d5dd4ab1335deefa7749fdbbcc3.asciidoc new file mode 100644 index 0000000..a1864db --- /dev/null +++ b/docs/examples/8eaf4d5dd4ab1335deefa7749fdbbcc3.asciidoc @@ -0,0 +1,20 @@ +// query-dsl/function-score-query.asciidoc:269 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "field_value_factor": { + "field": "likes", + "factor": 1.2, + "modifier": "sqrt", + "missing": 1, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8f0511f8a5cb176ff2afdd4311799a33.asciidoc b/docs/examples/8f0511f8a5cb176ff2afdd4311799a33.asciidoc new file mode 100644 index 0000000..7856ce1 --- /dev/null +++ b/docs/examples/8f0511f8a5cb176ff2afdd4311799a33.asciidoc @@ -0,0 +1,17 @@ +// search/count.asciidoc:92 + +[source, python] +---- +resp = client.index( + index="twitter", id="1", refresh=True, body={"user": "kimchy"}, +) +print(resp) + +resp = client.count(index="twitter", q="user:kimchy") +print(resp) + +resp = client.count( + index="twitter", body={"query": {"term": {"user": "kimchy"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/8fdf2344c4fb3de6902ad7c5735270df.asciidoc b/docs/examples/8fdf2344c4fb3de6902ad7c5735270df.asciidoc new file mode 100644 index 0000000..1d7adef --- /dev/null +++ b/docs/examples/8fdf2344c4fb3de6902ad7c5735270df.asciidoc @@ -0,0 +1,12 @@ +// docs/get.asciidoc:65 + +[source, python] +---- +resp = client.get( + index="twitter", + id="0", + _source_includes="*.id", + _source_excludes="entities", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/913770050ebbf3b9b549a899bc11060a.asciidoc b/docs/examples/913770050ebbf3b9b549a899bc11060a.asciidoc new file mode 100644 index 0000000..8726f65 --- /dev/null +++ b/docs/examples/913770050ebbf3b9b549a899bc11060a.asciidoc @@ -0,0 +1,17 @@ +// docs/get.asciidoc:302 + +[source, python] +---- +resp = client.indices.create( + index="twitter", + body={ + "mappings": { + "properties": { + "counter": {"type": "integer", "store": False}, + "tags": {"type": "keyword", "store": True}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/9166cf38427d5cde5d2ec12a2012b669.asciidoc b/docs/examples/9166cf38427d5cde5d2ec12a2012b669.asciidoc new file mode 100644 index 0000000..0afbb22 --- /dev/null +++ b/docs/examples/9166cf38427d5cde5d2ec12a2012b669.asciidoc @@ -0,0 +1,15 @@ +// indices/templates.asciidoc:231 + +[source, python] +---- +resp = client.indices.put_template( + name="template_1", + body={ + "index_patterns": ["*"], + "order": 0, + "settings": {"number_of_shards": 1}, + "version": 123, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/93f1bdd72e79827dcf9a34efa02fd977.asciidoc b/docs/examples/93f1bdd72e79827dcf9a34efa02fd977.asciidoc new file mode 100644 index 0000000..e342f7e --- /dev/null +++ b/docs/examples/93f1bdd72e79827dcf9a34efa02fd977.asciidoc @@ -0,0 +1,15 @@ +// aggregations/bucket/terms-aggregation.asciidoc:224 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": {"field": "genre", "order": {"_key": "asc"}} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/9524a9b7373fa4eb2905183b0e806962.asciidoc b/docs/examples/9524a9b7373fa4eb2905183b0e806962.asciidoc new file mode 100644 index 0000000..3b1e037 --- /dev/null +++ b/docs/examples/9524a9b7373fa4eb2905183b0e806962.asciidoc @@ -0,0 +1,22 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:567 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sales_over_time": { + "date_histogram": { + "field": "date", + "calendar_interval": "1M", + "format": "yyyy-MM-dd", + "keyed": True, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/96de5703ba0bd43fd4ac239ec5408542.asciidoc b/docs/examples/96de5703ba0bd43fd4ac239ec5408542.asciidoc new file mode 100644 index 0000000..cd3f50f --- /dev/null +++ b/docs/examples/96de5703ba0bd43fd4ac239ec5408542.asciidoc @@ -0,0 +1,17 @@ +// docs/update.asciidoc:96 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={ + "script": { + "source": "ctx._source.counter += params.count", + "lang": "painless", + "params": {"count": 4}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/973a3ff47fc4ce036ecd9bd363fef9f7.asciidoc b/docs/examples/973a3ff47fc4ce036ecd9bd363fef9f7.asciidoc new file mode 100644 index 0000000..2b0f355 --- /dev/null +++ b/docs/examples/973a3ff47fc4ce036ecd9bd363fef9f7.asciidoc @@ -0,0 +1,16 @@ +// docs/reindex.asciidoc:780 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "metricbeat-*"}, + "dest": {"index": "metricbeat"}, + "script": { + "lang": "painless", + "source": "ctx._index = 'metricbeat-' + (ctx._index.substring('metricbeat-'.length(), ctx._index.length())) + '-1'", + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/978088f989d45dd09339582e9cbc60e0.asciidoc b/docs/examples/978088f989d45dd09339582e9cbc60e0.asciidoc new file mode 100644 index 0000000..5e6bd38 --- /dev/null +++ b/docs/examples/978088f989d45dd09339582e9cbc60e0.asciidoc @@ -0,0 +1,10 @@ +// api-conventions.asciidoc:79 + +[source, python] +---- +resp = client.search( + index="%3Clogstash-%7Bnow%2Fd%7D%3E", + body={"query": {"match": {"test": "data"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/979d25dff2d8987119410291ad47b0d1.asciidoc b/docs/examples/979d25dff2d8987119410291ad47b0d1.asciidoc new file mode 100644 index 0000000..ca41f78 --- /dev/null +++ b/docs/examples/979d25dff2d8987119410291ad47b0d1.asciidoc @@ -0,0 +1,20 @@ +// search/request/sort.asciidoc:444 + +[source, python] +---- +resp = client.search( + body={ + "sort": [ + { + "_geo_distance": { + "pin.location": {"lat": 40, "lon": -70}, + "order": "asc", + "unit": "km", + } + } + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/97babc8d19ef0866774576716eb6d19e.asciidoc b/docs/examples/97babc8d19ef0866774576716eb6d19e.asciidoc new file mode 100644 index 0000000..eec3f36 --- /dev/null +++ b/docs/examples/97babc8d19ef0866774576716eb6d19e.asciidoc @@ -0,0 +1,16 @@ +// docs/update-by-query.asciidoc:720 + +[source, python] +---- +resp = client.update_by_query( + index="test", refresh=True, conflicts="proceed", +) +print(resp) + +resp = client.search( + index="test", + filter_path="hits.total", + body={"query": {"match": {"flag": "foo"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/98234499cfec70487cec5d013e976a84.asciidoc b/docs/examples/98234499cfec70487cec5d013e976a84.asciidoc new file mode 100644 index 0000000..3ff9dc7 --- /dev/null +++ b/docs/examples/98234499cfec70487cec5d013e976a84.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:253 + +[source, python] +---- +resp = client.exists(index="twitter", id="0") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/98aeb275f829b5f7b8eb2147701565ff.asciidoc b/docs/examples/98aeb275f829b5f7b8eb2147701565ff.asciidoc new file mode 100644 index 0000000..350e349 --- /dev/null +++ b/docs/examples/98aeb275f829b5f7b8eb2147701565ff.asciidoc @@ -0,0 +1,17 @@ +// docs/update.asciidoc:177 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={ + "script": { + "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }", + "lang": "painless", + "params": {"tag": "green"}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/98b121bf47cebd85671a2cb519688d28.asciidoc b/docs/examples/98b121bf47cebd85671a2cb519688d28.asciidoc new file mode 100644 index 0000000..efb85bc --- /dev/null +++ b/docs/examples/98b121bf47cebd85671a2cb519688d28.asciidoc @@ -0,0 +1,18 @@ +// aggregations/bucket/terms-aggregation.asciidoc:520 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "JapaneseCars": { + "terms": {"field": "make", "include": ["mazda", "honda"]} + }, + "ActiveCarManufacturers": { + "terms": {"field": "make", "exclude": ["rover", "jensen"]} + }, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/98f14fddddea54a7d6149ab7b92e099d.asciidoc b/docs/examples/98f14fddddea54a7d6149ab7b92e099d.asciidoc new file mode 100644 index 0000000..410a15f --- /dev/null +++ b/docs/examples/98f14fddddea54a7d6149ab7b92e099d.asciidoc @@ -0,0 +1,7 @@ +// indices/delete-index.asciidoc:10 + +[source, python] +---- +resp = client.indices.delete(index="twitter") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/99a52be903945b17e734a1d02a57e958.asciidoc b/docs/examples/99a52be903945b17e734a1d02a57e958.asciidoc new file mode 100644 index 0000000..62f75a0 --- /dev/null +++ b/docs/examples/99a52be903945b17e734a1d02a57e958.asciidoc @@ -0,0 +1,9 @@ +// mapping.asciidoc:260 + +[source, python] +---- +resp = client.indices.get_field_mapping( + index="my-index", fields="employee-id", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/9a4d5e41c52c20635d1fd9c6e13f6c7a.asciidoc b/docs/examples/9a4d5e41c52c20635d1fd9c6e13f6c7a.asciidoc new file mode 100644 index 0000000..9bb54c6 --- /dev/null +++ b/docs/examples/9a4d5e41c52c20635d1fd9c6e13f6c7a.asciidoc @@ -0,0 +1,20 @@ +// docs/reindex.asciidoc:764 + +[source, python] +---- +resp = client.index( + index="metricbeat-2016.05.30", + id="1", + refresh=True, + body={"system.cpu.idle.pct": 0.908}, +) +print(resp) + +resp = client.index( + index="metricbeat-2016.05.31", + id="1", + refresh=True, + body={"system.cpu.idle.pct": 0.105}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/9a8995fd31351045d99c78e40444c8ea.asciidoc b/docs/examples/9a8995fd31351045d99c78e40444c8ea.asciidoc new file mode 100644 index 0000000..9eaf2fb --- /dev/null +++ b/docs/examples/9a8995fd31351045d99c78e40444c8ea.asciidoc @@ -0,0 +1,9 @@ +// aggregations/bucket/terms-aggregation.asciidoc:57 + +[source, python] +---- +resp = client.search( + body={"aggs": {"genres": {"terms": {"field": "genre"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/9e56d79ad9a02b642c361f0b85dd95d7.asciidoc b/docs/examples/9e56d79ad9a02b642c361f0b85dd95d7.asciidoc new file mode 100644 index 0000000..dc00921 --- /dev/null +++ b/docs/examples/9e56d79ad9a02b642c361f0b85dd95d7.asciidoc @@ -0,0 +1,10 @@ +// query-dsl/terms-query.asciidoc:127 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"color": {"type": "keyword"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a116949e446f34dc25ae57d4b703d0c1.asciidoc b/docs/examples/a116949e446f34dc25ae57d4b703d0c1.asciidoc new file mode 100644 index 0000000..5556da8 --- /dev/null +++ b/docs/examples/a116949e446f34dc25ae57d4b703d0c1.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/range-query.asciidoc:16 + +[source, python] +---- +resp = client.search( + body={"query": {"range": {"age": {"gte": 10, "lte": 20, "boost": 2}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a1db5c822745fe167e9ef854dca3d129.asciidoc b/docs/examples/a1db5c822745fe167e9ef854dca3d129.asciidoc new file mode 100644 index 0000000..885da2f --- /dev/null +++ b/docs/examples/a1db5c822745fe167e9ef854dca3d129.asciidoc @@ -0,0 +1,20 @@ +// search/request/sort.asciidoc:491 + +[source, python] +---- +resp = client.search( + body={ + "sort": [ + { + "_geo_distance": { + "pin.location": "drm3btev3e86", + "order": "asc", + "unit": "km", + } + } + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a2a25aad1fea9a541b52ac613c78fb64.asciidoc b/docs/examples/a2a25aad1fea9a541b52ac613c78fb64.asciidoc new file mode 100644 index 0000000..273f52b --- /dev/null +++ b/docs/examples/a2a25aad1fea9a541b52ac613c78fb64.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/query-string-query.asciidoc:297 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["content", "name^5"], + "query": "this AND that OR thus", + "tie_breaker": 0, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a34d70d7022eb4ba48909d440c80390f.asciidoc b/docs/examples/a34d70d7022eb4ba48909d440c80390f.asciidoc new file mode 100644 index 0000000..fb8e03c --- /dev/null +++ b/docs/examples/a34d70d7022eb4ba48909d440c80390f.asciidoc @@ -0,0 +1,10 @@ +// api-conventions.asciidoc:133 + +[source, python] +---- +resp = client.search( + index="%3Clogstash-%7Bnow%2Fd-2d%7D%3E%2C%3Clogstash-%7Bnow%2Fd-1d%7D%3E%2C%3Clogstash-%7Bnow%2Fd%7D%3E", + body={"query": {"match": {"test": "data"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a42f33e15b0995bb4b6058659bfdea85.asciidoc b/docs/examples/a42f33e15b0995bb4b6058659bfdea85.asciidoc new file mode 100644 index 0000000..6194e50 --- /dev/null +++ b/docs/examples/a42f33e15b0995bb4b6058659bfdea85.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/function-score-query.asciidoc:19 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "query": {"match_all": {}}, + "boost": "5", + "random_score": {}, + "boost_mode": "multiply", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a49169b4622918992411fab4ec48191b.asciidoc b/docs/examples/a49169b4622918992411fab4ec48191b.asciidoc new file mode 100644 index 0000000..2d63843 --- /dev/null +++ b/docs/examples/a49169b4622918992411fab4ec48191b.asciidoc @@ -0,0 +1,21 @@ +// aggregations/bucket/terms-aggregation.asciidoc:466 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "genres": { + "terms": { + "field": "genre", + "script": { + "source": "'Genre: ' +_value", + "lang": "painless", + }, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a4a396cd07657b3977713fb3a742c41b.asciidoc b/docs/examples/a4a396cd07657b3977713fb3a742c41b.asciidoc new file mode 100644 index 0000000..7472b88 --- /dev/null +++ b/docs/examples/a4a396cd07657b3977713fb3a742c41b.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:12 + +[source, python] +---- +resp = client.update_by_query(index="twitter", conflicts="proceed") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a5a7050fb9dcb9574e081957ade28617.asciidoc b/docs/examples/a5a7050fb9dcb9574e081957ade28617.asciidoc new file mode 100644 index 0000000..9286722 --- /dev/null +++ b/docs/examples/a5a7050fb9dcb9574e081957ade28617.asciidoc @@ -0,0 +1,12 @@ +// docs/delete-by-query.asciidoc:487 + +[source, python] +---- +resp = client.delete_by_query( + index="twitter", + refresh=True, + slices="5", + body={"query": {"range": {"likes": {"lt": 10}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a6f8636b03cc5f677b7d89e750328612.asciidoc b/docs/examples/a6f8636b03cc5f677b7d89e750328612.asciidoc new file mode 100644 index 0000000..33ea233 --- /dev/null +++ b/docs/examples/a6f8636b03cc5f677b7d89e750328612.asciidoc @@ -0,0 +1,7 @@ +// api-conventions.asciidoc:571 + +[source, python] +---- +resp = client.search(index="twitter", size="surprise_me") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a71c438cc4df1cafe3109ccff475afdb.asciidoc b/docs/examples/a71c438cc4df1cafe3109ccff475afdb.asciidoc new file mode 100644 index 0000000..0452c27 --- /dev/null +++ b/docs/examples/a71c438cc4df1cafe3109ccff475afdb.asciidoc @@ -0,0 +1,18 @@ +// mapping/types/numeric.asciidoc:22 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "number_of_bytes": {"type": "integer"}, + "time_in_seconds": {"type": "float"}, + "price": {"type": "scaled_float", "scaling_factor": 100}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a7c15fe6b5779c84ce9a34bf4b2a7ab7.asciidoc b/docs/examples/a7c15fe6b5779c84ce9a34bf4b2a7ab7.asciidoc new file mode 100644 index 0000000..eab85f6 --- /dev/null +++ b/docs/examples/a7c15fe6b5779c84ce9a34bf4b2a7ab7.asciidoc @@ -0,0 +1,10 @@ +// mapping/params/fielddata.asciidoc:84 + +[source, python] +---- +resp = client.indices.put_mapping( + index="my_index", + body={"properties": {"my_field": {"type": "text", "fielddata": True}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a80f5db4357bb25b8704d374c18318ed.asciidoc b/docs/examples/a80f5db4357bb25b8704d374c18318ed.asciidoc new file mode 100644 index 0000000..1e516fc --- /dev/null +++ b/docs/examples/a80f5db4357bb25b8704d374c18318ed.asciidoc @@ -0,0 +1,11 @@ +// query-dsl/term-query.asciidoc:165 + +[source, python] +---- +resp = client.search( + index="my_index", + pretty=True, + body={"query": {"match": {"full_text": "Quick Brown Foxes!"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/a8fba09a46b2c3524428aa3259b7124f.asciidoc b/docs/examples/a8fba09a46b2c3524428aa3259b7124f.asciidoc new file mode 100644 index 0000000..0140644 --- /dev/null +++ b/docs/examples/a8fba09a46b2c3524428aa3259b7124f.asciidoc @@ -0,0 +1,7 @@ +// indices/get-mapping.asciidoc:10 + +[source, python] +---- +resp = client.indices.get_mapping(index="twitter") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/aa6bfe54e2436eb668091fe31c2fbf4d.asciidoc b/docs/examples/aa6bfe54e2436eb668091fe31c2fbf4d.asciidoc new file mode 100644 index 0000000..5f5345a --- /dev/null +++ b/docs/examples/aa6bfe54e2436eb668091fe31c2fbf4d.asciidoc @@ -0,0 +1,37 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:502 + +[source, python] +---- +resp = client.index( + index="my_index", + id="1", + refresh=True, + body={"date": "2015-10-01T05:30:00Z"}, +) +print(resp) + +resp = client.index( + index="my_index", + id="2", + refresh=True, + body={"date": "2015-10-01T06:30:00Z"}, +) +print(resp) + +resp = client.search( + index="my_index", + size="0", + body={ + "aggs": { + "by_day": { + "date_histogram": { + "field": "date", + "calendar_interval": "day", + "offset": "+6h", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/abd4fc3ce7784413a56fe2dcfe2809b5.asciidoc b/docs/examples/abd4fc3ce7784413a56fe2dcfe2809b5.asciidoc new file mode 100644 index 0000000..9e32fbd --- /dev/null +++ b/docs/examples/abd4fc3ce7784413a56fe2dcfe2809b5.asciidoc @@ -0,0 +1,11 @@ +// docs/update-by-query.asciidoc:693 + +[source, python] +---- +resp = client.search( + index="test", + filter_path="hits.total", + body={"query": {"match": {"flag": "foo"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/abf329ebefaf58acd4ee30e685731499.asciidoc b/docs/examples/abf329ebefaf58acd4ee30e685731499.asciidoc new file mode 100644 index 0000000..a308bec --- /dev/null +++ b/docs/examples/abf329ebefaf58acd4ee30e685731499.asciidoc @@ -0,0 +1,10 @@ +// search/request/sort.asciidoc:122 + +[source, python] +---- +resp = client.indices.create( + index="index_double", + body={"mappings": {"properties": {"field": {"type": "double"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ac544eb247a29ca42aab13826ca88561.asciidoc b/docs/examples/ac544eb247a29ca42aab13826ca88561.asciidoc new file mode 100644 index 0000000..abf9a53 --- /dev/null +++ b/docs/examples/ac544eb247a29ca42aab13826ca88561.asciidoc @@ -0,0 +1,17 @@ +// docs/update.asciidoc:135 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={ + "script": { + "source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }", + "lang": "painless", + "params": {"tag": "blue"}, + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ad0dcbc7fc619e952c8825b8f307b7b2.asciidoc b/docs/examples/ad0dcbc7fc619e952c8825b8f307b7b2.asciidoc new file mode 100644 index 0000000..7b2d814 --- /dev/null +++ b/docs/examples/ad0dcbc7fc619e952c8825b8f307b7b2.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/multi-match-query.asciidoc:400 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "Jon", + "type": "cross_fields", + "fields": ["first", "first.edge", "last", "last.edge"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ad6ea0c1e46712aa1fd6d3bfa0ec979e.asciidoc b/docs/examples/ad6ea0c1e46712aa1fd6d3bfa0ec979e.asciidoc new file mode 100644 index 0000000..a5f996f --- /dev/null +++ b/docs/examples/ad6ea0c1e46712aa1fd6d3bfa0ec979e.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/query-string-query.asciidoc:42 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "query": "(new york city) OR (big apple)", + "default_field": "content", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ad79228630684d950fe9792a768d24c5.asciidoc b/docs/examples/ad79228630684d950fe9792a768d24c5.asciidoc new file mode 100644 index 0000000..2abcf85 --- /dev/null +++ b/docs/examples/ad79228630684d950fe9792a768d24c5.asciidoc @@ -0,0 +1,26 @@ +// indices/aliases.asciidoc:456 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + { + "add": { + "index": "test", + "alias": "alias1", + "is_write_index": False, + } + }, + { + "add": { + "index": "test2", + "alias": "alias1", + "is_write_index": True, + } + }, + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ae9b5fbd42af2386ffbf56ad4a697e51.asciidoc b/docs/examples/ae9b5fbd42af2386ffbf56ad4a697e51.asciidoc new file mode 100644 index 0000000..000039e --- /dev/null +++ b/docs/examples/ae9b5fbd42af2386ffbf56ad4a697e51.asciidoc @@ -0,0 +1,19 @@ +// search/request/sort.asciidoc:30 + +[source, python] +---- +resp = client.search( + index="my_index", + body={ + "sort": [ + {"post_date": {"order": "asc"}}, + "user", + {"name": "desc"}, + {"age": "desc"}, + "_score", + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ae9ccfaa146731ab9176df90670db1c2.asciidoc b/docs/examples/ae9ccfaa146731ab9176df90670db1c2.asciidoc new file mode 100644 index 0000000..45dc5b6 --- /dev/null +++ b/docs/examples/ae9ccfaa146731ab9176df90670db1c2.asciidoc @@ -0,0 +1,17 @@ +// docs/bulk.asciidoc:422 + +[source, python] +---- +resp = client.bulk( + body=[ + {"index": {"_index": "test", "_id": "1"}}, + {"field1": "value1"}, + {"delete": {"_index": "test", "_id": "2"}}, + {"create": {"_index": "test", "_id": "3"}}, + {"field1": "value3"}, + {"update": {"_id": "1", "_index": "test"}}, + {"doc": {"field2": "value2"}}, + ], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/af3fb9fa5691a7b37a6dc2a69ff66e64.asciidoc b/docs/examples/af3fb9fa5691a7b37a6dc2a69ff66e64.asciidoc new file mode 100644 index 0000000..6b9d916 --- /dev/null +++ b/docs/examples/af3fb9fa5691a7b37a6dc2a69ff66e64.asciidoc @@ -0,0 +1,14 @@ +// indices/aliases.asciidoc:200 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + {"remove": {"index": "test1", "alias": "alias1"}}, + {"add": {"index": "test1", "alias": "alias2"}}, + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/afc29b61c532cf683f749baf013e7bfe.asciidoc b/docs/examples/afc29b61c532cf683f749baf013e7bfe.asciidoc new file mode 100644 index 0000000..ee39f50 --- /dev/null +++ b/docs/examples/afc29b61c532cf683f749baf013e7bfe.asciidoc @@ -0,0 +1,14 @@ +// indices/put-mapping.asciidoc:536 + +[source, python] +---- +resp = client.indices.put_mapping( + index="my_index", + body={ + "properties": { + "user_id": {"type": "alias", "path": "user_identifier"} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b02e4907c9936c1adc16ccce9d49900d.asciidoc b/docs/examples/b02e4907c9936c1adc16ccce9d49900d.asciidoc new file mode 100644 index 0000000..cace8ed --- /dev/null +++ b/docs/examples/b02e4907c9936c1adc16ccce9d49900d.asciidoc @@ -0,0 +1,7 @@ +// cluster/health.asciidoc:143 + +[source, python] +---- +resp = client.cluster.health() +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b0d64d0a554549e5b2808002a0725493.asciidoc b/docs/examples/b0d64d0a554549e5b2808002a0725493.asciidoc new file mode 100644 index 0000000..c7d3da5 --- /dev/null +++ b/docs/examples/b0d64d0a554549e5b2808002a0725493.asciidoc @@ -0,0 +1,11 @@ +// search/request/scroll.asciidoc:161 + +[source, python] +---- +resp = client.clear_scroll( + body={ + "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b0eaf67e5cce24ef8889bf20951ccec1.asciidoc b/docs/examples/b0eaf67e5cce24ef8889bf20951ccec1.asciidoc new file mode 100644 index 0000000..ac0cedb --- /dev/null +++ b/docs/examples/b0eaf67e5cce24ef8889bf20951ccec1.asciidoc @@ -0,0 +1,19 @@ +// query-dsl/multi-match-query.asciidoc:130 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "dis_max": { + "queries": [ + {"match": {"subject": "brown fox"}}, + {"match": {"message": "brown fox"}}, + ], + "tie_breaker": 0.3, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b0ec418bf416c62bed602b0a32a6d5f5.asciidoc b/docs/examples/b0ec418bf416c62bed602b0a32a6d5f5.asciidoc new file mode 100644 index 0000000..ea9519b --- /dev/null +++ b/docs/examples/b0ec418bf416c62bed602b0a32a6d5f5.asciidoc @@ -0,0 +1,7 @@ +// indices/aliases.asciidoc:435 + +[source, python] +---- +resp = client.index(index="alias1", id="1", body={"foo": "bar"}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b1efa1c51a34dd5ab5511b71a399f5b1.asciidoc b/docs/examples/b1efa1c51a34dd5ab5511b71a399f5b1.asciidoc new file mode 100644 index 0000000..2d048e9 --- /dev/null +++ b/docs/examples/b1efa1c51a34dd5ab5511b71a399f5b1.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:413 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "source"}, + "dest": {"index": "dest", "pipeline": "some_ingest_pipeline"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b214942b938e47f2c486e523546cb574.asciidoc b/docs/examples/b214942b938e47f2c486e523546cb574.asciidoc new file mode 100644 index 0000000..20b6941 --- /dev/null +++ b/docs/examples/b214942b938e47f2c486e523546cb574.asciidoc @@ -0,0 +1,19 @@ +// mapping/types/nested.asciidoc:58 + +[source, python] +---- +resp = client.search( + index="my_index", + body={ + "query": { + "bool": { + "must": [ + {"match": {"user.first": "Alice"}}, + {"match": {"user.last": "Smith"}}, + ] + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b41dce56b0e640d32b1cf452f87cec17.asciidoc b/docs/examples/b41dce56b0e640d32b1cf452f87cec17.asciidoc new file mode 100644 index 0000000..15a6e50 --- /dev/null +++ b/docs/examples/b41dce56b0e640d32b1cf452f87cec17.asciidoc @@ -0,0 +1,12 @@ +// search/request/scroll.asciidoc:63 + +[source, python] +---- +resp = client.scroll( + body={ + "scroll": "1m", + "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b4392116f2cc57ce8064ccbad30318d5.asciidoc b/docs/examples/b4392116f2cc57ce8064ccbad30318d5.asciidoc new file mode 100644 index 0000000..d9db250 --- /dev/null +++ b/docs/examples/b4392116f2cc57ce8064ccbad30318d5.asciidoc @@ -0,0 +1,9 @@ +// indices/aliases.asciidoc:166 + +[source, python] +---- +resp = client.indices.update_aliases( + body={"actions": [{"add": {"index": "test1", "alias": "alias1"}}]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b4a0d0ed512dffc10ee53bca2feca49b.asciidoc b/docs/examples/b4a0d0ed512dffc10ee53bca2feca49b.asciidoc new file mode 100644 index 0000000..ceb01cb --- /dev/null +++ b/docs/examples/b4a0d0ed512dffc10ee53bca2feca49b.asciidoc @@ -0,0 +1,28 @@ +// query-dsl/function-score-query.asciidoc:41 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "query": {"match_all": {}}, + "boost": "5", + "functions": [ + { + "filter": {"match": {"test": "bar"}}, + "random_score": {}, + "weight": 23, + }, + {"filter": {"match": {"test": "cat"}}, "weight": 42}, + ], + "max_boost": 42, + "score_mode": "max", + "boost_mode": "multiply", + "min_score": 42, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b5f95bc097a201b29c7200fc8d3d31c1.asciidoc b/docs/examples/b5f95bc097a201b29c7200fc8d3d31c1.asciidoc new file mode 100644 index 0000000..471f589 --- /dev/null +++ b/docs/examples/b5f95bc097a201b29c7200fc8d3d31c1.asciidoc @@ -0,0 +1,26 @@ +// indices/templates.asciidoc:180 + +[source, python] +---- +resp = client.indices.put_template( + name="template_1", + body={ + "index_patterns": ["*"], + "order": 0, + "settings": {"number_of_shards": 1}, + "mappings": {"_source": {"enabled": False}}, + }, +) +print(resp) + +resp = client.indices.put_template( + name="template_2", + body={ + "index_patterns": ["te*"], + "order": 1, + "settings": {"number_of_shards": 1}, + "mappings": {"_source": {"enabled": True}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b68c85fe1b0d2f264dc0d1cbf530f319.asciidoc b/docs/examples/b68c85fe1b0d2f264dc0d1cbf530f319.asciidoc new file mode 100644 index 0000000..5305f9c --- /dev/null +++ b/docs/examples/b68c85fe1b0d2f264dc0d1cbf530f319.asciidoc @@ -0,0 +1,21 @@ +// query-dsl/function-score-query.asciidoc:175 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "query": {"match": {"message": "elasticsearch"}}, + "script_score": { + "script": { + "params": {"a": 5, "b": 1.2}, + "source": "params.a / Math.pow(params.b, doc['likes'].value)", + } + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b789292f9cf63ce912e058c46d90ce20.asciidoc b/docs/examples/b789292f9cf63ce912e058c46d90ce20.asciidoc new file mode 100644 index 0000000..7b16212 --- /dev/null +++ b/docs/examples/b789292f9cf63ce912e058c46d90ce20.asciidoc @@ -0,0 +1,20 @@ +// aggregations/bucket/datehistogram-aggregation.asciidoc:119 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "sales_over_time": { + "date_histogram": { + "field": "date", + "calendar_interval": "month", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b918d6b798da673a33e49b94f61dcdc0.asciidoc b/docs/examples/b918d6b798da673a33e49b94f61dcdc0.asciidoc new file mode 100644 index 0000000..14bc94d --- /dev/null +++ b/docs/examples/b918d6b798da673a33e49b94f61dcdc0.asciidoc @@ -0,0 +1,16 @@ +// docs/index_.asciidoc:367 + +[source, python] +---- +resp = client.index( + index="twitter", + id="1", + timeout="5m", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b919f88e6f47a40d5793479440a90ba6.asciidoc b/docs/examples/b919f88e6f47a40d5793479440a90ba6.asciidoc new file mode 100644 index 0000000..4a7f635 --- /dev/null +++ b/docs/examples/b919f88e6f47a40d5793479440a90ba6.asciidoc @@ -0,0 +1,66 @@ +// mapping/types/nested.asciidoc:85 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"user": {"type": "nested"}}}}, +) +print(resp) + +resp = client.index( + index="my_index", + id="1", + body={ + "group": "fans", + "user": [ + {"first": "John", "last": "Smith"}, + {"first": "Alice", "last": "White"}, + ], + }, +) +print(resp) + +resp = client.search( + index="my_index", + body={ + "query": { + "nested": { + "path": "user", + "query": { + "bool": { + "must": [ + {"match": {"user.first": "Alice"}}, + {"match": {"user.last": "Smith"}}, + ] + } + }, + } + } + }, +) +print(resp) + +resp = client.search( + index="my_index", + body={ + "query": { + "nested": { + "path": "user", + "query": { + "bool": { + "must": [ + {"match": {"user.first": "Alice"}}, + {"match": {"user.last": "White"}}, + ] + } + }, + "inner_hits": { + "highlight": {"fields": {"user.first": {}}} + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b93ed4ef309819734f0eeea82e8b0f1f.asciidoc b/docs/examples/b93ed4ef309819734f0eeea82e8b0f1f.asciidoc new file mode 100644 index 0000000..76fce28 --- /dev/null +++ b/docs/examples/b93ed4ef309819734f0eeea82e8b0f1f.asciidoc @@ -0,0 +1,18 @@ +// aggregations/bucket/filter-aggregation.asciidoc:9 + +[source, python] +---- +resp = client.search( + index="sales", + size="0", + body={ + "aggs": { + "t_shirts": { + "filter": {"term": {"type": "t-shirt"}}, + "aggs": {"avg_price": {"avg": {"field": "price"}}}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b94cee0f74f57742b3948f9b784dfdd4.asciidoc b/docs/examples/b94cee0f74f57742b3948f9b784dfdd4.asciidoc new file mode 100644 index 0000000..36eedee --- /dev/null +++ b/docs/examples/b94cee0f74f57742b3948f9b784dfdd4.asciidoc @@ -0,0 +1,12 @@ +// search/request/scroll.asciidoc:194 + +[source, python] +---- +resp = client.clear_scroll( + scroll_id=[ + "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==", + "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB", + ], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b997885974522ef439d5e345924cc5ba.asciidoc b/docs/examples/b997885974522ef439d5e345924cc5ba.asciidoc new file mode 100644 index 0000000..f083af2 --- /dev/null +++ b/docs/examples/b997885974522ef439d5e345924cc5ba.asciidoc @@ -0,0 +1,20 @@ +// search/request/sort.asciidoc:94 + +[source, python] +---- +resp = client.index( + index="my_index", + id="1", + refresh=True, + body={"product": "chocolate", "price": [20, 4]}, +) +print(resp) + +resp = client.search( + body={ + "query": {"term": {"product": "chocolate"}}, + "sort": [{"price": {"order": "asc", "mode": "avg"}}], + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b9a153725b28fdd0a5aabd7f17a8c2d7.asciidoc b/docs/examples/b9a153725b28fdd0a5aabd7f17a8c2d7.asciidoc new file mode 100644 index 0000000..dab5d5a --- /dev/null +++ b/docs/examples/b9a153725b28fdd0a5aabd7f17a8c2d7.asciidoc @@ -0,0 +1,7 @@ +// api-conventions.asciidoc:377 + +[source, python] +---- +resp = client.indices.get_settings(index="twitter", flat_settings="true") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/b9c5d7ca6ca9c6f747201f45337a4abf.asciidoc b/docs/examples/b9c5d7ca6ca9c6f747201f45337a4abf.asciidoc new file mode 100644 index 0000000..e275ed1 --- /dev/null +++ b/docs/examples/b9c5d7ca6ca9c6f747201f45337a4abf.asciidoc @@ -0,0 +1,10 @@ +// indices/create-index.asciidoc:99 + +[source, python] +---- +resp = client.indices.create( + index="twitter", + body={"settings": {"number_of_shards": 3, "number_of_replicas": 2}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ba0b4081c98f3387f76b77847c52ee9a.asciidoc b/docs/examples/ba0b4081c98f3387f76b77847c52ee9a.asciidoc new file mode 100644 index 0000000..e934bf0 --- /dev/null +++ b/docs/examples/ba0b4081c98f3387f76b77847c52ee9a.asciidoc @@ -0,0 +1,22 @@ +// indices/update-settings.asciidoc:145 + +[source, python] +---- +resp = client.indices.close(index="twitter") +print(resp) + +resp = client.indices.put_settings( + index="twitter", + body={ + "analysis": { + "analyzer": { + "content": {"type": "custom", "tokenizer": "whitespace"} + } + } + }, +) +print(resp) + +resp = client.indices.open(index="twitter") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/bb143628fd04070683eeeadc9406d9cc.asciidoc b/docs/examples/bb143628fd04070683eeeadc9406d9cc.asciidoc new file mode 100644 index 0000000..29b614e --- /dev/null +++ b/docs/examples/bb143628fd04070683eeeadc9406d9cc.asciidoc @@ -0,0 +1,15 @@ +// docs/index_.asciidoc:454 + +[source, python] +---- +resp = client.index( + index="twitter", + id="1", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/bc1ad5cc6d3eab98e3ce01f209ba7094.asciidoc b/docs/examples/bc1ad5cc6d3eab98e3ce01f209ba7094.asciidoc new file mode 100644 index 0000000..905643d --- /dev/null +++ b/docs/examples/bc1ad5cc6d3eab98e3ce01f209ba7094.asciidoc @@ -0,0 +1,13 @@ +// indices/aliases.asciidoc:342 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + {"add": {"index": "test", "alias": "alias1", "routing": "1"}} + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/bd5918ab903c0889bb1f09c8c2466e43.asciidoc b/docs/examples/bd5918ab903c0889bb1f09c8c2466e43.asciidoc new file mode 100644 index 0000000..388b9e6 --- /dev/null +++ b/docs/examples/bd5918ab903c0889bb1f09c8c2466e43.asciidoc @@ -0,0 +1,10 @@ +// indices/put-mapping.asciidoc:409 + +[source, python] +---- +resp = client.indices.create( + index="users", + body={"mappings": {"properties": {"user_id": {"type": "long"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/bdb30dd52d32f50994008f4f9c0da5f0.asciidoc b/docs/examples/bdb30dd52d32f50994008f4f9c0da5f0.asciidoc new file mode 100644 index 0000000..9117c2a --- /dev/null +++ b/docs/examples/bdb30dd52d32f50994008f4f9c0da5f0.asciidoc @@ -0,0 +1,9 @@ +// docs/update-by-query.asciidoc:510 + +[source, python] +---- +resp = client.update_by_query_rethrottle( + task_id="r1A2WoRbTwKZ516z6NEs5A:36619", requests_per_second="-1", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/be1bd47393646ac6bbee177d1cdb7738.asciidoc b/docs/examples/be1bd47393646ac6bbee177d1cdb7738.asciidoc new file mode 100644 index 0000000..8c06df8 --- /dev/null +++ b/docs/examples/be1bd47393646ac6bbee177d1cdb7738.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/query-string-query.asciidoc:472 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["title", "content"], + "query": "this that thus", + "minimum_should_match": 2, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/be3a6431d01846950dc1a39a7a6a1faa.asciidoc b/docs/examples/be3a6431d01846950dc1a39a7a6a1faa.asciidoc new file mode 100644 index 0000000..3fd2980 --- /dev/null +++ b/docs/examples/be3a6431d01846950dc1a39a7a6a1faa.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:471 + +[source, python] +---- +resp = client.tasks.get(task_id="r1A2WoRbTwKZ516z6NEs5A:36619") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/be8f28f31207b173de61be032fcf239c.asciidoc b/docs/examples/be8f28f31207b173de61be032fcf239c.asciidoc new file mode 100644 index 0000000..ee20547 --- /dev/null +++ b/docs/examples/be8f28f31207b173de61be032fcf239c.asciidoc @@ -0,0 +1,7 @@ +// indices/get-index.asciidoc:10 + +[source, python] +---- +resp = client.indices.get(index="twitter") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/bfcd65ab85d684d36a8550080032958d.asciidoc b/docs/examples/bfcd65ab85d684d36a8550080032958d.asciidoc new file mode 100644 index 0000000..6f35886 --- /dev/null +++ b/docs/examples/bfcd65ab85d684d36a8550080032958d.asciidoc @@ -0,0 +1,7 @@ +// search/request-body.asciidoc:65 + +[source, python] +---- +resp = client.search(q="message:number", size="0", terminate_after="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/bfdad8a928ea30d7cf60d0a0a6bc6e2e.asciidoc b/docs/examples/bfdad8a928ea30d7cf60d0a0a6bc6e2e.asciidoc new file mode 100644 index 0000000..baea987 --- /dev/null +++ b/docs/examples/bfdad8a928ea30d7cf60d0a0a6bc6e2e.asciidoc @@ -0,0 +1,17 @@ +// docs/bulk.asciidoc:634 + +[source, python] +---- +resp = client.bulk( + filter_path="items.*.error", + body=[ + {"update": {"_id": "5", "_index": "index1"}}, + {"doc": {"my_field": "baz"}}, + {"update": {"_id": "6", "_index": "index1"}}, + {"doc": {"my_field": "baz"}}, + {"update": {"_id": "7", "_index": "index1"}}, + {"doc": {"my_field": "baz"}}, + ], +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c22b72c4a52ee098331b3f252c22860d.asciidoc b/docs/examples/c22b72c4a52ee098331b3f252c22860d.asciidoc new file mode 100644 index 0000000..b7bda14 --- /dev/null +++ b/docs/examples/c22b72c4a52ee098331b3f252c22860d.asciidoc @@ -0,0 +1,9 @@ +// docs/delete-by-query.asciidoc:362 + +[source, python] +---- +resp = client.delete_by_query( + index=["twitter", "blog"], body={"query": {"match_all": {}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c2c21e2824fbf6b7198ede30419da82b.asciidoc b/docs/examples/c2c21e2824fbf6b7198ede30419da82b.asciidoc new file mode 100644 index 0000000..db7d560 --- /dev/null +++ b/docs/examples/c2c21e2824fbf6b7198ede30419da82b.asciidoc @@ -0,0 +1,7 @@ +// search/request/scroll.asciidoc:186 + +[source, python] +---- +resp = client.clear_scroll(scroll_id="_all") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c32a3f8071d87f0a3f5a78e07fe7a669.asciidoc b/docs/examples/c32a3f8071d87f0a3f5a78e07fe7a669.asciidoc new file mode 100644 index 0000000..df67e1c --- /dev/null +++ b/docs/examples/c32a3f8071d87f0a3f5a78e07fe7a669.asciidoc @@ -0,0 +1,11 @@ +// docs/delete-by-query.asciidoc:376 + +[source, python] +---- +resp = client.delete_by_query( + index="twitter", + routing="1", + body={"query": {"range": {"age": {"gte": 10}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c48264ec5d9b9679fddd72e5c44425b9.asciidoc b/docs/examples/c48264ec5d9b9679fddd72e5c44425b9.asciidoc new file mode 100644 index 0000000..eeaeea7 --- /dev/null +++ b/docs/examples/c48264ec5d9b9679fddd72e5c44425b9.asciidoc @@ -0,0 +1,7 @@ +// cluster/health.asciidoc:179 + +[source, python] +---- +resp = client.cluster.health(index="twitter", level="shards") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c4b278ba293abd0d02a0b5ad1a99f84a.asciidoc b/docs/examples/c4b278ba293abd0d02a0b5ad1a99f84a.asciidoc new file mode 100644 index 0000000..2e2c858 --- /dev/null +++ b/docs/examples/c4b278ba293abd0d02a0b5ad1a99f84a.asciidoc @@ -0,0 +1,16 @@ +// docs/update-by-query.asciidoc:389 + +[source, python] +---- +resp = client.ingest.put_pipeline( + id="set-foo", + body={ + "description": "sets foo", + "processors": [{"set": {"field": "foo", "value": "bar"}}], + }, +) +print(resp) + +resp = client.update_by_query(index="twitter", pipeline="set-foo") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c5e5873783246c7b1c01d8464fed72c4.asciidoc b/docs/examples/c5e5873783246c7b1c01d8464fed72c4.asciidoc new file mode 100644 index 0000000..c0fbe4b --- /dev/null +++ b/docs/examples/c5e5873783246c7b1c01d8464fed72c4.asciidoc @@ -0,0 +1,7 @@ +// docs/delete.asciidoc:168 + +[source, python] +---- +resp = client.delete(index="twitter", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c612d93e7f682a0d731e385edf9f5d56.asciidoc b/docs/examples/c612d93e7f682a0d731e385edf9f5d56.asciidoc new file mode 100644 index 0000000..ea0fd2a --- /dev/null +++ b/docs/examples/c612d93e7f682a0d731e385edf9f5d56.asciidoc @@ -0,0 +1,10 @@ +// query-dsl/nested-query.asciidoc:23 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"obj1": {"type": "nested"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/c849c6c8f8659dbb93e1c14356f74e37.asciidoc b/docs/examples/c849c6c8f8659dbb93e1c14356f74e37.asciidoc new file mode 100644 index 0000000..1cded84 --- /dev/null +++ b/docs/examples/c849c6c8f8659dbb93e1c14356f74e37.asciidoc @@ -0,0 +1,10 @@ +// indices/put-mapping.asciidoc:234 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={"mappings": {"properties": {"city": {"type": "text"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cb01106bf524df5e0501d4c655c1aa7b.asciidoc b/docs/examples/cb01106bf524df5e0501d4c655c1aa7b.asciidoc new file mode 100644 index 0000000..39648c0 --- /dev/null +++ b/docs/examples/cb01106bf524df5e0501d4c655c1aa7b.asciidoc @@ -0,0 +1,14 @@ +// docs/reindex.asciidoc:260 + +[source, python] +---- +resp = client.reindex( + slices="5", + refresh=True, + body={ + "source": {"index": "twitter"}, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cd247f267968aa0927bfdad56852f8f5.asciidoc b/docs/examples/cd247f267968aa0927bfdad56852f8f5.asciidoc new file mode 100644 index 0000000..cd75a66 --- /dev/null +++ b/docs/examples/cd247f267968aa0927bfdad56852f8f5.asciidoc @@ -0,0 +1,9 @@ +// getting-started.asciidoc:482 + +[source, python] +---- +resp = client.search( + index="bank", body={"query": {"match": {"address": "mill lane"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cd5bc5bf7cd58d7b1492c9c298b345f6.asciidoc b/docs/examples/cd5bc5bf7cd58d7b1492c9c298b345f6.asciidoc new file mode 100644 index 0000000..eb416bc --- /dev/null +++ b/docs/examples/cd5bc5bf7cd58d7b1492c9c298b345f6.asciidoc @@ -0,0 +1,22 @@ +// aggregations/bucket/terms-aggregation.asciidoc:672 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "actors": { + "terms": { + "field": "actors", + "size": 10, + "collect_mode": "breadth_first", + }, + "aggs": { + "costars": {"terms": {"field": "actors", "size": 5}} + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cde4dddae5c06e7f1d38c9d933dbc7ac.asciidoc b/docs/examples/cde4dddae5c06e7f1d38c9d933dbc7ac.asciidoc new file mode 100644 index 0000000..6a69a3e --- /dev/null +++ b/docs/examples/cde4dddae5c06e7f1d38c9d933dbc7ac.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:319 + +[source, python] +---- +resp = client.update_by_query(index=["twitter", "blog"]) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cdedd5f33f7e5f7acde561e97bff61de.asciidoc b/docs/examples/cdedd5f33f7e5f7acde561e97bff61de.asciidoc new file mode 100644 index 0000000..7a994b7 --- /dev/null +++ b/docs/examples/cdedd5f33f7e5f7acde561e97bff61de.asciidoc @@ -0,0 +1,11 @@ +// query-dsl/term-query.asciidoc:132 + +[source, python] +---- +resp = client.search( + index="my_index", + pretty=True, + body={"query": {"term": {"full_text": "Quick Brown Foxes!"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cf02e3d8b371bd59f0224967c36330da.asciidoc b/docs/examples/cf02e3d8b371bd59f0224967c36330da.asciidoc new file mode 100644 index 0000000..a55f5e5 --- /dev/null +++ b/docs/examples/cf02e3d8b371bd59f0224967c36330da.asciidoc @@ -0,0 +1,7 @@ +// indices/get-mapping.asciidoc:60 + +[source, python] +---- +resp = client.indices.get_mapping(index=["twitter", "kimchy"]) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cfbaea6f0df045c5d940bbb6a9c69cd8.asciidoc b/docs/examples/cfbaea6f0df045c5d940bbb6a9c69cd8.asciidoc new file mode 100644 index 0000000..ac3d7f8 --- /dev/null +++ b/docs/examples/cfbaea6f0df045c5d940bbb6a9c69cd8.asciidoc @@ -0,0 +1,18 @@ +// getting-started.asciidoc:665 + +[source, python] +---- +resp = client.search( + index="bank", + body={ + "size": 0, + "aggs": { + "group_by_state": { + "terms": {"field": "state.keyword"}, + "aggs": {"average_balance": {"avg": {"field": "balance"}}}, + } + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/cfc37446bd892d1ac42a3c8e8b204e6c.asciidoc b/docs/examples/cfc37446bd892d1ac42a3c8e8b204e6c.asciidoc new file mode 100644 index 0000000..93934ad --- /dev/null +++ b/docs/examples/cfc37446bd892d1ac42a3c8e8b204e6c.asciidoc @@ -0,0 +1,7 @@ +// docs/reindex.asciidoc:731 + +[source, python] +---- +resp = client.get(index="test2", id="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d0a8a938a2fa913b6fdbc871079a59dd.asciidoc b/docs/examples/d0a8a938a2fa913b6fdbc871079a59dd.asciidoc new file mode 100644 index 0000000..d8520c9 --- /dev/null +++ b/docs/examples/d0a8a938a2fa913b6fdbc871079a59dd.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/term-query.asciidoc:28 + +[source, python] +---- +resp = client.search( + body={"query": {"term": {"user": {"value": "Kimchy", "boost": 1}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d17269bb80fb63ec0bf37d219e003dcb.asciidoc b/docs/examples/d17269bb80fb63ec0bf37d219e003dcb.asciidoc new file mode 100644 index 0000000..79410d2 --- /dev/null +++ b/docs/examples/d17269bb80fb63ec0bf37d219e003dcb.asciidoc @@ -0,0 +1,23 @@ +// search/request/sort.asciidoc:391 + +[source, python] +---- +resp = client.search( + body={ + "sort": [ + { + "_geo_distance": { + "pin.location": [-70, 40], + "order": "asc", + "unit": "km", + "mode": "min", + "distance_type": "arc", + "ignore_unmapped": True, + } + } + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d1b3b7d2bb2ab90d15fd10318abd24db.asciidoc b/docs/examples/d1b3b7d2bb2ab90d15fd10318abd24db.asciidoc new file mode 100644 index 0000000..93a098b --- /dev/null +++ b/docs/examples/d1b3b7d2bb2ab90d15fd10318abd24db.asciidoc @@ -0,0 +1,19 @@ +// search/request/sort.asciidoc:11 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "post_date": {"type": "date"}, + "user": {"type": "keyword"}, + "name": {"type": "keyword"}, + "age": {"type": "integer"}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d1bcf2eb63a462bfdcf01a68e68d5b4a.asciidoc b/docs/examples/d1bcf2eb63a462bfdcf01a68e68d5b4a.asciidoc new file mode 100644 index 0000000..e9f58c9 --- /dev/null +++ b/docs/examples/d1bcf2eb63a462bfdcf01a68e68d5b4a.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/terms-query.asciidoc:186 + +[source, python] +---- +resp = client.search( + index="my_index", + pretty=True, + body={ + "query": { + "terms": { + "color": {"index": "my_index", "id": "2", "path": "color"} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d222c6a6ec7a3beca6c97011b0874512.asciidoc b/docs/examples/d222c6a6ec7a3beca6c97011b0874512.asciidoc new file mode 100644 index 0000000..fe2b418 --- /dev/null +++ b/docs/examples/d222c6a6ec7a3beca6c97011b0874512.asciidoc @@ -0,0 +1,12 @@ +// docs/get.asciidoc:278 + +[source, python] +---- +resp = client.get_source( + index="twitter", + id="1", + _source_includes="*.id", + _source_excludes="entities", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d3016e4e8025362ad9a05ee86bb2061f.asciidoc b/docs/examples/d3016e4e8025362ad9a05ee86bb2061f.asciidoc new file mode 100644 index 0000000..ac8a11d --- /dev/null +++ b/docs/examples/d3016e4e8025362ad9a05ee86bb2061f.asciidoc @@ -0,0 +1,9 @@ +// indices/aliases.asciidoc:12 + +[source, python] +---- +resp = client.indices.update_aliases( + body={"actions": [{"add": {"index": "twitter", "alias": "alias1"}}]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d3088d5fa59b3ab110f64fb4f9b0065c.asciidoc b/docs/examples/d3088d5fa59b3ab110f64fb4f9b0065c.asciidoc new file mode 100644 index 0000000..309f471 --- /dev/null +++ b/docs/examples/d3088d5fa59b3ab110f64fb4f9b0065c.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/terms-query.asciidoc:145 + +[source, python] +---- +resp = client.index( + index="my_index", id="1", body={"color": ["blue", "green"]}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d31062ff8c015387889fed4ad86fd914.asciidoc b/docs/examples/d31062ff8c015387889fed4ad86fd914.asciidoc new file mode 100644 index 0000000..7264598 --- /dev/null +++ b/docs/examples/d31062ff8c015387889fed4ad86fd914.asciidoc @@ -0,0 +1,19 @@ +// query-dsl/wildcard-query.asciidoc:21 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "wildcard": { + "user": { + "value": "ki*y", + "boost": 1, + "rewrite": "constant_score", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d4b4cefba4318caeba7480187faf2b13.asciidoc b/docs/examples/d4b4cefba4318caeba7480187faf2b13.asciidoc new file mode 100644 index 0000000..438bc12 --- /dev/null +++ b/docs/examples/d4b4cefba4318caeba7480187faf2b13.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/term-query.asciidoc:113 + +[source, python] +---- +resp = client.index( + index="my_index", id="1", body={"full_text": "Quick Brown Foxes!"}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d50a3c64890f88af32c6d4ef4899d82a.asciidoc b/docs/examples/d50a3c64890f88af32c6d4ef4899d82a.asciidoc new file mode 100644 index 0000000..f94b44d --- /dev/null +++ b/docs/examples/d50a3c64890f88af32c6d4ef4899d82a.asciidoc @@ -0,0 +1,20 @@ +// search/request/sort.asciidoc:470 + +[source, python] +---- +resp = client.search( + body={ + "sort": [ + { + "_geo_distance": { + "pin.location": "40,-70", + "order": "asc", + "unit": "km", + } + } + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d5dcddc6398b473b6ad9bce5c6adf986.asciidoc b/docs/examples/d5dcddc6398b473b6ad9bce5c6adf986.asciidoc new file mode 100644 index 0000000..023fe51 --- /dev/null +++ b/docs/examples/d5dcddc6398b473b6ad9bce5c6adf986.asciidoc @@ -0,0 +1,7 @@ +// search/request/scroll.asciidoc:95 + +[source, python] +---- +resp = client.search(scroll="1m", body={"sort": ["_doc"]}) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d718b63cf1b6591a1d59a0cf4fd995eb.asciidoc b/docs/examples/d718b63cf1b6591a1d59a0cf4fd995eb.asciidoc new file mode 100644 index 0000000..e4259cb --- /dev/null +++ b/docs/examples/d718b63cf1b6591a1d59a0cf4fd995eb.asciidoc @@ -0,0 +1,16 @@ +// docs/index_.asciidoc:500 + +[source, python] +---- +resp = client.index( + index="twitter", + id="1", + op_type="create", + body={ + "user": "kimchy", + "post_date": "2009-11-15T14:12:12", + "message": "trying out Elasticsearch", + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d8b115341da772a628a024e7d1644e73.asciidoc b/docs/examples/d8b115341da772a628a024e7d1644e73.asciidoc new file mode 100644 index 0000000..be330b0 --- /dev/null +++ b/docs/examples/d8b115341da772a628a024e7d1644e73.asciidoc @@ -0,0 +1,7 @@ +// docs/update-by-query.asciidoc:327 + +[source, python] +---- +resp = client.update_by_query(index="twitter", routing="1") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d8b2a88b5eca99d3691ad3cd40266736.asciidoc b/docs/examples/d8b2a88b5eca99d3691ad3cd40266736.asciidoc new file mode 100644 index 0000000..bde826c --- /dev/null +++ b/docs/examples/d8b2a88b5eca99d3691ad3cd40266736.asciidoc @@ -0,0 +1,18 @@ +// mapping.asciidoc:147 + +[source, python] +---- +resp = client.indices.create( + index="my-index", + body={ + "mappings": { + "properties": { + "age": {"type": "integer"}, + "email": {"type": "keyword"}, + "name": {"type": "text"}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d90a84a24a407731dfc1929ac8327746.asciidoc b/docs/examples/d90a84a24a407731dfc1929ac8327746.asciidoc new file mode 100644 index 0000000..bad9a60 --- /dev/null +++ b/docs/examples/d90a84a24a407731dfc1929ac8327746.asciidoc @@ -0,0 +1,7 @@ +// docs/delete.asciidoc:127 + +[source, python] +---- +resp = client.delete(index="twitter", id="1", timeout="5m") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/d9474f66970c6955e24b17c7447e7b5f.asciidoc b/docs/examples/d9474f66970c6955e24b17c7447e7b5f.asciidoc new file mode 100644 index 0000000..23ae8f1 --- /dev/null +++ b/docs/examples/d9474f66970c6955e24b17c7447e7b5f.asciidoc @@ -0,0 +1,16 @@ +// indices/put-mapping.asciidoc:144 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "name": {"properties": {"first": {"type": "text"}}} + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/db6cba451ba562abe953d09ad80cc15c.asciidoc b/docs/examples/db6cba451ba562abe953d09ad80cc15c.asciidoc new file mode 100644 index 0000000..d6e5d45 --- /dev/null +++ b/docs/examples/db6cba451ba562abe953d09ad80cc15c.asciidoc @@ -0,0 +1,13 @@ +// query-dsl/query-string-query.asciidoc:333 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": {"query": "city.\\*:(this AND that OR thus)"} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/dc15e2373e5ecbe09b4ea0858eb63d47.asciidoc b/docs/examples/dc15e2373e5ecbe09b4ea0858eb63d47.asciidoc new file mode 100644 index 0000000..a9519b3 --- /dev/null +++ b/docs/examples/dc15e2373e5ecbe09b4ea0858eb63d47.asciidoc @@ -0,0 +1,28 @@ +// aggregations/bucket/terms-aggregation.asciidoc:309 + +[source, python] +---- +resp = client.search( + body={ + "aggs": { + "countries": { + "terms": { + "field": "artist.country", + "order": {"rock>playback_stats.avg": "desc"}, + }, + "aggs": { + "rock": { + "filter": {"term": {"genre": "rock"}}, + "aggs": { + "playback_stats": { + "stats": {"field": "play_count"} + } + }, + } + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/de139866a220124360e5e27d1a736ea4.asciidoc b/docs/examples/de139866a220124360e5e27d1a736ea4.asciidoc new file mode 100644 index 0000000..10e173c --- /dev/null +++ b/docs/examples/de139866a220124360e5e27d1a736ea4.asciidoc @@ -0,0 +1,23 @@ +// search/request/sort.asciidoc:262 + +[source, python] +---- +resp = client.search( + body={ + "query": {"term": {"product": "chocolate"}}, + "sort": [ + { + "offer.price": { + "mode": "avg", + "order": "asc", + "nested": { + "path": "offer", + "filter": {"term": {"offer.color": "blue"}}, + }, + } + } + ], + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/de176bc4788ea286fff9e92418a43ea8.asciidoc b/docs/examples/de176bc4788ea286fff9e92418a43ea8.asciidoc new file mode 100644 index 0000000..b1b0db5 --- /dev/null +++ b/docs/examples/de176bc4788ea286fff9e92418a43ea8.asciidoc @@ -0,0 +1,20 @@ +// indices/aliases.asciidoc:270 + +[source, python] +---- +resp = client.indices.create(index="test") +print(resp) + +resp = client.indices.create(index="test_2") +print(resp) + +resp = client.indices.update_aliases( + body={ + "actions": [ + {"add": {"index": "test_2", "alias": "test"}}, + {"remove_index": {"index": "test"}}, + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/df17f920b0deab3529b98df88b781f55.asciidoc b/docs/examples/df17f920b0deab3529b98df88b781f55.asciidoc new file mode 100644 index 0000000..54693f4 --- /dev/null +++ b/docs/examples/df17f920b0deab3529b98df88b781f55.asciidoc @@ -0,0 +1,27 @@ +// query-dsl/function-score-query.asciidoc:578 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "functions": [ + {"gauss": {"price": {"origin": "0", "scale": "20"}}}, + { + "gauss": { + "location": { + "origin": "11, 12", + "scale": "2km", + } + } + }, + ], + "query": {"match": {"properties": "balcony"}}, + "score_mode": "multiply", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/dfac8d098b50aa0181161bcd17b38ef4.asciidoc b/docs/examples/dfac8d098b50aa0181161bcd17b38ef4.asciidoc new file mode 100644 index 0000000..2b6054f --- /dev/null +++ b/docs/examples/dfac8d098b50aa0181161bcd17b38ef4.asciidoc @@ -0,0 +1,9 @@ +// indices/update-settings.asciidoc:97 + +[source, python] +---- +resp = client.indices.put_settings( + index="twitter", body={"index": {"refresh_interval": "-1"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/dfb1fe96d806a644214d06f9b4b87878.asciidoc b/docs/examples/dfb1fe96d806a644214d06f9b4b87878.asciidoc new file mode 100644 index 0000000..96ea95e --- /dev/null +++ b/docs/examples/dfb1fe96d806a644214d06f9b4b87878.asciidoc @@ -0,0 +1,11 @@ +// docs/delete-by-query.asciidoc:394 + +[source, python] +---- +resp = client.delete_by_query( + index="twitter", + scroll_size="5000", + body={"query": {"term": {"user": "kimchy"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/dfef545b1e2c247bafd1347e8e807ac1.asciidoc b/docs/examples/dfef545b1e2c247bafd1347e8e807ac1.asciidoc new file mode 100644 index 0000000..c44bb3e --- /dev/null +++ b/docs/examples/dfef545b1e2c247bafd1347e8e807ac1.asciidoc @@ -0,0 +1,13 @@ +// indices/create-index.asciidoc:123 + +[source, python] +---- +resp = client.indices.create( + index="test", + body={ + "settings": {"number_of_shards": 1}, + "mappings": {"properties": {"field1": {"type": "text"}}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e0d6e02b998bdea99c9c08dcc3630c5e.asciidoc b/docs/examples/e0d6e02b998bdea99c9c08dcc3630c5e.asciidoc new file mode 100644 index 0000000..3e590b5 --- /dev/null +++ b/docs/examples/e0d6e02b998bdea99c9c08dcc3630c5e.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/match-query.asciidoc:18 + +[source, python] +---- +resp = client.search( + body={"query": {"match": {"message": {"query": "this is a test"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e17e8852ec3f31781e1364f4dffeb6d0.asciidoc b/docs/examples/e17e8852ec3f31781e1364f4dffeb6d0.asciidoc new file mode 100644 index 0000000..42f9354 --- /dev/null +++ b/docs/examples/e17e8852ec3f31781e1364f4dffeb6d0.asciidoc @@ -0,0 +1,15 @@ +// query-dsl/query-string-query.asciidoc:281 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "query": "(content:this OR name:this) AND (content:that OR name:that)" + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e21e1c26dc8687e7bf7bd2bf019a6698.asciidoc b/docs/examples/e21e1c26dc8687e7bf7bd2bf019a6698.asciidoc new file mode 100644 index 0000000..4599081 --- /dev/null +++ b/docs/examples/e21e1c26dc8687e7bf7bd2bf019a6698.asciidoc @@ -0,0 +1,11 @@ +// docs/delete-by-query.asciidoc:349 + +[source, python] +---- +resp = client.delete_by_query( + index="twitter", + conflicts="proceed", + body={"query": {"match_all": {}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e270f3f721a5712cd11a5ca03554f5b0.asciidoc b/docs/examples/e270f3f721a5712cd11a5ca03554f5b0.asciidoc new file mode 100644 index 0000000..7dad266 --- /dev/null +++ b/docs/examples/e270f3f721a5712cd11a5ca03554f5b0.asciidoc @@ -0,0 +1,18 @@ +// query-dsl/multi-match-query.asciidoc:170 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "Will Smith", + "type": "best_fields", + "fields": ["first_name", "last_name"], + "operator": "and", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e2a042c629429855c3bcaefffb26b7fa.asciidoc b/docs/examples/e2a042c629429855c3bcaefffb26b7fa.asciidoc new file mode 100644 index 0000000..b45860f --- /dev/null +++ b/docs/examples/e2a042c629429855c3bcaefffb26b7fa.asciidoc @@ -0,0 +1,19 @@ +// mapping/types/date.asciidoc:77 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "date": { + "type": "date", + "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis", + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e30ea6e3823a139d7693d8cce1920a06.asciidoc b/docs/examples/e30ea6e3823a139d7693d8cce1920a06.asciidoc new file mode 100644 index 0000000..7e4da96 --- /dev/null +++ b/docs/examples/e30ea6e3823a139d7693d8cce1920a06.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/multi-match-query.asciidoc:50 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "multi_match": { + "query": "this is a test", + "fields": ["subject^3", "message"], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e4be53736bcc02b03068fd72fdbfe271.asciidoc b/docs/examples/e4be53736bcc02b03068fd72fdbfe271.asciidoc new file mode 100644 index 0000000..a16d7a6 --- /dev/null +++ b/docs/examples/e4be53736bcc02b03068fd72fdbfe271.asciidoc @@ -0,0 +1,9 @@ +// indices/put-mapping.asciidoc:92 + +[source, python] +---- +resp = client.indices.put_mapping( + index="publications", body={"properties": {"title": {"type": "text"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e567e6dbf86300142573c73789c8fce4.asciidoc b/docs/examples/e567e6dbf86300142573c73789c8fce4.asciidoc new file mode 100644 index 0000000..e14b597 --- /dev/null +++ b/docs/examples/e567e6dbf86300142573c73789c8fce4.asciidoc @@ -0,0 +1,9 @@ +// docs/reindex.asciidoc:276 + +[source, python] +---- +resp = client.search( + index="new_twitter", size="0", filter_path="hits.total", +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e5d2172b524332196cac0f031c043659.asciidoc b/docs/examples/e5d2172b524332196cac0f031c043659.asciidoc new file mode 100644 index 0000000..80be453 --- /dev/null +++ b/docs/examples/e5d2172b524332196cac0f031c043659.asciidoc @@ -0,0 +1,14 @@ +// indices/create-index.asciidoc:81 + +[source, python] +---- +resp = client.indices.create( + index="twitter", + body={ + "settings": { + "index": {"number_of_shards": 3, "number_of_replicas": 2} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e5f50b31f165462d883ecbff45f74985.asciidoc b/docs/examples/e5f50b31f165462d883ecbff45f74985.asciidoc new file mode 100644 index 0000000..4a06421 --- /dev/null +++ b/docs/examples/e5f50b31f165462d883ecbff45f74985.asciidoc @@ -0,0 +1,23 @@ +// indices/templates.asciidoc:20 + +[source, python] +---- +resp = client.indices.put_template( + name="template_1", + body={ + "index_patterns": ["te*", "bar*"], + "settings": {"number_of_shards": 1}, + "mappings": { + "_source": {"enabled": False}, + "properties": { + "host_name": {"type": "keyword"}, + "created_at": { + "type": "date", + "format": "EEE MMM dd HH:mm:ss Z yyyy", + }, + }, + }, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e8e451bc8c45bcf16df43804c4fc8329.asciidoc b/docs/examples/e8e451bc8c45bcf16df43804c4fc8329.asciidoc new file mode 100644 index 0000000..e1a69a8 --- /dev/null +++ b/docs/examples/e8e451bc8c45bcf16df43804c4fc8329.asciidoc @@ -0,0 +1,17 @@ +// search/request/sort.asciidoc:597 + +[source, python] +---- +resp = client.search( + body={ + "track_scores": True, + "sort": [ + {"post_date": {"order": "desc"}}, + {"name": "desc"}, + {"age": "desc"}, + ], + "query": {"term": {"user": "kimchy"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e9c2e15b36372d5281c879d336322b6c.asciidoc b/docs/examples/e9c2e15b36372d5281c879d336322b6c.asciidoc new file mode 100644 index 0000000..d3f83dd --- /dev/null +++ b/docs/examples/e9c2e15b36372d5281c879d336322b6c.asciidoc @@ -0,0 +1,12 @@ +// docs/reindex.asciidoc:679 + +[source, python] +---- +resp = client.reindex( + body={ + "source": {"index": "twitter", "_source": ["user", "_doc"]}, + "dest": {"index": "new_twitter"}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/e9fe608f105d7e3268a15e409e2cb9ab.asciidoc b/docs/examples/e9fe608f105d7e3268a15e409e2cb9ab.asciidoc new file mode 100644 index 0000000..e39d9da --- /dev/null +++ b/docs/examples/e9fe608f105d7e3268a15e409e2cb9ab.asciidoc @@ -0,0 +1,41 @@ +// aggregations/metrics/valuecount-aggregation.asciidoc:96 + +[source, python] +---- +resp = client.index( + index="metrics_index", + id="1", + body={ + "network.name": "net-1", + "latency_histo": { + "values": [0.1, 0.2, 0.3, 0.4, 0.5], + "counts": [3, 7, 23, 12, 6], + }, + }, +) +print(resp) + +resp = client.index( + index="metrics_index", + id="2", + body={ + "network.name": "net-2", + "latency_histo": { + "values": [0.1, 0.2, 0.3, 0.4, 0.5], + "counts": [8, 17, 8, 7, 6], + }, + }, +) +print(resp) + +resp = client.search( + index="metrics_index", + size="0", + body={ + "aggs": { + "total_requests": {"value_count": {"field": "latency_histo"}} + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ea02de2dbe05091fcb0dac72c8ba5f83.asciidoc b/docs/examples/ea02de2dbe05091fcb0dac72c8ba5f83.asciidoc new file mode 100644 index 0000000..f487324 --- /dev/null +++ b/docs/examples/ea02de2dbe05091fcb0dac72c8ba5f83.asciidoc @@ -0,0 +1,12 @@ +// docs/update-by-query.asciidoc:586 + +[source, python] +---- +resp = client.update_by_query( + index="twitter", + refresh=True, + slices="5", + body={"script": {"source": "ctx._source['extra'] = 'test'"}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/eb30ba547e4a7b8f54f33ab259aca523.asciidoc b/docs/examples/eb30ba547e4a7b8f54f33ab259aca523.asciidoc new file mode 100644 index 0000000..83d427b --- /dev/null +++ b/docs/examples/eb30ba547e4a7b8f54f33ab259aca523.asciidoc @@ -0,0 +1,11 @@ +// docs/update.asciidoc:153 + +[source, python] +---- +resp = client.update( + index="test", + id="1", + body={"script": "ctx._source.new_field = 'value_of_new_field'"}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ebb6b59fbc9325c17e45f524602d6be2.asciidoc b/docs/examples/ebb6b59fbc9325c17e45f524602d6be2.asciidoc new file mode 100644 index 0000000..2c75f85 --- /dev/null +++ b/docs/examples/ebb6b59fbc9325c17e45f524602d6be2.asciidoc @@ -0,0 +1,10 @@ +// docs/delete-by-query.asciidoc:10 + +[source, python] +---- +resp = client.delete_by_query( + index="twitter", + body={"query": {"match": {"message": "some message"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ec27afee074001b0e4e393611010842b.asciidoc b/docs/examples/ec27afee074001b0e4e393611010842b.asciidoc new file mode 100644 index 0000000..630ce29 --- /dev/null +++ b/docs/examples/ec27afee074001b0e4e393611010842b.asciidoc @@ -0,0 +1,22 @@ +// query-dsl/function-score-query.asciidoc:380 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "gauss": { + "date": { + "origin": "2013-09-17", + "scale": "10d", + "offset": "5d", + "decay": 0.5, + } + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ec473de07fe89bcbac1f8e278617fe46.asciidoc b/docs/examples/ec473de07fe89bcbac1f8e278617fe46.asciidoc new file mode 100644 index 0000000..112391a --- /dev/null +++ b/docs/examples/ec473de07fe89bcbac1f8e278617fe46.asciidoc @@ -0,0 +1,20 @@ +// query-dsl/function-score-query.asciidoc:137 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "function_score": { + "query": {"match": {"message": "elasticsearch"}}, + "script_score": { + "script": { + "source": "Math.log(2 + doc['likes'].value)" + } + }, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ef0f4fa4272c47ff62fb7b422cf975e7.asciidoc b/docs/examples/ef0f4fa4272c47ff62fb7b422cf975e7.asciidoc new file mode 100644 index 0000000..7e4ab41 --- /dev/null +++ b/docs/examples/ef0f4fa4272c47ff62fb7b422cf975e7.asciidoc @@ -0,0 +1,12 @@ +// search/request/sort.asciidoc:345 + +[source, python] +---- +resp = client.search( + body={ + "sort": [{"price": {"missing": "_last"}}], + "query": {"term": {"product": "chocolate"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/ef9111c1648d7820925f12e07d1346c5.asciidoc b/docs/examples/ef9111c1648d7820925f12e07d1346c5.asciidoc new file mode 100644 index 0000000..1382688 --- /dev/null +++ b/docs/examples/ef9111c1648d7820925f12e07d1346c5.asciidoc @@ -0,0 +1,19 @@ +// mapping/params/fielddata.asciidoc:56 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "my_field": { + "type": "text", + "fields": {"keyword": {"type": "keyword"}}, + } + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f085fb032dae56a3b104ab874eaea2ad.asciidoc b/docs/examples/f085fb032dae56a3b104ab874eaea2ad.asciidoc new file mode 100644 index 0000000..079fe8f --- /dev/null +++ b/docs/examples/f085fb032dae56a3b104ab874eaea2ad.asciidoc @@ -0,0 +1,11 @@ +// aggregations/bucket/terms-aggregation.asciidoc:748 + +[source, python] +---- +resp = client.search( + body={ + "aggs": {"tags": {"terms": {"field": "tags", "missing": "N/A"}}} + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f0e21e03a07c8fa0209b0aafdb3791e6.asciidoc b/docs/examples/f0e21e03a07c8fa0209b0aafdb3791e6.asciidoc new file mode 100644 index 0000000..0d19cfb --- /dev/null +++ b/docs/examples/f0e21e03a07c8fa0209b0aafdb3791e6.asciidoc @@ -0,0 +1,14 @@ +// indices/aliases.asciidoc:218 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + {"add": {"index": "test1", "alias": "alias1"}}, + {"add": {"index": "test2", "alias": "alias1"}}, + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f29a28fffa7ec604a33a838f48f7ea79.asciidoc b/docs/examples/f29a28fffa7ec604a33a838f48f7ea79.asciidoc new file mode 100644 index 0000000..1cee07f --- /dev/null +++ b/docs/examples/f29a28fffa7ec604a33a838f48f7ea79.asciidoc @@ -0,0 +1,22 @@ +// query-dsl/query_filter_context.asciidoc:62 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "bool": { + "must": [ + {"match": {"title": "Search"}}, + {"match": {"content": "Elasticsearch"}}, + ], + "filter": [ + {"term": {"status": "published"}}, + {"range": {"publish_date": {"gte": "2015-01-01"}}}, + ], + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f2d68493abd3ca430bd03a7f7f8d18f9.asciidoc b/docs/examples/f2d68493abd3ca430bd03a7f7f8d18f9.asciidoc new file mode 100644 index 0000000..2e54acf --- /dev/null +++ b/docs/examples/f2d68493abd3ca430bd03a7f7f8d18f9.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/query-string-query.asciidoc:265 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["content", "name"], + "query": "this AND that", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f32f0c19b42de3b87dd764fe4ca17e7c.asciidoc b/docs/examples/f32f0c19b42de3b87dd764fe4ca17e7c.asciidoc new file mode 100644 index 0000000..aeb6824 --- /dev/null +++ b/docs/examples/f32f0c19b42de3b87dd764fe4ca17e7c.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/query-string-query.asciidoc:418 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "default_field": "title", + "query": "ny city", + "auto_generate_synonyms_phrase_query": False, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f4a1008b3f9baa67bb03ce9ef5ab4cb4.asciidoc b/docs/examples/f4a1008b3f9baa67bb03ce9ef5ab4cb4.asciidoc new file mode 100644 index 0000000..ccd1fec --- /dev/null +++ b/docs/examples/f4a1008b3f9baa67bb03ce9ef5ab4cb4.asciidoc @@ -0,0 +1,10 @@ +// search/request/sort.asciidoc:180 + +[source, python] +---- +resp = client.indices.create( + index="index_double", + body={"mappings": {"properties": {"field": {"type": "date"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f6b5032bf27c2445d28845be0d413970.asciidoc b/docs/examples/f6b5032bf27c2445d28845be0d413970.asciidoc new file mode 100644 index 0000000..7e82513 --- /dev/null +++ b/docs/examples/f6b5032bf27c2445d28845be0d413970.asciidoc @@ -0,0 +1,10 @@ +// search/request/sort.asciidoc:134 + +[source, python] +---- +resp = client.indices.create( + index="index_long", + body={"mappings": {"properties": {"field": {"type": "long"}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f6d6889667f56b8f49d2858070571a6b.asciidoc b/docs/examples/f6d6889667f56b8f49d2858070571a6b.asciidoc new file mode 100644 index 0000000..c16cef5 --- /dev/null +++ b/docs/examples/f6d6889667f56b8f49d2858070571a6b.asciidoc @@ -0,0 +1,20 @@ +// indices/aliases.asciidoc:409 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + { + "add": { + "index": "test", + "alias": "alias1", + "is_write_index": True, + } + }, + {"add": {"index": "test2", "alias": "alias1"}}, + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f70a54cd9a9f4811bf962e469f2ca2ea.asciidoc b/docs/examples/f70a54cd9a9f4811bf962e469f2ca2ea.asciidoc new file mode 100644 index 0000000..e15aff3 --- /dev/null +++ b/docs/examples/f70a54cd9a9f4811bf962e469f2ca2ea.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/bool-query.asciidoc:88 + +[source, python] +---- +resp = client.search( + body={"query": {"bool": {"filter": {"term": {"status": "active"}}}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f8cc4b331a19ff4df8e4a490f906ee69.asciidoc b/docs/examples/f8cc4b331a19ff4df8e4a490f906ee69.asciidoc new file mode 100644 index 0000000..e1d2034 --- /dev/null +++ b/docs/examples/f8cc4b331a19ff4df8e4a490f906ee69.asciidoc @@ -0,0 +1,7 @@ +// getting-started.asciidoc:167 + +[source, python] +---- +resp = client.cat.health(v=True) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/f9636d7ef1a45be4f36418c875cf6bef.asciidoc b/docs/examples/f9636d7ef1a45be4f36418c875cf6bef.asciidoc new file mode 100644 index 0000000..d862bba --- /dev/null +++ b/docs/examples/f9636d7ef1a45be4f36418c875cf6bef.asciidoc @@ -0,0 +1,24 @@ +// docs/update.asciidoc:296 + +[source, python] +---- +resp = client.update( + index="sessions", + id="dh3sgudg8gsrgl", + body={ + "scripted_upsert": True, + "script": { + "id": "my_web_session_summariser", + "params": { + "pageViewEvent": { + "url": "foo.com/bar", + "response": 404, + "time": "2014-01-01 12:32", + } + }, + }, + "upsert": {}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fa0f4485cd48f986b7ae8cbb24e331c4.asciidoc b/docs/examples/fa0f4485cd48f986b7ae8cbb24e331c4.asciidoc new file mode 100644 index 0000000..f641e99 --- /dev/null +++ b/docs/examples/fa0f4485cd48f986b7ae8cbb24e331c4.asciidoc @@ -0,0 +1,20 @@ +// indices/aliases.asciidoc:362 + +[source, python] +---- +resp = client.indices.update_aliases( + body={ + "actions": [ + { + "add": { + "index": "test", + "alias": "alias2", + "search_routing": "1,2", + "index_routing": "2", + } + } + ] + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fa2fe60f570bd930d2891778c6efbfe6.asciidoc b/docs/examples/fa2fe60f570bd930d2891778c6efbfe6.asciidoc new file mode 100644 index 0000000..e4a6a52 --- /dev/null +++ b/docs/examples/fa2fe60f570bd930d2891778c6efbfe6.asciidoc @@ -0,0 +1,9 @@ +// query-dsl/match-query.asciidoc:150 + +[source, python] +---- +resp = client.search( + body={"query": {"match": {"message": "this is a test"}}}, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fa88f6f5a7d728ec4f1d05244228cb09.asciidoc b/docs/examples/fa88f6f5a7d728ec4f1d05244228cb09.asciidoc new file mode 100644 index 0000000..29f978b --- /dev/null +++ b/docs/examples/fa88f6f5a7d728ec4f1d05244228cb09.asciidoc @@ -0,0 +1,16 @@ +// query-dsl/bool-query.asciidoc:107 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "bool": { + "must": {"match_all": {}}, + "filter": {"term": {"status": "active"}}, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fabe14480624a99e8ee42c7338672058.asciidoc b/docs/examples/fabe14480624a99e8ee42c7338672058.asciidoc new file mode 100644 index 0000000..f1c7b41 --- /dev/null +++ b/docs/examples/fabe14480624a99e8ee42c7338672058.asciidoc @@ -0,0 +1,7 @@ +// indices/create-index.asciidoc:203 + +[source, python] +---- +resp = client.indices.create(index="test", wait_for_active_shards="2") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fbcf5078a6a9e09790553804054c36b3.asciidoc b/docs/examples/fbcf5078a6a9e09790553804054c36b3.asciidoc new file mode 100644 index 0000000..7345790 --- /dev/null +++ b/docs/examples/fbcf5078a6a9e09790553804054c36b3.asciidoc @@ -0,0 +1,7 @@ +// docs/get.asciidoc:224 + +[source, python] +---- +resp = client.get(index="twitter", id="0") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fc8097bdfb6f3a4017bf4186ccca8a84.asciidoc b/docs/examples/fc8097bdfb6f3a4017bf4186ccca8a84.asciidoc new file mode 100644 index 0000000..3517385 --- /dev/null +++ b/docs/examples/fc8097bdfb6f3a4017bf4186ccca8a84.asciidoc @@ -0,0 +1,45 @@ +// mapping/params/multi-fields.asciidoc:75 + +[source, python] +---- +resp = client.indices.create( + index="my_index", + body={ + "mappings": { + "properties": { + "text": { + "type": "text", + "fields": { + "english": {"type": "text", "analyzer": "english"} + }, + } + } + } + }, +) +print(resp) + +resp = client.index( + index="my_index", id="1", body={"text": "quick brown fox"}, +) +print(resp) + +resp = client.index( + index="my_index", id="2", body={"text": "quick brown foxes"}, +) +print(resp) + +resp = client.search( + index="my_index", + body={ + "query": { + "multi_match": { + "query": "quick brown foxes", + "fields": ["text", "text.english"], + "type": "most_fields", + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fdcaba9547180439ff4b6275034a5170.asciidoc b/docs/examples/fdcaba9547180439ff4b6275034a5170.asciidoc new file mode 100644 index 0000000..b143cb7 --- /dev/null +++ b/docs/examples/fdcaba9547180439ff4b6275034a5170.asciidoc @@ -0,0 +1,14 @@ +// search/request/scroll.asciidoc:268 + +[source, python] +---- +resp = client.search( + index="twitter", + scroll="1m", + body={ + "slice": {"field": "date", "id": 0, "max": 10}, + "query": {"match": {"title": "elasticsearch"}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fdd38f0d248385a444c777e7acd97846.asciidoc b/docs/examples/fdd38f0d248385a444c777e7acd97846.asciidoc new file mode 100644 index 0000000..3022434 --- /dev/null +++ b/docs/examples/fdd38f0d248385a444c777e7acd97846.asciidoc @@ -0,0 +1,17 @@ +// query-dsl/query-string-query.asciidoc:496 + +[source, python] +---- +resp = client.search( + body={ + "query": { + "query_string": { + "fields": ["title", "content"], + "query": "this OR that OR thus", + "minimum_should_match": 2, + } + } + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/fe5763d32955e8b65eb3048e97b1580c.asciidoc b/docs/examples/fe5763d32955e8b65eb3048e97b1580c.asciidoc new file mode 100644 index 0000000..a2e92d9 --- /dev/null +++ b/docs/examples/fe5763d32955e8b65eb3048e97b1580c.asciidoc @@ -0,0 +1,7 @@ +// indices/update-settings.asciidoc:127 + +[source, python] +---- +resp = client.indices.forcemerge(index="twitter", max_num_segments="5") +print(resp) +---- \ No newline at end of file diff --git a/docs/examples/feefeb68144002fd1fff57b77b95b85e.asciidoc b/docs/examples/feefeb68144002fd1fff57b77b95b85e.asciidoc new file mode 100644 index 0000000..d4afd16 --- /dev/null +++ b/docs/examples/feefeb68144002fd1fff57b77b95b85e.asciidoc @@ -0,0 +1,13 @@ +// getting-started.asciidoc:578 + +[source, python] +---- +resp = client.search( + index="bank", + body={ + "size": 0, + "aggs": {"group_by_state": {"terms": {"field": "state.keyword"}}}, + }, +) +print(resp) +---- \ No newline at end of file diff --git a/docs/getting-started.mdx b/docs/getting-started.mdx deleted file mode 100644 index 33d1e7b..0000000 --- a/docs/getting-started.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -id: gettingStartedServerlessPy -slug: /serverless-python/docs/getting-started -title: Getting started with the Serverless Python client -description: This page contains quickstart information about the Serverless Python client. -date: 2023-04-27 -tags: ['serverless','Python client','docs', 'getting started', 'Python'] ---- - -This page guides you through the installation process of the Serverless Python client, shows you how to instantiate the client, and how to perform basic -Elasticsearch operations with it. - -## Requirements - -* Python 3.7 or higher installed on your system. - -## Installation - -### Using the command line - -You can install the Elasticsearch Serverless Python client with the following -commands: - -```bash -``` - - -## Instantiate a client - -You can instantiate a client by running the following command: - -```python - -``` - -You can find the Elasticsearch endpoint on the Cloud deployment management page. - - - -You can create a new API Key under **Stack Management** > **Security**: - - - - -## Using the API - -After you instantiated a client with your API key and Elasticsearch endpoint, -you can start ingesting documents into the Elasticsearch Service. You can use -the Bulk API for this. This API enables you to index, update, and delete several -documents in one request. - - -### Creating an index and ingesting documents - -You can call the `bulk` API with a body parameter, an array of hashes that -define the action, and a document. - -The following is an example of indexing some classic books into the `books` -index: - -```python - -``` - -When you use the client to make a request to Elasticsearch, it returns an API -response object. You can check the HTTP return code by calling `status` and the -HTTP headers by calling `headers` on the response object. The response object -also behaves as a Hash, so you can access the body values directly as seen on -the previous example with ``. - - -### Searching - -Now that some data is available, you can search your documents using the -**Search API**: - -```python -``` diff --git a/docs/guide/configuration.asciidoc b/docs/guide/configuration.asciidoc new file mode 100644 index 0000000..f084754 --- /dev/null +++ b/docs/guide/configuration.asciidoc @@ -0,0 +1,440 @@ +[[config]] +== Configuration + +This page contains information about the most important configuration options of +the Python {es} client. + + +[discrete] +[[tls-and-ssl]] +=== TLS/SSL + +The options in this section can only be used when the node is configured for HTTPS. An error will be raised if using these options with an HTTP node. + +[discrete] +==== Verifying server certificates + +The typical route to verify a cluster certificate is via a "CA bundle" which can be specified via the `ca_certs` parameter. If no options are given and the https://github.com/certifi/python-certifi[certifi package] is installed then certifi's CA bundle is used by default. + +If you have your own CA bundle to use you can configure via the `ca_certs` parameter: + +[source,python] +------------------------------------ +es = Elasticsearch( + "https://...", + ca_certs="/path/to/certs.pem" +) +------------------------------------ + +If using a generated certificate or certificate with a known fingerprint you can use the `ssl_assert_fingerprint` to specify the fingerprint which tries to match the server's leaf certificate during the TLS handshake. If there is any matching certificate the connection is verified, otherwise a `TlsError` is raised. + +In Python 3.9 and earlier only the leaf certificate will be verified but in Python 3.10+ private APIs are used to verify any certificate in the certificate chain. This helps when using certificates that are generated on a multi-node cluster. + +[source,python] +------------------------------------ +es = Elasticsearch( + "https://...", + ssl_assert_fingerprint=( + "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3" + ) +) +------------------------------------ + +To disable certificate verification use the `verify_certs=False` parameter. This option should be avoided in production, instead use the other options to verify the clusters' certificate. + +[source,python] +------------------------------------ +es = Elasticsearch( + "https://...", + verify_certs=False +) +------------------------------------ + +[discrete] +==== TLS versions + +Configuring the minimum TLS version to connect to is done via the `ssl_version` parameter. By default this is set to a minimum value of TLSv1.2. In Python 3.7+ you can use the new `ssl.TLSVersion` enumeration to specify versions. + +[source,python] +------------------------------------ +import ssl + +# Python 3.6 +es = Elasticsearch( + ..., + ssl_version=ssl.PROTOCOL_TSLv1_2 +) + +# Python 3.7+ +es = Elasticsearch( + ..., + ssl_version=ssl.TLSVersion.TLSv1_2 +) +------------------------------------ + +[discrete] +==== Client TLS certificate authentication + +Elasticsearch can be configured to authenticate clients via TLS client certificates. Client certificate and keys can be configured via the `client_cert` and `client_key` parameters: + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + client_cert="/path/to/cert.pem", + client_key="/path/to/key.pem", +) +------------------------------------ + + +[discrete] +==== Using an SSLContext + +For advanced users an `ssl.SSLContext` object can be used for configuring TLS via the `ssl_context` parameter. The `ssl_context` parameter can't be combined with any other TLS options except for the `ssl_assert_fingerprint` parameter. + +[source,python] +------------------------------------ +import ssl + +# Create and configure an SSLContext +ctx = ssl.create_default_context() +ctx.load_verify_locations(...) + +es = Elasticsearch( + ..., + ssl_context=ctx +) +------------------------------------ + + +[discrete] +[[compression]] +=== HTTP compression + +Compression of HTTP request and response bodies can be enabled with the `http_compress` parameter. +If enabled then HTTP request bodies will be compressed with `gzip` and HTTP responses will include +the `Accept-Encoding: gzip` HTTP header. By default compression is disabled. + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + http_compress=True # Enable compression! +) +------------------------------------ + +HTTP compression is recommended to be enabled when requests are traversing the network. +Compression is automatically enabled when connecting to Elastic Cloud. + + +[discrete] +[[timeouts]] +=== Request timeouts + +Requests can be configured to timeout if taking too long to be serviced. The `request_timeout` parameter can be passed via the client constructor or the client `.options()` method. When the request times out the node will raise a `ConnectionTimeout` exception which can trigger retries. + +Setting `request_timeout` to `None` will disable timeouts. + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + request_timeout=10 # 10 second timeout +) + +# Search request will timeout in 5 seconds +es.options(request_timeout=5).search(...) +------------------------------------ + +[discrete] +==== API and server timeouts + +There are API-level timeouts to take into consideration when making requests which can cause the request to timeout on server-side rather than client-side. You may need to configure both a transport and API level timeout for long running operations. + +In the example below there are three different configurable timeouts for the `cluster.health` API all with different meanings for the request: + +[source,python] +------------------------------------ +es.options( + # Amount of time to wait for an HTTP response to start. + request_timeout=30 +).cluster.health( + # Amount of time to wait to collect info on all nodes. + timeout=30, + # Amount of time to wait for info from the master node. + master_timeout=10, +) +------------------------------------ + + +[discrete] +[[retries]] +=== Retries + +Requests can be retried if they don't return with a successful response. This provides a way for requests to be resilient against transient failures or overloaded nodes. + +The maximum number of retries per request can be configured via the `max_retries` parameter. Setting this parameter to 0 disables retries. This parameter can be set in the client constructor or per-request via the client `.options()` method: + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + max_retries=5 +) + +# For this API request we disable retries with 'max_retries=0' +es.options(max_retries=0).index( + index="blogs", + document={ + "title": "..." + } +) +------------------------------------ + +[discrete] +==== Retrying on connection errors and timeouts + +Connection errors are automatically retried if retries are enabled. Retrying requests on connection timeouts can be enabled or disabled via the `retry_on_timeout` parameter. This parameter can be set on the client constructor or via the client `.options()` method: + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + retry_on_timeout=True +) +es.options(retry_on_timeout=False).info() +------------------------------------ + +[discrete] +==== Retrying status codes + +By default if retries are enabled `retry_on_status` is set to `(429, 502, 503, 504)`. This parameter can be set on the client constructor or via the client `.options()` method. Setting this value to `()` will disable the default behavior. + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + retry_on_status=() +) + +# Retry this API on '500 Internal Error' statuses +es.options(retry_on_status=[500]).index( + index="blogs", + document={ + "title": "..." + } +) +------------------------------------ + +[discrete] +==== Ignoring status codes + +By default an `ApiError` exception will be raised for any non-2XX HTTP requests that exhaust retries, if any. If you're expecting an HTTP error from the API but aren't interested in raising an exception you can use the `ignore_status` parameter via the client `.options()` method. + +A good example where this is useful is setting up or cleaning up resources in a cluster in a robust way: + +[source,python] +------------------------------------ +es = Elasticsearch(...) + +# API request is robust against the index not existing: +resp = es.options(ignore_status=404).indices.delete(index="delete-this") +resp.meta.status # Can be either '2XX' or '404' + +# API request is robust against the index already existing: +resp = es.options(ignore_status=[400]).indices.create( + index="create-this", + mapping={ + "properties": {"field": {"type": "integer"}} + } +) +resp.meta.status # Can be either '2XX' or '400' +------------------------------------ + +When using the `ignore_status` parameter the error response will be returned serialized just like a non-error response. In these cases it can be useful to inspect the HTTP status of the response. To do this you can inspect the `resp.meta.status`. + +[discrete] +[[sniffing]] +=== Sniffing for new nodes + +Additional nodes can be discovered by a process called "sniffing" where the client will query the cluster for more nodes that can handle requests. + +Sniffing can happen at three different times: on client instantiation, before requests, and on a node failure. These three behaviors can be enabled and disabled with the `sniff_on_start`, `sniff_before_requests`, and `sniff_on_node_failure` parameters. + +IMPORTANT: When using an HTTP load balancer or proxy you cannot use sniffing functionality as the cluster would supply the client with IP addresses to directly connect to the cluster, circumventing the load balancer. Depending on your configuration this might be something you don't want or break completely. + +[discrete] +==== Waiting between sniffing attempts + +To avoid needlessly sniffing too often there is a delay between attempts to discover new nodes. This value can be controlled via the `min_delay_between_sniffing` parameter. + +[discrete] +==== Filtering nodes which are sniffed + +By default nodes which are marked with only a `master` role will not be used. To change the behavior the parameter `sniffed_node_callback` can be used. To mark a sniffed node not to be added to the node pool +return `None` from the `sniffed_node_callback`, otherwise return a `NodeConfig` instance. + +[source,python] +------------------------------------ +from typing import Optional, Dict, Any +from elastic_transport import NodeConfig +from elasticsearch import Elasticsearch + +def filter_master_eligible_nodes( + node_info: Dict[str, Any], + node_config: NodeConfig +) -> Optional[NodeConfig]: + # This callback ignores all nodes that are master eligible + # instead of master-only nodes (default behavior) + if "master" in node_info.get("roles", ()): + return None + return node_config + +client = Elasticsearch( + "https://localhost:9200", + sniffed_node_callback=filter_master_eligible_nodes +) +------------------------------------ + +The `node_info` parameter is part of the response from the `nodes.info()` API, below is an example +of what that object looks like: + +[source,json] +------------------------------------ +{ + "name": "SRZpKFZ", + "transport_address": "127.0.0.1:9300", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "5.0.0", + "build_hash": "253032b", + "roles": ["master", "data", "ingest"], + "http": { + "bound_address": ["[fe80::1]:9200", "[::1]:9200", "127.0.0.1:9200"], + "publish_address": "1.1.1.1:123", + "max_content_length_in_bytes": 104857600 + } +} +------------------------------------ + + +[discrete] +[[node-pool]] +=== Node Pool + +[discrete] +==== Selecting a node from the pool + +You can specify a node selector pattern via the `node_selector_class` parameter. The supported values are `round_robin` and `random`. Default is `round_robin`. + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + node_selector_class="round_robin" +) +------------------------------------ + +Custom selectors are also supported: + +[source,python] +------------------------------------ +from elastic_transport import NodeSelector + +class CustomSelector(NodeSelector): + def select(nodes): ... + +es = Elasticsearch( + ..., + node_selector_class=CustomSelector +) +------------------------------------ + +[discrete] +==== Marking nodes dead and alive + +Individual nodes of Elasticsearch may have transient connectivity or load issues which may make them unable to service requests. To combat this the pool of nodes will detect when a node isn't able to service requests due to transport or API errors. + +After a node has been timed out it will be moved back to the set of "alive" nodes but only after the node returns a successful response will the node be marked as "alive" in terms of consecutive errors. + +The `dead_node_backoff_factor` and `max_dead_node_backoff` parameters can be used to configure how long the node pool will put the node into timeout with each consecutive failure. Both parameters use a unit of seconds. + +The calculation is equal to `min(dead_node_backoff_factor * (2 ** (consecutive_failures - 1)), max_dead_node_backoff)`. + + +[discrete] +[[serializer]] +=== Serializers + +Serializers transform bytes on the wire into native Python objects and vice-versa. By default the client ships with serializers for `application/json`, `application/x-ndjson`, `text/*`, and `application/mapbox-vector-tile`. + +You can define custom serializers via the `serializers` parameter: + +[source,python] +------------------------------------ +from elasticsearch import Elasticsearch, JsonSerializer + +class JsonSetSerializer(JsonSerializer): + """Custom JSON serializer that handles Python sets""" + def default(self, data: Any) -> Any: + if isinstance(data, set): + return list(data) + return super().default(data) + +es = Elasticsearch( + ..., + # Serializers are a mapping of 'mimetype' to Serializer class. + serializers={"application/json": JsonSetSerializer()} +) +------------------------------------ + + +[discrete] +[[nodes]] +=== Nodes + +[discrete] +==== Node implementations + +The default node class for synchronous I/O is `urllib3` and the default node class for asynchronous I/O is `aiohttp`. + +For all of the built-in HTTP node implementations like `urllib3`, `requests`, and `aiohttp` you can specify with a simple string to the `node_class` parameter: + +[source,python] +------------------------------------ +from elasticsearch import Elasticsearch + +es = Elasticsearch( + ..., + node_class="requests" +) +------------------------------------ + +You can also specify a custom node implementation via the `node_class` parameter: + +[source,python] +------------------------------------ +from elasticsearch import Elasticsearch +from elastic_transport import Urllib3HttpNode + +class CustomHttpNode(Urllib3HttpNode): + ... + +es = Elasticsearch( + ... + node_class=CustomHttpNode +) +------------------------------------ + +[discrete] +==== HTTP connections per node + +Each node contains its own pool of HTTP connections to allow for concurrent requests. This value is configurable via the `connections_per_node` parameter: + +[source,python] +------------------------------------ +es = Elasticsearch( + ..., + connections_per_node=5 +) +------------------------------------ diff --git a/docs/guide/connecting.asciidoc b/docs/guide/connecting.asciidoc new file mode 100644 index 0000000..34f6229 --- /dev/null +++ b/docs/guide/connecting.asciidoc @@ -0,0 +1,439 @@ +[[connecting]] +== Connecting + +This page contains the information you need to connect the Client with {es}. + +[discrete] +[[connect-ec]] +=== Connecting to Elastic Cloud + +https://www.elastic.co/guide/en/cloud/current/ec-getting-started.html[Elastic Cloud] +is the easiest way to get started with {es}. When connecting to Elastic Cloud +with the Python {es} client you should always use the `cloud_id` +parameter to connect. You can find this value within the "Manage Deployment" +page after you've created a cluster (look in the top-left if you're in Kibana). + +We recommend using a Cloud ID whenever possible because your client will be +automatically configured for optimal use with Elastic Cloud including HTTPS and +HTTP compression. + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Password for the 'elastic' user generated by Elasticsearch +ELASTIC_PASSWORD = "" + +# Found in the 'Manage Deployment' page +CLOUD_ID = "deployment-name:dXMtZWFzdDQuZ2Nw..." + +# Create the client instance +client = Elasticsearch( + cloud_id=CLOUD_ID, + basic_auth=("elastic", ELASTIC_PASSWORD) +) + +# Successful response! +client.info() +# {'name': 'instance-0000000000', 'cluster_name': ...} +---- + +[discrete] +[[connect-self-managed-new]] +=== Connecting to a self-managed cluster + +By default {es} will start with security features like authentication and TLS +enabled. To connect to the {es} cluster you'll need to configure the Python {es} +client to use HTTPS with the generated CA certificate in order to make requests +successfully. + +If you're just getting started with {es} we recommend reading the documentation +on https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html[configuring] +and +https://www.elastic.co/guide/en/elasticsearch/reference/current/starting-elasticsearch.html[starting {es}] +to ensure your cluster is running as expected. + +When you start {es} for the first time you'll see a distinct block like the one +below in the output from {es} (you may have to scroll up if it's been a while): + +```sh +---------------------------------------------------------------- +-> Elasticsearch security features have been automatically configured! +-> Authentication is enabled and cluster connections are encrypted. + +-> Password for the elastic user (reset with `bin/elasticsearch-reset-password -u elastic`): + lhQpLELkjkrawaBoaz0Q + +-> HTTP CA certificate SHA-256 fingerprint: + a52dd93511e8c6045e21f16654b77c9ee0f34aea26d9f40320b531c474676228 +... +---------------------------------------------------------------- +``` + +Note down the `elastic` user password and HTTP CA fingerprint for the next +sections. In the examples below they will be stored in the variables +`ELASTIC_PASSWORD` and `CERT_FINGERPRINT` respectively. + +Depending on the circumstances there are two options for verifying the HTTPS +connection, either verifying with the CA certificate itself or via the HTTP CA +certificate fingerprint. + +[discrete] +==== Verifying HTTPS with CA certificates + +Using the `ca_certs` option is the default way the Python {es} client verifies +an HTTPS connection. + +The generated root CA certificate can be found in the `certs` directory in your +{es} config location (`$ES_CONF_PATH/certs/http_ca.crt`). If you're running {es} +in Docker there is +https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html[additional documentation for retrieving the CA certificate]. + +Once you have the `http_ca.crt` file somewhere accessible pass the path to the +client via `ca_certs`: + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Password for the 'elastic' user generated by Elasticsearch +ELASTIC_PASSWORD = "" + +# Create the client instance +client = Elasticsearch( + "https://localhost:9200", + ca_certs="/path/to/http_ca.crt", + basic_auth=("elastic", ELASTIC_PASSWORD) +) + +# Successful response! +client.info() +# {'name': 'instance-0000000000', 'cluster_name': ...} +---- + +NOTE: If you don't specify `ca_certs` or `ssl_assert_fingerprint` then the +https://certifiio.readthedocs.io[certifi package] will be used for `ca_certs` by +default if available. + +[discrete] +==== Verifying HTTPS with certificate fingerprints (Python 3.10 or later) + +NOTE: Using this method **requires using Python 3.10 or later** and isn't +available when using the `aiohttp` HTTP client library so can't be used with +`AsyncElasticsearch`. + +This method of verifying the HTTPS connection takes advantage of the certificate +fingerprint value noted down earlier. Take this SHA256 fingerprint value and +pass it to the Python {es} client via `ssl_assert_fingerprint`: + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Fingerprint either from Elasticsearch startup or above script. +# Colons and uppercase/lowercase don't matter when using +# the 'ssl_assert_fingerprint' parameter +CERT_FINGERPRINT = "A5:2D:D9:35:11:E8:C6:04:5E:21:F1:66:54:B7:7C:9E:E0:F3:4A:EA:26:D9:F4:03:20:B5:31:C4:74:67:62:28" + +# Password for the 'elastic' user generated by Elasticsearch +ELASTIC_PASSWORD = "" + +client = Elasticsearch( + "https://localhost:9200", + ssl_assert_fingerprint=CERT_FINGERPRINT, + basic_auth=("elastic", ELASTIC_PASSWORD) +) + +# Successful response! +client.info() +# {'name': 'instance-0000000000', 'cluster_name': ...} +---- + +The certificate fingerprint can be calculated using `openssl x509` with the +certificate file: + +[source,sh] +---- +openssl x509 -fingerprint -sha256 -noout -in /path/to/http_ca.crt +---- + +If you don't have access to the generated CA file from {es} you can use the +following script to output the root CA fingerprint of the {es} instance with +`openssl s_client`: + +[source,sh] +---- +# Replace the values of 'localhost' and '9200' to the +# corresponding host and port values for the cluster. +openssl s_client -connect localhost:9200 -servername localhost -showcerts /dev/null \ + | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin +---- + +The output of `openssl x509` will look something like this: + +[source,sh] +---- +SHA256 Fingerprint=A5:2D:D9:35:11:E8:C6:04:5E:21:F1:66:54:B7:7C:9E:E0:F3:4A:EA:26:D9:F4:03:20:B5:31:C4:74:67:62:28 +---- + + +[discrete] +[[connect-no-security]] +=== Connecting without security enabled + +WARNING: Running {es} without security enabled is not recommended. + +If your cluster is configured with +https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html[security explicitly disabled] +then you can connect via HTTP: + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Create the client instance +client = Elasticsearch("http://localhost:9200") + +# Successful response! +client.info() +# {'name': 'instance-0000000000', 'cluster_name': ...} +---- + +[discrete] +[[connect-url]] +=== Connecting to multiple nodes + +The Python {es} client supports sending API requests to multiple nodes in the +cluster. This means that work will be more evenly spread across the cluster +instead of hammering the same node over and over with requests. To configure the +client with multiple nodes you can pass a list of URLs, each URL will be used as +a separate node in the pool. + +[source,python] +---- +from elasticsearch import Elasticsearch + +# List of nodes to connect use with different hosts and ports. +NODES = [ + "https://localhost:9200", + "https://localhost:9201", + "https://localhost:9202", +] + +# Password for the 'elastic' user generated by Elasticsearch +ELASTIC_PASSWORD = "" + +client = Elasticsearch( + NODES, + ca_certs="/path/to/http_ca.crt", + basic_auth=("elastic", ELASTIC_PASSWORD) +) +---- + +By default nodes are selected using round-robin, but alternate node selection +strategies can be configured with `node_selector_class` parameter. + +NOTE: If your {es} cluster is behind a load balancer like when using Elastic +Cloud you won't need to configure multiple nodes. Instead use the load balancer +host and port. + + +[discrete] +[[authentication]] +=== Authentication + +This section contains code snippets to show you how to connect to various {es} +providers. All authentication methods are supported on the client constructor +or via the per-request `.options()` method: + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Authenticate from the constructor +es = Elasticsearch( + "https://localhost:9200", + ca_certs="/path/to/http_ca.crt", + basic_auth=("username", "password") +) + +# Authenticate via the .options() method: +es.options( + basic_auth=("username", "password") +).indices.get(index="*") + +# You can persist the authenticated client to use +# later or use for multiple API calls: +auth_client = es.options( + api_key=("api-key-id", "api-key-secret") +) +for i in range(10): + auth_client.index( + index="example-index", + document={"field": i} + ) +---- + + +[discrete] +[[auth-basic]] +==== HTTP Basic authentication (Username and Password) + +HTTP Basic authentication uses the `basic_auth` parameter by passing in a +username and password within a tuple: + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Adds the HTTP header 'Authorization: Basic ' +es = Elasticsearch( + "https://localhost:9200", + ca_certs="/path/to/http_ca.crt", + basic_auth=("username", "password") +) +---- + + +[discrete] +[[auth-bearer]] +==== HTTP Bearer authentication + +HTTP Bearer authentication uses the `bearer_auth` parameter by passing the token +as a string. This authentication method is used by +https://www.elastic.co/guide/en/elasticsearch/reference/{branch}/security-api-create-service-token.html[Service Account Tokens] +and https://www.elastic.co/guide/en/elasticsearch/reference/{branch}/security-api-get-token.html[Bearer Tokens]. + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Adds the HTTP header 'Authorization: Bearer token-value' +es = Elasticsearch( + "https://localhost:9200", + bearer_auth="token-value" +) +---- + + +[discrete] +[[auth-apikey]] +==== API Key authentication + +You can configure the client to use {es}'s API Key for connecting to your +cluster. Note that you need the values of `id` and `api_key` to +https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[authenticate via an API Key]. + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Adds the HTTP header 'Authorization: ApiKey ' +es = Elasticsearch( + "https://localhost:9200", + ca_certs="/path/to/http_ca.crt", + api_key=("api_key.id", "api_key.api_key") +) +---- + +[discrete] +[[compatibility-mode]] +=== Enabling the Compatibility Mode + +The {es} server version 8.0 is introducing a new compatibility mode that allows +you a smoother upgrade experience from 7 to 8. In a nutshell, you can use the +latest 7.x Python {es} {es} client with an 8.x {es} server, giving more room to +coordinate the upgrade of your codebase to the next major version. + +If you want to leverage this functionality, please make sure that you are using +the latest 7.x Python {es} client and set the environment variable +`ELASTIC_CLIENT_APIVERSIONING` to `true`. The client is handling the rest +internally. For every 8.0 and beyond Python {es} client, you're all set! The +compatibility mode is enabled by default. + +[discrete] +[[connecting-faas]] +=== Using the Client in a Function-as-a-Service Environment + +This section illustrates the best practices for leveraging the {es} client in a +Function-as-a-Service (FaaS) environment. + +The most influential optimization is to initialize the client outside of the +function, the global scope. + +This practice does not only improve performance but also enables background +functionality as – for example – +https://www.elastic.co/blog/elasticsearch-sniffing-best-practices-what-when-why-how[sniffing]. +The following examples provide a skeleton for the best practices. + +IMPORTANT: The async client shouldn't be used within Function-as-a-Service as a new event + loop must be started for each invocation. Instead the synchronous `Elasticsearch` + client is recommended. + +[discrete] +[[connecting-faas-gcp]] +==== GCP Cloud Functions + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Client initialization +client = Elasticsearch( + cloud_id="deployment-name:ABCD...", + api_key=... +) + +def main(request): + # Use the client + client.search(index=..., query={"match_all": {}}) + +---- + +[discrete] +[[connecting-faas-aws]] +==== AWS Lambda + +[source,python] +---- +from elasticsearch import Elasticsearch + +# Client initialization +client = Elasticsearch( + cloud_id="deployment-name:ABCD...", + api_key=... +) + +def main(event, context): + # Use the client + client.search(index=..., query={"match_all": {}}) + +---- + +[discrete] +[[connecting-faas-azure]] +==== Azure Functions + +[source,python] +---- +import azure.functions as func +from elasticsearch import Elasticsearch + +# Client initialization +client = Elasticsearch( + cloud_id="deployment-name:ABCD...", + api_key=... +) + +def main(request: func.HttpRequest) -> func.HttpResponse: + # Use the client + client.search(index=..., query={"match_all": {}}) + +---- + +Resources used to assess these recommendations: + +* https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations[GCP Cloud Functions: Tips & Tricks] +* https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html[Best practices for working with AWS Lambda functions] +* https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-python?tabs=azurecli-linux%2Capplication-level#global-variables[Azure Functions Python developer guide] +* https://docs.aws.amazon.com/lambda/latest/operatorguide/global-scope.html[AWS Lambda: Comparing the effect of global scope] diff --git a/docs/guide/examples.asciidoc b/docs/guide/examples.asciidoc new file mode 100644 index 0000000..4db7799 --- /dev/null +++ b/docs/guide/examples.asciidoc @@ -0,0 +1,111 @@ +[[examples]] +== Examples + +Below you can find examples of how to use the most frequently called APIs with +the Python client. + +* <> +* <> +* <> +* <> +* <> +* <> + +[discrete] +[[ex-index]] +=== Indexing a document + +To index a document, you need to specify three pieces of information: `index`, +`id`, and a `body`: + +[source,py] +---------------------------- +from datetime import datetime +from elasticsearch import Elasticsearch +es = Elasticsearch('https://localhost:9200') + +doc = { + 'author': 'author_name', + 'text': 'Interensting content...', + 'timestamp': datetime.now(), +} +resp = es.index(index="test-index", id=1, document=doc) +print(resp['result']) +---------------------------- + + +[discrete] +[[ex-get]] +=== Getting a document + +To get a document, you need to specify its `index` and `id`: + +[source,py] +---------------------------- +resp = es.get(index="test-index", id=1) +print(resp['_source']) +---------------------------- + + +[discrete] +[[ex-refresh]] +=== Refreshing an index + +You can perform the refresh operation on an index: + +[source,py] +---------------------------- +es.indices.refresh(index="test-index") +---------------------------- + + +[discrete] +[[ex-search]] +=== Searching for a document + +The `search()` method returns results that are matching a query: + +[source,py] +---------------------------- +resp = es.search(index="test-index", query={"match_all": {}}) +print("Got %d Hits:" % resp['hits']['total']['value']) +for hit in resp['hits']['hits']: + print("%(timestamp)s %(author)s: %(text)s" % hit["_source"]) +---------------------------- + + +[discrete] +[[ex-update]] +=== Updating a document + +To update a document, you need to specify three pieces of information: `index`, +`id`, and a `body`: + +[source,py] +---------------------------- +from datetime import datetime +from elasticsearch import Elasticsearch + +client = Elasticsearch('https://localhost:9200') + +doc = { + 'author': 'author_name', + 'text': 'Interensting modified content...', + 'timestamp': datetime.now(), +} +resp = client.update(index="test-index", id=1, document=doc) +print(resp['result']) +---------------------------- + + +[discrete] +[[ex-delete]] +=== Deleting a document + +You can delete a document by specifying its `index`, and `id` in the `delete()` +method: + +[source,py] +---------------------------- +client.delete(index="test-index", id=1) +---------------------------- \ No newline at end of file diff --git a/docs/guide/getting-started.asciidoc b/docs/guide/getting-started.asciidoc new file mode 100644 index 0000000..4fd275a --- /dev/null +++ b/docs/guide/getting-started.asciidoc @@ -0,0 +1,153 @@ +[[getting-started-python]] +== Getting started + +This page guides you through the installation process of the Python client, +shows you how to instantiate the client, and how to perform basic Elasticsearch +operations with it. + +[discrete] +=== Requirements + +* https://www.python.org/[Python] 3.6 or newer +* https://pip.pypa.io/en/stable/[`pip`], installed by default alongside Python + +[discrete] +=== Installation + +To install the latest version of the client, run the following command: + +[source,shell] +-------------------------- +python -m pip install elasticsearch +-------------------------- + +Refer to the <> page to learn more. + + +[discrete] +=== Connecting + +You can connect to the Elastic Cloud using an API key and the Elasticsearch +endpoint. + +[source,py] +---- +from elasticsearch import Elasticsearch + +client = Elasticsearch( + "https://...", # Elasticsearch endpoint + api_key=('api-key-id', 'api-key-secret'), # API key ID and secret +) +---- + +Your Elasticsearch endpoint can be found on the **My deployment** page of your +deployment: + +image::images/es-endpoint.jpg[alt="Finding Elasticsearch endpoint",align="center"] + +You can generate an API key on the **Management** page under Security. + +image::images/create-api-key.png[alt="Create API key",align="center"] + +For other connection options, refer to the <> section. + + +[discrete] +=== Operations + +Time to use Elasticsearch! This section walks you through the basic, and most +important, operations of Elasticsearch. For more operations and more advanced +examples, refer to the <> page. + + +[discrete] +==== Creating an index + +This is how you create the `my_index` index: + +[source,py] +---- +client.indices.create(index="my_index") +---- + + +[discrete] +==== Indexing documents + +This is a simple way of indexing a document: + +[source,py] +---- +client.index( + index="my_index", + id="my_document_id", + document={ + "foo": "foo", + "bar": "bar", + } +) +---- + + +[discrete] +==== Getting documents + +You can get documents by using the following code: + +[source,py] +---- +client.get(index="my_index", id="my_document_id") +---- + + +[discrete] +==== Searching documents + +This is how you can create a single match query with the Python client: + +[source,py] +---- +client.search(index="my_index", query={ + "match": { + "foo": "foo" + } +}) +---- + + +[discrete] +==== Updating documents + +This is how you can update a document, for example to add a new field: + +[source,py] +---- +client.update(index="my_index", id="my_document_id", doc={ + "foo": "bar", + "new_field": "new value", +}) +---- + + +[discrete] +==== Deleting documents + +[source,py] +---- +client.delete(index="my_index", id="my_document_id") +---- + + +[discrete] +==== Deleting an index + +[source,py] +---- +client.indices.delete(index="my_index") +---- + + +[discrete] +== Further reading + +* Use <> for a more comfortable experience with the APIs. diff --git a/docs/guide/helpers.asciidoc b/docs/guide/helpers.asciidoc new file mode 100644 index 0000000..ae425c0 --- /dev/null +++ b/docs/guide/helpers.asciidoc @@ -0,0 +1,90 @@ +[[client-helpers]] +== Client helpers + +You can find here a collection of simple helper functions that abstract some +specifics of the raw API. For detailed examples, refer to +https://elasticsearch-serverless-python.readthedocs.io/en/stable/helpers.html[this page]. + + +[discrete] +[[bulk-helpers]] +=== Bulk helpers + +There are several helpers for the bulk API since its requirement for specific +formatting and other considerations can make it cumbersome if used directly. + +All bulk helpers accept an instance of `{es}` class and an iterable `action` +(any iterable, can also be a generator, which is ideal in most cases since it +allows you to index large datasets without the need of loading them into +memory). + +The items in the iterable `action` should be the documents we wish to index in +several formats. The most common one is the same as returned by `search()`, for +example: + +[source,yml] +---------------------------- +{ + '_index': 'index-name', + '_id': 42, + '_routing': 5, + 'pipeline': 'my-ingest-pipeline', + '_source': { + "title": "Hello World!", + "body": "..." + } +} +---------------------------- + +Alternatively, if `_source` is not present, it pops all metadata fields from +the doc and use the rest as the document data: + +[source,yml] +---------------------------- +{ + "_id": 42, + "_routing": 5, + "title": "Hello World!", + "body": "..." +} +---------------------------- + +The `bulk()` api accepts `index`, `create`, `delete`, and `update` actions. Use +the `_op_type` field to specify an action (`_op_type` defaults to `index`): + +[source,yml] +---------------------------- +{ + '_op_type': 'delete', + '_index': 'index-name', + '_id': 42, +} +{ + '_op_type': 'update', + '_index': 'index-name', + '_id': 42, + 'doc': {'question': 'The life, universe and everything.'} +} +---------------------------- + + +[discrete] +[[scan]] +=== Scan + +Simple abstraction on top of the `scroll()` API - a simple iterator that yields +all hits as returned by underlining scroll requests. + +By default scan does not return results in any pre-determined order. To have a +standard order in the returned documents (either by score or explicit sort +definition) when scrolling, use `preserve_order=True`. This may be an expensive +operation and will negate the performance benefits of using `scan`. + + +[source,py] +---------------------------- +scan(es, + query={"query": {"match": {"title": "python"}}}, + index="orders-*" +) +---------------------------- diff --git a/docs/guide/images/create-api-key.png b/docs/guide/images/create-api-key.png new file mode 100644 index 0000000000000000000000000000000000000000..d75c230300b1509869c461e5f38ddcc3d9d3d690 GIT binary patch literal 80572 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
    yrzys%8TjQ)A>Fl2Fd^^<2l^4@6d5K{m(Bq)25Q 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

    * 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 z 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

    1TiyQU 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

    O>Q{$B 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


    ;2+5 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 Fe*ra3AesOG literal 0 HcmV?d00001 diff --git a/docs/guide/index-custom-title-page.html b/docs/guide/index-custom-title-page.html new file mode 100644 index 0000000..c4bba6f --- /dev/null +++ b/docs/guide/index-custom-title-page.html @@ -0,0 +1,185 @@ + + +
    + +
    + +

    Get to know the Python client

    + +
    +
    +

    + + Connecting +

    +
    + +
    + +
    +
    +

    + + Using the Python client +

    +
    + +
    + +
    +
    +

    + + Developer docs +

    +
    + +
    + +

    Explore by use case

    + + + +

    View all Elastic docs

    \ No newline at end of file diff --git a/docs/guide/index.asciidoc b/docs/guide/index.asciidoc new file mode 100644 index 0000000..fbb97d4 --- /dev/null +++ b/docs/guide/index.asciidoc @@ -0,0 +1,25 @@ += Elasticsearch Python Client + +:doctype: book + +include::{asciidoc-dir}/../../shared/versions/stack/{source_branch}.asciidoc[] + +include::{asciidoc-dir}/../../shared/attributes.asciidoc[] + +include::overview.asciidoc[] + +include::getting-started.asciidoc[] + +include::installation.asciidoc[] + +include::connecting.asciidoc[] + +include::configuration.asciidoc[] + +include::integrations.asciidoc[] + +include::examples.asciidoc[] + +include::helpers.asciidoc[] + +include::release-notes.asciidoc[] diff --git a/docs/guide/installation.asciidoc b/docs/guide/installation.asciidoc new file mode 100644 index 0000000..0557c75 --- /dev/null +++ b/docs/guide/installation.asciidoc @@ -0,0 +1,20 @@ +[[installation]] +== Installation + +The Python client for {es} can be installed with pip: + +[source,sh] +------------------------------------- +$ python -m pip install elasticsearch_serverless +------------------------------------- + +If your application uses async/await in Python you can install with the `async` +extra: + +[source,sh] +-------------------------------------------- +$ python -m pip install elasticsearch_serverless[async] +-------------------------------------------- + +Read more about +https://elasticsearch-serverless-python.readthedocs.io/en/master/async.html[how to use Asyncio with this project]. diff --git a/docs/guide/integrations.asciidoc b/docs/guide/integrations.asciidoc new file mode 100644 index 0000000..966fb57 --- /dev/null +++ b/docs/guide/integrations.asciidoc @@ -0,0 +1,55 @@ +[[integrations]] +== Integrations + +You can find integration options and information on this page. + + +[discrete] +[[transport]] +=== Transport + +The handling of connections, retries, and pooling is handled by the https://github.com/elastic/elastic-transport-python[Elastic Transport Python] library. +Documentation on the low-level classes is available on https://elastic-transport-python.readthedocs.io[Read the Docs]. + + +[discrete] +[[opaque-id]] +=== Tracking requests with Opaque ID + +You can enrich your requests against Elasticsearch with an identifier string, that allows you to discover this identifier in https://www.elastic.co/guide/en/elasticsearch/reference/current/logging.html#deprecation-logging[deprecation logs], to support you with https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-slowlog.html#_identifying_search_slow_log_origin[identifying search slow log origin] +or to help with https://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html#_identifying_running_tasks[identifying running tasks]. + +The opaque ID can be set via the `opaque_id` parameter via the client `.options()` method: + +[source,python] +------------------------------------ +es = Elasticsearch(...) +es.options(opaque_id="request-id-...").search(...) +------------------------------------ + + +[discrete] +[[type-hints]] +=== Type Hints + +The library ships with https://www.python.org/dev/peps/pep-0484[type hints] and supports basic static type analysis with tools like http://mypy-lang.org[Mypy] and https://github.com/microsoft/pyright[Pyright]. + +If we write a script that has a type error like using `request_timeout` with a `str` argument instead of `float` and then run Mypy on the script: + +[source,python] +------------------------------------ +# script.py +from elasticsearch_serverless import Elasticsearch + +es = Elasticsearch(...) +es.options( + request_timeout="5" # type error! +).search(...) + +# $ mypy script.py +# script.py:5: error: Argument "request_timeout" to "search" of "Elasticsearch" has +# incompatible type "str"; expected "Union[int, float, None]" +# Found 1 error in 1 file (checked 1 source file) +------------------------------------ + +Type hints also allow tools like your IDE to check types and provide better auto-complete functionality. diff --git a/docs/guide/overview.asciidoc b/docs/guide/overview.asciidoc new file mode 100644 index 0000000..e1587b9 --- /dev/null +++ b/docs/guide/overview.asciidoc @@ -0,0 +1,88 @@ +[[overview]] +== Overview + +This is the official low-level Python client for {es}. Its goal is to provide +common ground for all {es}-related code in Python. For this reason, the client +is designed to be unopinionated and extendable. An API reference is available +on https://elasticsearch-serverless-python.readthedocs.io[Read the Docs]. + + +[discrete] +=== Compatibility + +Language clients are forward compatible; meaning that clients support communicating +with greater or equal minor versions of Elasticsearch. Elasticsearch language clients +are only backwards compatible with default distributions and without guarantees made. + +If you have a need to have multiple versions installed at the same time older +versions are also released as `elasticsearch2` and `elasticsearch5`. + + +[discrete] +=== Example use + +Simple use-case: + +[source,python] +------------------------------------ +>>> from datetime import datetime +>>> from elasticsearch_serverless import Elasticsearch + +# Connect to 'http://localhost:9200' +>>> es = Elasticsearch("http://localhost:9200") + +# Datetimes will be serialized: +>>> es.index(index="my-index-000001", id=42, document={"any": "data", "timestamp": datetime.now()}) +{'_id': '42', '_index': 'my-index-000001', '_type': 'test-type', '_version': 1, 'ok': True} + +# ...but not deserialized +>>> es.get(index="my-index-000001", id=42)['_source'] +{'any': 'data', 'timestamp': '2013-05-12T19:45:31.804229'} +------------------------------------ + +TIP: For an elaborate example of how to ingest data into Elastic Cloud, +refer to {cloud}/ec-getting-started-python.html[this page]. + + +[discrete] +=== Features + +The client's features include: + +* Translating basic Python data types to and from JSON + +* Configurable automatic discovery of cluster nodes + +* Persistent connections + +* Load balancing (with pluggable selection strategy) across all available nodes + +* Node timeouts on transient errors + +* Thread safety + +* Pluggable architecture + +The client also contains a convenient set of +https://elasticsearch-serverless-python.readthedocs.org/en/master/helpers.html[helpers] for +some of the more engaging tasks like bulk indexing and reindexing. + + +[discrete] +=== Elasticsearch DSL + +For a more high level client library with more limited scope, have a look at +https://elasticsearch-dsl.readthedocs.org/[elasticsearch-dsl] - a more Pythonic library +sitting on top of `elasticsearch-serverless-python`. + +It provides a more convenient and idiomatic way to write and manipulate +https://elasticsearch-dsl.readthedocs.org/en/latest/search_dsl.html[queries]. It +stays close to the Elasticsearch JSON DSL, mirroring its terminology and +structure while exposing the whole range of the DSL from Python either directly +using defined classes or a queryset-like expressions. + +It also provides an optional +https://elasticsearch-dsl.readthedocs.org/en/latest/persistence.html#doctype[persistence +layer] for working with documents as Python objects in an ORM-like fashion: +defining mappings, retrieving and saving documents, wrapping the document data +in user-defined classes. diff --git a/docs/guide/release-notes.asciidoc b/docs/guide/release-notes.asciidoc new file mode 100644 index 0000000..3f85964 --- /dev/null +++ b/docs/guide/release-notes.asciidoc @@ -0,0 +1,492 @@ +[[release-notes]] +== Release notes + +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> + +[discrete] +[[rn-8-8-0]] +=== 8.8.0 (2023-05-25) + +* Added `include_defaults` parameter to the `cluster.get_component_template`, `indices.get_data_stream`, and `indices.get_index_template` API +* Added the `indices.delete_data_lifecycle`, `indices.explain_data_lifecycle`, `indices.get_data_lifecycle`, and `indices.put_data_lifecycle` APIs +* Added the **experimental** `search_application.delete`, `search_application.delete_behavioral_analytics`, `search_application.get`, `search_application.get_behavioral_analytics`, `search_application.list`, `search_application.put`, `search_application.put_behavioral_analytics`, and `search_application.search` APIs. + +[discrete] +[[rn-8-7-0]] +=== 8.7.0 (2023-04-06) + +* Added the ``health_report`` API +* Added the ``transform.schedule_now_transform`` API +* Added the ``from_`` request parameter to the ``transform.start_transform`` API +* Added the ``buffer``, ``grid_agg``, and ``with_labels`` parameters to the ``search_mvt`` API +* Added the ``allow_auto_create`` parameter to the ``cluster.create_component_template`` API +* Added the ``delete_user_annotations`` parameter to the ``ml.delete_job``, ``ml.reset_job`` API +* Added the ``start`` and ``end`` parameters to the ``ml.preview_datafeed`` API +* Added the ``priority`` parameter to the ``ml.start_datafeed`` API +* Added the ``job_id`` parameter to the ``ml.update_datafeed`` API +* Added the ``model_prune_window`` parameter to the ``ml.update_job`` API +* Added the ``feature_states`` parameter to the ``snapshot.restore_snapshot`` API +* Added the ``timeout`` parameter to the ``transform.get_transform_stats`` API +* Added the ``from_`` parameter to the ``transform.start_transform`` API +* Changed the ``input`` parameter of the ``ml.put_trained_models`` API from required to optional +* Fixed the ``cluster.create_component_template`` API by removing the erroneously provided ``aliases``, ``mappings``, and ``settings`` parameters. Only the ``template`` parameter should be used for specifying component templates. + +[discrete] +[[rn-8-6-2]] +=== 8.6.2 (2023-02-16) + +* Client is compatible with Elasticsearch 8.6.2 + +[discrete] +[[rn-8-6-1]] +=== 8.6.1 (2023-01-27) + +* Client is compatible with Elasticsearch 8.6.1 + +[discrete] +==== Core + +* Added the `expand_wildcards`, `preference`, and `routing` parameters to the `open_point_in_time` API. + +[discrete] +[[rn-8-6-0]] +=== 8.6.0 (2023-01-10) + +* Client is compatible with Elasticsearch 8.6.0 + +[discrete] +==== Core + +* Changed the `fields` parameter of the `field_caps` API to be encoded in the HTTP request body. +* Changed the `index` parameter of the `rank_eval` API to be optional. +* Changed the `requests` parameter of the `rank_eval` API to be optional. + +[discrete] +==== CAT + +* Added the `time` parameter to the `cat.indices` API + +[discrete] +==== Machine Learning + +* Fixed the `model_id` parameter of the `ml.clear_trained_model_deployment_cache` API to be required. + +[discrete] +[[rn-8-5-3]] +=== 8.5.3 (2022-12-08) + +* Client is compatible with Elasticsearch 8.5.3 + +[discrete] +[[rn-8-5-2]] +=== 8.5.2 (2022-11-23) + +* Client is compatible with Elasticsearch 8.5.2 + +[discrete] +[[rn-8-5-1]] +=== 8.5.1 (2022-11-21) + +* Client is compatible with Elasticsearch 8.5.1 + +[discrete] +[[rn-8-5-0]] +=== 8.5.0 (2022-11-2) + +[discrete] +==== Indices + +* Added the **experimental** `indices.downsample` API + +[discrete] +==== Rollup + +* Removed the deprecated `rollup.rollup` API. + +[discrete] +==== Snapshot + +* Added the `index_names` parameter to the `snapshot.get` API. + +[discrete] +==== Machine Learning + +* Added the **beta** `ml.clear_trained_model_deployment_cache` API. +* Changed the `ml.put_trained_model_definition_part` API from **experimental** to **stable**. +* Changed the `ml.put_trained_model_vocabulary` API from **experimental** to **stable**. +* Changed the `ml.start_trained_model_deployment` API from **experimental** to **stable**. +* Changed the `ml.stop_trained_model_deployment` API from **experimental** to **stable**. + +[discrete] +==== Security + +* Added the `with_limited_by` parameter to the `get_api_key` API. +* Added the `with_limited_by` parameter to the `query_api_keys` API. +* Added the `with_profile_uid` parameter to the `get_user` API. +* Changed the `security.activate_user_profile` API from **beta** to **stable**. +* Changed the `security.disable_user_profile` API from **beta** to **stable**. +* Changed the `security.enable_user_profile` API from **beta** to **stable**. +* Changed the `security.get_user_profile` API from **beta** to **stable**. +* Changed the `security.suggest_user_profiles` API from **beta** to **stable**. +* Changed the `security.update_user_profile_data` API from **beta** to **stable**. +* Changed the `security.has_privileges_user_profile` API from **experimental** to **stable**. + +[discrete] +[[rn-8-4-3]] +=== 8.4.3 (2022-10-06) + +* Client is compatible with Elasticsearch 8.4.3 + +[discrete] +[[rn-8-4-2]] +=== 8.4.2 (2022-09-20) + +[discrete] +==== Documents + +* Added the `error_trace`, `filter_path`, `human` and `pretty` parameters to the `get_source` API. +* Added the `ext` parameter to the `search` API. + +[discrete] +==== Async Search + +* Added the `ext` parameter to the `async_search.submit` API. + +[discrete] +==== Fleet + +* Added the `ext` parameter to the `fleet.search` API. + +[discrete] +[[rn-8-4-1]] +=== 8.4.1 (2022-09-06) + +* Client is compatible with Elasticsearch 8.4.1 + +[discrete] +[[rn-8-4-0]] +=== 8.4.0 (2022-08-25) + +[discrete] +==== Search + +* Added the `knn` parameter to the `search` API. +* Added the `knn` parameter to the `async_search.submit` API. + +[discrete] +==== Machine Learning + +* Added the `cache_size` parameter to the `ml.start_trained_model_deployment` API. + +[discrete] +==== Security + +* Added the `security.update_api_key` API. + +[discrete] +[[rn-8-3-3]] +=== 8.3.3 (2022-08-01) + +* Client is compatible with Elasticsearch 8.3.3 + +[discrete] +[[rn-8-3-2]] +=== 8.3.2 (2022-08-01) + +[discrete] +==== Security + +* Added the `refresh` parameter to the `security.create_service_token` API. + +[discrete] +[[rn-8-3-1]] +=== 8.3.1 (2022-06-30) + +[discrete] +==== Security + +* Added the **experimental** `security.has_privileges_user_profile` API. +* Added the `hint` parameter to the **experimental** `security.suggest_user_profiles` API. + +[discrete] +[[rn-8-3-0]] +=== 8.3.0 (2022-06-29) + +* Client is compatible with Elasticsearch 8.3.0 + +[discrete] +[[rn-8-2-3]] +=== 8.2.3 (2022-06-15) + +[discrete] +==== Documents + +* Added the `routing` parameter to the `msearch` API. + +[discrete] +==== CAT + +* Added the `cat.component_templates` API. + +[discrete] +==== Ingest + +* Added the `if_version` parameter to the `ingest.put_pipeline` API. + +[discrete] +==== Security + +* Changed the `name` parameter for the `security.create_service_token` API from required to optional. +* Added the `refresh` parameter to the `security.create_service_token` API. +* Changed the name of `access` parameter to the `labels` parameter in the `security.update_user_profile_data` API. + +[discrete] +==== Shutdown + +* Added the `timeout` and `master_timeout` parameters to the `shutdown.get_node`, `shutdown.delete_node`, and `shutdown.put_node` APIs. +* Added the `reason`, `type`, `allocation_delay`, and `target_node_name` parameters to the `shutdown.put_node` API. + +[discrete] +[[rn-8-2-2]] +=== 8.2.2 (2022-06-01) + +* Client is compatible with Elasticsearch 8.2.2 + +[discrete] +[[rn-8-2-1]] +=== 8.2.1 (2022-06-01) + +[discrete] +==== Machine Learning + +* Added the `inference_config` parameter to the `ml.infer_trained_model_deployment` API + +[discrete] +[[rn-8-2-0]] +=== 8.2.0 (2022-05-03) + +[discrete] +==== Client + +* Re-introduced support for passing `requests.auth.BaseAuth` objects to the `http_auth` parameter which was available in 7.x. + +[discrete] +==== Search + +* Added the `filter` parameter to the **experimental** `knn_search` API + +[discrete] +==== Documents + +* Changed the `source` and `dest` parameters for the `reindex` API from optional to required + +[discrete] +==== Indices + +* Added the `indices.field_usage_stats` API +* Added the `indices.modify_data_stream` API +* Added the `fields` and `types` parameters to the `field_caps` API +* Added the `ignore_unvailable` parameter to the `open_point_in_time` API +* Added the `master_timeout` and `timeout` parameters to the `indices.delete` API +* Added the `features` parameter to the `indices.get` API + +[discrete] +==== Machine Learning + +* Added the `ml.get_memory_stats` API + +[discrete] +==== Migrations + +* Added the `migrations.get_feature_upgrade_status` API +* Added the `migrations.post_feature_upgrade` API + +[discrete] +==== Nodes + +* Added the `nodes.clear_repositories_metering_archive` API +* Added the `nodes.get_repositories_metering_info` API + +[discrete] +==== Security + +* Added the **beta** `security.activate_user_profile` API +* Added the **beta** `security.disable_user_profile` API +* Added the **beta** `security.enable_user_profile` API +* Added the **beta** `security.get_user_profile` API +* Added the **beta** `security.suggest_user_profiles` API +* Added the **beta** `security.update_user_profile_data` API + +[discrete] +==== SQL + +* Added the `catalog`, `index_using_frozen`, `keep_alive`, `keep_on_completion`, `runtime_mappings`, and `wait_for_completion_timeout` parameters to the `sql.query` API + +[discrete] +[[rn-8-1-2]] +=== 8.1.2 (2022-03-30) + +* Client is compatible with Elasticsearch 8.1.2 + + +[discrete] +[[rn-8-1-1]] +=== 8.1.1 (2022-03-22) + +[discrete] +==== Documents + +* Changed the `source` and `dest` parameters of the `reindex` API to be required. + +[discrete] +==== Mappings + +* Changed the `fields` parameter of the `field_caps` API to be required. + + +[discrete] +[[rn-8-1-0]] +=== 8.1.0 (2022-03-08) + +[discrete] +==== Transforms + +* Added the `transform.reset_transform` API + + +[discrete] +[[rn-8-0-0]] +=== 8.0.0 (2022-02-10) + +[discrete] +==== Added + +* Added the top-level `.options()` method to `Elasticsearch` and `AsyncElasticsearch` for modifying transport options. +* Added parameters corresponding to JSON request body fields for all APIs +* Added `basic_auth` parameter for specifying username and password authentication +* Added `bearer_auth` parameter for specifying an HTTP bearer token or service token +* Added the `meta` property to `ApiError` to access the HTTP response metadata of an error. +* Added a check that a compatible version of the `elastic-transport` package is installed. + +[discrete] +==== Changed + +* Changed the transport layer to use the `elastic-transport` package +* Changed user-defined `body` parameters to have semantic names (e.g `index(document={...})` instead of `index(body={...})`). +* Changed responses to be objects with two properties, `meta` for response metadata (HTTP status, headers, node, etc) and `body` for a typed body. +* Changed `AsyncElasticsearch` to always be available, regardless of whether `aiohttp` is installed +* Changed exception hierarchy, the major change is a new exception `ApiError` which differentiates between an error that's raised from the transport layer (previously `elasticsearch.exceptions.TransportError`, now `elastic_transport.TransportError`) and one raised from the API layer +* Changed the name of `JSONSerializer` to `JsonSerializer` for consistency with other serializer names. Added an alias to the old name for backwards compatibility +* Changed the default mimetypes (`application/json`) to instead use compatibility mimetypes (`application/vnd.elasticsearch+json`) which always request for responses compatibility with version 8.x. + +[discrete] +==== Removed + +* Removed support for Python 2.7 and Python 3.5, the library now supports only Python 3.6+ +* Removed the `elasticsearch.connection` module as all functionality has been moved to the `elastic-transport` package +* Removed the default URL of `http://localhost:9200` due to Elasticsearch 8.0 default configuration being `https://localhost:9200`. + The client's connection to Elasticsearch now must be specified with scheme, host, and port or with the `cloud_id` parameter +* Removed the ability to use positional arguments with API methods. Going forward all API parameters must be keyword-only parameters +* Removed the `doc_type`, `include_type_name`, and `copy_settings` parameters from many document and index APIs + +[discrete] +==== Deprecated + +* Deprecated the `body` and `params` parameters on all APIs +* Deprecated setting transport options `http_auth`, `api_key`, `ignore`, `request_timeout`, `headers`, and `opaque_id` + All of these settings should instead be set via the `.options()` method +* Deprecated the `elasticsearch.transport` and `elasticsearch.client` modules. These modules will be removed in a future version + +[discrete] +==== CAT + +* Removed the deprecated `local` parameter from the `cat.indices`, `cat.nodes`, `cat.shards` API +* Removed the deprecated `allow_no_datafeeds` parameter from the `cat.ml_datafeeds` API +* Removed the deprecated `allow_no_jobs` parameter from the `cat.ml_jobs` API +* Removed the deprecated `size` parameter from the `cat.thread_pool` API +* Added the `time` parameter to the `cat.thread_pool` API + +[discrete] +==== Documents + +* Removed the deprecated `size` parameter from the `delete_by_query` API +* Removed the deprecated `size` parameter from the `update_by_query` API + +[discrete] +==== Indices + +* Removed the deprecated `indices.flush_synced` API +* Removed the deprecated `indices.freeze` API +* Removed the deprecated `indices.get_upgrade` API +* Removed the deprecated `indices.upgrade` API +* Removed the deprecated `indices.exist_type` API +* Removed the deprecated parameter `copy_settings` from the `indices.shrink` API +* Deprecated the `verbose` parameter of the `indices.segments` API + +[discrete] +==== License / X-Pack + +* Deprecated the `accept_enterprise` parameter of the `license.get` API +* Deprecated the `accept_enterprise` parameter of the `xpack.info` API + +[discrete] +==== Machine Learning + +* Added the **experimental** `ml.infer_trained_model_deployment` API +* Added the **experimental** `ml.put_trained_model_definition_part` API +* Added the **experimental** `ml.put_trained_model_vocabulary` API +* Added the **experimental** `ml.start_trained_model_deployment` API +* Added the **experimental** `ml.stop_trained_model_deployment` API +* Added the `timeout` parameter to the `ml.delete_trained_model` API +* Removed the deprecated `allow_no_jobs` parameter from the `ml.close_job` API +* Removed the deprecated `ml.find_text_structure` API +* Removed the deprecated `allow_no_datafeeds` parameter from the `ml.get_datafeed_stats` API +* Removed the deprecated `allow_no_datafeeds` parameter from the `ml.get_datafeeds` API +* Removed the deprecated `allow_no_jobs` parameter from the `ml.get_job_stats` API +* Removed the deprecated `allow_no_jobs` parameter from the `ml.get_jobs` API +* Removed the deprecated `allow_no_jobs` parameter from the `ml.get_overall_buckets` API + +[discrete] +==== Search + +* Added the **experimental** `knn_search` API + +[discrete] +==== Searchable Snapshots + +* Removed the deprecated `searchable_snapshots.repository_stats` API + +[discrete] +==== Snapshots + +* Changed the `snapshot.delete` API to accept multiple snapshots + +[discrete] +==== Security + +* Added the `security.enroll_kibana` API +* Added the `security.enroll_node` API diff --git a/docs/images/copy-endpoint.gif b/docs/images/copy-endpoint.gif deleted file mode 100644 index af2883dfdd1704f891b0b88b1c29f86b8608cdcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335063 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%0DqxQq#BP1^s#;AtTpIA}wB3 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�C`AJ(B6 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 diff --git a/docs/landing-page.mdx b/docs/landing-page.mdx deleted file mode 100644 index dce5516..0000000 --- a/docs/landing-page.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: pyClient -slug: /serverless-python/docs/ -title: Welcome to the documentation of the Serverless Python client -description: Home of the Serverless Python client. -date: 2023-04-27 -tags: ['serverless','Python client','docs', 'Python'] ---- - -This is the official Python client for the **Elasticsearch Serverless** service. - -If you want to develop your Python application with the Elasticsearch Stack, refer -to the [Elasticsearch Client](https://github.com/elastic/elasticsearch-py) -instead. - -If you want to develop your Python application with Elastic Enterprise Search, -refer to the -[Enterprise Search Client](https://github.com/elastic/enterprise-search-python/). - -## Important materials - -* [Elasticsearch Serverless Python client repository](https://github.com/elastic/elasticsearch-serverless-python) diff --git a/docs/questions-and-assumptions.md b/docs/questions-and-assumptions.md deleted file mode 100644 index 716d839..0000000 --- a/docs/questions-and-assumptions.md +++ /dev/null @@ -1,31 +0,0 @@ -# Questions and assumptions - -## Initial questions - -### Do we have a specification? - -Not yet. -For now we've decided to extract a few APIs from the Elasticsearch spec and generate code based on that. - -### How do we test against a running server? - -We are testing it with a Cloud instance of Serverless in the QA environment. - -### YAML Tests - -The Elasticsearch team is working on YAML tests. -Enrico proposed we could maintain our own set of lighter YAML tests for Serverless clients since the API will be smaller. -This way we wouldn't need to worry about the cleanup phase and all the errors it produces, and we wouldn't need to be on top of the changes in the Java code to understand how to run our integration tests. - -## Docs - -One of the outcomes of this work is coordinating with the docs team to create doc books for this client and tie that up together with the docs infra. - -## Assumptions - -## Notes - -### Code generation - -The code for the current APIs was generated from the Elasticsearch client's code generator for this prototype, but there'll be further work with code generation. -As such, a lot of code used in `elasticsearch-py` is duplicated here. diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile new file mode 100644 index 0000000..e188f83 --- /dev/null +++ b/docs/sphinx/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Elasticsearch.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Elasticsearch.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Elasticsearch" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Elasticsearch" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/sphinx/api.rst b/docs/sphinx/api.rst new file mode 100644 index 0000000..60fc550 --- /dev/null +++ b/docs/sphinx/api.rst @@ -0,0 +1,223 @@ +.. _api: + +Elasticsearch API Reference +=========================== + +All the API calls map the raw REST API as closely as possible, including the +distinction between required and optional arguments to the calls. Keyword +arguments are required for all + +.. note:: + + Some API parameters in Elasticsearch are reserved keywords in Python. + For example the ``from`` query parameter for pagination would be + aliased as ``from_``. + + +Elasticsearch +------------- + +.. py:module:: elasticsearch + +.. autoclass:: Elasticsearch + :members: + +.. py:module:: elasticsearch.client + +Async Search +------------ + +.. autoclass:: AsyncSearchClient + :members: + +Autoscaling +----------- + +.. autoclass:: AutoscalingClient + :members: + +Cat +--- + +.. autoclass:: CatClient + :members: + +Cross-Cluster Replication (CCR) +------------------------------- + +.. autoclass:: CcrClient + :members: + +Cluster +------- + +.. autoclass:: ClusterClient + :members: + +Dangling Indices +---------------- + +.. autoclass:: DanglingIndicesClient + :members: + +Enrich Policies +--------------- + +.. autoclass:: EnrichClient + :members: + +Event Query Language (EQL) +-------------------------- + +.. autoclass:: EqlClient + :members: + +Snapshottable Features +---------------------- + +.. autoclass:: FeaturesClient + :members: + +Fleet +----- + +.. autoclass:: FleetClient + :members: + +Graph Explore +------------- + +.. autoclass:: GraphClient + :members: + +Index Lifecycle Management (ILM) +-------------------------------- + +.. autoclass:: IlmClient + :members: + +Indices +------- + +.. autoclass:: IndicesClient + :members: + +Ingest Pipelines +---------------- + +.. autoclass:: IngestClient + :members: + +License +------- + +.. autoclass:: LicenseClient + :members: + +Logstash +-------- + +.. autoclass:: LogstashClient + :members: + +Migration +--------- + +.. autoclass:: MigrationClient + :members: + +Machine Learning (ML) +--------------------- + +.. autoclass:: MlClient + :members: + +Monitoring +---------- + +.. autoclass:: MonitoringClient + :members: + +Nodes +----- + +.. autoclass:: NodesClient + :members: + +Rollup Indices +-------------- + +.. autoclass:: RollupClient + :members: + +Searchable Snapshots +-------------------- + +.. autoclass:: SearchableSnapshotsClient + :members: + +Security +-------- + +.. autoclass:: SecurityClient + :members: + +Shutdown +-------- + +.. autoclass:: ShutdownClient + :members: + +Snapshot Lifecycle Management (SLM) +----------------------------------- + +.. autoclass:: SlmClient + :members: + +Snapshots +--------- + +.. autoclass:: SnapshotClient + :members: + +SQL +--- + +.. autoclass:: SqlClient + :members: + +TLS/SSL +------- + +.. autoclass:: SslClient + :members: + +Tasks +----- + +.. autoclass:: TasksClient + :members: + +Text Structure +-------------- + +.. autoclass:: TextStructureClient + :members: + +Transforms +---------- + +.. autoclass:: TransformClient + :members: + +Watcher +------- + +.. autoclass:: WatcherClient + :members: + +X-Pack +------ + +.. autoclass:: XPackClient + :members: diff --git a/docs/sphinx/async.rst b/docs/sphinx/async.rst new file mode 100644 index 0000000..095cd3c --- /dev/null +++ b/docs/sphinx/async.rst @@ -0,0 +1,225 @@ +Using Asyncio with Elasticsearch +================================ + + .. py:module:: elasticsearch + +For Python 3.6+ the ``elasticsearch_serverless`` package supports async/await with +`Asyncio `_ and `Aiohttp `_. +You can either install ``aiohttp`` directly or use the ``[async]`` extra: + + .. code-block:: bash + + $ python -m pip install elasticsearch_serverless>=7.8.0 aiohttp + + # - OR - + + $ python -m pip install elasticsearch_serverless[async]>=7.8.0 + + .. note:: + Async functionality is a new feature of this library in v7.8.0+ so + `please open an issue `_ + if you find an issue or have a question about async support. + +Getting Started with Async +-------------------------- + +After installation all async API endpoints are available via :class:`~elasticsearch_serverless.AsyncElasticsearch` +and are used in the same way as other APIs, just with an extra ``await``: + + .. code-block:: python + + import asyncio + from elasticsearch_serverless import AsyncElasticsearch + + es = AsyncElasticsearch() + + async def main(): + resp = await es.search( + index="documents", + body={"query": {"match_all": {}}}, + size=20, + ) + print(resp) + + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + +All APIs that are available under the sync client are also available under the async client. + +ASGI Applications and Elastic APM +--------------------------------- + +`ASGI `_ (Asynchronous Server Gateway Interface) is a new way to +serve Python web applications making use of async I/O to achieve better performance. +Some examples of ASGI frameworks include FastAPI, Django 3.0+, and Starlette. +If you're using one of these frameworks along with Elasticsearch then you +should be using :py:class:`~elasticsearch_serverless.AsyncElasticsearch` to avoid blocking +the event loop with synchronous network calls for optimal performance. + +`Elastic APM `_ +also supports tracing of async Elasticsearch queries just the same as +synchronous queries. For an example on how to configure ``AsyncElasticsearch`` with +a popular ASGI framework `FastAPI `_ and APM tracing +there is a `pre-built example `_ +in the ``examples/fastapi-apm`` directory. + +Frequently Asked Questions +-------------------------- + +NameError / ImportError when importing ``AsyncElasticsearch``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If when trying to use ``AsyncElasticsearch`` and you're receiving a ``NameError`` or ``ImportError`` +you should ensure that you're running Python 3.6+ (check with ``$ python --version``) and +that you have ``aiohttp`` installed in your environment (check with ``$ python -m pip freeze | grep aiohttp``). +If either of the above conditions is not met then async support won't be available. + +What about the ``elasticsearch-async`` package? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously asyncio was supported separately via the `elasticsearch-async `_ +package. The ``elasticsearch-async`` package has been deprecated in favor of +``AsyncElasticsearch`` provided by the ``elasticsearch`` package +in v7.8 and onwards. + +Receiving 'Unclosed client session / connector' warning? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This warning is created by ``aiohttp`` when an open HTTP connection is +garbage collected. You'll typically run into this when closing your application. +To resolve the issue ensure that :meth:`~elasticsearch_serverless.AsyncElasticsearch.close` +is called before the :py:class:`~elasticsearch_serverless.AsyncElasticsearch` instance is garbage collected. + +For example if using FastAPI that might look like this: + + .. code-block:: python + + from fastapi import FastAPI + from elasticsearch_serverless import AsyncElasticsearch + + app = FastAPI() + es = AsyncElasticsearch() + + # This gets called once the app is shutting down. + @app.on_event("shutdown") + async def app_shutdown(): + await es.close() + + +Async Helpers +------------- + +Async variants of all helpers are available in ``elasticsearch_serverless.helpers`` +and are all prefixed with ``async_*``. You'll notice that these APIs +are identical to the ones in the sync :ref:`helpers` documentation. + +All async helpers that accept an iterator or generator also accept async iterators +and async generators. + + .. py:module:: elasticsearch_serverless.helpers + +Bulk and Streaming Bulk +~~~~~~~~~~~~~~~~~~~~~~~ + + .. autofunction:: async_bulk + + .. code-block:: python + + import asyncio + from elasticsearch_serverless import AsyncElasticsearch + from elasticsearch_serverless.helpers import async_bulk + + es = AsyncElasticsearch() + + async def gendata(): + mywords = ['foo', 'bar', 'baz'] + for word in mywords: + yield { + "_index": "mywords", + "doc": {"word": word}, + } + + async def main(): + await async_bulk(es, gendata()) + + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + + .. autofunction:: async_streaming_bulk + + .. code-block:: python + + import asyncio + from elasticsearch_serverless import AsyncElasticsearch + from elasticsearch_serverless.helpers import async_streaming_bulk + + es = AsyncElasticsearch() + + async def gendata(): + mywords = ['foo', 'bar', 'baz'] + for word in mywords: + yield { + "_index": "mywords", + "word": word, + } + + async def main(): + async for ok, result in async_streaming_bulk(es, gendata()): + action, result = result.popitem() + if not ok: + print("failed to %s document %s" % ()) + + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + +Scan +~~~~ + + .. autofunction:: async_scan + + .. code-block:: python + + import asyncio + from elasticsearch_serverless import AsyncElasticsearch + from elasticsearch_serverless.helpers import async_scan + + es = AsyncElasticsearch() + + async def main(): + async for doc in async_scan( + client=es, + query={"query": {"match": {"title": "python"}}}, + index="orders-*" + ): + print(doc) + + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + +Reindex +~~~~~~~ + + .. autofunction:: async_reindex + + +API Reference +------------- + + .. py:module:: elasticsearch_serverless + +The API of :class:`~elasticsearch_serverless.AsyncElasticsearch` is nearly identical +to the API of :class:`~elasticsearch_serverless.Elasticsearch` with the exception that +every API call like :py:func:`~elasticsearch_serverless.AsyncElasticsearch.search` is +an ``async`` function and requires an ``await`` to properly return the response +body. + +AsyncElasticsearch +~~~~~~~~~~~~~~~~~~ + + .. note:: + + To reference Elasticsearch APIs that are namespaced like ``.indices.create()`` + refer to the sync API reference. These APIs are identical between sync and async. + + .. autoclass:: AsyncElasticsearch + :members: diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py new file mode 100644 index 0000000..4b26b55 --- /dev/null +++ b/docs/sphinx/conf.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import datetime +import os + +import elasticsearch + +extensions = ["sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.intersphinx"] + +autoclass_content = "both" + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix of source filenames. +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = "Python Elasticsearch client" +copyright = "%d, Elasticsearch B.V" % datetime.date.today().year + +version = elasticsearch.__versionstr__ +release = version + +pygments_style = "sphinx" + +on_rtd = os.environ.get("READTHEDOCS", None) == "True" + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + + html_theme = "sphinx_rtd_theme" + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "elastic-transport": ( + "https://elastic-transport-python.readthedocs.io/en/latest", + None, + ), +} diff --git a/docs/sphinx/exceptions.rst b/docs/sphinx/exceptions.rst new file mode 100644 index 0000000..32c01d6 --- /dev/null +++ b/docs/sphinx/exceptions.rst @@ -0,0 +1,36 @@ +.. _exceptions: + +Exceptions & Warnings +===================== + +.. py:module:: elasticsearch + +API Errors +---------- + +These errors are triggered from an HTTP response that isn't 2XX: + +.. autoclass:: ApiError + :members: +.. autoclass:: NotFoundError +.. autoclass:: ConflictError +.. autoclass:: RequestError +.. autoclass:: AuthenticationException +.. autoclass:: AuthorizationException +.. autoclass:: UnsupportedProductError + +Transport and Connection Errors +------------------------------- + +These errors are triggered by an error occurring before an HTTP response arrives: + +.. autoclass:: TransportError +.. autoclass:: SerializationError +.. autoclass:: ConnectionError +.. autoclass:: ConnectionTimeout +.. autoclass:: SSLError + +Warnings +-------- + +.. autoclass:: ElasticsearchWarning diff --git a/docs/sphinx/helpers.rst b/docs/sphinx/helpers.rst new file mode 100644 index 0000000..a5fb5f6 --- /dev/null +++ b/docs/sphinx/helpers.rst @@ -0,0 +1,135 @@ +.. _helpers: + +Helpers +======= + +Collection of simple helper functions that abstract some specifics of the raw API. + + +Bulk helpers +------------ + +There are several helpers for the ``bulk`` API since its requirement for +specific formatting and other considerations can make it cumbersome if used directly. + +All bulk helpers accept an instance of ``Elasticsearch`` class and an iterable +``actions`` (any iterable, can also be a generator, which is ideal in most +cases since it will allow you to index large datasets without the need of +loading them into memory). + +The items in the ``action`` iterable should be the documents we wish to index +in several formats. The most common one is the same as returned by +:meth:`~elasticsearch_serverless.Elasticsearch.search`, for example: + +.. code:: python + + { + '_index': 'index-name', + '_id': 42, + '_routing': 5, + 'pipeline': 'my-ingest-pipeline', + '_source': { + "title": "Hello World!", + "body": "..." + } + } + +Alternatively, if `_source` is not present, it will pop all metadata fields +from the doc and use the rest as the document data: + +.. code:: python + + { + "_id": 42, + "_routing": 5, + "title": "Hello World!", + "body": "..." + } + +The :meth:`~elasticsearch_serverless.Elasticsearch.bulk` api accepts ``index``, ``create``, +``delete``, and ``update`` actions. Use the ``_op_type`` field to specify an +action (``_op_type`` defaults to ``index``): + +.. code:: python + + { + '_op_type': 'delete', + '_index': 'index-name', + '_id': 42, + } + { + '_op_type': 'update', + '_index': 'index-name', + '_id': 42, + 'doc': {'question': 'The life, universe and everything.'} + } + + +Example: +~~~~~~~~ + +Lets say we have an iterable of data. Lets say a list of words called ``mywords`` +and we want to index those words into individual documents where the structure of the +document is like ``{"word": ""}``. + +.. code:: python + + def gendata(): + mywords = ['foo', 'bar', 'baz'] + for word in mywords: + yield { + "_index": "mywords", + "word": word, + } + + bulk(es, gendata()) + + +For a more complete and complex example please take a look at +https://github.com/elastic/elasticsearch-serverless-python/blob/main/examples/bulk-ingest + +The :meth:`~elasticsearch_serverless.Elasticsearch.parallel_bulk` api is a wrapper around the :meth:`~elasticsearch_serverless.Elasticsearch.bulk` api to provide threading. :meth:`~elasticsearch_serverless.Elasticsearch.parallel_bulk` returns a generator which must be consumed to produce results. + +To see the results use: + +.. code:: python + + for success, info in parallel_bulk(...): + if not success: + print('A document failed:', info) + +If you don't care about the results, you can use deque from collections: + +.. code:: python + + from collections import deque + deque(parallel_bulk(...), maxlen=0) + +.. note:: + + When reading raw json strings from a file, you can also pass them in + directly (without decoding to dicts first). In that case, however, you lose + the ability to specify anything (index, op_type and even id) on a per-record + basis, all documents will just be sent to elasticsearch to be indexed + as-is. + + +.. py:module:: elasticsearch_serverless.helpers + +.. autofunction:: streaming_bulk + +.. autofunction:: parallel_bulk + +.. autofunction:: bulk + + +Scan +---- + +.. autofunction:: scan + + +Reindex +------- + +.. autofunction:: reindex diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst new file mode 100644 index 0000000..db23a87 --- /dev/null +++ b/docs/sphinx/index.rst @@ -0,0 +1,124 @@ +Python Elasticsearch Client +=========================== + +Official low-level client for Elasticsearch. Its goal is to provide common +ground for all Elasticsearch-related code in Python; because of this it tries +to be opinion-free and very extendable. + + +Installation +------------ + +Install the ``elasticsearch_serverless`` package with `pip +`_: + +.. code-block:: console + + $ python -m pip install elasticsearch_serverless + +If your application uses async/await in Python you can install with +the ``async`` extra: + +.. code-block:: console + + $ python -m pip install elasticsearch_serverless[async] + +Read more about `how to use asyncio with this project `_. + + +Compatibility +------------- + +Language clients are forward compatible; meaning that clients support communicating +with greater or equal minor versions of Elasticsearch. Elasticsearch language clients +are only backwards compatible with default distributions and without guarantees made. + +If you have a need to have multiple versions installed at the same time older +versions are also released as ``elasticsearch2``, ``elasticsearch5`` and ``elasticsearch6``. + + +Example Usage +------------- + +.. code-block:: python + + from datetime import datetime + from elasticsearch_serverless import Elasticsearch + es = Elasticsearch() + + doc = { + 'author': 'kimchy', + 'text': 'Elasticsearch: cool. bonsai cool.', + 'timestamp': datetime.now(), + } + resp = es.index(index="test-index", id=1, document=doc) + print(resp['result']) + + resp = es.get(index="test-index", id=1) + print(resp['_source']) + + es.indices.refresh(index="test-index") + + resp = es.search(index="test-index", query={"match_all": {}}) + print("Got %d Hits:" % resp['hits']['total']['value']) + for hit in resp['hits']['hits']: + print("%(timestamp)s %(author)s: %(text)s" % hit["_source"]) + + +Features +-------- + +This client was designed as very thin wrapper around Elasticsearch's REST API to +allow for maximum flexibility. This means that there are no opinions in this +client; it also means that some of the APIs are a little cumbersome to use from +Python. We have created some :ref:`helpers` to help with this issue as well as +a more high level library (`elasticsearch-dsl`_) on top of this one to provide +a more convenient way of working with Elasticsearch. + + + +Elasticsearch-DSL +----------------- + +For a more high level client library with more limited scope, have a look at +`elasticsearch-dsl`_ - a more pythonic library sitting on top of +``elasticsearch-serverless-python``. + +`elasticsearch-dsl`_ provides a more convenient and idiomatic way to write and manipulate +`queries`_ by mirroring the terminology and structure of Elasticsearch JSON DSL +while exposing the whole range of the DSL from Python +either directly using defined classes or a queryset-like expressions. + +It also provides an optional `persistence layer`_ for working with documents as +Python objects in an ORM-like fashion: defining mappings, retrieving and saving +documents, wrapping the document data in user-defined classes. + +.. _elasticsearch-dsl: https://elasticsearch-dsl.readthedocs.io/ +.. _queries: https://elasticsearch-dsl.readthedocs.io/en/latest/search_dsl.html +.. _persistence layer: https://elasticsearch-dsl.readthedocs.io/en/latest/persistence.html#doctype + + +Contents +-------- + +.. toctree:: + :maxdepth: 3 + + api + exceptions + async + helpers + Release Notes + +License +------- + +Copyright 2023 Elasticsearch B.V. Licensed under the Apache License, Version 2.0. + + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/elasticsearch_serverless/__init__.py b/elasticsearch_serverless/__init__.py new file mode 100644 index 0000000..8b38b39 --- /dev/null +++ b/elasticsearch_serverless/__init__.py @@ -0,0 +1,91 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +# flake8: noqa + +import logging +import re +import warnings + +from elastic_transport import __version__ as _elastic_transport_version + +from ._utils import fixup_module_metadata +from ._version import __versionstr__ + +# Ensure that a compatible version of elastic-transport is installed. +_version_groups = tuple(int(x) for x in re.search(r"^(\d+)\.(\d+)\.(\d+)", _elastic_transport_version).groups()) # type: ignore +if _version_groups < (8, 0, 0) or _version_groups > (9, 0, 0): + raise ImportError( + "An incompatible version of elastic-transport is installed. Must be between " + "v8.0.0 and v9.0.0. Install the correct version with the following command: " + "$ python -m pip install 'elastic-transport>=8, <9'" + ) + +_version_groups = re.search(r"^(\d+)\.(\d+)\.(\d+)", __versionstr__).groups() # type: ignore +_major, _minor, _patch = (int(x) for x in _version_groups) +VERSION = __version__ = (_major, _minor, _patch) + +logger = logging.getLogger("elasticsearch") +logger.addHandler(logging.NullHandler()) + +from ._async.client import AsyncElasticsearch as AsyncElasticsearch +from ._sync.client import Elasticsearch as Elasticsearch +from .exceptions import ElasticsearchDeprecationWarning # noqa: F401 +from .exceptions import ( + ApiError, + AuthenticationException, + AuthorizationException, + BadRequestError, + ConflictError, + ConnectionError, + ConnectionTimeout, + ElasticsearchWarning, + NotFoundError, + RequestError, + SerializationError, + SSLError, + TransportError, + UnsupportedProductError, +) +from .serializer import JSONSerializer, JsonSerializer + +# Only raise one warning per deprecation message so as not +# to spam up the user if the same action is done multiple times. +warnings.simplefilter("default", category=ElasticsearchWarning, append=True) + +__all__ = [ + "ApiError", + "AsyncElasticsearch", + "BadRequestError", + "Elasticsearch", + "JsonSerializer", + "SerializationError", + "TransportError", + "NotFoundError", + "ConflictError", + "RequestError", + "ConnectionError", + "SSLError", + "ConnectionTimeout", + "AuthenticationException", + "AuthorizationException", + "UnsupportedProductError", + "ElasticsearchWarning", +] + +fixup_module_metadata(__name__, globals()) +del fixup_module_metadata diff --git a/elasticsearch_serverless/_async/__init__.py b/elasticsearch_serverless/_async/__init__.py new file mode 100644 index 0000000..2a87d18 --- /dev/null +++ b/elasticsearch_serverless/_async/__init__.py @@ -0,0 +1,16 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. diff --git a/elasticsearch_serverless/_async/client/__init__.py b/elasticsearch_serverless/_async/client/__init__.py new file mode 100644 index 0000000..315092e --- /dev/null +++ b/elasticsearch_serverless/_async/client/__init__.py @@ -0,0 +1,4058 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + + +import logging +import typing as t +import warnings + +from elastic_transport import ( + AsyncTransport, + BaseNode, + HeadApiResponse, + NodeConfig, + NodePool, + NodeSelector, + ObjectApiResponse, + Serializer, +) +from elastic_transport.client_utils import DEFAULT, DefaultType + +from ...exceptions import ApiError, TransportError +from ...serializer import DEFAULT_SERIALIZERS +from ._base import ( + BaseClient, + create_sniff_callback, + default_sniff_callback, + resolve_auth_headers, +) +from .async_search import AsyncSearchClient +from .autoscaling import AutoscalingClient +from .cat import CatClient +from .ccr import CcrClient +from .cluster import ClusterClient +from .dangling_indices import DanglingIndicesClient +from .enrich import EnrichClient +from .eql import EqlClient +from .features import FeaturesClient +from .fleet import FleetClient +from .graph import GraphClient +from .ilm import IlmClient +from .indices import IndicesClient +from .ingest import IngestClient +from .license import LicenseClient +from .logstash import LogstashClient +from .migration import MigrationClient +from .ml import MlClient +from .monitoring import MonitoringClient +from .nodes import NodesClient +from .rollup import RollupClient +from .search_application import SearchApplicationClient +from .searchable_snapshots import SearchableSnapshotsClient +from .security import SecurityClient +from .shutdown import ShutdownClient +from .slm import SlmClient +from .snapshot import SnapshotClient +from .sql import SqlClient +from .ssl import SslClient +from .tasks import TasksClient +from .text_structure import TextStructureClient +from .transform import TransformClient +from .utils import ( + _TYPE_HOSTS, + CLIENT_META_SERVICE, + SKIP_IN_PATH, + _quote, + _rewrite_parameters, + client_node_configs, + is_requests_http_auth, + is_requests_node_class, +) +from .watcher import WatcherClient +from .xpack import XPackClient + +logger = logging.getLogger("elasticsearch") + + +SelfType = t.TypeVar("SelfType", bound="AsyncElasticsearch") + + +class AsyncElasticsearch(BaseClient): + """ + Elasticsearch low-level client. Provides a straightforward mapping from + Python to Elasticsearch REST APIs. + + The client instance has additional attributes to update APIs in different + namespaces such as ``async_search``, ``indices``, ``security``, and more: + + .. code-block:: python + + client = Elasticsearch("http://localhost:9200") + + # Get Document API + client.get(index="*", id="1") + + # Get Index API + client.indices.get(index="*") + + Transport options can be set on the client constructor or using + the :meth:`~elasticsearch.Elasticsearch.options` method: + + .. code-block:: python + + # Set 'api_key' on the constructor + client = Elasticsearch( + "http://localhost:9200", + api_key=("id", "api_key") + ) + client.search(...) + + # Set 'api_key' per request + client.options(api_key=("id", "api_key")).search(...) + """ + + def __init__( + self, + hosts: t.Optional[_TYPE_HOSTS] = None, + *, + # API + cloud_id: t.Optional[str] = None, + api_key: t.Optional[t.Union[str, t.Tuple[str, str]]] = None, + basic_auth: t.Optional[t.Union[str, t.Tuple[str, str]]] = None, + bearer_auth: t.Optional[str] = None, + opaque_id: t.Optional[str] = None, + # Node + headers: t.Union[DefaultType, t.Mapping[str, str]] = DEFAULT, + connections_per_node: t.Union[DefaultType, int] = DEFAULT, + http_compress: t.Union[DefaultType, bool] = DEFAULT, + verify_certs: t.Union[DefaultType, bool] = DEFAULT, + ca_certs: t.Union[DefaultType, str] = DEFAULT, + client_cert: t.Union[DefaultType, str] = DEFAULT, + client_key: t.Union[DefaultType, str] = DEFAULT, + ssl_assert_hostname: t.Union[DefaultType, str] = DEFAULT, + ssl_assert_fingerprint: t.Union[DefaultType, str] = DEFAULT, + ssl_version: t.Union[DefaultType, int] = DEFAULT, + ssl_context: t.Union[DefaultType, t.Any] = DEFAULT, + ssl_show_warn: t.Union[DefaultType, bool] = DEFAULT, + # Transport + transport_class: t.Type[AsyncTransport] = AsyncTransport, + request_timeout: t.Union[DefaultType, None, float] = DEFAULT, + node_class: t.Union[DefaultType, t.Type[BaseNode]] = DEFAULT, + node_pool_class: t.Union[DefaultType, t.Type[NodePool]] = DEFAULT, + randomize_nodes_in_pool: t.Union[DefaultType, bool] = DEFAULT, + node_selector_class: t.Union[DefaultType, t.Type[NodeSelector]] = DEFAULT, + dead_node_backoff_factor: t.Union[DefaultType, float] = DEFAULT, + max_dead_node_backoff: t.Union[DefaultType, float] = DEFAULT, + serializer: t.Optional[Serializer] = None, + serializers: t.Union[DefaultType, t.Mapping[str, Serializer]] = DEFAULT, + default_mimetype: str = "application/json", + max_retries: t.Union[DefaultType, int] = DEFAULT, + retry_on_status: t.Union[DefaultType, int, t.Collection[int]] = DEFAULT, + retry_on_timeout: t.Union[DefaultType, bool] = DEFAULT, + sniff_on_start: t.Union[DefaultType, bool] = DEFAULT, + sniff_before_requests: t.Union[DefaultType, bool] = DEFAULT, + sniff_on_node_failure: t.Union[DefaultType, bool] = DEFAULT, + sniff_timeout: t.Union[DefaultType, None, float] = DEFAULT, + min_delay_between_sniffing: t.Union[DefaultType, None, float] = DEFAULT, + sniffed_node_callback: t.Optional[ + t.Callable[[t.Dict[str, t.Any], NodeConfig], t.Optional[NodeConfig]] + ] = None, + meta_header: t.Union[DefaultType, bool] = DEFAULT, + timeout: t.Union[DefaultType, None, float] = DEFAULT, + randomize_hosts: t.Union[DefaultType, bool] = DEFAULT, + host_info_callback: t.Optional[ + t.Callable[ + [t.Dict[str, t.Any], t.Dict[str, t.Union[str, int]]], + t.Optional[t.Dict[str, t.Union[str, int]]], + ] + ] = None, + sniffer_timeout: t.Union[DefaultType, None, float] = DEFAULT, + sniff_on_connection_fail: t.Union[DefaultType, bool] = DEFAULT, + http_auth: t.Union[DefaultType, t.Any] = DEFAULT, + maxsize: t.Union[DefaultType, int] = DEFAULT, + # Internal use only + _transport: t.Optional[AsyncTransport] = None, + ) -> None: + if hosts is None and cloud_id is None and _transport is None: + raise ValueError("Either 'hosts' or 'cloud_id' must be specified") + + if timeout is not DEFAULT: + if request_timeout is not DEFAULT: + raise ValueError( + "Can't specify both 'timeout' and 'request_timeout', " + "instead only specify 'request_timeout'" + ) + warnings.warn( + "The 'timeout' parameter is deprecated in favor of 'request_timeout'", + category=DeprecationWarning, + stacklevel=2, + ) + request_timeout = timeout + + if serializer is not None: + if serializers is not DEFAULT: + raise ValueError( + "Can't specify both 'serializer' and 'serializers' parameters " + "together. Instead only specify one of the other." + ) + serializers = {default_mimetype: serializer} + + if randomize_hosts is not DEFAULT: + if randomize_nodes_in_pool is not DEFAULT: + raise ValueError( + "Can't specify both 'randomize_hosts' and 'randomize_nodes_in_pool', " + "instead only specify 'randomize_nodes_in_pool'" + ) + warnings.warn( + "The 'randomize_hosts' parameter is deprecated in favor of 'randomize_nodes_in_pool'", + category=DeprecationWarning, + stacklevel=2, + ) + randomize_nodes_in_pool = randomize_hosts + + if sniffer_timeout is not DEFAULT: + if min_delay_between_sniffing is not DEFAULT: + raise ValueError( + "Can't specify both 'sniffer_timeout' and 'min_delay_between_sniffing', " + "instead only specify 'min_delay_between_sniffing'" + ) + warnings.warn( + "The 'sniffer_timeout' parameter is deprecated in favor of 'min_delay_between_sniffing'", + category=DeprecationWarning, + stacklevel=2, + ) + min_delay_between_sniffing = sniffer_timeout + + if sniff_on_connection_fail is not DEFAULT: + if sniff_on_node_failure is not DEFAULT: + raise ValueError( + "Can't specify both 'sniff_on_connection_fail' and 'sniff_on_node_failure', " + "instead only specify 'sniff_on_node_failure'" + ) + warnings.warn( + "The 'sniff_on_connection_fail' parameter is deprecated in favor of 'sniff_on_node_failure'", + category=DeprecationWarning, + stacklevel=2, + ) + sniff_on_node_failure = sniff_on_connection_fail + + if maxsize is not DEFAULT: + if connections_per_node is not DEFAULT: + raise ValueError( + "Can't specify both 'maxsize' and 'connections_per_node', " + "instead only specify 'connections_per_node'" + ) + warnings.warn( + "The 'maxsize' parameter is deprecated in favor of 'connections_per_node'", + category=DeprecationWarning, + stacklevel=2, + ) + connections_per_node = maxsize + + # Setting min_delay_between_sniffing=True implies sniff_before_requests=True + if min_delay_between_sniffing is not DEFAULT: + sniff_before_requests = True + + sniffing_options = ( + sniff_timeout, + sniff_on_start, + sniff_before_requests, + sniff_on_node_failure, + sniffed_node_callback, + min_delay_between_sniffing, + sniffed_node_callback, + ) + if cloud_id is not None and any( + x is not DEFAULT and x is not None for x in sniffing_options + ): + raise ValueError( + "Sniffing should not be enabled when connecting to Elastic Cloud" + ) + + sniff_callback = None + if host_info_callback is not None: + if sniffed_node_callback is not None: + raise ValueError( + "Can't specify both 'host_info_callback' and 'sniffed_node_callback', " + "instead only specify 'sniffed_node_callback'" + ) + warnings.warn( + "The 'host_info_callback' parameter is deprecated in favor of 'sniffed_node_callback'", + category=DeprecationWarning, + stacklevel=2, + ) + + sniff_callback = create_sniff_callback( + host_info_callback=host_info_callback + ) + elif sniffed_node_callback is not None: + sniff_callback = create_sniff_callback( + sniffed_node_callback=sniffed_node_callback + ) + elif ( + sniff_on_start is True + or sniff_before_requests is True + or sniff_on_node_failure is True + ): + sniff_callback = default_sniff_callback + + if _transport is None: + requests_session_auth = None + if http_auth is not None and http_auth is not DEFAULT: + if is_requests_http_auth(http_auth): + # If we're using custom requests authentication + # then we need to alert the user that they also + # need to use 'node_class=requests'. + if not is_requests_node_class(node_class): + raise ValueError( + "Using a custom 'requests.auth.AuthBase' class for " + "'http_auth' must be used with node_class='requests'" + ) + + # Reset 'http_auth' to DEFAULT so it's not consumed below. + requests_session_auth = http_auth + http_auth = DEFAULT + + node_configs = client_node_configs( + hosts, + cloud_id=cloud_id, + requests_session_auth=requests_session_auth, + connections_per_node=connections_per_node, + http_compress=http_compress, + verify_certs=verify_certs, + ca_certs=ca_certs, + client_cert=client_cert, + client_key=client_key, + ssl_assert_hostname=ssl_assert_hostname, + ssl_assert_fingerprint=ssl_assert_fingerprint, + ssl_version=ssl_version, + ssl_context=ssl_context, + ssl_show_warn=ssl_show_warn, + ) + transport_kwargs: t.Dict[str, t.Any] = {} + if node_class is not DEFAULT: + transport_kwargs["node_class"] = node_class + if node_pool_class is not DEFAULT: + transport_kwargs["node_pool_class"] = node_class + if randomize_nodes_in_pool is not DEFAULT: + transport_kwargs["randomize_nodes_in_pool"] = randomize_nodes_in_pool + if node_selector_class is not DEFAULT: + transport_kwargs["node_selector_class"] = node_selector_class + if dead_node_backoff_factor is not DEFAULT: + transport_kwargs["dead_node_backoff_factor"] = dead_node_backoff_factor + if max_dead_node_backoff is not DEFAULT: + transport_kwargs["max_dead_node_backoff"] = max_dead_node_backoff + if meta_header is not DEFAULT: + transport_kwargs["meta_header"] = meta_header + + transport_serializers = DEFAULT_SERIALIZERS.copy() + if serializers is not DEFAULT: + transport_serializers.update(serializers) + + # Override compatibility serializers from their non-compat mimetypes too. + # So we use the same serializer for requests and responses. + for mime_subtype in ("json", "x-ndjson"): + if f"application/{mime_subtype}" in serializers: + compat_mimetype = ( + f"application/vnd.elasticsearch+{mime_subtype}" + ) + if compat_mimetype not in serializers: + transport_serializers[compat_mimetype] = serializers[ + f"application/{mime_subtype}" + ] + + transport_kwargs["serializers"] = transport_serializers + + transport_kwargs["default_mimetype"] = default_mimetype + if sniff_on_start is not DEFAULT: + transport_kwargs["sniff_on_start"] = sniff_on_start + if sniff_before_requests is not DEFAULT: + transport_kwargs["sniff_before_requests"] = sniff_before_requests + if sniff_on_node_failure is not DEFAULT: + transport_kwargs["sniff_on_node_failure"] = sniff_on_node_failure + if sniff_timeout is not DEFAULT: + transport_kwargs["sniff_timeout"] = sniff_timeout + if min_delay_between_sniffing is not DEFAULT: + transport_kwargs[ + "min_delay_between_sniffing" + ] = min_delay_between_sniffing + + _transport = transport_class( + node_configs, + client_meta_service=CLIENT_META_SERVICE, + sniff_callback=sniff_callback, + **transport_kwargs, + ) + + super().__init__(_transport) + + # These are set per-request so are stored separately. + self._request_timeout = request_timeout + self._max_retries = max_retries + self._retry_on_timeout = retry_on_timeout + if isinstance(retry_on_status, int): + retry_on_status = (retry_on_status,) + self._retry_on_status = retry_on_status + + else: + super().__init__(_transport) + + if headers is not DEFAULT and headers is not None: + self._headers.update(headers) + if opaque_id is not DEFAULT and opaque_id is not None: # type: ignore[comparison-overlap] + self._headers["x-opaque-id"] = opaque_id + self._headers = resolve_auth_headers( + self._headers, + http_auth=http_auth, + api_key=api_key, + basic_auth=basic_auth, + bearer_auth=bearer_auth, + ) + + # namespaced clients for compatibility with API names + self.async_search = AsyncSearchClient(self) + self.autoscaling = AutoscalingClient(self) + self.cat = CatClient(self) + self.cluster = ClusterClient(self) + self.fleet = FleetClient(self) + self.features = FeaturesClient(self) + self.indices = IndicesClient(self) + self.ingest = IngestClient(self) + self.nodes = NodesClient(self) + self.snapshot = SnapshotClient(self) + self.tasks = TasksClient(self) + + self.xpack = XPackClient(self) + self.ccr = CcrClient(self) + self.dangling_indices = DanglingIndicesClient(self) + self.enrich = EnrichClient(self) + self.eql = EqlClient(self) + self.graph = GraphClient(self) + self.ilm = IlmClient(self) + self.license = LicenseClient(self) + self.logstash = LogstashClient(self) + self.migration = MigrationClient(self) + self.ml = MlClient(self) + self.monitoring = MonitoringClient(self) + self.rollup = RollupClient(self) + self.search_application = SearchApplicationClient(self) + self.searchable_snapshots = SearchableSnapshotsClient(self) + self.security = SecurityClient(self) + self.slm = SlmClient(self) + self.shutdown = ShutdownClient(self) + self.sql = SqlClient(self) + self.ssl = SslClient(self) + self.text_structure = TextStructureClient(self) + self.transform = TransformClient(self) + self.watcher = WatcherClient(self) + + def __repr__(self) -> str: + try: + # get a list of all connections + nodes = [node.base_url for node in self.transport.node_pool.all()] + # truncate to 5 if there are too many + if len(nodes) > 5: + nodes = nodes[:5] + ["..."] + return f"<{self.__class__.__name__}({nodes})>" + except Exception: + # probably operating on custom transport and connection_pool, ignore + return super().__repr__() + + async def __aenter__(self) -> "AsyncElasticsearch": + try: + # All this to avoid a Mypy error when using unasync. + await getattr(self.transport, "_async_call")() + except AttributeError: + pass + return self + + async def __aexit__(self, *_: t.Any) -> None: + await self.close() + + def options( + self: SelfType, + *, + opaque_id: t.Union[DefaultType, str] = DEFAULT, + api_key: t.Union[DefaultType, str, t.Tuple[str, str]] = DEFAULT, + basic_auth: t.Union[DefaultType, str, t.Tuple[str, str]] = DEFAULT, + bearer_auth: t.Union[DefaultType, str] = DEFAULT, + headers: t.Union[DefaultType, t.Mapping[str, str]] = DEFAULT, + request_timeout: t.Union[DefaultType, t.Optional[float]] = DEFAULT, + ignore_status: t.Union[DefaultType, int, t.Collection[int]] = DEFAULT, + max_retries: t.Union[DefaultType, int] = DEFAULT, + retry_on_status: t.Union[DefaultType, int, t.Collection[int]] = DEFAULT, + retry_on_timeout: t.Union[DefaultType, bool] = DEFAULT, + ) -> SelfType: + client = type(self)(_transport=self.transport) + + resolved_headers = headers if headers is not DEFAULT else None + resolved_headers = resolve_auth_headers( + headers=resolved_headers, + api_key=api_key, + basic_auth=basic_auth, + bearer_auth=bearer_auth, + ) + resolved_opaque_id = opaque_id if opaque_id is not DEFAULT else None + if resolved_opaque_id: + resolved_headers["x-opaque-id"] = resolved_opaque_id + + if resolved_headers: + new_headers = self._headers.copy() + new_headers.update(resolved_headers) + client._headers = new_headers + else: + client._headers = self._headers.copy() + + if request_timeout is not DEFAULT: + client._request_timeout = request_timeout + else: + client._request_timeout = self._request_timeout + + if ignore_status is not DEFAULT: + if isinstance(ignore_status, int): + ignore_status = (ignore_status,) + client._ignore_status = ignore_status + else: + client._ignore_status = self._ignore_status + + if max_retries is not DEFAULT: + if not isinstance(max_retries, int): + raise TypeError("'max_retries' must be of type 'int'") + client._max_retries = max_retries + else: + client._max_retries = self._max_retries + + if retry_on_status is not DEFAULT: + if isinstance(retry_on_status, int): + retry_on_status = (retry_on_status,) + client._retry_on_status = retry_on_status + else: + client._retry_on_status = self._retry_on_status + + if retry_on_timeout is not DEFAULT: + if not isinstance(retry_on_timeout, bool): + raise TypeError("'retry_on_timeout' must be of type 'bool'") + client._retry_on_timeout = retry_on_timeout + else: + client._retry_on_timeout = self._retry_on_timeout + + return client + + async def close(self) -> None: + """Closes the Transport and all internal connections""" + await self.transport.close() + + @_rewrite_parameters() + async def ping( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[t.List[str], str]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> bool: + """ + Returns True if a successful response returns from the info() API, + otherwise returns False. This API call can fail either at the transport + layer (due to connection errors or timeouts) or from a non-2XX HTTP response + (due to authentication or authorization issues). + + If you want to discover why the request failed you should use the ``info()`` API. + + ``_ + """ + __path = "/" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + try: + await self.perform_request( + "HEAD", __path, params=__query, headers=__headers + ) + return True + except (ApiError, TransportError): + return False + + # AUTO-GENERATED-API-DEFINITIONS # + + @_rewrite_parameters( + body_name="operations", + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def bulk( + self, + *, + operations: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pipeline: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + require_alias: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to perform multiple index/update/delete operations in a single request. + + ``_ + + :param operations: + :param index: Default index for items which don't provide one + :param pipeline: The pipeline id to preprocess incoming documents with + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param require_alias: Sets require_alias for all incoming documents. Defaults + to unset (false) + :param routing: Specific routing value + :param source: True or false to return the _source field or not, or default list + of fields to return, can be overridden on each sub-request + :param source_excludes: Default list of fields to exclude from the returned _source + field, can be overridden on each sub-request + :param source_includes: Default list of fields to extract and return from the + _source field, can be overridden on each sub-request + :param timeout: Explicit operation timeout + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the bulk operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if operations is None: + raise ValueError("Empty value passed for parameter 'operations'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_bulk" + else: + __path = "/_bulk" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pipeline is not None: + __query["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if require_alias is not None: + __query["require_alias"] = require_alias + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if timeout is not None: + __query["timeout"] = timeout + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __body = operations + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def clear_scroll( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + scroll_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Explicitly clears the search context for a scroll. + + ``_ + + :param scroll_id: + """ + __path = "/_search/scroll" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if scroll_id is not None: + __body["scroll_id"] = scroll_id + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def close_point_in_time( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Close a point in time + + ``_ + + :param id: + """ + if id is None: + raise ValueError("Empty value passed for parameter 'id'") + __path = "/_pit" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if id is not None: + __body["id"] = id + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def count( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + min_score: t.Optional[float] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + routing: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns number of documents matching a query. + + ``_ + + :param index: A comma-separated list of indices to restrict the results + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param min_score: Include only documents with a specific `_score` value in the + result + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param q: Query in the Lucene query string syntax + :param query: + :param routing: A comma-separated list of specific routing values + :param terminate_after: The maximum count for each shard, upon reaching which + the query execution will terminate early + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_count" + else: + __path = "/_count" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if min_score is not None: + __query["min_score"] = min_score + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if routing is not None: + __query["routing"] = routing + if terminate_after is not None: + __query["terminate_after"] = terminate_after + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="document", + ) + async def create( + self, + *, + index: str, + id: str, + document: t.Mapping[str, t.Any], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pipeline: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new document in the index. Returns a 409 response when a document with + a same ID already exists in the index. + + ``_ + + :param index: The name of the index + :param id: Document ID + :param document: + :param pipeline: The pipeline id to preprocess incoming documents with + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the index operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if document is None: + raise ValueError("Empty value passed for parameter 'document'") + __path = f"/{_quote(index)}/_create/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pipeline is not None: + __query["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __body = document + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def delete( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes a document from the index. + + ``_ + + :param index: The name of the index + :param id: The document ID + :param if_primary_term: only perform the delete operation if the last operation + that has changed the document has the specified primary term + :param if_seq_no: only perform the delete operation if the last operation that + has changed the document has the specified sequence number + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the delete operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + async def delete_by_query( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + conflicts: t.Optional[t.Union["t.Literal['abort', 'proceed']", str]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + max_docs: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + refresh: t.Optional[bool] = None, + request_cache: t.Optional[bool] = None, + requests_per_second: t.Optional[float] = None, + routing: t.Optional[str] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + scroll_size: t.Optional[int] = None, + search_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + slices: t.Optional[t.Union[int, t.Union["t.Literal['auto']", str]]] = None, + sort: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[bool] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes documents matching the provided query. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param conflicts: What to do when the delete by query hits version conflicts? + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param from_: Starting offset (default: 0) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_docs: + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param q: Query in the Lucene query string syntax + :param query: + :param refresh: Should the affected indexes be refreshed? + :param request_cache: Specify if request cache should be used for this request + or not, defaults to index level setting + :param requests_per_second: The throttle for this request in sub-requests per + second. -1 means no throttle. + :param routing: A comma-separated list of specific routing values + :param scroll: Specify how long a consistent view of the index should be maintained + for scrolled search + :param scroll_size: Size on the scroll request powering the delete by query + :param search_timeout: Explicit timeout for each search request. Defaults to + no timeout. + :param search_type: Search operation type + :param slice: + :param slices: The number of slices this task should be divided into. Defaults + to 1, meaning the task isn't sliced into subtasks. Can be set to `auto`. + :param sort: A comma-separated list of : pairs + :param stats: Specific 'tag' of the request for logging and statistical purposes + :param terminate_after: The maximum number of documents to collect for each shard, + upon reaching which the query execution will terminate early. + :param timeout: Time each individual bulk request should wait for shards that + are unavailable. + :param version: Specify whether to return document version as part of a hit + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the delete by query operation. Defaults to 1, meaning + the primary shard only. Set to `all` for all shard copies, otherwise set + to any non-negative value less than or equal to the total number of copies + for the shard (number of replicas + 1) + :param wait_for_completion: Should the request should block until the delete + by query is complete. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_delete_by_query" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if conflicts is not None: + __query["conflicts"] = conflicts + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if max_docs is not None: + __body["max_docs"] = max_docs + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if refresh is not None: + __query["refresh"] = refresh + if request_cache is not None: + __query["request_cache"] = request_cache + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second + if routing is not None: + __query["routing"] = routing + if scroll is not None: + __query["scroll"] = scroll + if scroll_size is not None: + __query["scroll_size"] = scroll_size + if search_timeout is not None: + __query["search_timeout"] = search_timeout + if search_type is not None: + __query["search_type"] = search_type + if slice is not None: + __body["slice"] = slice + if slices is not None: + __query["slices"] = slices + if sort is not None: + __query["sort"] = sort + if stats is not None: + __query["stats"] = stats + if terminate_after is not None: + __query["terminate_after"] = terminate_after + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def delete_script( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a script. + + ``_ + + :param id: Script ID + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_scripts/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def exists( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a document exists in an index. + + ``_ + + :param index: The name of the index + :param id: The document ID + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param realtime: Specify whether to perform the operation in realtime or search + mode + :param refresh: Refresh the shard containing the document before performing the + operation + :param routing: Specific routing value + :param source: True or false to return the _source field or not, or a list of + fields to return + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param stored_fields: A comma-separated list of stored fields to return in the + response + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def exists_source( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a document source exists in an index. + + ``_ + + :param index: The name of the index + :param id: The document ID + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param realtime: Specify whether to perform the operation in realtime or search + mode + :param refresh: Refresh the shard containing the document before performing the + operation + :param routing: Specific routing value + :param source: True or false to return the _source field or not, or a list of + fields to return + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_source/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def field_caps( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filters: t.Optional[str] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_unmapped: t.Optional[bool] = None, + index_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + types: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the information about the capabilities of fields among multiple indices. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (*). To target all data streams + and indices, omit this parameter or use * or _all. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with foo but no index starts with bar. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. + :param fields: List of fields to retrieve capabilities for. Wildcard (`*`) expressions + are supported. + :param filters: An optional set of filters: can include +metadata,-metadata,-nested,-multifield,-parent + :param ignore_unavailable: If `true`, missing or closed indices are not included + in the response. + :param include_unmapped: If true, unmapped fields are included in the response. + :param index_filter: Allows to filter indices if the provided query rewrites + to match_none on every shard. + :param runtime_mappings: Defines ad-hoc runtime fields in the request similar + to the way it is done in search requests. These fields exist only as part + of the query and take precedence over fields defined with the same name in + the index mappings. + :param types: Only return results for fields that have one of the types in the + list + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_field_caps" + else: + __path = "/_field_caps" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if filters is not None: + __query["filters"] = filters + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_unmapped is not None: + __query["include_unmapped"] = include_unmapped + if index_filter is not None: + __body["index_filter"] = index_filter + if pretty is not None: + __query["pretty"] = pretty + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if types is not None: + __query["types"] = types + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def get( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a document. + + ``_ + + :param index: Name of the index that contains the document. + :param id: Unique identifier of the document. + :param preference: Specifies the node or shard the operation should be performed + on. Random by default. + :param realtime: Boolean) If true, the request is real-time as opposed to near-real-time. + :param refresh: If true, Elasticsearch refreshes the affected shards to make + this operation visible to search. If false, do nothing with refreshes. + :param routing: Target the specified primary shard. + :param source: True or false to return the _source field or not, or a list of + fields to return. + :param source_excludes: A comma-separated list of source fields to exclude in + the response. + :param source_includes: A comma-separated list of source fields to include in + the response. + :param stored_fields: A comma-separated list of stored fields to return in the + response + :param version: Explicit version number for concurrency control. The specified + version must match the current version of the document for the request to + succeed. + :param version_type: Specific version type: internal, external, external_gte. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_script( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a script. + + ``_ + + :param id: Script ID + :param master_timeout: Specify timeout for connection to master + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_scripts/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def get_source( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the source of a document. + + ``_ + + :param index: Name of the index that contains the document. + :param id: Unique identifier of the document. + :param preference: Specifies the node or shard the operation should be performed + on. Random by default. + :param realtime: Boolean) If true, the request is real-time as opposed to near-real-time. + :param refresh: If true, Elasticsearch refreshes the affected shards to make + this operation visible to search. If false, do nothing with refreshes. + :param routing: Target the specified primary shard. + :param source: True or false to return the _source field or not, or a list of + fields to return. + :param source_excludes: A comma-separated list of source fields to exclude in + the response. + :param source_includes: A comma-separated list of source fields to include in + the response. + :param stored_fields: + :param version: Explicit version number for concurrency control. The specified + version must match the current version of the document for the request to + succeed. + :param version_type: Specific version type: internal, external, external_gte. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_source/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="document", + ) + async def index( + self, + *, + index: str, + document: t.Mapping[str, t.Any], + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + op_type: t.Optional[t.Union["t.Literal['create', 'index']", str]] = None, + pipeline: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + require_alias: t.Optional[bool] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a document in an index. + + ``_ + + :param index: The name of the index + :param document: + :param id: Document ID + :param if_primary_term: only perform the index operation if the last operation + that has changed the document has the specified primary term + :param if_seq_no: only perform the index operation if the last operation that + has changed the document has the specified sequence number + :param op_type: Explicit operation type. Defaults to `index` for requests with + an explicit document ID, and to `create`for requests without an explicit + document ID + :param pipeline: The pipeline id to preprocess incoming documents with + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param require_alias: When true, requires destination to be an alias. Default + is false + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the index operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if document is None: + raise ValueError("Empty value passed for parameter 'document'") + if index not in SKIP_IN_PATH and id not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __method = "PUT" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_doc" + __method = "POST" + else: + raise ValueError("Couldn't find a path for the given parameters") + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if op_type is not None: + __query["op_type"] = op_type + if pipeline is not None: + __query["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if require_alias is not None: + __query["require_alias"] = require_alias + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __body = document + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + __method, __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def info( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns basic information about the cluster. + + ``_ + """ + __path = "/" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def mget( + self, + *, + index: t.Optional[str] = None, + docs: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ids: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to get multiple documents in one request. + + ``_ + + :param index: Name of the index to retrieve documents from when `ids` are specified, + or when a document in the `docs` array does not specify an index. + :param docs: The documents you want to retrieve. Required if no index is specified + in the request URI. + :param ids: The IDs of the documents you want to retrieve. Allowed when the index + is specified in the request URI. + :param preference: Specifies the node or shard the operation should be performed + on. Random by default. + :param realtime: If `true`, the request is real-time as opposed to near-real-time. + :param refresh: If `true`, the request refreshes relevant shards before retrieving + documents. + :param routing: Custom value used to route operations to a specific shard. + :param source: True or false to return the `_source` field or not, or a list + of fields to return. + :param source_excludes: A comma-separated list of source fields to exclude from + the response. You can also use this parameter to exclude fields from the + subset specified in `_source_includes` query parameter. + :param source_includes: A comma-separated list of source fields to include in + the response. If this parameter is specified, only these source fields are + returned. You can exclude fields from this subset using the `_source_excludes` + query parameter. If the `_source` parameter is `false`, this parameter is + ignored. + :param stored_fields: If `true`, retrieves the document fields stored in the + index rather than the document `_source`. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_mget" + else: + __path = "/_mget" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if docs is not None: + __body["docs"] = docs + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ids is not None: + __body["ids"] = ids + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="searches", + ) + async def msearch( + self, + *, + searches: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + max_concurrent_searches: t.Optional[int] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + pre_filter_shard_size: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to execute several search operations in one request. + + ``_ + + :param searches: + :param index: Comma-separated list of data streams, indices, and index aliases + to search. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param ccs_minimize_roundtrips: If true, network roundtrips between the coordinating + node and remote clusters are minimized for cross-cluster search requests. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. + :param ignore_throttled: If true, concrete, expanded or aliased indices are ignored + when frozen. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param max_concurrent_searches: Maximum number of concurrent searches the multi + search API can execute. + :param max_concurrent_shard_requests: Maximum number of concurrent shard requests + that each sub-search request executes per node. + :param pre_filter_shard_size: Defines a threshold that enforces a pre-filter + roundtrip to prefilter search shards based on query rewriting if the number + of shards the search request expands to exceeds the threshold. This filter + roundtrip can limit the number of shards significantly if for instance a + shard can not match any documents based on its rewrite method i.e., if date + filters are mandatory to match but the shard bounds and the query are disjoint. + :param rest_total_hits_as_int: If true, hits.total are returned as an integer + in the response. Defaults to false, which returns an object. + :param routing: Custom routing value used to route search operations to a specific + shard. + :param search_type: Indicates whether global term and document frequencies should + be used when scoring returned documents. + :param typed_keys: Specifies whether aggregation and suggester names should be + prefixed by their respective types in the response. + """ + if searches is None: + raise ValueError("Empty value passed for parameter 'searches'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_msearch" + else: + __path = "/_msearch" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if max_concurrent_searches is not None: + __query["max_concurrent_searches"] = max_concurrent_searches + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if search_type is not None: + __query["search_type"] = search_type + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __body = searches + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="search_templates", + ) + async def msearch_template( + self, + *, + search_templates: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_concurrent_searches: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to execute several search template operations in one request. + + ``_ + + :param search_templates: + :param index: A comma-separated list of index names to use as default + :param ccs_minimize_roundtrips: Indicates whether network round-trips should + be minimized as part of cross-cluster search requests execution + :param max_concurrent_searches: Controls the maximum number of concurrent searches + the multi search api will execute + :param rest_total_hits_as_int: Indicates whether hits.total should be rendered + as an integer or an object in the rest search response + :param search_type: Search operation type + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + """ + if search_templates is None: + raise ValueError("Empty value passed for parameter 'search_templates'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_msearch/template" + else: + __path = "/_msearch/template" + __query: t.Dict[str, t.Any] = {} + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_concurrent_searches is not None: + __query["max_concurrent_searches"] = max_concurrent_searches + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if search_type is not None: + __query["search_type"] = search_type + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __body = search_templates + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def mtermvectors( + self, + *, + index: t.Optional[str] = None, + docs: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + field_statistics: t.Optional[bool] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ids: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + offsets: t.Optional[bool] = None, + payloads: t.Optional[bool] = None, + positions: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + routing: t.Optional[str] = None, + term_statistics: t.Optional[bool] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns multiple termvectors in one request. + + ``_ + + :param index: The index in which the document resides. + :param docs: + :param field_statistics: Specifies if document count, sum of document frequencies + and sum of total term frequencies should be returned. Applies to all returned + documents unless otherwise specified in body "params" or "docs". + :param fields: A comma-separated list of fields to return. Applies to all returned + documents unless otherwise specified in body "params" or "docs". + :param ids: + :param offsets: Specifies if term offsets should be returned. Applies to all + returned documents unless otherwise specified in body "params" or "docs". + :param payloads: Specifies if term payloads should be returned. Applies to all + returned documents unless otherwise specified in body "params" or "docs". + :param positions: Specifies if term positions should be returned. Applies to + all returned documents unless otherwise specified in body "params" or "docs". + :param preference: Specify the node or shard the operation should be performed + on (default: random) .Applies to all returned documents unless otherwise + specified in body "params" or "docs". + :param realtime: Specifies if requests are real-time as opposed to near-real-time + (default: true). + :param routing: Specific routing value. Applies to all returned documents unless + otherwise specified in body "params" or "docs". + :param term_statistics: Specifies if total term frequency and document frequency + should be returned. Applies to all returned documents unless otherwise specified + in body "params" or "docs". + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_mtermvectors" + else: + __path = "/_mtermvectors" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if docs is not None: + __body["docs"] = docs + if error_trace is not None: + __query["error_trace"] = error_trace + if field_statistics is not None: + __query["field_statistics"] = field_statistics + if fields is not None: + __query["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ids is not None: + __body["ids"] = ids + if offsets is not None: + __query["offsets"] = offsets + if payloads is not None: + __query["payloads"] = payloads + if positions is not None: + __query["positions"] = positions + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if routing is not None: + __query["routing"] = routing + if term_statistics is not None: + __query["term_statistics"] = term_statistics + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def open_point_in_time( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + keep_alive: t.Union["t.Literal[-1]", "t.Literal[0]", str], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + routing: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Open a point in time that can be used in subsequent searches + + ``_ + + :param index: A comma-separated list of index names to open point in time; use + `_all` or empty string to perform the operation on all indices + :param keep_alive: Specific the time to live for the point in time + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param routing: Specific routing value + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if keep_alive is None: + raise ValueError("Empty value passed for parameter 'keep_alive'") + __path = f"/{_quote(index)}/_pit" + __query: t.Dict[str, t.Any] = {} + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if routing is not None: + __query["routing"] = routing + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_script( + self, + *, + id: str, + script: t.Mapping[str, t.Any], + context: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a script. + + ``_ + + :param id: Script ID + :param script: + :param context: Script context + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if script is None: + raise ValueError("Empty value passed for parameter 'script'") + if id not in SKIP_IN_PATH and context not in SKIP_IN_PATH: + __path = f"/_scripts/{_quote(id)}/{_quote(context)}" + elif id not in SKIP_IN_PATH: + __path = f"/_scripts/{_quote(id)}" + else: + raise ValueError("Couldn't find a path for the given parameters") + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if script is not None: + __body["script"] = script + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def rank_eval( + self, + *, + requests: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + metric: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + search_type: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to evaluate the quality of ranked search results over a set of typical + search queries + + ``_ + + :param requests: A set of typical search requests, together with their provided + ratings. + :param index: Comma-separated list of data streams, indices, and index aliases + used to limit the request. Wildcard (`*`) expressions are supported. To target + all data streams and indices in a cluster, omit this parameter or use `_all` + or `*`. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with `foo` but no index starts with `bar`. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: If `true`, missing or closed indices are not included + in the response. + :param metric: Definition of the evaluation metric to calculate. + :param search_type: Search operation type + """ + if requests is None: + raise ValueError("Empty value passed for parameter 'requests'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_rank_eval" + else: + __path = "/_rank_eval" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if requests is not None: + __body["requests"] = requests + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if metric is not None: + __body["metric"] = metric + if pretty is not None: + __query["pretty"] = pretty + if search_type is not None: + __query["search_type"] = search_type + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params"}, + ) + async def render_search_template( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + file: t.Optional[str] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + source: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to use the Mustache language to pre-render a search definition. + + ``_ + + :param id: The id of the stored search template + :param file: + :param params: + :param source: + """ + if id not in SKIP_IN_PATH: + __path = f"/_render/template/{_quote(id)}" + else: + __path = "/_render/template" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if file is not None: + __body["file"] = file + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if params is not None: + __body["params"] = params + if pretty is not None: + __query["pretty"] = pretty + if source is not None: + __body["source"] = source + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def scripts_painless_execute( + self, + *, + context: t.Optional[str] = None, + context_setup: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + script: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows an arbitrary script to be executed and a result to be returned + + ``_ + + :param context: + :param context_setup: + :param script: + """ + __path = "/_scripts/painless/_execute" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if context is not None: + __body["context"] = context + if context_setup is not None: + __body["context_setup"] = context_setup + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if script is not None: + __body["script"] = script + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def scroll( + self, + *, + scroll_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to retrieve a large numbers of results from a single search request. + + ``_ + + :param scroll_id: Scroll ID of the search. + :param rest_total_hits_as_int: If true, the API response’s hit.total property + is returned as an integer. If false, the API response’s hit.total property + is returned as an object. + :param scroll: Period to retain the search context for scrolling. + """ + if scroll_id is None: + raise ValueError("Empty value passed for parameter 'scroll_id'") + __path = "/_search/scroll" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if scroll_id is not None: + __body["scroll_id"] = scroll_id + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if scroll is not None: + __body["scroll"] = scroll + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + "from": "from_", + }, + ) + async def search( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + batched_reduce_size: t.Optional[int] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + collapse: t.Optional[t.Mapping[str, t.Any]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + docvalue_fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + ext: t.Optional[t.Mapping[str, t.Any]] = None, + fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + highlight: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indices_boost: t.Optional[ + t.Union[t.List[t.Mapping[str, float]], t.Tuple[t.Mapping[str, float], ...]] + ] = None, + knn: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + lenient: t.Optional[bool] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + min_compatible_shard_node: t.Optional[str] = None, + min_score: t.Optional[float] = None, + pit: t.Optional[t.Mapping[str, t.Any]] = None, + post_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pre_filter_shard_size: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + rank: t.Optional[t.Mapping[str, t.Any]] = None, + request_cache: t.Optional[bool] = None, + rescore: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + seq_no_primary_term: t.Optional[bool] = None, + size: t.Optional[int] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + suggest: t.Optional[t.Mapping[str, t.Any]] = None, + suggest_field: t.Optional[str] = None, + suggest_mode: t.Optional[ + t.Union["t.Literal['always', 'missing', 'popular']", str] + ] = None, + suggest_size: t.Optional[int] = None, + suggest_text: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[str] = None, + track_scores: t.Optional[bool] = None, + track_total_hits: t.Optional[t.Union[bool, int]] = None, + typed_keys: t.Optional[bool] = None, + version: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns results matching a query. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param aggregations: + :param aggs: + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param allow_partial_search_results: Indicate if an error should be returned + if there is a partial search failure or timeout + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param batched_reduce_size: The number of shard results that should be reduced + at once on the coordinating node. This value should be used as a protection + mechanism to reduce the memory overhead per search request if the potential + number of shards in the request can be large. + :param ccs_minimize_roundtrips: Indicates whether network round-trips should + be minimized as part of cross-cluster search requests execution + :param collapse: + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc + values for field names matching these patterns in the hits.fields property + of the response. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: If true, returns detailed information about score computation + as part of a hit. + :param ext: Configuration of search extensions defined by Elasticsearch plugins. + :param fields: Array of wildcard (*) patterns. The request returns values for + field names matching these patterns in the hits.fields property of the response. + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the search_after parameter. + :param highlight: + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param indices_boost: Boosts the _score of documents from specified indices. + :param knn: Defines the approximate kNN search to run. + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_concurrent_shard_requests: The number of concurrent shard requests + per node this search executes concurrently. This value should be used to + limit the impact of the search on the cluster in order to limit the number + of concurrent shard requests + :param min_compatible_shard_node: The minimum compatible version that all shards + involved in search should have for this request to be successful + :param min_score: Minimum _score for matching documents. Documents with a lower + _score are not included in the search results. + :param pit: Limits the search to a point in time (PIT). If you provide a PIT, + you cannot specify an in the request path. + :param post_filter: + :param pre_filter_shard_size: A threshold that enforces a pre-filter roundtrip + to prefilter search shards based on query rewriting if the number of shards + the search request expands to exceeds the threshold. This filter roundtrip + can limit the number of shards significantly if for instance a shard can + not match any documents based on its rewrite method ie. if date filters are + mandatory to match but the shard bounds and the query are disjoint. + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param profile: + :param q: Query in the Lucene query string syntax + :param query: Defines the search definition using the Query DSL. + :param rank: Defines the Reciprocal Rank Fusion (RRF) to use + :param request_cache: Specify if request cache should be used for this request + or not, defaults to index level setting + :param rescore: + :param rest_total_hits_as_int: Indicates whether hits.total should be rendered + as an integer or an object in the rest search response + :param routing: A comma-separated list of specific routing values + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param script_fields: Retrieve a script evaluation (based on different fields) + for each hit. + :param scroll: Specify how long a consistent view of the index should be maintained + for scrolled search + :param search_after: + :param search_type: Search operation type + :param seq_no_primary_term: If true, returns sequence number and primary term + of the last modification of each hit. See Optimistic concurrency control. + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the from and size parameters. To page through + more hits, use the search_after parameter. + :param slice: + :param sort: + :param source: Indicates which source fields are returned for matching documents. + These fields are returned in the hits._source property of the search response. + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param stats: Stats groups to associate with the search. Each group maintains + a statistics aggregation for its associated searches. You can retrieve these + stats using the indices stats API. + :param stored_fields: List of stored fields to return as part of a hit. If no + fields are specified, no stored fields are included in the response. If this + field is specified, the _source parameter defaults to false. You can pass + _source: true to return both source fields and stored fields in the search + response. + :param suggest: + :param suggest_field: Specifies which field to use for suggestions. + :param suggest_mode: Specify suggest mode + :param suggest_size: How many suggestions to return in response + :param suggest_text: The source text for which the suggestions should be returned. + :param terminate_after: Maximum number of documents to collect for each shard. + If a query reaches this limit, Elasticsearch terminates the query early. + Elasticsearch collects documents before sorting. Defaults to 0, which does + not terminate query execution early. + :param timeout: Specifies the period of time to wait for a response from each + shard. If no response is received before the timeout expires, the request + fails and returns an error. Defaults to no timeout. + :param track_scores: If true, calculate and return document scores, even if the + scores are not used for sorting. + :param track_total_hits: Number of hits matching the query to count accurately. + If true, the exact number of hits is returned at the cost of some performance. + If false, the response does not include the total number of hits matching + the query. Defaults to 10,000 hits. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + :param version: If true, returns document version as part of a hit. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_search" + else: + __path = "/_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if batched_reduce_size is not None: + __query["batched_reduce_size"] = batched_reduce_size + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if collapse is not None: + __body["collapse"] = collapse + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if docvalue_fields is not None: + __body["docvalue_fields"] = docvalue_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if ext is not None: + __body["ext"] = ext + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if highlight is not None: + __body["highlight"] = highlight + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indices_boost is not None: + __body["indices_boost"] = indices_boost + if knn is not None: + __body["knn"] = knn + if lenient is not None: + __query["lenient"] = lenient + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if min_compatible_shard_node is not None: + __query["min_compatible_shard_node"] = min_compatible_shard_node + if min_score is not None: + __body["min_score"] = min_score + if pit is not None: + __body["pit"] = pit + if post_filter is not None: + __body["post_filter"] = post_filter + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if rank is not None: + __body["rank"] = rank + if request_cache is not None: + __query["request_cache"] = request_cache + if rescore is not None: + __body["rescore"] = rescore + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll is not None: + __query["scroll"] = scroll + if search_after is not None: + __body["search_after"] = search_after + if search_type is not None: + __query["search_type"] = search_type + if seq_no_primary_term is not None: + __body["seq_no_primary_term"] = seq_no_primary_term + if size is not None: + __body["size"] = size + if slice is not None: + __body["slice"] = slice + if sort is not None: + __body["sort"] = sort + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stats is not None: + __body["stats"] = stats + if stored_fields is not None: + __body["stored_fields"] = stored_fields + if suggest is not None: + __body["suggest"] = suggest + if suggest_field is not None: + __query["suggest_field"] = suggest_field + if suggest_mode is not None: + __query["suggest_mode"] = suggest_mode + if suggest_size is not None: + __query["suggest_size"] = suggest_size + if suggest_text is not None: + __query["suggest_text"] = suggest_text + if terminate_after is not None: + __body["terminate_after"] = terminate_after + if timeout is not None: + __body["timeout"] = timeout + if track_scores is not None: + __body["track_scores"] = track_scores + if track_total_hits is not None: + __body["track_total_hits"] = track_total_hits + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if version is not None: + __body["version"] = version + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params"}, + ) + async def search_template( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + id: t.Optional[str] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + source: t.Optional[str] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to use the Mustache language to pre-render a search definition. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases to search. + Supports wildcards (*). + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param ccs_minimize_roundtrips: Indicates whether network round-trips should + be minimized as part of cross-cluster search requests execution + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: + :param id: ID of the search template to use. If no source is specified, this + parameter is required. + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param params: + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param profile: + :param rest_total_hits_as_int: If true, hits.total are rendered as an integer + in the response. + :param routing: Custom value used to route operations to a specific shard. + :param scroll: Specifies how long a consistent view of the index should be maintained + for scrolled search. + :param search_type: The type of the search operation. + :param source: An inline search template. Supports the same parameters as the + search API's request body. Also supports Mustache variables. If no id is + specified, this parameter is required. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_search/template" + else: + __path = "/_search/template" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if id is not None: + __body["id"] = id + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if params is not None: + __body["params"] = params + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if scroll is not None: + __query["scroll"] = scroll + if search_type is not None: + __query["search_type"] = search_type + if source is not None: + __body["source"] = source + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def terms_enum( + self, + *, + index: str, + field: str, + case_insensitive: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + index_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + search_after: t.Optional[str] = None, + size: t.Optional[int] = None, + string: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + The terms enum API can be used to discover terms in the index that begin with + the provided string. It is designed for low-latency look-ups used in auto-complete + scenarios. + + ``_ + + :param index: Comma-separated list of data streams, indices, and index aliases + to search. Wildcard (*) expressions are supported. + :param field: The string to match at the start of indexed terms. If not provided, + all terms in the field are considered. + :param case_insensitive: When true the provided search string is matched against + index terms without case sensitivity. + :param index_filter: Allows to filter an index shard if the provided query rewrites + to match_none. + :param search_after: + :param size: How many matching terms to return. + :param string: The string after which terms in the index should be returned. + Allows for a form of pagination if the last result from one request is passed + as the search_after parameter for a subsequent request. + :param timeout: The maximum length of time to spend collecting results. Defaults + to "1s" (one second). If the timeout is exceeded the complete flag set to + false in the response and the results may be partial or empty. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if field is None: + raise ValueError("Empty value passed for parameter 'field'") + __path = f"/{_quote(index)}/_terms_enum" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if field is not None: + __body["field"] = field + if case_insensitive is not None: + __body["case_insensitive"] = case_insensitive + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if index_filter is not None: + __body["index_filter"] = index_filter + if pretty is not None: + __query["pretty"] = pretty + if search_after is not None: + __body["search_after"] = search_after + if size is not None: + __body["size"] = size + if string is not None: + __body["string"] = string + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def termvectors( + self, + *, + index: str, + id: t.Optional[str] = None, + doc: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + field_statistics: t.Optional[bool] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + offsets: t.Optional[bool] = None, + payloads: t.Optional[bool] = None, + per_field_analyzer: t.Optional[t.Mapping[str, str]] = None, + positions: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + routing: t.Optional[str] = None, + term_statistics: t.Optional[bool] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information and statistics about terms in the fields of a particular + document. + + ``_ + + :param index: The index in which the document resides. + :param id: The id of the document, when not specified a doc param should be supplied. + :param doc: + :param field_statistics: Specifies if document count, sum of document frequencies + and sum of total term frequencies should be returned. + :param fields: A comma-separated list of fields to return. + :param filter: + :param offsets: Specifies if term offsets should be returned. + :param payloads: Specifies if term payloads should be returned. + :param per_field_analyzer: + :param positions: Specifies if term positions should be returned. + :param preference: Specify the node or shard the operation should be performed + on (default: random). + :param realtime: Specifies if request is real-time as opposed to near-real-time + (default: true). + :param routing: Specific routing value. + :param term_statistics: Specifies if total term frequency and document frequency + should be returned. + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if index not in SKIP_IN_PATH and id not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_termvectors/{_quote(id)}" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_termvectors" + else: + raise ValueError("Couldn't find a path for the given parameters") + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if doc is not None: + __body["doc"] = doc + if error_trace is not None: + __query["error_trace"] = error_trace + if field_statistics is not None: + __query["field_statistics"] = field_statistics + if fields is not None: + __query["fields"] = fields + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if offsets is not None: + __query["offsets"] = offsets + if payloads is not None: + __query["payloads"] = payloads + if per_field_analyzer is not None: + __body["per_field_analyzer"] = per_field_analyzer + if positions is not None: + __query["positions"] = positions + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if routing is not None: + __query["routing"] = routing + if term_statistics is not None: + __query["term_statistics"] = term_statistics + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + async def update( + self, + *, + index: str, + id: str, + detect_noop: t.Optional[bool] = None, + doc: t.Optional[t.Mapping[str, t.Any]] = None, + doc_as_upsert: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + lang: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + require_alias: t.Optional[bool] = None, + retry_on_conflict: t.Optional[int] = None, + routing: t.Optional[str] = None, + script: t.Optional[t.Mapping[str, t.Any]] = None, + scripted_upsert: t.Optional[bool] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + upsert: t.Optional[t.Mapping[str, t.Any]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates a document with a script or partial document. + + ``_ + + :param index: The name of the index + :param id: Document ID + :param detect_noop: Set to false to disable setting 'result' in the response + to 'noop' if no change to the document occurred. + :param doc: A partial update to an existing document. + :param doc_as_upsert: Set to true to use the contents of 'doc' as the value of + 'upsert' + :param if_primary_term: Only perform the operation if the document has this primary + term. + :param if_seq_no: Only perform the operation if the document has this sequence + number. + :param lang: The script language. + :param refresh: If 'true', Elasticsearch refreshes the affected shards to make + this operation visible to search, if 'wait_for' then wait for a refresh to + make this operation visible to search, if 'false' do nothing with refreshes. + :param require_alias: If true, the destination must be an index alias. + :param retry_on_conflict: Specify how many times should the operation be retried + when a conflict occurs. + :param routing: Custom value used to route operations to a specific shard. + :param script: Script to execute to update the document. + :param scripted_upsert: Set to true to execute the script whether or not the + document exists. + :param source: Set to false to disable source retrieval. You can also specify + a comma-separated list of the fields you want to retrieve. + :param source_excludes: Specify the source fields you want to exclude. + :param source_includes: Specify the source fields you want to retrieve. + :param timeout: Period to wait for dynamic mapping updates and active shards. + This guarantees Elasticsearch waits for at least the timeout before failing. + The actual wait time could be longer, particularly when multiple waits occur. + :param upsert: If the document does not already exist, the contents of 'upsert' + are inserted as a new document. If the document exists, the 'script' is executed. + :param wait_for_active_shards: The number of shard copies that must be active + before proceeding with the operations. Set to 'all' or any positive integer + up to the total number of shards in the index (number_of_replicas+1). Defaults + to 1 meaning the primary shard. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_update/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if detect_noop is not None: + __body["detect_noop"] = detect_noop + if doc is not None: + __body["doc"] = doc + if doc_as_upsert is not None: + __body["doc_as_upsert"] = doc_as_upsert + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if lang is not None: + __query["lang"] = lang + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if require_alias is not None: + __query["require_alias"] = require_alias + if retry_on_conflict is not None: + __query["retry_on_conflict"] = retry_on_conflict + if routing is not None: + __query["routing"] = routing + if script is not None: + __body["script"] = script + if scripted_upsert is not None: + __body["scripted_upsert"] = scripted_upsert + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if timeout is not None: + __query["timeout"] = timeout + if upsert is not None: + __body["upsert"] = upsert + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + async def update_by_query( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + conflicts: t.Optional[t.Union["t.Literal['abort', 'proceed']", str]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + max_docs: t.Optional[int] = None, + pipeline: t.Optional[str] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + refresh: t.Optional[bool] = None, + request_cache: t.Optional[bool] = None, + requests_per_second: t.Optional[float] = None, + routing: t.Optional[str] = None, + script: t.Optional[t.Mapping[str, t.Any]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + scroll_size: t.Optional[int] = None, + search_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + slices: t.Optional[t.Union[int, t.Union["t.Literal['auto']", str]]] = None, + sort: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[bool] = None, + version_type: t.Optional[bool] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Performs an update on every document in the index without changing the source, + for example to pick up a mapping change. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param conflicts: + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param from_: Starting offset (default: 0) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_docs: + :param pipeline: Ingest pipeline to set on index requests made by this action. + (default: none) + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param query: + :param refresh: Should the affected indexes be refreshed? + :param request_cache: Specify if request cache should be used for this request + or not, defaults to index level setting + :param requests_per_second: The throttle to set on this request in sub-requests + per second. -1 means no throttle. + :param routing: A comma-separated list of specific routing values + :param script: + :param scroll: Specify how long a consistent view of the index should be maintained + for scrolled search + :param scroll_size: Size on the scroll request powering the update by query + :param search_timeout: Explicit timeout for each search request. Defaults to + no timeout. + :param search_type: Search operation type + :param slice: + :param slices: The number of slices this task should be divided into. Defaults + to 1, meaning the task isn't sliced into subtasks. Can be set to `auto`. + :param sort: A comma-separated list of : pairs + :param stats: Specific 'tag' of the request for logging and statistical purposes + :param terminate_after: The maximum number of documents to collect for each shard, + upon reaching which the query execution will terminate early. + :param timeout: Time each individual bulk request should wait for shards that + are unavailable. + :param version: Specify whether to return document version as part of a hit + :param version_type: Should the document increment the version number (internal) + on hit or not (reindex) + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the update by query operation. Defaults to 1, meaning + the primary shard only. Set to `all` for all shard copies, otherwise set + to any non-negative value less than or equal to the total number of copies + for the shard (number of replicas + 1) + :param wait_for_completion: Should the request should block until the update + by query operation is complete. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_update_by_query" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if conflicts is not None: + __body["conflicts"] = conflicts + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if max_docs is not None: + __body["max_docs"] = max_docs + if pipeline is not None: + __query["pipeline"] = pipeline + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if refresh is not None: + __query["refresh"] = refresh + if request_cache is not None: + __query["request_cache"] = request_cache + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second + if routing is not None: + __query["routing"] = routing + if script is not None: + __body["script"] = script + if scroll is not None: + __query["scroll"] = scroll + if scroll_size is not None: + __query["scroll_size"] = scroll_size + if search_timeout is not None: + __query["search_timeout"] = search_timeout + if search_type is not None: + __query["search_type"] = search_type + if slice is not None: + __body["slice"] = slice + if slices is not None: + __query["slices"] = slices + if sort is not None: + __query["sort"] = sort + if stats is not None: + __query["stats"] = stats + if terminate_after is not None: + __query["terminate_after"] = terminate_after + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/_base.py b/elasticsearch_serverless/_async/client/_base.py new file mode 100644 index 0000000..ea969b6 --- /dev/null +++ b/elasticsearch_serverless/_async/client/_base.py @@ -0,0 +1,391 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import re +import warnings +from typing import ( + Any, + Callable, + Collection, + Dict, + Iterable, + List, + Mapping, + Optional, + Tuple, + Union, +) + +from elastic_transport import ( + ApiResponse, + AsyncTransport, + BinaryApiResponse, + HeadApiResponse, + HttpHeaders, + ListApiResponse, + NodeConfig, + ObjectApiResponse, + SniffOptions, + TextApiResponse, +) +from elastic_transport.client_utils import DEFAULT, DefaultType + +from ..._version import __versionstr__ +from ...compat import warn_stacklevel +from ...exceptions import ( + HTTP_EXCEPTIONS, + ApiError, + ConnectionError, + ElasticsearchWarning, + SerializationError, + UnsupportedProductError, +) +from .utils import _TYPE_ASYNC_SNIFF_CALLBACK, _base64_auth_header, _quote_query + +_WARNING_RE = re.compile(r"\"([^\"]*)\"") +_COMPAT_MIMETYPE_TEMPLATE = "application/vnd.elasticsearch+%s; compatible-with=" + str( + __versionstr__.partition(".")[0] +) +_COMPAT_MIMETYPE_RE = re.compile(r"application/(json|x-ndjson|vnd\.mapbox-vector-tile)") +_COMPAT_MIMETYPE_SUB = _COMPAT_MIMETYPE_TEMPLATE % (r"\g<1>",) + + +def resolve_auth_headers( + headers: Optional[Mapping[str, str]], + http_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT, + api_key: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT, + basic_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT, + bearer_auth: Union[DefaultType, None, str] = DEFAULT, +) -> HttpHeaders: + if headers is None: + headers = HttpHeaders() + elif not isinstance(headers, HttpHeaders): + headers = HttpHeaders(headers) + + resolved_http_auth = http_auth if http_auth is not DEFAULT else None + resolved_basic_auth = basic_auth if basic_auth is not DEFAULT else None + if resolved_http_auth is not None: + if resolved_basic_auth is not None: + raise ValueError( + "Can't specify both 'http_auth' and 'basic_auth', " + "instead only specify 'basic_auth'" + ) + if isinstance(http_auth, str) or ( + isinstance(resolved_http_auth, (list, tuple)) + and all(isinstance(x, str) for x in resolved_http_auth) + ): + resolved_basic_auth = resolved_http_auth + else: + raise TypeError( + "The deprecated 'http_auth' parameter must be either 'Tuple[str, str]' or 'str'. " + "Use either the 'basic_auth' parameter instead" + ) + + warnings.warn( + "The 'http_auth' parameter is deprecated. " + "Use 'basic_auth' or 'bearer_auth' parameters instead", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + + resolved_api_key = api_key if api_key is not DEFAULT else None + resolved_bearer_auth = bearer_auth if bearer_auth is not DEFAULT else None + if resolved_api_key or resolved_basic_auth or resolved_bearer_auth: + if ( + sum( + x is not None + for x in ( + resolved_api_key, + resolved_basic_auth, + resolved_bearer_auth, + ) + ) + > 1 + ): + raise ValueError( + "Can only set one of 'api_key', 'basic_auth', and 'bearer_auth'" + ) + if headers and headers.get("authorization", None) is not None: + raise ValueError( + "Can't set 'Authorization' HTTP header with other authentication options" + ) + if resolved_api_key: + headers["authorization"] = f"ApiKey {_base64_auth_header(resolved_api_key)}" + if resolved_basic_auth: + headers[ + "authorization" + ] = f"Basic {_base64_auth_header(resolved_basic_auth)}" + if resolved_bearer_auth: + headers["authorization"] = f"Bearer {resolved_bearer_auth}" + + return headers + + +def create_sniff_callback( + host_info_callback: Optional[ + Callable[[Dict[str, Any], Dict[str, Any]], Optional[Dict[str, Any]]] + ] = None, + sniffed_node_callback: Optional[ + Callable[[Dict[str, Any], NodeConfig], Optional[NodeConfig]] + ] = None, +) -> _TYPE_ASYNC_SNIFF_CALLBACK: + assert (host_info_callback is None) != (sniffed_node_callback is None) + + # Wrap the deprecated 'host_info_callback' into 'sniffed_node_callback' + if host_info_callback is not None: + + def _sniffed_node_callback( + node_info: Dict[str, Any], node_config: NodeConfig + ) -> Optional[NodeConfig]: + assert host_info_callback is not None + if ( + host_info_callback( # type ignore[misc] + node_info, {"host": node_config.host, "port": node_config.port} + ) + is None + ): + return None + return node_config + + sniffed_node_callback = _sniffed_node_callback + + async def sniff_callback( + transport: AsyncTransport, sniff_options: SniffOptions + ) -> List[NodeConfig]: + for _ in transport.node_pool.all(): + try: + meta, node_infos = await transport.perform_request( + "GET", + "/_nodes/_all/http", + headers={ + "accept": "application/vnd.elasticsearch+json; compatible-with=8" + }, + request_timeout=( + sniff_options.sniff_timeout + if not sniff_options.is_initial_sniff + else None + ), + ) + except (SerializationError, ConnectionError): + continue + + if not 200 <= meta.status <= 299: + continue + + node_configs = [] + for node_info in node_infos.get("nodes", {}).values(): + address = node_info.get("http", {}).get("publish_address") + if not address or ":" not in address: + continue + + if "/" in address: + # Support 7.x host/ip:port behavior where http.publish_host has been set. + fqdn, ipaddress = address.split("/", 1) + host = fqdn + _, port_str = ipaddress.rsplit(":", 1) + port = int(port_str) + else: + host, port_str = address.rsplit(":", 1) + port = int(port_str) + + assert sniffed_node_callback is not None + sniffed_node = sniffed_node_callback( + node_info, meta.node.replace(host=host, port=port) + ) + if sniffed_node is None: + continue + + # Use the node which was able to make the request as a base. + node_configs.append(sniffed_node) + + if node_configs: + return node_configs + + return [] + + return sniff_callback + + +def _default_sniffed_node_callback( + node_info: Dict[str, Any], node_config: NodeConfig +) -> Optional[NodeConfig]: + if node_info.get("roles", []) == ["master"]: + return None + return node_config + + +default_sniff_callback = create_sniff_callback( + sniffed_node_callback=_default_sniffed_node_callback +) + + +class BaseClient: + def __init__(self, _transport: AsyncTransport) -> None: + self._transport = _transport + self._client_meta: Union[DefaultType, Tuple[Tuple[str, str], ...]] = DEFAULT + self._headers = HttpHeaders() + self._request_timeout: Union[DefaultType, Optional[float]] = DEFAULT + self._ignore_status: Union[DefaultType, Collection[int]] = DEFAULT + self._max_retries: Union[DefaultType, int] = DEFAULT + self._retry_on_timeout: Union[DefaultType, bool] = DEFAULT + self._retry_on_status: Union[DefaultType, Collection[int]] = DEFAULT + self._verified_elasticsearch = False + + @property + def transport(self) -> AsyncTransport: + return self._transport + + async def perform_request( + self, + method: str, + path: str, + *, + params: Optional[Mapping[str, Any]] = None, + headers: Optional[Mapping[str, str]] = None, + body: Optional[Any] = None, + ) -> ApiResponse[Any]: + if headers: + request_headers = self._headers.copy() + request_headers.update(headers) + else: + request_headers = self._headers + + def mimetype_header_to_compat(header: str) -> None: + # Converts all parts of a Accept/Content-Type headers + # from application/X -> application/vnd.elasticsearch+X + nonlocal request_headers + mimetype = request_headers.get(header, None) + if mimetype: + request_headers[header] = _COMPAT_MIMETYPE_RE.sub( + _COMPAT_MIMETYPE_SUB, mimetype + ) + + mimetype_header_to_compat("Accept") + mimetype_header_to_compat("Content-Type") + + if params: + target = f"{path}?{_quote_query(params)}" + else: + target = path + + meta, resp_body = await self.transport.perform_request( + method, + target, + headers=request_headers, + body=body, + request_timeout=self._request_timeout, + max_retries=self._max_retries, + retry_on_status=self._retry_on_status, + retry_on_timeout=self._retry_on_timeout, + client_meta=self._client_meta, + ) + + # HEAD with a 404 is returned as a normal response + # since this is used as an 'exists' functionality. + if not (method == "HEAD" and meta.status == 404) and ( + not 200 <= meta.status < 299 + and ( + self._ignore_status is DEFAULT + or self._ignore_status is None + or meta.status not in self._ignore_status + ) + ): + message = str(resp_body) + + # If the response is an error response try parsing + # the raw Elasticsearch error before raising. + if isinstance(resp_body, dict): + try: + error = resp_body.get("error", message) + if isinstance(error, dict) and "type" in error: + error = error["type"] + message = error + except (ValueError, KeyError, TypeError): + pass + + raise HTTP_EXCEPTIONS.get(meta.status, ApiError)( + message=message, meta=meta, body=resp_body + ) + + # 'X-Elastic-Product: Elasticsearch' should be on every 2XX response. + if not self._verified_elasticsearch: + # If the header is set we mark the server as verified. + if meta.headers.get("x-elastic-product", "") == "Elasticsearch": + self._verified_elasticsearch = True + # Otherwise we only raise an error on 2XX responses. + elif meta.status >= 200 and meta.status < 300: + raise UnsupportedProductError( + message=( + "The client noticed that the server is not Elasticsearch " + "and we do not support this unknown product" + ), + meta=meta, + body=resp_body, + ) + + # 'Warning' headers should be reraised as 'ElasticsearchWarning' + if "warning" in meta.headers: + warning_header = (meta.headers.get("warning") or "").strip() + warning_messages: Iterable[str] = _WARNING_RE.findall(warning_header) or ( + warning_header, + ) + stacklevel = warn_stacklevel() + for warning_message in warning_messages: + warnings.warn( + warning_message, + category=ElasticsearchWarning, + stacklevel=stacklevel, + ) + + if method == "HEAD": + response = HeadApiResponse(meta=meta) + elif isinstance(resp_body, dict): + response = ObjectApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + elif isinstance(resp_body, list): + response = ListApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + elif isinstance(resp_body, str): + response = TextApiResponse( # type: ignore[assignment] + body=resp_body, + meta=meta, + ) + elif isinstance(resp_body, bytes): + response = BinaryApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + else: + response = ApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + + return response + + +class NamespacedClient(BaseClient): + def __init__(self, client: "BaseClient") -> None: + self._client = client + super().__init__(self._client.transport) + + async def perform_request( + self, + method: str, + path: str, + *, + params: Optional[Mapping[str, Any]] = None, + headers: Optional[Mapping[str, str]] = None, + body: Optional[Any] = None, + ) -> ApiResponse[Any]: + # Use the internal clients .perform_request() implementation + # so we take advantage of their transport options. + return await self._client.perform_request( + method, path, params=params, headers=headers, body=body + ) diff --git a/elasticsearch_serverless/_async/client/async_search.py b/elasticsearch_serverless/_async/client/async_search.py new file mode 100644 index 0000000..bee44a1 --- /dev/null +++ b/elasticsearch_serverless/_async/client/async_search.py @@ -0,0 +1,603 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class AsyncSearchClient(NamespacedClient): + @_rewrite_parameters() + async def delete( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an async search by ID. If the search is still running, the search request + will be cancelled. Otherwise, the saved search results are deleted. + + ``_ + + :param id: A unique identifier for the async search. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_async_search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + pretty: t.Optional[bool] = None, + typed_keys: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the results of a previously submitted async search request given its + ID. + + ``_ + + :param id: A unique identifier for the async search. + :param keep_alive: Specifies how long the async search should be available in + the cluster. When not specified, the `keep_alive` set with the corresponding + submit async request will be used. Otherwise, it is possible to override + the value and extend the validity of the request. When this period expires, + the search, if still running, is cancelled. If the search is completed, its + saved results are deleted. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + :param wait_for_completion_timeout: Specifies to wait for the search to be completed + up until the provided timeout. Final results will be returned if available + before the timeout expires, otherwise the currently available results will + be returned once the timeout expires. By default no timeout is set meaning + that the currently available results will be returned without any additional + wait. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_async_search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if pretty is not None: + __query["pretty"] = pretty + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def status( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the status of a previously submitted async search request given its + ID. + + ``_ + + :param id: A unique identifier for the async search. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_async_search/status/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + "from": "from_", + }, + ) + async def submit( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + batched_reduce_size: t.Optional[int] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + collapse: t.Optional[t.Mapping[str, t.Any]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + docvalue_fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + ext: t.Optional[t.Mapping[str, t.Any]] = None, + fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + highlight: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indices_boost: t.Optional[ + t.Union[t.List[t.Mapping[str, float]], t.Tuple[t.Mapping[str, float], ...]] + ] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + keep_on_completion: t.Optional[bool] = None, + knn: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + lenient: t.Optional[bool] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + min_compatible_shard_node: t.Optional[str] = None, + min_score: t.Optional[float] = None, + pit: t.Optional[t.Mapping[str, t.Any]] = None, + post_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pre_filter_shard_size: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + request_cache: t.Optional[bool] = None, + rescore: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + seq_no_primary_term: t.Optional[bool] = None, + size: t.Optional[int] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + suggest: t.Optional[t.Mapping[str, t.Any]] = None, + suggest_field: t.Optional[str] = None, + suggest_mode: t.Optional[ + t.Union["t.Literal['always', 'missing', 'popular']", str] + ] = None, + suggest_size: t.Optional[int] = None, + suggest_text: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[str] = None, + track_scores: t.Optional[bool] = None, + track_total_hits: t.Optional[t.Union[bool, int]] = None, + typed_keys: t.Optional[bool] = None, + version: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Executes a search request asynchronously. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param aggregations: + :param aggs: + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param allow_partial_search_results: Indicate if an error should be returned + if there is a partial search failure or timeout + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param batched_reduce_size: Affects how often partial results become available, + which happens whenever shard results are reduced. A partial reduction is + performed every time the coordinating node has received a certain number + of new shard responses (5 by default). + :param ccs_minimize_roundtrips: The default value is the only supported value. + :param collapse: + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc + values for field names matching these patterns in the hits.fields property + of the response. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: If true, returns detailed information about score computation + as part of a hit. + :param ext: Configuration of search extensions defined by Elasticsearch plugins. + :param fields: Array of wildcard (*) patterns. The request returns values for + field names matching these patterns in the hits.fields property of the response. + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the search_after parameter. + :param highlight: + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param indices_boost: Boosts the _score of documents from specified indices. + :param keep_alive: Specifies how long the async search needs to be available. + Ongoing async searches and any saved search results are deleted after this + period. + :param keep_on_completion: If `true`, results are stored for later retrieval + when the search completes within the `wait_for_completion_timeout`. + :param knn: Defines the approximate kNN search to run. + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_concurrent_shard_requests: The number of concurrent shard requests + per node this search executes concurrently. This value should be used to + limit the impact of the search on the cluster in order to limit the number + of concurrent shard requests + :param min_compatible_shard_node: + :param min_score: Minimum _score for matching documents. Documents with a lower + _score are not included in the search results. + :param pit: Limits the search to a point in time (PIT). If you provide a PIT, + you cannot specify an in the request path. + :param post_filter: + :param pre_filter_shard_size: The default value cannot be changed, which enforces + the execution of a pre-filter roundtrip to retrieve statistics from each + shard so that the ones that surely don’t hold any document matching the query + get skipped. + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param profile: + :param q: Query in the Lucene query string syntax + :param query: Defines the search definition using the Query DSL. + :param request_cache: Specify if request cache should be used for this request + or not, defaults to true + :param rescore: + :param rest_total_hits_as_int: + :param routing: A comma-separated list of specific routing values + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param script_fields: Retrieve a script evaluation (based on different fields) + for each hit. + :param scroll: + :param search_after: + :param search_type: Search operation type + :param seq_no_primary_term: If true, returns sequence number and primary term + of the last modification of each hit. See Optimistic concurrency control. + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the from and size parameters. To page through + more hits, use the search_after parameter. + :param slice: + :param sort: + :param source: Indicates which source fields are returned for matching documents. + These fields are returned in the hits._source property of the search response. + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param stats: Stats groups to associate with the search. Each group maintains + a statistics aggregation for its associated searches. You can retrieve these + stats using the indices stats API. + :param stored_fields: List of stored fields to return as part of a hit. If no + fields are specified, no stored fields are included in the response. If this + field is specified, the _source parameter defaults to false. You can pass + _source: true to return both source fields and stored fields in the search + response. + :param suggest: + :param suggest_field: Specifies which field to use for suggestions. + :param suggest_mode: Specify suggest mode + :param suggest_size: How many suggestions to return in response + :param suggest_text: The source text for which the suggestions should be returned. + :param terminate_after: Maximum number of documents to collect for each shard. + If a query reaches this limit, Elasticsearch terminates the query early. + Elasticsearch collects documents before sorting. Defaults to 0, which does + not terminate query execution early. + :param timeout: Specifies the period of time to wait for a response from each + shard. If no response is received before the timeout expires, the request + fails and returns an error. Defaults to no timeout. + :param track_scores: If true, calculate and return document scores, even if the + scores are not used for sorting. + :param track_total_hits: Number of hits matching the query to count accurately. + If true, the exact number of hits is returned at the cost of some performance. + If false, the response does not include the total number of hits matching + the query. Defaults to 10,000 hits. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + :param version: If true, returns document version as part of a hit. + :param wait_for_completion_timeout: Blocks and waits until the search is completed + up to a certain timeout. When the async search completes within the timeout, + the response won’t include the ID as the results are not stored in the cluster. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_async_search" + else: + __path = "/_async_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if batched_reduce_size is not None: + __query["batched_reduce_size"] = batched_reduce_size + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if collapse is not None: + __body["collapse"] = collapse + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if docvalue_fields is not None: + __body["docvalue_fields"] = docvalue_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if ext is not None: + __body["ext"] = ext + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if highlight is not None: + __body["highlight"] = highlight + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indices_boost is not None: + __body["indices_boost"] = indices_boost + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if keep_on_completion is not None: + __query["keep_on_completion"] = keep_on_completion + if knn is not None: + __body["knn"] = knn + if lenient is not None: + __query["lenient"] = lenient + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if min_compatible_shard_node is not None: + __query["min_compatible_shard_node"] = min_compatible_shard_node + if min_score is not None: + __body["min_score"] = min_score + if pit is not None: + __body["pit"] = pit + if post_filter is not None: + __body["post_filter"] = post_filter + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if request_cache is not None: + __query["request_cache"] = request_cache + if rescore is not None: + __body["rescore"] = rescore + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll is not None: + __query["scroll"] = scroll + if search_after is not None: + __body["search_after"] = search_after + if search_type is not None: + __query["search_type"] = search_type + if seq_no_primary_term is not None: + __body["seq_no_primary_term"] = seq_no_primary_term + if size is not None: + __body["size"] = size + if slice is not None: + __body["slice"] = slice + if sort is not None: + __body["sort"] = sort + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stats is not None: + __body["stats"] = stats + if stored_fields is not None: + __body["stored_fields"] = stored_fields + if suggest is not None: + __body["suggest"] = suggest + if suggest_field is not None: + __query["suggest_field"] = suggest_field + if suggest_mode is not None: + __query["suggest_mode"] = suggest_mode + if suggest_size is not None: + __query["suggest_size"] = suggest_size + if suggest_text is not None: + __query["suggest_text"] = suggest_text + if terminate_after is not None: + __body["terminate_after"] = terminate_after + if timeout is not None: + __body["timeout"] = timeout + if track_scores is not None: + __body["track_scores"] = track_scores + if track_total_hits is not None: + __body["track_total_hits"] = track_total_hits + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if version is not None: + __body["version"] = version + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/autoscaling.py b/elasticsearch_serverless/_async/client/autoscaling.py new file mode 100644 index 0000000..b45bc9c --- /dev/null +++ b/elasticsearch_serverless/_async/client/autoscaling.py @@ -0,0 +1,175 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class AutoscalingClient(NamespacedClient): + @_rewrite_parameters() + async def delete_autoscaling_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an autoscaling policy. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. + + ``_ + + :param name: the name of the autoscaling policy + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_autoscaling/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_autoscaling_capacity( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets the current autoscaling capacity based on the configured autoscaling policy. + Designed for indirect use by ECE/ESS and ECK. Direct use is not supported. + + ``_ + """ + __path = "/_autoscaling/capacity" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_autoscaling_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves an autoscaling policy. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. + + ``_ + + :param name: the name of the autoscaling policy + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_autoscaling/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="policy", + ) + async def put_autoscaling_policy( + self, + *, + name: str, + policy: t.Mapping[str, t.Any], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new autoscaling policy. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. + + ``_ + + :param name: the name of the autoscaling policy + :param policy: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if policy is None: + raise ValueError("Empty value passed for parameter 'policy'") + __path = f"/_autoscaling/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = policy + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/cat.py b/elasticsearch_serverless/_async/client/cat.py new file mode 100644 index 0000000..ef7411c --- /dev/null +++ b/elasticsearch_serverless/_async/client/cat.py @@ -0,0 +1,1161 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse, TextApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class CatClient(NamespacedClient): + @_rewrite_parameters() + async def aliases( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Shows information about currently configured aliases to indices including filter + and routing infos. + + ``_ + + :param name: A comma-separated list of aliases to retrieve. Supports wildcards + (`*`). To retrieve all aliases, omit this parameter or use `*` or `_all`. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + if name not in SKIP_IN_PATH: + __path = f"/_cat/aliases/{_quote(name)}" + else: + __path = "/_cat/aliases" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def component_templates( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Returns information about existing component_templates templates. + + ``_ + + :param name: The name of the component template. Accepts wildcard expressions. + If omitted, all component templates are returned. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + if name not in SKIP_IN_PATH: + __path = f"/_cat/component_templates/{_quote(name)}" + else: + __path = "/_cat/component_templates" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def count( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Provides quick access to the document count of the entire cluster, or individual + indices. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + if index not in SKIP_IN_PATH: + __path = f"/_cat/count/{_quote(index)}" + else: + __path = "/_cat/count" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def help( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> TextApiResponse: + """ + Returns help for the Cat APIs. + + ``_ + + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + __path = "/_cat" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def indices( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + health: t.Optional[t.Union["t.Literal['green', 'red', 'yellow']", str]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_unloaded_segments: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + pri: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Returns information about indices: number of primaries and replicas, document + counts, disk size, ... + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param bytes: The unit used to display byte values. + :param expand_wildcards: The type of index that wildcard patterns can match. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param health: The health status used to limit returned indices. By default, + the response includes indices of any health status. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param include_unloaded_segments: If true, the response includes information + from segments that are not loaded into memory. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param pri: If true, the response only includes information from primary shards. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if index not in SKIP_IN_PATH: + __path = f"/_cat/indices/{_quote(index)}" + else: + __path = "/_cat/indices" + __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if health is not None: + __query["health"] = health + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if include_unloaded_segments is not None: + __query["include_unloaded_segments"] = include_unloaded_segments + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if pri is not None: + __query["pri"] = pri + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def ml_data_frame_analytics( + self, + *, + id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + time: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about data frame analytics jobs. + + ``_ + + :param id: The ID of the data frame analytics to fetch + :param allow_no_match: Whether to ignore if a wildcard expression matches no + configs. (This includes `_all` string or when no configs have been specified) + :param bytes: The unit in which to display byte values + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param time: Unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if id not in SKIP_IN_PATH: + __path = f"/_cat/ml/data_frame/analytics/{_quote(id)}" + else: + __path = "/_cat/ml/data_frame/analytics" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def ml_datafeeds( + self, + *, + datafeed_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + ..., + ], + ], + ] + ] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about datafeeds. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. + :param allow_no_match: Specifies what to do when the request: * Contains wildcard + expressions and there are no datafeeds that match. * Contains the `_all` + string or no identifiers and there are no matches. * Contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty datafeeds + array when there are no matches and the subset of results when there are + partial matches. If `false`, the API returns a 404 status code when there + are no matches or only partial matches. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_cat/ml/datafeeds/{_quote(datafeed_id)}" + else: + __path = "/_cat/ml/datafeeds" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def ml_jobs( + self, + *, + job_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + ..., + ], + ], + ] + ] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param allow_no_match: Specifies what to do when the request: * Contains wildcard + expressions and there are no jobs that match. * Contains the `_all` string + or no identifiers and there are no matches. * Contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty jobs + array when there are no matches and the subset of results when there are + partial matches. If `false`, the API returns a 404 status code when there + are no matches or only partial matches. + :param bytes: The unit used to display byte values. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if job_id not in SKIP_IN_PATH: + __path = f"/_cat/ml/anomaly_detectors/{_quote(job_id)}" + else: + __path = "/_cat/ml/anomaly_detectors" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def ml_trained_models( + self, + *, + model_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + from_: t.Optional[int] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + size: t.Optional[int] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about inference trained models. + + ``_ + + :param model_id: A unique identifier for the trained model. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no models that match; contains the `_all` string + or no identifiers and there are no matches; contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty array + when there are no matches and the subset of results when there are partial + matches. If `false`, the API returns a 404 status code when there are no + matches or only partial matches. + :param bytes: The unit used to display byte values. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param from_: Skips the specified number of transforms. + :param h: A comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: A comma-separated list of column names or aliases used to sort the + response. + :param size: The maximum number of transforms to display. + :param v: When set to `true` will enable verbose output. + """ + if model_id not in SKIP_IN_PATH: + __path = f"/_cat/ml/trained_models/{_quote(model_id)}" + else: + __path = "/_cat/ml/trained_models" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if from_ is not None: + __query["from"] = from_ + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if size is not None: + __query["size"] = size + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def transforms( + self, + *, + transform_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + from_: t.Optional[int] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + size: t.Optional[int] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about transforms. + + ``_ + + :param transform_id: A transform identifier or a wildcard expression. If you + do not specify one of these options, the API returns information for all + transforms. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no transforms that match; contains the `_all` string + or no identifiers and there are no matches; contains wildcard expressions + and there are only partial matches. If `true`, it returns an empty transforms + array when there are no matches and the subset of results when there are + partial matches. If `false`, the request returns a 404 status code when there + are no matches or only partial matches. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param from_: Skips the specified number of transforms. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param size: The maximum number of transforms to obtain. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if transform_id not in SKIP_IN_PATH: + __path = f"/_cat/transforms/{_quote(transform_id)}" + else: + __path = "/_cat/transforms" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if from_ is not None: + __query["from"] = from_ + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if size is not None: + __query["size"] = size + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/ccr.py b/elasticsearch_serverless/_async/client/ccr.py new file mode 100644 index 0000000..8ab9795 --- /dev/null +++ b/elasticsearch_serverless/_async/client/ccr.py @@ -0,0 +1,749 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class CcrClient(NamespacedClient): + @_rewrite_parameters() + async def delete_auto_follow_pattern( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes auto-follow patterns. + + ``_ + + :param name: The name of the auto follow pattern. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ccr/auto_follow/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def follow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + leader_index: t.Optional[str] = None, + max_outstanding_read_requests: t.Optional[int] = None, + max_outstanding_write_requests: t.Optional[int] = None, + max_read_request_operation_count: t.Optional[int] = None, + max_read_request_size: t.Optional[str] = None, + max_retry_delay: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + max_write_buffer_count: t.Optional[int] = None, + max_write_buffer_size: t.Optional[str] = None, + max_write_request_operation_count: t.Optional[int] = None, + max_write_request_size: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + read_poll_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + remote_cluster: t.Optional[str] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new follower index configured to follow the referenced leader index. + + ``_ + + :param index: The name of the follower index + :param leader_index: + :param max_outstanding_read_requests: + :param max_outstanding_write_requests: + :param max_read_request_operation_count: + :param max_read_request_size: + :param max_retry_delay: + :param max_write_buffer_count: + :param max_write_buffer_size: + :param max_write_request_operation_count: + :param max_write_request_size: + :param read_poll_timeout: + :param remote_cluster: + :param wait_for_active_shards: Sets the number of shard copies that must be active + before returning. Defaults to 0. Set to `all` for all shard copies, otherwise + set to any non-negative value less than or equal to the total number of copies + for the shard (number of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/follow" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if leader_index is not None: + __body["leader_index"] = leader_index + if max_outstanding_read_requests is not None: + __body["max_outstanding_read_requests"] = max_outstanding_read_requests + if max_outstanding_write_requests is not None: + __body["max_outstanding_write_requests"] = max_outstanding_write_requests + if max_read_request_operation_count is not None: + __body[ + "max_read_request_operation_count" + ] = max_read_request_operation_count + if max_read_request_size is not None: + __body["max_read_request_size"] = max_read_request_size + if max_retry_delay is not None: + __body["max_retry_delay"] = max_retry_delay + if max_write_buffer_count is not None: + __body["max_write_buffer_count"] = max_write_buffer_count + if max_write_buffer_size is not None: + __body["max_write_buffer_size"] = max_write_buffer_size + if max_write_request_operation_count is not None: + __body[ + "max_write_request_operation_count" + ] = max_write_request_operation_count + if max_write_request_size is not None: + __body["max_write_request_size"] = max_write_request_size + if pretty is not None: + __query["pretty"] = pretty + if read_poll_timeout is not None: + __body["read_poll_timeout"] = read_poll_timeout + if remote_cluster is not None: + __body["remote_cluster"] = remote_cluster + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def follow_info( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about all follower indices, including parameters and status + for each follower index + + ``_ + + :param index: A comma-separated list of index patterns; use `_all` to perform + the operation on all indices + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/info" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def follow_stats( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves follower stats. return shard-level stats about the following tasks + associated with each shard for the specified indices. + + ``_ + + :param index: A comma-separated list of index patterns; use `_all` to perform + the operation on all indices + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def forget_follower( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + follower_cluster: t.Optional[str] = None, + follower_index: t.Optional[str] = None, + follower_index_uuid: t.Optional[str] = None, + human: t.Optional[bool] = None, + leader_remote_cluster: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes the follower retention leases from the leader. + + ``_ + + :param index: the name of the leader index for which specified follower retention + leases should be removed + :param follower_cluster: + :param follower_index: + :param follower_index_uuid: + :param leader_remote_cluster: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/forget_follower" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if follower_cluster is not None: + __body["follower_cluster"] = follower_cluster + if follower_index is not None: + __body["follower_index"] = follower_index + if follower_index_uuid is not None: + __body["follower_index_uuid"] = follower_index_uuid + if human is not None: + __query["human"] = human + if leader_remote_cluster is not None: + __body["leader_remote_cluster"] = leader_remote_cluster + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def get_auto_follow_pattern( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets configured auto-follow patterns. Returns the specified auto-follow pattern + collection. + + ``_ + + :param name: Specifies the auto-follow pattern collection that you want to retrieve. + If you do not specify a name, the API returns information for all collections. + """ + if name not in SKIP_IN_PATH: + __path = f"/_ccr/auto_follow/{_quote(name)}" + else: + __path = "/_ccr/auto_follow" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def pause_auto_follow_pattern( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Pauses an auto-follow pattern + + ``_ + + :param name: The name of the auto follow pattern that should pause discovering + new indices to follow. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ccr/auto_follow/{_quote(name)}/pause" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def pause_follow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Pauses a follower index. The follower index will not fetch any additional operations + from the leader index. + + ``_ + + :param index: The name of the follower index that should pause following its + leader index. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/pause_follow" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_auto_follow_pattern( + self, + *, + name: str, + remote_cluster: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + follow_index_pattern: t.Optional[str] = None, + human: t.Optional[bool] = None, + leader_index_exclusion_patterns: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + leader_index_patterns: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + max_outstanding_read_requests: t.Optional[int] = None, + max_outstanding_write_requests: t.Optional[int] = None, + max_read_request_operation_count: t.Optional[int] = None, + max_read_request_size: t.Optional[t.Union[int, str]] = None, + max_retry_delay: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + max_write_buffer_count: t.Optional[int] = None, + max_write_buffer_size: t.Optional[t.Union[int, str]] = None, + max_write_request_operation_count: t.Optional[int] = None, + max_write_request_size: t.Optional[t.Union[int, str]] = None, + pretty: t.Optional[bool] = None, + read_poll_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new named collection of auto-follow patterns against a specified remote + cluster. Newly created indices on the remote cluster matching any of the specified + patterns will be automatically configured as follower indices. + + ``_ + + :param name: The name of the collection of auto-follow patterns. + :param remote_cluster: The remote cluster containing the leader indices to match + against. + :param follow_index_pattern: The name of follower index. The template {{leader_index}} + can be used to derive the name of the follower index from the name of the + leader index. When following a data stream, use {{leader_index}}; CCR does + not support changes to the names of a follower data stream’s backing indices. + :param leader_index_exclusion_patterns: An array of simple index patterns that + can be used to exclude indices from being auto-followed. Indices in the remote + cluster whose names are matching one or more leader_index_patterns and one + or more leader_index_exclusion_patterns won’t be followed. + :param leader_index_patterns: An array of simple index patterns to match against + indices in the remote cluster specified by the remote_cluster field. + :param max_outstanding_read_requests: The maximum number of outstanding reads + requests from the remote cluster. + :param max_outstanding_write_requests: The maximum number of outstanding reads + requests from the remote cluster. + :param max_read_request_operation_count: The maximum number of operations to + pull per read from the remote cluster. + :param max_read_request_size: The maximum size in bytes of per read of a batch + of operations pulled from the remote cluster. + :param max_retry_delay: The maximum time to wait before retrying an operation + that failed exceptionally. An exponential backoff strategy is employed when + retrying. + :param max_write_buffer_count: The maximum number of operations that can be queued + for writing. When this limit is reached, reads from the remote cluster will + be deferred until the number of queued operations goes below the limit. + :param max_write_buffer_size: The maximum total bytes of operations that can + be queued for writing. When this limit is reached, reads from the remote + cluster will be deferred until the total bytes of queued operations goes + below the limit. + :param max_write_request_operation_count: The maximum number of operations per + bulk write request executed on the follower. + :param max_write_request_size: The maximum total bytes of operations per bulk + write request executed on the follower. + :param read_poll_timeout: The maximum time to wait for new operations on the + remote cluster when the follower index is synchronized with the leader index. + When the timeout has elapsed, the poll for operations will return to the + follower so that it can update some statistics. Then the follower will immediately + attempt to read from the leader again. + :param settings: Settings to override from the leader index. Note that certain + settings can not be overrode (e.g., index.number_of_shards). + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if remote_cluster is None: + raise ValueError("Empty value passed for parameter 'remote_cluster'") + __path = f"/_ccr/auto_follow/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if remote_cluster is not None: + __body["remote_cluster"] = remote_cluster + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if follow_index_pattern is not None: + __body["follow_index_pattern"] = follow_index_pattern + if human is not None: + __query["human"] = human + if leader_index_exclusion_patterns is not None: + __body["leader_index_exclusion_patterns"] = leader_index_exclusion_patterns + if leader_index_patterns is not None: + __body["leader_index_patterns"] = leader_index_patterns + if max_outstanding_read_requests is not None: + __body["max_outstanding_read_requests"] = max_outstanding_read_requests + if max_outstanding_write_requests is not None: + __body["max_outstanding_write_requests"] = max_outstanding_write_requests + if max_read_request_operation_count is not None: + __body[ + "max_read_request_operation_count" + ] = max_read_request_operation_count + if max_read_request_size is not None: + __body["max_read_request_size"] = max_read_request_size + if max_retry_delay is not None: + __body["max_retry_delay"] = max_retry_delay + if max_write_buffer_count is not None: + __body["max_write_buffer_count"] = max_write_buffer_count + if max_write_buffer_size is not None: + __body["max_write_buffer_size"] = max_write_buffer_size + if max_write_request_operation_count is not None: + __body[ + "max_write_request_operation_count" + ] = max_write_request_operation_count + if max_write_request_size is not None: + __body["max_write_request_size"] = max_write_request_size + if pretty is not None: + __query["pretty"] = pretty + if read_poll_timeout is not None: + __body["read_poll_timeout"] = read_poll_timeout + if settings is not None: + __body["settings"] = settings + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def resume_auto_follow_pattern( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resumes an auto-follow pattern that has been paused + + ``_ + + :param name: The name of the auto follow pattern to resume discovering new indices + to follow. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ccr/auto_follow/{_quote(name)}/resume" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def resume_follow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_outstanding_read_requests: t.Optional[int] = None, + max_outstanding_write_requests: t.Optional[int] = None, + max_read_request_operation_count: t.Optional[int] = None, + max_read_request_size: t.Optional[str] = None, + max_retry_delay: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + max_write_buffer_count: t.Optional[int] = None, + max_write_buffer_size: t.Optional[str] = None, + max_write_request_operation_count: t.Optional[int] = None, + max_write_request_size: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + read_poll_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resumes a follower index that has been paused + + ``_ + + :param index: The name of the follow index to resume following. + :param max_outstanding_read_requests: + :param max_outstanding_write_requests: + :param max_read_request_operation_count: + :param max_read_request_size: + :param max_retry_delay: + :param max_write_buffer_count: + :param max_write_buffer_size: + :param max_write_request_operation_count: + :param max_write_request_size: + :param read_poll_timeout: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/resume_follow" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_outstanding_read_requests is not None: + __body["max_outstanding_read_requests"] = max_outstanding_read_requests + if max_outstanding_write_requests is not None: + __body["max_outstanding_write_requests"] = max_outstanding_write_requests + if max_read_request_operation_count is not None: + __body[ + "max_read_request_operation_count" + ] = max_read_request_operation_count + if max_read_request_size is not None: + __body["max_read_request_size"] = max_read_request_size + if max_retry_delay is not None: + __body["max_retry_delay"] = max_retry_delay + if max_write_buffer_count is not None: + __body["max_write_buffer_count"] = max_write_buffer_count + if max_write_buffer_size is not None: + __body["max_write_buffer_size"] = max_write_buffer_size + if max_write_request_operation_count is not None: + __body[ + "max_write_request_operation_count" + ] = max_write_request_operation_count + if max_write_request_size is not None: + __body["max_write_request_size"] = max_write_request_size + if pretty is not None: + __query["pretty"] = pretty + if read_poll_timeout is not None: + __body["read_poll_timeout"] = read_poll_timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets all stats related to cross-cluster replication. + + ``_ + """ + __path = "/_ccr/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def unfollow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops the following task associated with a follower index and removes index metadata + and settings associated with cross-cluster replication. + + ``_ + + :param index: The name of the follower index that should be turned into a regular + index. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/unfollow" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/cluster.py b/elasticsearch_serverless/_async/client/cluster.py new file mode 100644 index 0000000..daf3b27 --- /dev/null +++ b/elasticsearch_serverless/_async/client/cluster.py @@ -0,0 +1,539 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import HeadApiResponse, ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class ClusterClient(NamespacedClient): + @_rewrite_parameters() + async def delete_component_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a component template + + ``_ + + :param name: Comma-separated list or wildcard expression of component template + names used to limit the request. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_component_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def exists_component_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular component template exist + + ``_ + + :param name: Comma-separated list of component template names used to limit the + request. Wildcard (*) expressions are supported. + :param local: If true, the request retrieves information from the local node + only. Defaults to false, which means information is retrieved from the master + node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_component_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_component_template( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns one or more component templates + + ``_ + + :param name: Comma-separated list of component template names used to limit the + request. Wildcard (`*`) expressions are supported. + :param flat_settings: If `true`, returns settings in flat format. + :param include_defaults: Return all default configurations for the component + template (default: false) + :param local: If `true`, the request retrieves information from the local node + only. If `false`, information is retrieved from the master node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name not in SKIP_IN_PATH: + __path = f"/_component_template/{_quote(name)}" + else: + __path = "/_component_template" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_settings( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns cluster settings. + + ``_ + + :param flat_settings: If `true`, returns settings in flat format. + :param include_defaults: If `true`, returns default cluster settings from the + local node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + __path = "/_cluster/settings" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def info( + self, + *, + target: t.Union[ + t.Union[ + "t.Literal['_all', 'http', 'ingest', 'script', 'thread_pool']", str + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['_all', 'http', 'ingest', 'script', 'thread_pool']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['_all', 'http', 'ingest', 'script', 'thread_pool']", + str, + ], + ..., + ], + ], + ], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns different information about the cluster. + + ``_ + + :param target: Limits the information returned to the specific target. Supports + a comma-separated list, such as http,ingest. + """ + if target in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'target'") + __path = f"/_info/{_quote(target)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def pending_tasks( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a list of any cluster-level changes (e.g. create index, update mapping, + allocate or fail shard) which have not yet been executed. + + ``_ + + :param local: If `true`, the request retrieves information from the local node + only. If `false`, information is retrieved from the master node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + __path = "/_cluster/pending_tasks" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + async def put_component_template( + self, + *, + name: str, + template: t.Mapping[str, t.Any], + allow_auto_create: t.Optional[bool] = None, + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a component template + + ``_ + + :param name: Name of the component template to create. Elasticsearch includes + the following built-in component templates: `logs-mappings`; 'logs-settings`; + `metrics-mappings`; `metrics-settings`;`synthetics-mapping`; `synthetics-settings`. + Elastic Agent uses these templates to configure backing indices for its data + streams. If you use Elastic Agent and want to overwrite one of these templates, + set the `version` for your replacement template higher than the current version. + If you don’t use Elastic Agent and want to disable all built-in component + and index templates, set `stack.templates.enabled` to `false` using the cluster + update settings API. + :param template: The template to be applied which includes mappings, settings, + or aliases configuration. + :param allow_auto_create: This setting overrides the value of the `action.auto_create_index` + cluster setting. If set to `true` in a template, then indices can be automatically + created using that template even if auto-creation of indices is disabled + via `actions.auto_create_index`. If set to `false` then data streams matching + the template must always be explicitly created. + :param create: If `true`, this request cannot replace or update existing component + templates. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param meta: Optional user metadata about the component template. May have any + contents. This map is not automatically generated by Elasticsearch. This + information is stored in the cluster state, so keeping it short is preferable. + To unset `_meta`, replace the template without specifying this information. + :param version: Version number used to manage component templates externally. + This number isn't automatically generated or incremented by Elasticsearch. + To unset a version, replace the template without specifying a version. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if template is None: + raise ValueError("Empty value passed for parameter 'template'") + __path = f"/_component_template/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if template is not None: + __body["template"] = template + if allow_auto_create is not None: + __body["allow_auto_create"] = allow_auto_create + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_settings( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + persistent: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + transient: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the cluster settings. + + ``_ + + :param flat_settings: Return settings in flat format (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + :param persistent: + :param timeout: Explicit operation timeout + :param transient: + """ + __path = "/_cluster/settings" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if persistent is not None: + __body["persistent"] = persistent + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if transient is not None: + __body["transient"] = transient + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def stats( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns high-level overview of cluster statistics. + + ``_ + + :param node_id: Comma-separated list of node filters used to limit returned information. + Defaults to all nodes in the cluster. + :param flat_settings: If `true`, returns settings in flat format. + :param timeout: Period to wait for each node to respond. If a node does not respond + before its timeout expires, the response does not include its stats. However, + timed out nodes are included in the response’s `_nodes.failed` property. + Defaults to no timeout. + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_cluster/stats/nodes/{_quote(node_id)}" + else: + __path = "/_cluster/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/dangling_indices.py b/elasticsearch_serverless/_async/client/dangling_indices.py new file mode 100644 index 0000000..0b1f2c9 --- /dev/null +++ b/elasticsearch_serverless/_async/client/dangling_indices.py @@ -0,0 +1,162 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class DanglingIndicesClient(NamespacedClient): + @_rewrite_parameters() + async def delete_dangling_index( + self, + *, + index_uuid: str, + accept_data_loss: bool, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes the specified dangling index + + ``_ + + :param index_uuid: The UUID of the dangling index + :param accept_data_loss: Must be set to true in order to delete the dangling + index + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if index_uuid in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index_uuid'") + if accept_data_loss is None: + raise ValueError("Empty value passed for parameter 'accept_data_loss'") + __path = f"/_dangling/{_quote(index_uuid)}" + __query: t.Dict[str, t.Any] = {} + if accept_data_loss is not None: + __query["accept_data_loss"] = accept_data_loss + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def import_dangling_index( + self, + *, + index_uuid: str, + accept_data_loss: bool, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Imports the specified dangling index + + ``_ + + :param index_uuid: The UUID of the dangling index + :param accept_data_loss: Must be set to true in order to import the dangling + index + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if index_uuid in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index_uuid'") + if accept_data_loss is None: + raise ValueError("Empty value passed for parameter 'accept_data_loss'") + __path = f"/_dangling/{_quote(index_uuid)}" + __query: t.Dict[str, t.Any] = {} + if accept_data_loss is not None: + __query["accept_data_loss"] = accept_data_loss + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def list_dangling_indices( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns all dangling indices. + + ``_ + """ + __path = "/_dangling" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/enrich.py b/elasticsearch_serverless/_async/client/enrich.py new file mode 100644 index 0000000..db09589 --- /dev/null +++ b/elasticsearch_serverless/_async/client/enrich.py @@ -0,0 +1,222 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class EnrichClient(NamespacedClient): + @_rewrite_parameters() + async def delete_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing enrich policy and its enrich index. + + ``_ + + :param name: The name of the enrich policy + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_enrich/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def execute_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates the enrich index for an existing enrich policy. + + ``_ + + :param name: The name of the enrich policy + :param wait_for_completion: Should the request should block until the execution + is complete. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_enrich/policy/{_quote(name)}/_execute" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_policy( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets information about an enrich policy. + + ``_ + + :param name: A comma-separated list of enrich policy names + """ + if name not in SKIP_IN_PATH: + __path = f"/_enrich/policy/{_quote(name)}" + else: + __path = "/_enrich/policy" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + geo_match: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + match: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + range: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new enrich policy. + + ``_ + + :param name: The name of the enrich policy + :param geo_match: + :param match: + :param range: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_enrich/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if geo_match is not None: + __body["geo_match"] = geo_match + if human is not None: + __query["human"] = human + if match is not None: + __body["match"] = match + if pretty is not None: + __query["pretty"] = pretty + if range is not None: + __body["range"] = range + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets enrich coordinator statistics and information about enrich policies that + are currently executing. + + ``_ + """ + __path = "/_enrich/_stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/eql.py b/elasticsearch_serverless/_async/client/eql.py new file mode 100644 index 0000000..bae907f --- /dev/null +++ b/elasticsearch_serverless/_async/client/eql.py @@ -0,0 +1,298 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class EqlClient(NamespacedClient): + @_rewrite_parameters() + async def delete( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an async EQL search by ID. If the search is still running, the search + request will be cancelled. Otherwise, the saved search results are deleted. + + ``_ + + :param id: Identifier for the search to delete. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_eql/search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + pretty: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns async results from previously executed Event Query Language (EQL) search + + ``_ + + :param id: Identifier for the search. + :param keep_alive: Period for which the search and its results are stored on + the cluster. Defaults to the keep_alive value set by the search’s EQL search + API request. + :param wait_for_completion_timeout: Timeout duration to wait for the request + to finish. Defaults to no timeout, meaning the request waits for complete + search results. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_eql/search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_status( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the status of a previously submitted async or stored Event Query Language + (EQL) search + + ``_ + + :param id: Identifier for the search. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_eql/search/status/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def search( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + query: str, + allow_no_indices: t.Optional[bool] = None, + case_sensitive: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + event_category_field: t.Optional[str] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + fetch_size: t.Optional[int] = None, + fields: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + filter: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + keep_on_completion: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + result_position: t.Optional[t.Union["t.Literal['head', 'tail']", str]] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + size: t.Optional[int] = None, + tiebreaker_field: t.Optional[str] = None, + timestamp_field: t.Optional[str] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns results matching a query expressed in Event Query Language (EQL) + + ``_ + + :param index: The name of the index to scope the operation + :param query: EQL query you wish to run. + :param allow_no_indices: + :param case_sensitive: + :param event_category_field: Field containing the event classification, such + as process, file, or network. + :param expand_wildcards: + :param fetch_size: Maximum number of events to search at a time for sequence + queries. + :param fields: Array of wildcard (*) patterns. The response returns values for + field names matching these patterns in the fields property of each hit. + :param filter: Query, written in Query DSL, used to filter the events on which + the EQL query runs. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param keep_alive: + :param keep_on_completion: + :param result_position: + :param runtime_mappings: + :param size: For basic queries, the maximum number of matching events to return. + Defaults to 10 + :param tiebreaker_field: Field used to sort hits with the same timestamp in ascending + order + :param timestamp_field: Field containing event timestamp. Default "@timestamp" + :param wait_for_completion_timeout: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if query is None: + raise ValueError("Empty value passed for parameter 'query'") + __path = f"/{_quote(index)}/_eql/search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if query is not None: + __body["query"] = query + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if case_sensitive is not None: + __body["case_sensitive"] = case_sensitive + if error_trace is not None: + __query["error_trace"] = error_trace + if event_category_field is not None: + __body["event_category_field"] = event_category_field + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if fetch_size is not None: + __body["fetch_size"] = fetch_size + if fields is not None: + __body["fields"] = fields + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if keep_alive is not None: + __body["keep_alive"] = keep_alive + if keep_on_completion is not None: + __body["keep_on_completion"] = keep_on_completion + if pretty is not None: + __query["pretty"] = pretty + if result_position is not None: + __body["result_position"] = result_position + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if size is not None: + __body["size"] = size + if tiebreaker_field is not None: + __body["tiebreaker_field"] = tiebreaker_field + if timestamp_field is not None: + __body["timestamp_field"] = timestamp_field + if wait_for_completion_timeout is not None: + __body["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/features.py b/elasticsearch_serverless/_async/client/features.py new file mode 100644 index 0000000..5c19b7b --- /dev/null +++ b/elasticsearch_serverless/_async/client/features.py @@ -0,0 +1,88 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class FeaturesClient(NamespacedClient): + @_rewrite_parameters() + async def get_features( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets a list of features which can be included in snapshots using the feature_states + field when creating a snapshot + + ``_ + """ + __path = "/_features" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def reset_features( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resets the internal state of features, usually by deleting system indices + + ``_ + """ + __path = "/_features/_reset" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/fleet.py b/elasticsearch_serverless/_async/client/fleet.py new file mode 100644 index 0000000..e296a41 --- /dev/null +++ b/elasticsearch_serverless/_async/client/fleet.py @@ -0,0 +1,631 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class FleetClient(NamespacedClient): + @_rewrite_parameters() + async def global_checkpoints( + self, + *, + index: str, + checkpoints: t.Optional[t.Union[t.List[int], t.Tuple[int, ...]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_advance: t.Optional[bool] = None, + wait_for_index: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the current global checkpoints for an index. This API is design for internal + use by the fleet server project. + + ``_ + + :param index: A single index or index alias that resolves to a single index. + :param checkpoints: A comma separated list of previous global checkpoints. When + used in combination with `wait_for_advance`, the API will only return once + the global checkpoints advances past the checkpoints. Providing an empty + list will cause Elasticsearch to immediately return the current global checkpoints. + :param timeout: Period to wait for a global checkpoints to advance past `checkpoints`. + :param wait_for_advance: A boolean value which controls whether to wait (until + the timeout) for the global checkpoints to advance past the provided `checkpoints`. + :param wait_for_index: A boolean value which controls whether to wait (until + the timeout) for the target index to exist and all primary shards be active. + Can only be true when `wait_for_advance` is true. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_fleet/global_checkpoints" + __query: t.Dict[str, t.Any] = {} + if checkpoints is not None: + __query["checkpoints"] = checkpoints + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_advance is not None: + __query["wait_for_advance"] = wait_for_advance + if wait_for_index is not None: + __query["wait_for_index"] = wait_for_index + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="searches", + ) + async def msearch( + self, + *, + searches: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[str] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + max_concurrent_searches: t.Optional[int] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + pre_filter_shard_size: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + typed_keys: t.Optional[bool] = None, + wait_for_checkpoints: t.Optional[ + t.Union[t.List[int], t.Tuple[int, ...]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Multi Search API where the search will only be executed after specified checkpoints + are available due to a refresh. This API is designed for internal use by the + fleet server project. + + :param searches: + :param index: A single target to search. If the target is an index alias, it + must resolve to a single index. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param allow_partial_search_results: If true, returns partial results if there + are shard request timeouts or [shard failures](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-replication.html#shard-failures). + If false, returns an error with no partial results. Defaults to the configured + cluster setting `search.default_allow_partial_results` which is true by default. + :param ccs_minimize_roundtrips: If true, network roundtrips between the coordinating + node and remote clusters are minimized for cross-cluster search requests. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. + :param ignore_throttled: If true, concrete, expanded or aliased indices are ignored + when frozen. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param max_concurrent_searches: Maximum number of concurrent searches the multi + search API can execute. + :param max_concurrent_shard_requests: Maximum number of concurrent shard requests + that each sub-search request executes per node. + :param pre_filter_shard_size: Defines a threshold that enforces a pre-filter + roundtrip to prefilter search shards based on query rewriting if the number + of shards the search request expands to exceeds the threshold. This filter + roundtrip can limit the number of shards significantly if for instance a + shard can not match any documents based on its rewrite method i.e., if date + filters are mandatory to match but the shard bounds and the query are disjoint. + :param rest_total_hits_as_int: If true, hits.total are returned as an integer + in the response. Defaults to false, which returns an object. + :param search_type: Indicates whether global term and document frequencies should + be used when scoring returned documents. + :param typed_keys: Specifies whether aggregation and suggester names should be + prefixed by their respective types in the response. + :param wait_for_checkpoints: A comma separated list of checkpoints. When configured, + the search API will only be executed on a shard after the relevant checkpoint + has become visible for search. Defaults to an empty list which will cause + Elasticsearch to immediately execute the search. + """ + if searches is None: + raise ValueError("Empty value passed for parameter 'searches'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_fleet/_fleet_msearch" + else: + __path = "/_fleet/_fleet_msearch" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if max_concurrent_searches is not None: + __query["max_concurrent_searches"] = max_concurrent_searches + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if search_type is not None: + __query["search_type"] = search_type + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if wait_for_checkpoints is not None: + __query["wait_for_checkpoints"] = wait_for_checkpoints + __body = searches + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + "from": "from_", + }, + ) + async def search( + self, + *, + index: str, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + batched_reduce_size: t.Optional[int] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + collapse: t.Optional[t.Mapping[str, t.Any]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + docvalue_fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + ext: t.Optional[t.Mapping[str, t.Any]] = None, + fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + highlight: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indices_boost: t.Optional[ + t.Union[t.List[t.Mapping[str, float]], t.Tuple[t.Mapping[str, float], ...]] + ] = None, + lenient: t.Optional[bool] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + min_compatible_shard_node: t.Optional[str] = None, + min_score: t.Optional[float] = None, + pit: t.Optional[t.Mapping[str, t.Any]] = None, + post_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pre_filter_shard_size: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + request_cache: t.Optional[bool] = None, + rescore: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + seq_no_primary_term: t.Optional[bool] = None, + size: t.Optional[int] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + suggest: t.Optional[t.Mapping[str, t.Any]] = None, + suggest_field: t.Optional[str] = None, + suggest_mode: t.Optional[ + t.Union["t.Literal['always', 'missing', 'popular']", str] + ] = None, + suggest_size: t.Optional[int] = None, + suggest_text: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[str] = None, + track_scores: t.Optional[bool] = None, + track_total_hits: t.Optional[t.Union[bool, int]] = None, + typed_keys: t.Optional[bool] = None, + version: t.Optional[bool] = None, + wait_for_checkpoints: t.Optional[ + t.Union[t.List[int], t.Tuple[int, ...]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Search API where the search will only be executed after specified checkpoints + are available due to a refresh. This API is designed for internal use by the + fleet server project. + + :param index: A single target to search. If the target is an index alias, it + must resolve to a single index. + :param aggregations: + :param aggs: + :param allow_no_indices: + :param allow_partial_search_results: If true, returns partial results if there + are shard request timeouts or [shard failures](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-replication.html#shard-failures). + If false, returns an error with no partial results. Defaults to the configured + cluster setting `search.default_allow_partial_results` which is true by default. + :param analyze_wildcard: + :param analyzer: + :param batched_reduce_size: + :param ccs_minimize_roundtrips: + :param collapse: + :param default_operator: + :param df: + :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc + values for field names matching these patterns in the hits.fields property + of the response. + :param expand_wildcards: + :param explain: If true, returns detailed information about score computation + as part of a hit. + :param ext: Configuration of search extensions defined by Elasticsearch plugins. + :param fields: Array of wildcard (*) patterns. The request returns values for + field names matching these patterns in the hits.fields property of the response. + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the search_after parameter. + :param highlight: + :param ignore_throttled: + :param ignore_unavailable: + :param indices_boost: Boosts the _score of documents from specified indices. + :param lenient: + :param max_concurrent_shard_requests: + :param min_compatible_shard_node: + :param min_score: Minimum _score for matching documents. Documents with a lower + _score are not included in the search results. + :param pit: Limits the search to a point in time (PIT). If you provide a PIT, + you cannot specify an in the request path. + :param post_filter: + :param pre_filter_shard_size: + :param preference: + :param profile: + :param q: + :param query: Defines the search definition using the Query DSL. + :param request_cache: + :param rescore: + :param rest_total_hits_as_int: + :param routing: + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param script_fields: Retrieve a script evaluation (based on different fields) + for each hit. + :param scroll: + :param search_after: + :param search_type: + :param seq_no_primary_term: If true, returns sequence number and primary term + of the last modification of each hit. See Optimistic concurrency control. + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the from and size parameters. To page through + more hits, use the search_after parameter. + :param slice: + :param sort: + :param source: Indicates which source fields are returned for matching documents. + These fields are returned in the hits._source property of the search response. + :param source_excludes: + :param source_includes: + :param stats: Stats groups to associate with the search. Each group maintains + a statistics aggregation for its associated searches. You can retrieve these + stats using the indices stats API. + :param stored_fields: List of stored fields to return as part of a hit. If no + fields are specified, no stored fields are included in the response. If this + field is specified, the _source parameter defaults to false. You can pass + _source: true to return both source fields and stored fields in the search + response. + :param suggest: + :param suggest_field: Specifies which field to use for suggestions. + :param suggest_mode: + :param suggest_size: + :param suggest_text: The source text for which the suggestions should be returned. + :param terminate_after: Maximum number of documents to collect for each shard. + If a query reaches this limit, Elasticsearch terminates the query early. + Elasticsearch collects documents before sorting. Defaults to 0, which does + not terminate query execution early. + :param timeout: Specifies the period of time to wait for a response from each + shard. If no response is received before the timeout expires, the request + fails and returns an error. Defaults to no timeout. + :param track_scores: If true, calculate and return document scores, even if the + scores are not used for sorting. + :param track_total_hits: Number of hits matching the query to count accurately. + If true, the exact number of hits is returned at the cost of some performance. + If false, the response does not include the total number of hits matching + the query. Defaults to 10,000 hits. + :param typed_keys: + :param version: If true, returns document version as part of a hit. + :param wait_for_checkpoints: A comma separated list of checkpoints. When configured, + the search API will only be executed on a shard after the relevant checkpoint + has become visible for search. Defaults to an empty list which will cause + Elasticsearch to immediately execute the search. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_fleet/_fleet_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if batched_reduce_size is not None: + __query["batched_reduce_size"] = batched_reduce_size + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if collapse is not None: + __body["collapse"] = collapse + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if docvalue_fields is not None: + __body["docvalue_fields"] = docvalue_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if ext is not None: + __body["ext"] = ext + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if highlight is not None: + __body["highlight"] = highlight + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indices_boost is not None: + __body["indices_boost"] = indices_boost + if lenient is not None: + __query["lenient"] = lenient + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if min_compatible_shard_node is not None: + __query["min_compatible_shard_node"] = min_compatible_shard_node + if min_score is not None: + __body["min_score"] = min_score + if pit is not None: + __body["pit"] = pit + if post_filter is not None: + __body["post_filter"] = post_filter + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if request_cache is not None: + __query["request_cache"] = request_cache + if rescore is not None: + __body["rescore"] = rescore + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll is not None: + __query["scroll"] = scroll + if search_after is not None: + __body["search_after"] = search_after + if search_type is not None: + __query["search_type"] = search_type + if seq_no_primary_term is not None: + __body["seq_no_primary_term"] = seq_no_primary_term + if size is not None: + __body["size"] = size + if slice is not None: + __body["slice"] = slice + if sort is not None: + __body["sort"] = sort + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stats is not None: + __body["stats"] = stats + if stored_fields is not None: + __body["stored_fields"] = stored_fields + if suggest is not None: + __body["suggest"] = suggest + if suggest_field is not None: + __query["suggest_field"] = suggest_field + if suggest_mode is not None: + __query["suggest_mode"] = suggest_mode + if suggest_size is not None: + __query["suggest_size"] = suggest_size + if suggest_text is not None: + __query["suggest_text"] = suggest_text + if terminate_after is not None: + __body["terminate_after"] = terminate_after + if timeout is not None: + __body["timeout"] = timeout + if track_scores is not None: + __body["track_scores"] = track_scores + if track_total_hits is not None: + __body["track_total_hits"] = track_total_hits + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if version is not None: + __body["version"] = version + if wait_for_checkpoints is not None: + __query["wait_for_checkpoints"] = wait_for_checkpoints + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/graph.py b/elasticsearch_serverless/_async/client/graph.py new file mode 100644 index 0000000..6391a7d --- /dev/null +++ b/elasticsearch_serverless/_async/client/graph.py @@ -0,0 +1,96 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class GraphClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + async def explore( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + connections: t.Optional[t.Mapping[str, t.Any]] = None, + controls: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + vertices: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Explore extracted and summarized information about the documents and terms in + an index. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param connections: + :param controls: + :param query: + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param vertices: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_graph/explore" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if connections is not None: + __body["connections"] = connections + if controls is not None: + __body["controls"] = controls + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if vertices is not None: + __body["vertices"] = vertices + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/ilm.py b/elasticsearch_serverless/_async/client/ilm.py new file mode 100644 index 0000000..83ffa2a --- /dev/null +++ b/elasticsearch_serverless/_async/client/ilm.py @@ -0,0 +1,543 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class IlmClient(NamespacedClient): + @_rewrite_parameters() + async def delete_lifecycle( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes the specified lifecycle policy definition. A currently used policy cannot + be deleted. + + ``_ + + :param name: Identifier for the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ilm/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def explain_lifecycle( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + only_errors: t.Optional[bool] = None, + only_managed: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the index's current lifecycle state, such as the + currently executing phase, action, and step. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases to target. + Supports wildcards (`*`). To target all data streams and indices, use `*` + or `_all`. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param only_errors: Filters the returned indices to only indices that are managed + by ILM and are in an error state, either due to an encountering an error + while executing the policy, or attempting to use a policy that does not exist. + :param only_managed: Filters the returned indices to only indices that are managed + by ILM. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ilm/explain" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if only_errors is not None: + __query["only_errors"] = only_errors + if only_managed is not None: + __query["only_managed"] = only_managed + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_lifecycle( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the specified policy definition. Includes the policy version and last + modified date. + + ``_ + + :param name: Identifier for the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name not in SKIP_IN_PATH: + __path = f"/_ilm/policy/{_quote(name)}" + else: + __path = "/_ilm/policy" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the current index lifecycle management (ILM) status. + + ``_ + """ + __path = "/_ilm/status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def migrate_to_data_tiers( + self, + *, + dry_run: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + legacy_template_to_delete: t.Optional[str] = None, + node_attribute: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Migrates the indices and ILM policies away from custom node attribute allocation + routing to data tiers routing + + ``_ + + :param dry_run: If true, simulates the migration from node attributes based allocation + filters to data tiers, but does not perform the migration. This provides + a way to retrieve the indices and ILM policies that need to be migrated. + :param legacy_template_to_delete: + :param node_attribute: + """ + __path = "/_ilm/migrate_to_data_tiers" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if dry_run is not None: + __query["dry_run"] = dry_run + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if legacy_template_to_delete is not None: + __body["legacy_template_to_delete"] = legacy_template_to_delete + if node_attribute is not None: + __body["node_attribute"] = node_attribute + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def move_to_step( + self, + *, + index: str, + current_step: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + next_step: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Manually moves an index into the specified step and executes that step. + + ``_ + + :param index: The name of the index whose lifecycle step is to change + :param current_step: + :param next_step: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/_ilm/move/{_quote(index)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if current_step is not None: + __body["current_step"] = current_step + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if next_step is not None: + __body["next_step"] = next_step + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_lifecycle( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + policy: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a lifecycle policy + + ``_ + + :param name: Identifier for the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param policy: + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ilm/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if policy is not None: + __body["policy"] = policy + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def remove_policy( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes the assigned lifecycle policy and stops managing the specified index + + ``_ + + :param index: The name of the index to remove policy on + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ilm/remove" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def retry( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retries executing the policy for an index that is in the ERROR step. + + ``_ + + :param index: The name of the indices (comma-separated) whose failed lifecycle + step is to be retry + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ilm/retry" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def start( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Start the index lifecycle management (ILM) plugin. + + ``_ + + :param master_timeout: + :param timeout: + """ + __path = "/_ilm/start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stop( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Halts all lifecycle management operations and stops the index lifecycle management + (ILM) plugin + + ``_ + + :param master_timeout: + :param timeout: + """ + __path = "/_ilm/stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/indices.py b/elasticsearch_serverless/_async/client/indices.py new file mode 100644 index 0000000..639109c --- /dev/null +++ b/elasticsearch_serverless/_async/client/indices.py @@ -0,0 +1,2795 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import HeadApiResponse, ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class IndicesClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + async def analyze( + self, + *, + index: t.Optional[str] = None, + analyzer: t.Optional[str] = None, + attributes: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + char_filter: t.Optional[ + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ] + ] = None, + error_trace: t.Optional[bool] = None, + explain: t.Optional[bool] = None, + field: t.Optional[str] = None, + filter: t.Optional[ + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + normalizer: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + text: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + tokenizer: t.Optional[t.Union[str, t.Mapping[str, t.Any]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Performs the analysis process on a text and return the tokens breakdown of the + text. + + ``_ + + :param index: The name of the index to scope the operation + :param analyzer: + :param attributes: + :param char_filter: + :param explain: + :param field: + :param filter: + :param normalizer: + :param text: + :param tokenizer: + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_analyze" + else: + __path = "/_analyze" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analyzer is not None: + __body["analyzer"] = analyzer + if attributes is not None: + __body["attributes"] = attributes + if char_filter is not None: + __body["char_filter"] = char_filter + if error_trace is not None: + __query["error_trace"] = error_trace + if explain is not None: + __body["explain"] = explain + if field is not None: + __body["field"] = field + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if normalizer is not None: + __body["normalizer"] = normalizer + if pretty is not None: + __query["pretty"] = pretty + if text is not None: + __body["text"] = text + if tokenizer is not None: + __body["tokenizer"] = tokenizer + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def create( + self, + *, + index: str, + aliases: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + mappings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates an index with optional settings and mappings. + + ``_ + + :param index: The name of the index + :param aliases: + :param mappings: Mapping for fields in the index. If specified, this mapping + can include: - Field names - Field data types - Mapping parameters + :param master_timeout: Specify timeout for connection to master + :param settings: + :param timeout: Explicit operation timeout + :param wait_for_active_shards: Set the number of active shards to wait for before + the operation returns. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aliases is not None: + __body["aliases"] = aliases + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if mappings is not None: + __body["mappings"] = mappings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if settings is not None: + __body["settings"] = settings + if timeout is not None: + __query["timeout"] = timeout + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def create_data_stream( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a data stream + + ``_ + + :param name: Name of the data stream, which must meet the following criteria: + Lowercase only; Cannot include `\\`, `/`, `*`, `?`, `"`, `<`, `>`, `|`, `,`, + `#`, `:`, or a space character; Cannot start with `-`, `_`, `+`, or `.ds-`; + Cannot be `.` or `..`; Cannot be longer than 255 bytes. Multi-byte characters + count towards this limit faster. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def data_streams_stats( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Provides statistics on operations happening in a data stream. + + ``_ + + :param name: A comma-separated list of data stream names; use `_all` or empty + string to perform the operation on all data streams + :param expand_wildcards: + """ + if name not in SKIP_IN_PATH: + __path = f"/_data_stream/{_quote(name)}/_stats" + else: + __path = "/_data_stream/_stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an index. + + ``_ + + :param index: A comma-separated list of indices to delete; use `_all` or `*` + string to delete all indices + :param allow_no_indices: Ignore if a wildcard expression resolves to no concrete + indices (default: false) + :param expand_wildcards: Whether wildcard expressions should get expanded to + open, closed, or hidden indices + :param ignore_unavailable: Ignore unavailable indexes (default: false) + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_alias( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an alias. + + ``_ + + :param index: A comma-separated list of index names (supports wildcards); use + `_all` for all indices + :param name: A comma-separated list of aliases to delete (supports wildcards); + use `_all` to delete all aliases for the specified indices. + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit timestamp for the document + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_data_lifecycle( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes the data lifecycle of the selected data streams. + + ``_ + + :param name: A comma-separated list of data streams of which the data lifecycle + will be deleted; use `*` to get all data streams + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit timestamp for the document + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}/_lifecycle" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_data_stream( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a data stream. + + ``_ + + :param name: Comma-separated list of data streams to delete. Wildcard (`*`) expressions + are supported. + :param expand_wildcards: Type of data stream that wildcard patterns can match. + Supports comma-separated values,such as `open,hidden`. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_index_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an index template. + + ``_ + + :param name: Comma-separated list of index template names used to limit the request. + Wildcard (*) expressions are supported. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_template( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an index template. + + ``_ + + :param name: The name of the template + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def disk_usage( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flush: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + run_expensive_tasks: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Analyzes the disk usage of each field of an index or data stream + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. It’s recommended to execute this API with a single + index (or the latest backing index of a data stream) as the API consumes + resources significantly. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as open,hidden. + :param flush: If true, the API performs a flush before analysis. If false, the + response may not include uncommitted data. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param run_expensive_tasks: Analyzing field disk usage is resource-intensive. + To use the API, this parameter must be set to true. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_disk_usage" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flush is not None: + __query["flush"] = flush + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if pretty is not None: + __query["pretty"] = pretty + if run_expensive_tasks is not None: + __query["run_expensive_tasks"] = run_expensive_tasks + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def exists( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular index exists. + + ``_ + + :param index: A comma-separated list of index names + :param allow_no_indices: Ignore if a wildcard expression resolves to no concrete + indices (default: false) + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param flat_settings: Return settings in flat format (default: false) + :param ignore_unavailable: Ignore unavailable indexes (default: false) + :param include_defaults: Whether to return all default setting for each of the + indices. + :param local: Return local information, do not retrieve the state from master + node (default: false) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def exists_alias( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular alias exists. + + ``_ + + :param name: A comma-separated list of alias names to return + :param index: A comma-separated list of index names to filter aliases + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param local: Return local information, do not retrieve the state from master + node (default: false) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + elif name not in SKIP_IN_PATH: + __path = f"/_alias/{_quote(name)}" + else: + raise ValueError("Couldn't find a path for the given parameters") + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def exists_index_template( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular index template exists. + + ``_ + + :param name: Comma-separated list of index template names used to limit the request. + Wildcard (*) expressions are supported. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def exists_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular index template exists. + + ``_ + + :param name: The comma separated names of the index templates + :param flat_settings: Return settings in flat format (default: false) + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def explain_data_lifecycle( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the index's current DLM lifecycle, such as any potential + encountered error, time since creation etc. + + ``_ + + :param index: The name of the index to explain + :param include_defaults: indicates if the API should return the default values + the system uses for the index's lifecycle + :param master_timeout: Specify timeout for connection to master + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_lifecycle/explain" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + features: t.Optional[ + t.Union[ + t.Union["t.Literal['aliases', 'mappings', 'settings']", str], + t.Union[ + t.List[ + t.Union["t.Literal['aliases', 'mappings', 'settings']", str] + ], + t.Tuple[ + t.Union["t.Literal['aliases', 'mappings', 'settings']", str], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about one or more indices. + + ``_ + + :param index: Comma-separated list of data streams, indices, and index aliases + used to limit the request. Wildcard expressions (*) are supported. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as open,hidden. + :param features: Return only information on specified index features + :param flat_settings: If true, returns settings in flat format. + :param ignore_unavailable: If false, requests that target a missing index return + an error. + :param include_defaults: If true, return all default settings in the response. + :param local: If true, the request retrieves information from the local node + only. Defaults to false, which means information is retrieved from the master + node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if features is not None: + __query["features"] = features + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_alias( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns an alias. + + ``_ + + :param index: A comma-separated list of index names to filter aliases + :param name: A comma-separated list of alias names to return + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param local: Return local information, do not retrieve the state from master + node (default: false) + """ + if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_alias" + elif name not in SKIP_IN_PATH: + __path = f"/_alias/{_quote(name)}" + else: + __path = "/_alias" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_data_lifecycle( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the data lifecycle of the selected data streams. + + ``_ + + :param name: A comma-separated list of data streams to get; use `*` to get all + data streams + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param include_defaults: Return all relevant default configurations for the data + stream (default: false) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}/_lifecycle" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_data_stream( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns data streams. + + ``_ + + :param name: Comma-separated list of data stream names used to limit the request. + Wildcard (`*`) expressions are supported. If omitted, all data streams are + returned. + :param expand_wildcards: Type of data stream that wildcard patterns can match. + Supports comma-separated values, such as `open,hidden`. + :param include_defaults: If true, returns all relevant default configurations + for the index template. + """ + if name not in SKIP_IN_PATH: + __path = f"/_data_stream/{_quote(name)}" + else: + __path = "/_data_stream" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_index_template( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns an index template. + + ``_ + + :param name: Comma-separated list of index template names used to limit the request. + Wildcard (*) expressions are supported. + :param flat_settings: If true, returns settings in flat format. + :param include_defaults: If true, returns all relevant default configurations + for the index template. + :param local: If true, the request retrieves information from the local node + only. Defaults to false, which means information is retrieved from the master + node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name not in SKIP_IN_PATH: + __path = f"/_index_template/{_quote(name)}" + else: + __path = "/_index_template" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_mapping( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns mappings for one or more indices. + + ``_ + + :param index: A comma-separated list of index names + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Specify timeout for connection to master + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_mapping" + else: + __path = "/_mapping" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_settings( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns settings for one or more indices. + + ``_ + + :param index: A comma-separated list of index names; use `_all` or empty string + to perform the operation on all indices + :param name: The name of the settings that should be included + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param flat_settings: Return settings in flat format (default: false) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param include_defaults: Whether to return all default setting for each of the + indices. + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Specify timeout for connection to master + """ + if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_settings/{_quote(name)}" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_settings" + elif name not in SKIP_IN_PATH: + __path = f"/_settings/{_quote(name)}" + else: + __path = "/_settings" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_template( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns an index template. + + ``_ + + :param name: The comma separated names of the index templates + :param flat_settings: Return settings in flat format (default: false) + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + """ + if name not in SKIP_IN_PATH: + __path = f"/_template/{_quote(name)}" + else: + __path = "/_template" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def migrate_to_data_stream( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Migrates an alias to a data stream + + ``_ + + :param name: The name of the alias to migrate + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/_migrate/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def modify_data_stream( + self, + *, + actions: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Modifies a data stream + + ``_ + + :param actions: Actions to perform. + """ + if actions is None: + raise ValueError("Empty value passed for parameter 'actions'") + __path = "/_data_stream/_modify" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __body["actions"] = actions + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_alias( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + name: str, + error_trace: t.Optional[bool] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + index_routing: t.Optional[str] = None, + is_write_index: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + routing: t.Optional[str] = None, + search_routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates an alias. + + ``_ + + :param index: A comma-separated list of index names the alias should point to + (supports wildcards); use `_all` to perform the operation on all indices. + :param name: The name of the alias to be created or updated + :param filter: + :param index_routing: + :param is_write_index: + :param master_timeout: Specify timeout for connection to master + :param routing: + :param search_routing: + :param timeout: Explicit timestamp for the document + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if index_routing is not None: + __body["index_routing"] = index_routing + if is_write_index is not None: + __body["is_write_index"] = is_write_index + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if routing is not None: + __body["routing"] = routing + if search_routing is not None: + __body["search_routing"] = search_routing + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_data_lifecycle( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + data_retention: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the data lifecycle of the selected data streams. + + ``_ + + :param name: A comma-separated list of data streams whose lifecycle will be updated; + use `*` to set the lifecycle to all data streams + :param data_retention: + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit timestamp for the document + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}/_lifecycle" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if data_retention is not None: + __body["data_retention"] = data_retention + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + async def put_index_template( + self, + *, + name: str, + composed_of: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + create: t.Optional[bool] = None, + data_stream: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + index_patterns: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + priority: t.Optional[int] = None, + template: t.Optional[t.Mapping[str, t.Any]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates an index template. + + ``_ + + :param name: Index or template name + :param composed_of: + :param create: Whether the index template should only be added if new or can + also replace an existing one + :param data_stream: + :param index_patterns: + :param meta: + :param priority: + :param template: + :param version: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if composed_of is not None: + __body["composed_of"] = composed_of + if create is not None: + __query["create"] = create + if data_stream is not None: + __body["data_stream"] = data_stream + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if index_patterns is not None: + __body["index_patterns"] = index_patterns + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if priority is not None: + __body["priority"] = priority + if template is not None: + __body["template"] = template + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_field_names": "field_names", + "_meta": "meta", + "_routing": "routing", + "_source": "source", + }, + ) + async def put_mapping( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + date_detection: t.Optional[bool] = None, + dynamic: t.Optional[ + t.Union["t.Literal['false', 'runtime', 'strict', 'true']", str] + ] = None, + dynamic_date_formats: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + dynamic_templates: t.Optional[ + t.Union[ + t.Mapping[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Mapping[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Mapping[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + field_names: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + numeric_detection: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + properties: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + routing: t.Optional[t.Mapping[str, t.Any]] = None, + runtime: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + source: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + write_index_only: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the index mappings. + + ``_ + + :param index: A comma-separated list of index names the mapping should be added + to (supports wildcards); use `_all` or omit to add the mapping on all indices. + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param date_detection: Controls whether dynamic date detection is enabled. + :param dynamic: Controls whether new fields are added dynamically. + :param dynamic_date_formats: If date detection is enabled then new string fields + are checked against 'dynamic_date_formats' and if the value matches then + a new date field is added instead of string. + :param dynamic_templates: Specify dynamic templates for the mapping. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param field_names: Control whether field names are enabled for the index. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param master_timeout: Specify timeout for connection to master + :param meta: A mapping type can have custom meta data associated with it. These + are not used at all by Elasticsearch, but can be used to store application-specific + metadata. + :param numeric_detection: Automatically map strings into numeric data types for + all fields. + :param properties: Mapping for a field. For new fields, this mapping can include: + - Field name - Field data type - Mapping parameters + :param routing: Enable making a routing value required on indexed documents. + :param runtime: Mapping of runtime fields for the index. + :param source: Control whether the _source field is enabled on the index. + :param timeout: Explicit operation timeout + :param write_index_only: When true, applies mappings only to the write index + of an alias or data stream + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_mapping" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if date_detection is not None: + __body["date_detection"] = date_detection + if dynamic is not None: + __body["dynamic"] = dynamic + if dynamic_date_formats is not None: + __body["dynamic_date_formats"] = dynamic_date_formats + if dynamic_templates is not None: + __body["dynamic_templates"] = dynamic_templates + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if field_names is not None: + __body["_field_names"] = field_names + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if numeric_detection is not None: + __body["numeric_detection"] = numeric_detection + if pretty is not None: + __query["pretty"] = pretty + if properties is not None: + __body["properties"] = properties + if routing is not None: + __body["_routing"] = routing + if runtime is not None: + __body["runtime"] = runtime + if source is not None: + __body["_source"] = source + if timeout is not None: + __query["timeout"] = timeout + if write_index_only is not None: + __query["write_index_only"] = write_index_only + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="settings", + ) + async def put_settings( + self, + *, + settings: t.Mapping[str, t.Any], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + preserve_existing: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the index settings. + + ``_ + + :param settings: + :param index: A comma-separated list of index names; use `_all` or empty string + to perform the operation on all indices + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param flat_settings: Return settings in flat format (default: false) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param master_timeout: Specify timeout for connection to master + :param preserve_existing: Whether to update existing settings. If set to `true` + existing settings on an index remain unchanged, the default is `false` + :param timeout: Explicit operation timeout + """ + if settings is None: + raise ValueError("Empty value passed for parameter 'settings'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_settings" + else: + __path = "/_settings" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if preserve_existing is not None: + __query["preserve_existing"] = preserve_existing + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __body = settings + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_template( + self, + *, + name: str, + aliases: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + index_patterns: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + mappings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + order: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates an index template. + + ``_ + + :param name: The name of the template + :param aliases: Aliases for the index. + :param create: If true, this request cannot replace or update existing index + templates. + :param flat_settings: + :param index_patterns: Array of wildcard expressions used to match the names + of indices during creation. + :param mappings: Mapping for fields in the index. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param order: Order in which Elasticsearch applies this template if index matches + multiple templates. Templates with lower 'order' values are merged first. + Templates with higher 'order' values are merged later, overriding templates + with lower values. + :param settings: Configuration options for the index. + :param timeout: + :param version: Version number used to manage index templates externally. This + number is not automatically generated by Elasticsearch. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_template/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aliases is not None: + __body["aliases"] = aliases + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if index_patterns is not None: + __body["index_patterns"] = index_patterns + if mappings is not None: + __body["mappings"] = mappings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if order is not None: + __body["order"] = order + if pretty is not None: + __query["pretty"] = pretty + if settings is not None: + __body["settings"] = settings + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def resolve_index( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about any matching indices, aliases, and data streams + + ``_ + + :param name: A comma-separated list of names or wildcard expressions + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_resolve/index/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def rollover( + self, + *, + alias: str, + new_index: t.Optional[str] = None, + aliases: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + conditions: t.Optional[t.Mapping[str, t.Any]] = None, + dry_run: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + mappings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates an alias to point to a new index when the existing index is considered + to be too large or too old. + + ``_ + + :param alias: The name of the alias to rollover + :param new_index: The name of the rollover index + :param aliases: + :param conditions: + :param dry_run: If set to true the rollover action will only be validated but + not actually performed even if a condition matches. The default is false + :param mappings: + :param master_timeout: Specify timeout for connection to master + :param settings: + :param timeout: Explicit operation timeout + :param wait_for_active_shards: Set the number of active shards to wait for on + the newly created rollover index before the operation returns. + """ + if alias in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'alias'") + if alias not in SKIP_IN_PATH and new_index not in SKIP_IN_PATH: + __path = f"/{_quote(alias)}/_rollover/{_quote(new_index)}" + elif alias not in SKIP_IN_PATH: + __path = f"/{_quote(alias)}/_rollover" + else: + raise ValueError("Couldn't find a path for the given parameters") + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aliases is not None: + __body["aliases"] = aliases + if conditions is not None: + __body["conditions"] = conditions + if dry_run is not None: + __query["dry_run"] = dry_run + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if mappings is not None: + __body["mappings"] = mappings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if settings is not None: + __body["settings"] = settings + if timeout is not None: + __query["timeout"] = timeout + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + async def simulate_index_template( + self, + *, + name: str, + allow_auto_create: t.Optional[bool] = None, + composed_of: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + create: t.Optional[bool] = None, + data_stream: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + index_patterns: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + priority: t.Optional[int] = None, + template: t.Optional[t.Mapping[str, t.Any]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Simulate matching the given index name against the index templates in the system + + ``_ + + :param name: Index or template name to simulate + :param allow_auto_create: + :param composed_of: + :param create: If `true`, the template passed in the body is only used if no + existing templates match the same index patterns. If `false`, the simulation + uses the template with the highest priority. Note that the template is not + permanently added or updated in either case; it is only used for the simulation. + :param data_stream: + :param include_defaults: If true, returns all relevant default configurations + for the index template. + :param index_patterns: + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param meta: + :param priority: + :param template: + :param version: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/_simulate_index/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_auto_create is not None: + __body["allow_auto_create"] = allow_auto_create + if composed_of is not None: + __body["composed_of"] = composed_of + if create is not None: + __query["create"] = create + if data_stream is not None: + __body["data_stream"] = data_stream + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if index_patterns is not None: + __body["index_patterns"] = index_patterns + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if priority is not None: + __body["priority"] = priority + if template is not None: + __body["template"] = template + if version is not None: + __body["version"] = version + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="template", + ) + async def simulate_template( + self, + *, + name: t.Optional[str] = None, + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + template: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Simulate resolving the given template name or body + + ``_ + + :param name: Name of the index template to simulate. To test a template configuration + before you add it to the cluster, omit this parameter and specify the template + configuration in the request body. + :param create: If true, the template passed in the body is only used if no existing + templates match the same index patterns. If false, the simulation uses the + template with the highest priority. Note that the template is not permanently + added or updated in either case; it is only used for the simulation. + :param include_defaults: If true, returns all relevant default configurations + for the index template. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param template: + """ + if name not in SKIP_IN_PATH: + __path = f"/_index_template/_simulate/{_quote(name)}" + else: + __path = "/_index_template/_simulate" + __query: t.Dict[str, t.Any] = {} + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __body = template + if not __body: + __body = None + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def update_aliases( + self, + *, + actions: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates index aliases. + + ``_ + + :param actions: + :param master_timeout: Specify timeout for connection to master + :param timeout: Request timeout + """ + __path = "/_aliases" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __body["actions"] = actions + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def validate_query( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + all_shards: t.Optional[bool] = None, + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + rewrite: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows a user to validate a potentially expensive query without executing it. + + ``_ + + :param index: A comma-separated list of index names to restrict the operation; + use `_all` or empty string to perform the operation on all indices + :param all_shards: Execute validation on all shards instead of one random shard + per index + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: Return detailed information about the error + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param q: Query in the Lucene query string syntax + :param query: + :param rewrite: Provide a more detailed explanation showing the actual Lucene + query that will be executed. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_validate/query" + else: + __path = "/_validate/query" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if all_shards is not None: + __query["all_shards"] = all_shards + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __query["explain"] = explain + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if rewrite is not None: + __query["rewrite"] = rewrite + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/ingest.py b/elasticsearch_serverless/_async/client/ingest.py new file mode 100644 index 0000000..7d1f4a3 --- /dev/null +++ b/elasticsearch_serverless/_async/client/ingest.py @@ -0,0 +1,295 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class IngestClient(NamespacedClient): + @_rewrite_parameters() + async def delete_pipeline( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a pipeline. + + ``_ + + :param id: Pipeline ID + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: Explicit operation timeout + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ingest/pipeline/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_pipeline( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + summary: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a pipeline. + + ``_ + + :param id: Comma separated list of pipeline ids. Wildcards supported + :param master_timeout: Explicit operation timeout for connection to master node + :param summary: Return pipelines without their definitions (default: false) + """ + if id not in SKIP_IN_PATH: + __path = f"/_ingest/pipeline/{_quote(id)}" + else: + __path = "/_ingest/pipeline" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if summary is not None: + __query["summary"] = summary + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def processor_grok( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a list of the built-in patterns. + + ``_ + """ + __path = "/_ingest/processor/grok" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + async def put_pipeline( + self, + *, + id: str, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_version: t.Optional[int] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + on_failure: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + pretty: t.Optional[bool] = None, + processors: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a pipeline. + + ``_ + + :param id: ID of the ingest pipeline to create or update. + :param description: Description of the ingest pipeline. + :param if_version: Required version for optimistic concurrency control for pipeline + updates + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param meta: Optional metadata about the ingest pipeline. May have any contents. + This map is not automatically generated by Elasticsearch. + :param on_failure: Processors to run immediately after a processor failure. Each + processor supports a processor-level `on_failure` value. If a processor without + an `on_failure` value fails, Elasticsearch uses this pipeline-level parameter + as a fallback. The processors in this parameter run sequentially in the order + specified. Elasticsearch will not attempt to run the pipeline's remaining + processors. + :param processors: Processors used to perform transformations on documents before + indexing. Processors run sequentially in the order specified. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param version: Version number used by external systems to track ingest pipelines. + This parameter is intended for external systems only. Elasticsearch does + not use or validate pipeline version numbers. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ingest/pipeline/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_version is not None: + __query["if_version"] = if_version + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if on_failure is not None: + __body["on_failure"] = on_failure + if pretty is not None: + __query["pretty"] = pretty + if processors is not None: + __body["processors"] = processors + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def simulate( + self, + *, + id: t.Optional[str] = None, + docs: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pipeline: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + verbose: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to simulate a pipeline with example documents. + + ``_ + + :param id: Pipeline ID + :param docs: + :param pipeline: + :param verbose: Verbose mode. Display data output for each processor in executed + pipeline + """ + if id not in SKIP_IN_PATH: + __path = f"/_ingest/pipeline/{_quote(id)}/_simulate" + else: + __path = "/_ingest/pipeline/_simulate" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if docs is not None: + __body["docs"] = docs + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pipeline is not None: + __body["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if verbose is not None: + __query["verbose"] = verbose + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/license.py b/elasticsearch_serverless/_async/client/license.py new file mode 100644 index 0000000..77ff833 --- /dev/null +++ b/elasticsearch_serverless/_async/client/license.py @@ -0,0 +1,294 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class LicenseClient(NamespacedClient): + @_rewrite_parameters() + async def delete( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes licensing information for the cluster + + ``_ + """ + __path = "/_license" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + accept_enterprise: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves licensing information for the cluster + + ``_ + + :param accept_enterprise: If `true`, this parameter returns enterprise for Enterprise + license types. If `false`, this parameter returns platinum for both platinum + and enterprise license types. This behavior is maintained for backwards compatibility. + This parameter is deprecated and will always be set to true in 8.x. + :param local: Specifies whether to retrieve local information. The default value + is `false`, which means the information is retrieved from the master node. + """ + __path = "/_license" + __query: t.Dict[str, t.Any] = {} + if accept_enterprise is not None: + __query["accept_enterprise"] = accept_enterprise + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_basic_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the status of the basic license. + + ``_ + """ + __path = "/_license/basic_status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_trial_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the status of the trial license. + + ``_ + """ + __path = "/_license/trial_status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def post( + self, + *, + acknowledge: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + license: t.Optional[t.Mapping[str, t.Any]] = None, + licenses: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the license for the cluster. + + ``_ + + :param acknowledge: Specifies whether you acknowledge the license changes. + :param license: + :param licenses: A sequence of one or more JSON documents containing the license + information. + """ + __path = "/_license" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if acknowledge is not None: + __query["acknowledge"] = acknowledge + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if license is not None: + __body["license"] = license + if licenses is not None: + __body["licenses"] = licenses + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def post_start_basic( + self, + *, + acknowledge: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts an indefinite basic license. + + ``_ + + :param acknowledge: whether the user has acknowledged acknowledge messages (default: + false) + """ + __path = "/_license/start_basic" + __query: t.Dict[str, t.Any] = {} + if acknowledge is not None: + __query["acknowledge"] = acknowledge + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def post_start_trial( + self, + *, + acknowledge: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + type_query_string: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + starts a limited time trial license. + + ``_ + + :param acknowledge: whether the user has acknowledged acknowledge messages (default: + false) + :param type_query_string: + """ + __path = "/_license/start_trial" + __query: t.Dict[str, t.Any] = {} + if acknowledge is not None: + __query["acknowledge"] = acknowledge + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if type_query_string is not None: + __query["type_query_string"] = type_query_string + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/logstash.py b/elasticsearch_serverless/_async/client/logstash.py new file mode 100644 index 0000000..4524743 --- /dev/null +++ b/elasticsearch_serverless/_async/client/logstash.py @@ -0,0 +1,143 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class LogstashClient(NamespacedClient): + @_rewrite_parameters() + async def delete_pipeline( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes Logstash Pipelines used by Central Management + + ``_ + + :param id: The ID of the Pipeline + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_logstash/pipeline/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_pipeline( + self, + *, + id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves Logstash Pipelines used by Central Management + + ``_ + + :param id: A comma-separated list of Pipeline IDs + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if id not in SKIP_IN_PATH: + __path = f"/_logstash/pipeline/{_quote(id)}" + else: + __path = "/_logstash/pipeline" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="pipeline", + ) + async def put_pipeline( + self, + *, + id: str, + pipeline: t.Mapping[str, t.Any], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Adds and updates Logstash Pipelines used for Central Management + + ``_ + + :param id: The ID of the Pipeline + :param pipeline: + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if pipeline is None: + raise ValueError("Empty value passed for parameter 'pipeline'") + __path = f"/_logstash/pipeline/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = pipeline + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/migration.py b/elasticsearch_serverless/_async/client/migration.py new file mode 100644 index 0000000..8ba52c0 --- /dev/null +++ b/elasticsearch_serverless/_async/client/migration.py @@ -0,0 +1,127 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class MigrationClient(NamespacedClient): + @_rewrite_parameters() + async def deprecations( + self, + *, + index: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about different cluster, node, and index level settings + that use deprecated features that will be removed or changed in the next major + version. + + ``_ + + :param index: Comma-separate list of data streams or indices to check. Wildcard + (*) expressions are supported. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_migration/deprecations" + else: + __path = "/_migration/deprecations" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_feature_upgrade_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Find out whether system features need to be upgraded or not + + ``_ + """ + __path = "/_migration/system_features" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def post_feature_upgrade( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Begin upgrades for system features + + ``_ + """ + __path = "/_migration/system_features" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/ml.py b/elasticsearch_serverless/_async/client/ml.py new file mode 100644 index 0000000..1df92fb --- /dev/null +++ b/elasticsearch_serverless/_async/client/ml.py @@ -0,0 +1,3306 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class MlClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + async def close_job( + self, + *, + job_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Closes one or more anomaly detection jobs. A job can be opened and closed multiple + times throughout its lifecycle. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, or a wildcard expression. You can close multiple anomaly detection + jobs in a single API request by using a group name, a comma-separated list + of jobs, or a wildcard expression. You can close all jobs by using `_all` + or by specifying `*` as the job identifier. + :param allow_no_match: Refer to the description for the `allow_no_match` query + parameter. + :param force: Refer to the descriptiion for the `force` query parameter. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_close" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __body["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def delete_calendar( + self, + *, + calendar_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_calendar_event( + self, + *, + calendar_id: str, + event_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes scheduled events from a calendar. + + ``_ + + :param calendar_id: The ID of the calendar to modify + :param event_id: The ID of the event to remove from the calendar + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if event_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'event_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/events/{_quote(event_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_calendar_job( + self, + *, + calendar_id: str, + job_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes anomaly detection jobs from a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param job_id: An identifier for the anomaly detection jobs. It can be a job + identifier, a group name, or a comma-separated list of jobs or groups. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/jobs/{_quote(job_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_data_frame_analytics( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. + :param force: If `true`, it deletes a job that is not stopped; this method is + quicker than stopping and deleting the job. + :param timeout: The time to wait for the job to be deleted. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_datafeed( + self, + *, + datafeed_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param force: Use to forcefully delete a started datafeed; this method is quicker + than stopping and deleting the datafeed. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_filter( + self, + *, + filter_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a filter. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + """ + if filter_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'filter_id'") + __path = f"/_ml/filters/{_quote(filter_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_job( + self, + *, + job_id: str, + delete_user_annotations: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing anomaly detection job. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param delete_user_annotations: Specifies whether annotations that have been + added by the user should be deleted along with any auto-generated annotations + when the job is reset. + :param force: Use to forcefully delete an opened job; this method is quicker + than closing and deleting the job. + :param wait_for_completion: Specifies whether the request should return immediately + or wait until the job deletion completes. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}" + __query: t.Dict[str, t.Any] = {} + if delete_user_annotations is not None: + __query["delete_user_annotations"] = delete_user_annotations + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_trained_model( + self, + *, + model_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing trained inference model that is currently not referenced + by an ingest pipeline. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param force: Forcefully deletes a trained model that is referenced by ingest + pipelines or has a started deployment. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_trained_model_alias( + self, + *, + model_id: str, + model_alias: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a model alias that refers to the trained model + + ``_ + + :param model_id: The trained model ID to which the model alias refers. + :param model_alias: The model alias to delete. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if model_alias in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_alias'") + __path = f"/_ml/trained_models/{_quote(model_id)}/model_aliases/{_quote(model_alias)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def estimate_model_memory( + self, + *, + analysis_config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_bucket_cardinality: t.Optional[t.Mapping[str, int]] = None, + overall_cardinality: t.Optional[t.Mapping[str, int]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Estimates the model memory + + ``_ + + :param analysis_config: For a list of the properties that you can specify in + the `analysis_config` component of the body of this API. + :param max_bucket_cardinality: Estimates of the highest cardinality in a single + bucket that is observed for influencer fields over the time period that the + job analyzes data. To produce a good answer, values must be provided for + all influencer fields. Providing values for fields that are not listed as + `influencers` has no effect on the estimation. + :param overall_cardinality: Estimates of the cardinality that is observed for + fields over the whole time period that the job analyzes data. To produce + a good answer, values must be provided for fields referenced in the `by_field_name`, + `over_field_name` and `partition_field_name` of any detectors. Providing + values for other fields has no effect on the estimation. It can be omitted + from the request if no detectors have a `by_field_name`, `over_field_name` + or `partition_field_name`. + """ + __path = "/_ml/anomaly_detectors/_estimate_model_memory" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analysis_config is not None: + __body["analysis_config"] = analysis_config + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_bucket_cardinality is not None: + __body["max_bucket_cardinality"] = max_bucket_cardinality + if overall_cardinality is not None: + __body["overall_cardinality"] = overall_cardinality + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def evaluate_data_frame( + self, + *, + evaluation: t.Mapping[str, t.Any], + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Evaluates the data frame analytics for an annotated index. + + ``_ + + :param evaluation: Defines the type of evaluation you want to perform. + :param index: Defines the `index` in which the evaluation will be performed. + :param query: A query clause that retrieves a subset of data from the source + index. + """ + if evaluation is None: + raise ValueError("Empty value passed for parameter 'evaluation'") + if index is None: + raise ValueError("Empty value passed for parameter 'index'") + __path = "/_ml/data_frame/_evaluate" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if evaluation is not None: + __body["evaluation"] = evaluation + if index is not None: + __body["index"] = index + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def flush_job( + self, + *, + job_id: str, + advance_time: t.Optional[t.Union[str, t.Any]] = None, + calc_interim: t.Optional[bool] = None, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + skip_time: t.Optional[t.Union[str, t.Any]] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Forces any buffered data to be processed by the job. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param advance_time: Refer to the description for the `advance_time` query parameter. + :param calc_interim: Refer to the description for the `calc_interim` query parameter. + :param end: Refer to the description for the `end` query parameter. + :param skip_time: Refer to the description for the `skip_time` query parameter. + :param start: Refer to the description for the `start` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_flush" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if advance_time is not None: + __body["advance_time"] = advance_time + if calc_interim is not None: + __body["calc_interim"] = calc_interim + if end is not None: + __body["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if skip_time is not None: + __body["skip_time"] = skip_time + if start is not None: + __body["start"] = start + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_calendar_events( + self, + *, + calendar_id: str, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + job_id: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the scheduled events in calendars. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. You can get + information for multiple calendars by using a comma-separated list of ids + or a wildcard expression. You can get information for all calendars by using + `_all` or `*` or by omitting the calendar identifier. + :param end: Specifies to get events with timestamps earlier than this time. + :param from_: Skips the specified number of events. + :param job_id: Specifies to get events for a specific anomaly detection job identifier + or job group. It must be used with a calendar identifier of `_all` or `*`. + :param size: Specifies the maximum number of events to obtain. + :param start: Specifies to get events with timestamps after this time. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/events" + __query: t.Dict[str, t.Any] = {} + if end is not None: + __query["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if job_id is not None: + __query["job_id"] = job_id + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if start is not None: + __query["start"] = start + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + async def get_calendars( + self, + *, + calendar_id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + page: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for calendars. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. You can get + information for multiple calendars by using a comma-separated list of ids + or a wildcard expression. You can get information for all calendars by using + `_all` or `*` or by omitting the calendar identifier. + :param from_: Skips the specified number of calendars. This parameter is supported + only when you omit the calendar identifier. + :param page: This object is supported only when you omit the calendar identifier. + :param size: Specifies the maximum number of calendars to obtain. This parameter + is supported only when you omit the calendar identifier. + """ + if calendar_id not in SKIP_IN_PATH: + __path = f"/_ml/calendars/{_quote(calendar_id)}" + else: + __path = "/_ml/calendars" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if page is not None: + __body["page"] = page + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_data_frame_analytics( + self, + *, + id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for data frame analytics jobs. + + ``_ + + :param id: Identifier for the data frame analytics job. If you do not specify + this option, the API returns information for the first hundred data frame + analytics jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no data frame analytics jobs that match. 2. Contains + the `_all` string or no identifiers and there are no matches. 3. Contains + wildcard expressions and there are only partial matches. The default value + returns an empty data_frame_analytics array when there are no matches and + the subset of results when there are partial matches. If this parameter is + `false`, the request returns a 404 status code when there are no matches + or only partial matches. + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + :param from_: Skips the specified number of data frame analytics jobs. + :param size: Specifies the maximum number of data frame analytics jobs to obtain. + """ + if id not in SKIP_IN_PATH: + __path = f"/_ml/data_frame/analytics/{_quote(id)}" + else: + __path = "/_ml/data_frame/analytics" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_data_frame_analytics_stats( + self, + *, + id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + verbose: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for data frame analytics jobs. + + ``_ + + :param id: Identifier for the data frame analytics job. If you do not specify + this option, the API returns information for the first hundred data frame + analytics jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no data frame analytics jobs that match. 2. Contains + the `_all` string or no identifiers and there are no matches. 3. Contains + wildcard expressions and there are only partial matches. The default value + returns an empty data_frame_analytics array when there are no matches and + the subset of results when there are partial matches. If this parameter is + `false`, the request returns a 404 status code when there are no matches + or only partial matches. + :param from_: Skips the specified number of data frame analytics jobs. + :param size: Specifies the maximum number of data frame analytics jobs to obtain. + :param verbose: Defines whether the stats response should be verbose. + """ + if id not in SKIP_IN_PATH: + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_stats" + else: + __path = "/_ml/data_frame/analytics/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if verbose is not None: + __query["verbose"] = verbose + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_datafeed_stats( + self, + *, + datafeed_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for datafeeds. + + ``_ + + :param datafeed_id: Identifier for the datafeed. It can be a datafeed identifier + or a wildcard expression. If you do not specify one of these options, the + API returns information about all datafeeds. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no datafeeds that match. 2. Contains the `_all` + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. The default value is `true`, which returns + an empty `datafeeds` array when there are no matches and the subset of results + when there are partial matches. If this parameter is `false`, the request + returns a `404` status code when there are no matches or only partial matches. + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_stats" + else: + __path = "/_ml/datafeeds/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_datafeeds( + self, + *, + datafeed_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for datafeeds. + + ``_ + + :param datafeed_id: Identifier for the datafeed. It can be a datafeed identifier + or a wildcard expression. If you do not specify one of these options, the + API returns information about all datafeeds. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no datafeeds that match. 2. Contains the `_all` + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. The default value is `true`, which returns + an empty `datafeeds` array when there are no matches and the subset of results + when there are partial matches. If this parameter is `false`, the request + returns a `404` status code when there are no matches or only partial matches. + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}" + else: + __path = "/_ml/datafeeds" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_filters( + self, + *, + filter_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves filters. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + :param from_: Skips the specified number of filters. + :param size: Specifies the maximum number of filters to obtain. + """ + if filter_id not in SKIP_IN_PATH: + __path = f"/_ml/filters/{_quote(filter_id)}" + else: + __path = "/_ml/filters" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_job_stats( + self, + *, + job_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, a comma-separated list of jobs, or a wildcard expression. If + you do not specify one of these options, the API returns information for + all anomaly detection jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no jobs that match. 2. Contains the _all string + or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty `jobs` + array when there are no matches and the subset of results when there are + partial matches. If `false`, the API returns a `404` status code when there + are no matches or only partial matches. + """ + if job_id not in SKIP_IN_PATH: + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_stats" + else: + __path = "/_ml/anomaly_detectors/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_jobs( + self, + *, + job_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, or a wildcard expression. If you do not specify one of these + options, the API returns information for all anomaly detection jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no jobs that match. 2. Contains the _all string + or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. The default value is `true`, which returns + an empty `jobs` array when there are no matches and the subset of results + when there are partial matches. If this parameter is `false`, the request + returns a `404` status code when there are no matches or only partial matches. + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + """ + if job_id not in SKIP_IN_PATH: + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}" + else: + __path = "/_ml/anomaly_detectors" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def get_overall_buckets( + self, + *, + job_id: str, + allow_no_match: t.Optional[bool] = None, + bucket_span: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + exclude_interim: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + overall_score: t.Optional[t.Union[float, str]] = None, + pretty: t.Optional[bool] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + top_n: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves overall bucket results that summarize the bucket results of multiple + anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, a comma-separated list of jobs or groups, or a wildcard expression. + You can summarize the bucket results for all anomaly detection jobs by using + `_all` or by specifying `*` as the ``. + :param allow_no_match: Refer to the description for the `allow_no_match` query + parameter. + :param bucket_span: Refer to the description for the `bucket_span` query parameter. + :param end: Refer to the description for the `end` query parameter. + :param exclude_interim: Refer to the description for the `exclude_interim` query + parameter. + :param overall_score: Refer to the description for the `overall_score` query + parameter. + :param start: Refer to the description for the `start` query parameter. + :param top_n: Refer to the description for the `top_n` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/results/overall_buckets" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if bucket_span is not None: + __body["bucket_span"] = bucket_span + if end is not None: + __body["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_interim is not None: + __body["exclude_interim"] = exclude_interim + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if overall_score is not None: + __body["overall_score"] = overall_score + if pretty is not None: + __query["pretty"] = pretty + if start is not None: + __body["start"] = start + if top_n is not None: + __body["top_n"] = top_n + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_trained_models( + self, + *, + model_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + decompress_definition: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + include: t.Optional[ + t.Union[ + "t.Literal['definition', 'definition_status', 'feature_importance_baseline', 'hyperparameters', 'total_feature_importance']", + str, + ] + ] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + tags: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for a trained inference model. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param allow_no_match: Specifies what to do when the request: - Contains wildcard + expressions and there are no models that match. - Contains the _all string + or no identifiers and there are no matches. - Contains wildcard expressions + and there are only partial matches. If true, it returns an empty array when + there are no matches and the subset of results when there are partial matches. + :param decompress_definition: Specifies whether the included model definition + should be returned as a JSON map (true) or in a custom compressed format + (false). + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + :param from_: Skips the specified number of models. + :param include: A comma delimited string of optional fields to include in the + response body. + :param size: Specifies the maximum number of models to obtain. + :param tags: A comma delimited string of tags. A trained model can have many + tags, or none. When supplied, only trained models that contain all the supplied + tags are returned. + """ + if model_id not in SKIP_IN_PATH: + __path = f"/_ml/trained_models/{_quote(model_id)}" + else: + __path = "/_ml/trained_models" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if decompress_definition is not None: + __query["decompress_definition"] = decompress_definition + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if include is not None: + __query["include"] = include + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if tags is not None: + __query["tags"] = tags + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_trained_models_stats( + self, + *, + model_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for trained inference models. + + ``_ + + :param model_id: The unique identifier of the trained model or a model alias. + It can be a comma-separated list or a wildcard expression. + :param allow_no_match: Specifies what to do when the request: - Contains wildcard + expressions and there are no models that match. - Contains the _all string + or no identifiers and there are no matches. - Contains wildcard expressions + and there are only partial matches. If true, it returns an empty array when + there are no matches and the subset of results when there are partial matches. + :param from_: Skips the specified number of models. + :param size: Specifies the maximum number of models to obtain. + """ + if model_id not in SKIP_IN_PATH: + __path = f"/_ml/trained_models/{_quote(model_id)}/_stats" + else: + __path = "/_ml/trained_models/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def open_job( + self, + *, + job_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Opens one or more anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_open" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def post_calendar_events( + self, + *, + calendar_id: str, + events: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Posts scheduled events in a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param events: A list of one of more scheduled events. The event’s start and + end times can be specified as integer milliseconds since the epoch or as + a string in ISO 8601 format. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if events is None: + raise ValueError("Empty value passed for parameter 'events'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/events" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if events is not None: + __body["events"] = events + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def preview_data_frame_analytics( + self, + *, + id: t.Optional[str] = None, + config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Previews that will be analyzed given a data frame analytics config. + + ``_ + + :param id: Identifier for the data frame analytics job. + :param config: A data frame analytics config as described in create data frame + analytics jobs. Note that `id` and `dest` don’t need to be provided in the + context of this API. + """ + if id not in SKIP_IN_PATH: + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_preview" + else: + __path = "/_ml/data_frame/analytics/_preview" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if config is not None: + __body["config"] = config + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def preview_datafeed( + self, + *, + datafeed_id: t.Optional[str] = None, + datafeed_config: t.Optional[t.Mapping[str, t.Any]] = None, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + job_config: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Previews a datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. NOTE: If you use this path parameter, you cannot provide datafeed + or anomaly detection job configuration details in the request body. + :param datafeed_config: The datafeed definition to preview. + :param end: The end time when the datafeed preview should stop + :param job_config: The configuration details for the anomaly detection job that + is associated with the datafeed. If the `datafeed_config` object does not + include a `job_id` that references an existing anomaly detection job, you + must supply this `job_config` object. If you include both a `job_id` and + a `job_config`, the latter information is used. You cannot specify a `job_config` + object unless you also supply a `datafeed_config` object. + :param start: The start time from where the datafeed preview should begin + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_preview" + else: + __path = "/_ml/datafeeds/_preview" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if datafeed_config is not None: + __body["datafeed_config"] = datafeed_config + if end is not None: + __query["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if job_config is not None: + __body["job_config"] = job_config + if pretty is not None: + __query["pretty"] = pretty + if start is not None: + __query["start"] = start + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_calendar( + self, + *, + calendar_id: str, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + job_ids: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param description: A description of the calendar. + :param job_ids: An array of anomaly detection job identifiers. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if job_ids is not None: + __body["job_ids"] = job_ids + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def put_calendar_job( + self, + *, + calendar_id: str, + job_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Adds an anomaly detection job to a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param job_id: An identifier for the anomaly detection jobs. It can be a job + identifier, a group name, or a comma-separated list of jobs or groups. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/jobs/{_quote(job_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"headers"}, + ) + async def put_data_frame_analytics( + self, + *, + id: str, + analysis: t.Mapping[str, t.Any], + dest: t.Mapping[str, t.Any], + source: t.Mapping[str, t.Any], + allow_lazy_start: t.Optional[bool] = None, + analyzed_fields: t.Optional[t.Mapping[str, t.Any]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + headers: t.Optional[ + t.Mapping[str, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + human: t.Optional[bool] = None, + max_num_threads: t.Optional[int] = None, + model_memory_limit: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + version: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param analysis: The analysis configuration, which contains the information necessary + to perform one of the following types of analysis: classification, outlier + detection, or regression. + :param dest: The destination configuration. + :param source: The configuration of how to source the analysis data. + :param allow_lazy_start: Specifies whether this job can start when there is insufficient + machine learning node capacity for it to be immediately assigned to a node. + If set to `false` and a machine learning node with capacity to run the job + cannot be immediately found, the API returns an error. If set to `true`, + the API does not return an error; the job waits in the `starting` state until + sufficient machine learning node capacity is available. This behavior is + also affected by the cluster-wide `xpack.ml.max_lazy_ml_nodes` setting. + :param analyzed_fields: Specifies `includes` and/or `excludes` patterns to select + which fields will be included in the analysis. The patterns specified in + `excludes` are applied last, therefore `excludes` takes precedence. In other + words, if the same field is specified in both `includes` and `excludes`, + then the field will not be included in the analysis. If `analyzed_fields` + is not set, only the relevant fields will be included. For example, all the + numeric fields for outlier detection. The supported fields vary for each + type of analysis. Outlier detection requires numeric or `boolean` data to + analyze. The algorithms don’t support missing values therefore fields that + have data types other than numeric or boolean are ignored. Documents where + included fields contain missing values, null values, or an array are also + ignored. Therefore the `dest` index may contain documents that don’t have + an outlier score. Regression supports fields that are numeric, `boolean`, + `text`, `keyword`, and `ip` data types. It is also tolerant of missing values. + Fields that are supported are included in the analysis, other fields are + ignored. Documents where included fields contain an array with two or more + values are also ignored. Documents in the `dest` index that don’t contain + a results field are not included in the regression analysis. Classification + supports fields that are numeric, `boolean`, `text`, `keyword`, and `ip` + data types. It is also tolerant of missing values. Fields that are supported + are included in the analysis, other fields are ignored. Documents where included + fields contain an array with two or more values are also ignored. Documents + in the `dest` index that don’t contain a results field are not included in + the classification analysis. Classification analysis can be improved by mapping + ordinal variable values to a single number. For example, in case of age ranges, + you can model the values as `0-14 = 0`, `15-24 = 1`, `25-34 = 2`, and so + on. + :param description: A description of the job. + :param headers: + :param max_num_threads: The maximum number of threads to be used by the analysis. + Using more threads may decrease the time necessary to complete the analysis + at the cost of using more CPU. Note that the process may use additional threads + for operational functionality other than the analysis itself. + :param model_memory_limit: The approximate maximum amount of memory resources + that are permitted for analytical processing. If your `elasticsearch.yml` + file contains an `xpack.ml.max_model_memory_limit` setting, an error occurs + when you try to create data frame analytics jobs that have `model_memory_limit` + values greater than that setting. + :param version: + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if analysis is None: + raise ValueError("Empty value passed for parameter 'analysis'") + if dest is None: + raise ValueError("Empty value passed for parameter 'dest'") + if source is None: + raise ValueError("Empty value passed for parameter 'source'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analysis is not None: + __body["analysis"] = analysis + if dest is not None: + __body["dest"] = dest + if source is not None: + __body["source"] = source + if allow_lazy_start is not None: + __body["allow_lazy_start"] = allow_lazy_start + if analyzed_fields is not None: + __body["analyzed_fields"] = analyzed_fields + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if headers is not None: + __body["headers"] = headers + if human is not None: + __query["human"] = human + if max_num_threads is not None: + __body["max_num_threads"] = max_num_threads + if model_memory_limit is not None: + __body["model_memory_limit"] = model_memory_limit + if pretty is not None: + __query["pretty"] = pretty + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"headers"}, + ) + async def put_datafeed( + self, + *, + datafeed_id: str, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + chunking_config: t.Optional[t.Mapping[str, t.Any]] = None, + delayed_data_check_config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + headers: t.Optional[ + t.Mapping[str, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indexes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + indices: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + indices_options: t.Optional[t.Mapping[str, t.Any]] = None, + job_id: t.Optional[str] = None, + max_empty_searches: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + query_delay: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll_size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param aggregations: If set, the datafeed performs aggregation searches. Support + for aggregations is limited and should be used only with low cardinality + data. + :param allow_no_indices: If true, wildcard indices expressions that resolve into + no concrete indices are ignored. This includes the `_all` string or when + no indices are specified. + :param chunking_config: Datafeeds might be required to search over long time + periods, for several months or years. This search is split into time chunks + in order to ensure the load on Elasticsearch is managed. Chunking configuration + controls how the size of these time chunks are calculated; it is an advanced + configuration option. + :param delayed_data_check_config: Specifies whether the datafeed checks for missing + data and the size of the window. The datafeed can optionally search over + indices that have already been read in an effort to determine whether any + data has subsequently been added to the index. If missing data is found, + it is a good indication that the `query_delay` is set too low and the data + is being indexed after the datafeed has passed that moment in time. This + check runs only on real-time datafeeds. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values. + :param frequency: The interval at which scheduled queries are made while the + datafeed runs in real time. The default value is either the bucket span for + short bucket spans, or, for longer bucket spans, a sensible fraction of the + bucket span. When `frequency` is shorter than the bucket span, interim results + for the last (partial) bucket are written then eventually overwritten by + the full bucket results. If the datafeed uses aggregations, this value must + be divisible by the interval of the date histogram aggregation. + :param headers: + :param ignore_throttled: If true, concrete, expanded, or aliased indices are + ignored when frozen. + :param ignore_unavailable: If true, unavailable indices (missing or closed) are + ignored. + :param indexes: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices_options: Specifies index expansion options that are used during + search + :param job_id: Identifier for the anomaly detection job. + :param max_empty_searches: If a real-time datafeed has never seen any data (including + during any initial training period), it automatically stops and closes the + associated job after this many real-time searches return no documents. In + other words, it stops after `frequency` times `max_empty_searches` of real-time + operation. If not set, a datafeed with no end time that sees no data remains + started until it is explicitly stopped. By default, it is not set. + :param query: The Elasticsearch query domain-specific language (DSL). This value + corresponds to the query object in an Elasticsearch search POST body. All + the options that are supported by Elasticsearch can be used, as this object + is passed verbatim to Elasticsearch. + :param query_delay: The number of seconds behind real time that data is queried. + For example, if data from 10:04 a.m. might not be searchable in Elasticsearch + until 10:06 a.m., set this property to 120 seconds. The default value is + randomly selected between `60s` and `120s`. This randomness improves the + query performance when there are multiple jobs running on the same node. + :param runtime_mappings: Specifies runtime fields for the datafeed search. + :param script_fields: Specifies scripts that evaluate custom expressions and + returns script fields to the datafeed. The detector configuration objects + in a job can contain functions that use these script fields. + :param scroll_size: The size parameter that is used in Elasticsearch searches + when the datafeed does not use aggregations. The maximum value is the value + of `index.max_result_window`, which is 10,000 by default. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aggregations is not None: + __body["aggregations"] = aggregations + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if chunking_config is not None: + __body["chunking_config"] = chunking_config + if delayed_data_check_config is not None: + __body["delayed_data_check_config"] = delayed_data_check_config + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if headers is not None: + __body["headers"] = headers + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indexes is not None: + __body["indexes"] = indexes + if indices is not None: + __body["indices"] = indices + if indices_options is not None: + __body["indices_options"] = indices_options + if job_id is not None: + __body["job_id"] = job_id + if max_empty_searches is not None: + __body["max_empty_searches"] = max_empty_searches + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if query_delay is not None: + __body["query_delay"] = query_delay + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll_size is not None: + __body["scroll_size"] = scroll_size + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_filter( + self, + *, + filter_id: str, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + items: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a filter. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + :param description: A description of the filter. + :param items: The items of the filter. A wildcard `*` can be used at the beginning + or the end of an item. Up to 10000 items are allowed in each filter. + """ + if filter_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'filter_id'") + __path = f"/_ml/filters/{_quote(filter_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if items is not None: + __body["items"] = items + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_job( + self, + *, + job_id: str, + analysis_config: t.Mapping[str, t.Any], + data_description: t.Mapping[str, t.Any], + allow_lazy_open: t.Optional[bool] = None, + analysis_limits: t.Optional[t.Mapping[str, t.Any]] = None, + background_persist_interval: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + custom_settings: t.Optional[t.Any] = None, + daily_model_snapshot_retention_after_days: t.Optional[int] = None, + datafeed_config: t.Optional[t.Mapping[str, t.Any]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + groups: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + human: t.Optional[bool] = None, + model_plot_config: t.Optional[t.Mapping[str, t.Any]] = None, + model_snapshot_retention_days: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + renormalization_window_days: t.Optional[int] = None, + results_index_name: t.Optional[str] = None, + results_retention_days: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates an anomaly detection job. + + ``_ + + :param job_id: The identifier for the anomaly detection job. This identifier + can contain lowercase alphanumeric characters (a-z and 0-9), hyphens, and + underscores. It must start and end with alphanumeric characters. + :param analysis_config: Specifies how to analyze the data. After you create a + job, you cannot change the analysis configuration; all the properties are + informational. + :param data_description: Defines the format of the input data when you send data + to the job by using the post data API. Note that when configure a datafeed, + these properties are automatically set. When data is received via the post + data API, it is not stored in Elasticsearch. Only the results for anomaly + detection are retained. + :param allow_lazy_open: Advanced configuration option. Specifies whether this + job can open when there is insufficient machine learning node capacity for + it to be immediately assigned to a node. By default, if a machine learning + node with capacity to run the job cannot immediately be found, the open anomaly + detection jobs API returns an error. However, this is also subject to the + cluster-wide `xpack.ml.max_lazy_ml_nodes` setting. If this option is set + to true, the open anomaly detection jobs API does not return an error and + the job waits in the opening state until sufficient machine learning node + capacity is available. + :param analysis_limits: Limits can be applied for the resources required to hold + the mathematical models in memory. These limits are approximate and can be + set per job. They do not control the memory used by other processes, for + example the Elasticsearch Java processes. + :param background_persist_interval: Advanced configuration option. The time between + each periodic persistence of the model. The default value is a randomized + value between 3 to 4 hours, which avoids all jobs persisting at exactly the + same time. The smallest allowed value is 1 hour. For very large models (several + GB), persistence could take 10-20 minutes, so do not set the `background_persist_interval` + value too low. + :param custom_settings: Advanced configuration option. Contains custom meta data + about the job. + :param daily_model_snapshot_retention_after_days: Advanced configuration option, + which affects the automatic removal of old model snapshots for this job. + It specifies a period of time (in days) after which only the first snapshot + per day is retained. This period is relative to the timestamp of the most + recent snapshot for this job. Valid values range from 0 to `model_snapshot_retention_days`. + :param datafeed_config: Defines a datafeed for the anomaly detection job. If + Elasticsearch security features are enabled, your datafeed remembers which + roles the user who created it had at the time of creation and runs the query + using those same roles. If you provide secondary authorization headers, those + credentials are used instead. + :param description: A description of the job. + :param groups: A list of job groups. A job can belong to no groups or many. + :param model_plot_config: This advanced configuration option stores model information + along with the results. It provides a more detailed view into anomaly detection. + If you enable model plot it can add considerable overhead to the performance + of the system; it is not feasible for jobs with many entities. Model plot + provides a simplified and indicative view of the model and its bounds. It + does not display complex features such as multivariate correlations or multimodal + data. As such, anomalies may occasionally be reported which cannot be seen + in the model plot. Model plot config can be configured when the job is created + or updated later. It must be disabled if performance issues are experienced. + :param model_snapshot_retention_days: Advanced configuration option, which affects + the automatic removal of old model snapshots for this job. It specifies the + maximum period of time (in days) that snapshots are retained. This period + is relative to the timestamp of the most recent snapshot for this job. By + default, snapshots ten days older than the newest snapshot are deleted. + :param renormalization_window_days: Advanced configuration option. The period + over which adjustments to the score are applied, as new data is seen. The + default value is the longer of 30 days or 100 bucket spans. + :param results_index_name: A text string that affects the name of the machine + learning results index. By default, the job generates an index named `.ml-anomalies-shared`. + :param results_retention_days: Advanced configuration option. The period of time + (in days) that results are retained. Age is calculated relative to the timestamp + of the latest bucket result. If this property has a non-null value, once + per day at 00:30 (server time), results that are the specified number of + days older than the latest bucket result are deleted from Elasticsearch. + The default value is null, which means all results are retained. Annotations + generated by the system also count as results for retention purposes; they + are deleted after the same number of days as results. Annotations added by + users are retained forever. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + if analysis_config is None: + raise ValueError("Empty value passed for parameter 'analysis_config'") + if data_description is None: + raise ValueError("Empty value passed for parameter 'data_description'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analysis_config is not None: + __body["analysis_config"] = analysis_config + if data_description is not None: + __body["data_description"] = data_description + if allow_lazy_open is not None: + __body["allow_lazy_open"] = allow_lazy_open + if analysis_limits is not None: + __body["analysis_limits"] = analysis_limits + if background_persist_interval is not None: + __body["background_persist_interval"] = background_persist_interval + if custom_settings is not None: + __body["custom_settings"] = custom_settings + if daily_model_snapshot_retention_after_days is not None: + __body[ + "daily_model_snapshot_retention_after_days" + ] = daily_model_snapshot_retention_after_days + if datafeed_config is not None: + __body["datafeed_config"] = datafeed_config + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if groups is not None: + __body["groups"] = groups + if human is not None: + __query["human"] = human + if model_plot_config is not None: + __body["model_plot_config"] = model_plot_config + if model_snapshot_retention_days is not None: + __body["model_snapshot_retention_days"] = model_snapshot_retention_days + if pretty is not None: + __query["pretty"] = pretty + if renormalization_window_days is not None: + __body["renormalization_window_days"] = renormalization_window_days + if results_index_name is not None: + __body["results_index_name"] = results_index_name + if results_retention_days is not None: + __body["results_retention_days"] = results_retention_days + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_trained_model( + self, + *, + model_id: str, + compressed_definition: t.Optional[str] = None, + defer_definition_decompression: t.Optional[bool] = None, + definition: t.Optional[t.Mapping[str, t.Any]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + inference_config: t.Optional[t.Mapping[str, t.Any]] = None, + input: t.Optional[t.Mapping[str, t.Any]] = None, + metadata: t.Optional[t.Any] = None, + model_size_bytes: t.Optional[int] = None, + model_type: t.Optional[ + t.Union["t.Literal['lang_ident', 'pytorch', 'tree_ensemble']", str] + ] = None, + pretty: t.Optional[bool] = None, + tags: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates an inference trained model. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param compressed_definition: The compressed (GZipped and Base64 encoded) inference + definition of the model. If compressed_definition is specified, then definition + cannot be specified. + :param defer_definition_decompression: If set to `true` and a `compressed_definition` + is provided, the request defers definition decompression and skips relevant + validations. + :param definition: The inference definition for the model. If definition is specified, + then compressed_definition cannot be specified. + :param description: A human-readable description of the inference trained model. + :param inference_config: The default configuration for inference. This can be + either a regression or classification configuration. It must match the underlying + definition.trained_model's target_type. For pre-packaged models such as ELSER + the config is not required. + :param input: The input field names for the model definition. + :param metadata: An object map that contains metadata about the model. + :param model_size_bytes: The estimated memory usage in bytes to keep the trained + model in memory. This property is supported only if defer_definition_decompression + is true or the model definition is not supplied. + :param model_type: The model type. + :param tags: An array of tags to organize the model. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if compressed_definition is not None: + __body["compressed_definition"] = compressed_definition + if defer_definition_decompression is not None: + __query["defer_definition_decompression"] = defer_definition_decompression + if definition is not None: + __body["definition"] = definition + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if inference_config is not None: + __body["inference_config"] = inference_config + if input is not None: + __body["input"] = input + if metadata is not None: + __body["metadata"] = metadata + if model_size_bytes is not None: + __body["model_size_bytes"] = model_size_bytes + if model_type is not None: + __body["model_type"] = model_type + if pretty is not None: + __query["pretty"] = pretty + if tags is not None: + __body["tags"] = tags + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def put_trained_model_alias( + self, + *, + model_id: str, + model_alias: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + reassign: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new model alias (or reassigns an existing one) to refer to the trained + model + + ``_ + + :param model_id: The identifier for the trained model that the alias refers to. + :param model_alias: The alias to create or update. This value cannot end in numbers. + :param reassign: Specifies whether the alias gets reassigned to the specified + trained model if it is already assigned to a different model. If the alias + is already assigned and this parameter is false, the API returns an error. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if model_alias in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_alias'") + __path = f"/_ml/trained_models/{_quote(model_id)}/model_aliases/{_quote(model_alias)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if reassign is not None: + __query["reassign"] = reassign + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_trained_model_definition_part( + self, + *, + model_id: str, + part: int, + definition: str, + total_definition_length: int, + total_parts: int, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates part of a trained model definition + + ``_ + + :param model_id: The unique identifier of the trained model. + :param part: The definition part number. When the definition is loaded for inference + the definition parts are streamed in the order of their part number. The + first part must be `0` and the final part must be `total_parts - 1`. + :param definition: The definition part for the model. Must be a base64 encoded + string. + :param total_definition_length: The total uncompressed definition length in bytes. + Not base64 encoded. + :param total_parts: The total number of parts that will be uploaded. Must be + greater than 0. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if part in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'part'") + if definition is None: + raise ValueError("Empty value passed for parameter 'definition'") + if total_definition_length is None: + raise ValueError( + "Empty value passed for parameter 'total_definition_length'" + ) + if total_parts is None: + raise ValueError("Empty value passed for parameter 'total_parts'") + __path = f"/_ml/trained_models/{_quote(model_id)}/definition/{_quote(part)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if definition is not None: + __body["definition"] = definition + if total_definition_length is not None: + __body["total_definition_length"] = total_definition_length + if total_parts is not None: + __body["total_parts"] = total_parts + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_trained_model_vocabulary( + self, + *, + model_id: str, + vocabulary: t.Union[t.List[str], t.Tuple[str, ...]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + merges: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a trained model vocabulary + + ``_ + + :param model_id: The unique identifier of the trained model. + :param vocabulary: The model vocabulary, which must not be empty. + :param merges: The optional model merges if required by the tokenizer. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if vocabulary is None: + raise ValueError("Empty value passed for parameter 'vocabulary'") + __path = f"/_ml/trained_models/{_quote(model_id)}/vocabulary" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if vocabulary is not None: + __body["vocabulary"] = vocabulary + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if merges is not None: + __body["merges"] = merges + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def reset_job( + self, + *, + job_id: str, + delete_user_annotations: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resets an existing anomaly detection job. + + ``_ + + :param job_id: The ID of the job to reset. + :param delete_user_annotations: Specifies whether annotations that have been + added by the user should be deleted along with any auto-generated annotations + when the job is reset. + :param wait_for_completion: Should this request wait until the operation has + completed before returning. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_reset" + __query: t.Dict[str, t.Any] = {} + if delete_user_annotations is not None: + __query["delete_user_annotations"] = delete_user_annotations + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def start_data_frame_analytics( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts a data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param timeout: Controls the amount of time to wait until the data frame analytics + job starts. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def start_datafeed( + self, + *, + datafeed_id: str, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts one or more datafeeds. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param end: Refer to the description for the `end` query parameter. + :param start: Refer to the description for the `start` query parameter. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_start" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if end is not None: + __body["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if start is not None: + __body["start"] = start + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def start_trained_model_deployment( + self, + *, + model_id: str, + cache_size: t.Optional[t.Union[int, str]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + number_of_allocations: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + priority: t.Optional[t.Union["t.Literal['low', 'normal']", str]] = None, + queue_capacity: t.Optional[int] = None, + threads_per_allocation: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for: t.Optional[ + t.Union["t.Literal['fully_allocated', 'started', 'starting']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Start a trained model deployment. + + ``_ + + :param model_id: The unique identifier of the trained model. Currently, only + PyTorch models are supported. + :param cache_size: The inference cache size (in memory outside the JVM heap) + per node for the model. The default value is the same size as the `model_size_bytes`. + To disable the cache, `0b` can be provided. + :param number_of_allocations: The number of model allocations on each node where + the model is deployed. All allocations on a node share the same copy of the + model in memory but use a separate set of threads to evaluate the model. + Increasing this value generally increases the throughput. If this setting + is greater than the number of hardware threads it will automatically be changed + to a value less than the number of hardware threads. + :param priority: The deployment priority. + :param queue_capacity: Specifies the number of inference requests that are allowed + in the queue. After the number of requests exceeds this value, new requests + are rejected with a 429 error. + :param threads_per_allocation: Sets the number of threads used by each model + allocation during inference. This generally increases the inference speed. + The inference process is a compute-bound process; any number greater than + the number of available hardware threads on the machine does not increase + the inference speed. If this setting is greater than the number of hardware + threads it will automatically be changed to a value less than the number + of hardware threads. + :param timeout: Specifies the amount of time to wait for the model to deploy. + :param wait_for: Specifies the allocation status to wait for before returning. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}/deployment/_start" + __query: t.Dict[str, t.Any] = {} + if cache_size is not None: + __query["cache_size"] = cache_size + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if number_of_allocations is not None: + __query["number_of_allocations"] = number_of_allocations + if pretty is not None: + __query["pretty"] = pretty + if priority is not None: + __query["priority"] = priority + if queue_capacity is not None: + __query["queue_capacity"] = queue_capacity + if threads_per_allocation is not None: + __query["threads_per_allocation"] = threads_per_allocation + if timeout is not None: + __query["timeout"] = timeout + if wait_for is not None: + __query["wait_for"] = wait_for + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stop_data_frame_analytics( + self, + *, + id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops one or more data frame analytics jobs. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no data frame analytics jobs that match. 2. Contains + the _all string or no identifiers and there are no matches. 3. Contains wildcard + expressions and there are only partial matches. The default value is true, + which returns an empty data_frame_analytics array when there are no matches + and the subset of results when there are partial matches. If this parameter + is false, the request returns a 404 status code when there are no matches + or only partial matches. + :param force: If true, the data frame analytics job is stopped forcefully. + :param timeout: Controls the amount of time to wait until the data frame analytics + job stops. Defaults to 20 seconds. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_stop" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def stop_datafeed( + self, + *, + datafeed_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops one or more datafeeds. + + ``_ + + :param datafeed_id: Identifier for the datafeed. You can stop multiple datafeeds + in a single API request by using a comma-separated list of datafeeds or a + wildcard expression. You can close all datafeeds by using `_all` or by specifying + `*` as the identifier. + :param allow_no_match: Refer to the description for the `allow_no_match` query + parameter. + :param force: Refer to the description for the `force` query parameter. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_stop" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __body["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def stop_trained_model_deployment( + self, + *, + model_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stop a trained model deployment. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no deployments that match; contains the `_all` + string or no identifiers and there are no matches; or contains wildcard expressions + and there are only partial matches. By default, it returns an empty array + when there are no matches and the subset of results when there are partial + matches. If `false`, the request returns a 404 status code when there are + no matches or only partial matches. + :param force: Forcefully stops the deployment, even if it is used by ingest pipelines. + You can't use these pipelines until you restart the model deployment. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}/deployment/_stop" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def update_data_frame_analytics( + self, + *, + id: str, + allow_lazy_start: t.Optional[bool] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_num_threads: t.Optional[int] = None, + model_memory_limit: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of a data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param allow_lazy_start: Specifies whether this job can start when there is insufficient + machine learning node capacity for it to be immediately assigned to a node. + :param description: A description of the job. + :param max_num_threads: The maximum number of threads to be used by the analysis. + Using more threads may decrease the time necessary to complete the analysis + at the cost of using more CPU. Note that the process may use additional threads + for operational functionality other than the analysis itself. + :param model_memory_limit: The approximate maximum amount of memory resources + that are permitted for analytical processing. If your `elasticsearch.yml` + file contains an `xpack.ml.max_model_memory_limit` setting, an error occurs + when you try to create data frame analytics jobs that have `model_memory_limit` + values greater than that setting. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_lazy_start is not None: + __body["allow_lazy_start"] = allow_lazy_start + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_num_threads is not None: + __body["max_num_threads"] = max_num_threads + if model_memory_limit is not None: + __body["model_memory_limit"] = model_memory_limit + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def update_datafeed( + self, + *, + datafeed_id: str, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + chunking_config: t.Optional[t.Mapping[str, t.Any]] = None, + delayed_data_check_config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indexes: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + indices: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + indices_options: t.Optional[t.Mapping[str, t.Any]] = None, + job_id: t.Optional[str] = None, + max_empty_searches: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + query_delay: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll_size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of a datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param aggregations: If set, the datafeed performs aggregation searches. Support + for aggregations is limited and should be used only with low cardinality + data. + :param allow_no_indices: If `true`, wildcard indices expressions that resolve + into no concrete indices are ignored. This includes the `_all` string or + when no indices are specified. + :param chunking_config: Datafeeds might search over long time periods, for several + months or years. This search is split into time chunks in order to ensure + the load on Elasticsearch is managed. Chunking configuration controls how + the size of these time chunks are calculated; it is an advanced configuration + option. + :param delayed_data_check_config: Specifies whether the datafeed checks for missing + data and the size of the window. The datafeed can optionally search over + indices that have already been read in an effort to determine whether any + data has subsequently been added to the index. If missing data is found, + it is a good indication that the `query_delay` is set too low and the data + is being indexed after the datafeed has passed that moment in time. This + check runs only on real-time datafeeds. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values. Valid + values are: * `all`: Match any data stream or index, including hidden ones. + * `closed`: Match closed, non-hidden indices. Also matches any non-hidden + data stream. Data streams cannot be closed. * `hidden`: Match hidden data + streams and hidden indices. Must be combined with `open`, `closed`, or both. + * `none`: Wildcard patterns are not accepted. * `open`: Match open, non-hidden + indices. Also matches any non-hidden data stream. + :param frequency: The interval at which scheduled queries are made while the + datafeed runs in real time. The default value is either the bucket span for + short bucket spans, or, for longer bucket spans, a sensible fraction of the + bucket span. When `frequency` is shorter than the bucket span, interim results + for the last (partial) bucket are written then eventually overwritten by + the full bucket results. If the datafeed uses aggregations, this value must + be divisible by the interval of the date histogram aggregation. + :param ignore_throttled: If `true`, concrete, expanded or aliased indices are + ignored when frozen. + :param ignore_unavailable: If `true`, unavailable indices (missing or closed) + are ignored. + :param indexes: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices_options: Specifies index expansion options that are used during + search. + :param job_id: + :param max_empty_searches: If a real-time datafeed has never seen any data (including + during any initial training period), it automatically stops and closes the + associated job after this many real-time searches return no documents. In + other words, it stops after `frequency` times `max_empty_searches` of real-time + operation. If not set, a datafeed with no end time that sees no data remains + started until it is explicitly stopped. By default, it is not set. + :param query: The Elasticsearch query domain-specific language (DSL). This value + corresponds to the query object in an Elasticsearch search POST body. All + the options that are supported by Elasticsearch can be used, as this object + is passed verbatim to Elasticsearch. Note that if you change the query, the + analyzed data is also changed. Therefore, the time required to learn might + be long and the understandability of the results is unpredictable. If you + want to make significant changes to the source data, it is recommended that + you clone the job and datafeed and make the amendments in the clone. Let + both run in parallel and close one when you are satisfied with the results + of the job. + :param query_delay: The number of seconds behind real time that data is queried. + For example, if data from 10:04 a.m. might not be searchable in Elasticsearch + until 10:06 a.m., set this property to 120 seconds. The default value is + randomly selected between `60s` and `120s`. This randomness improves the + query performance when there are multiple jobs running on the same node. + :param runtime_mappings: Specifies runtime fields for the datafeed search. + :param script_fields: Specifies scripts that evaluate custom expressions and + returns script fields to the datafeed. The detector configuration objects + in a job can contain functions that use these script fields. + :param scroll_size: The size parameter that is used in Elasticsearch searches + when the datafeed does not use aggregations. The maximum value is the value + of `index.max_result_window`. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aggregations is not None: + __body["aggregations"] = aggregations + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if chunking_config is not None: + __body["chunking_config"] = chunking_config + if delayed_data_check_config is not None: + __body["delayed_data_check_config"] = delayed_data_check_config + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indexes is not None: + __body["indexes"] = indexes + if indices is not None: + __body["indices"] = indices + if indices_options is not None: + __body["indices_options"] = indices_options + if job_id is not None: + __body["job_id"] = job_id + if max_empty_searches is not None: + __body["max_empty_searches"] = max_empty_searches + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if query_delay is not None: + __body["query_delay"] = query_delay + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll_size is not None: + __body["scroll_size"] = scroll_size + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def update_filter( + self, + *, + filter_id: str, + add_items: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + remove_items: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the description of a filter, adds items, or removes items. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + :param add_items: The items to add to the filter. + :param description: A description for the filter. + :param remove_items: The items to remove from the filter. + """ + if filter_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'filter_id'") + __path = f"/_ml/filters/{_quote(filter_id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if add_items is not None: + __body["add_items"] = add_items + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if remove_items is not None: + __body["remove_items"] = remove_items + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def update_job( + self, + *, + job_id: str, + allow_lazy_open: t.Optional[bool] = None, + analysis_limits: t.Optional[t.Mapping[str, t.Any]] = None, + background_persist_interval: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + categorization_filters: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + custom_settings: t.Optional[t.Mapping[str, t.Any]] = None, + daily_model_snapshot_retention_after_days: t.Optional[int] = None, + description: t.Optional[str] = None, + detectors: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + groups: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + human: t.Optional[bool] = None, + model_plot_config: t.Optional[t.Mapping[str, t.Any]] = None, + model_prune_window: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + model_snapshot_retention_days: t.Optional[int] = None, + per_partition_categorization: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + renormalization_window_days: t.Optional[int] = None, + results_retention_days: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of an anomaly detection job. + + ``_ + + :param job_id: Identifier for the job. + :param allow_lazy_open: Advanced configuration option. Specifies whether this + job can open when there is insufficient machine learning node capacity for + it to be immediately assigned to a node. If `false` and a machine learning + node with capacity to run the job cannot immediately be found, the open anomaly + detection jobs API returns an error. However, this is also subject to the + cluster-wide `xpack.ml.max_lazy_ml_nodes` setting. If this option is set + to `true`, the open anomaly detection jobs API does not return an error and + the job waits in the opening state until sufficient machine learning node + capacity is available. + :param analysis_limits: + :param background_persist_interval: Advanced configuration option. The time between + each periodic persistence of the model. The default value is a randomized + value between 3 to 4 hours, which avoids all jobs persisting at exactly the + same time. The smallest allowed value is 1 hour. For very large models (several + GB), persistence could take 10-20 minutes, so do not set the value too low. + If the job is open when you make the update, you must stop the datafeed, + close the job, then reopen the job and restart the datafeed for the changes + to take effect. + :param categorization_filters: + :param custom_settings: Advanced configuration option. Contains custom meta data + about the job. For example, it can contain custom URL information as shown + in Adding custom URLs to machine learning results. + :param daily_model_snapshot_retention_after_days: Advanced configuration option, + which affects the automatic removal of old model snapshots for this job. + It specifies a period of time (in days) after which only the first snapshot + per day is retained. This period is relative to the timestamp of the most + recent snapshot for this job. Valid values range from 0 to `model_snapshot_retention_days`. + For jobs created before version 7.8.0, the default value matches `model_snapshot_retention_days`. + :param description: A description of the job. + :param detectors: An array of detector update objects. + :param groups: A list of job groups. A job can belong to no groups or many. + :param model_plot_config: + :param model_prune_window: + :param model_snapshot_retention_days: Advanced configuration option, which affects + the automatic removal of old model snapshots for this job. It specifies the + maximum period of time (in days) that snapshots are retained. This period + is relative to the timestamp of the most recent snapshot for this job. + :param per_partition_categorization: Settings related to how categorization interacts + with partition fields. + :param renormalization_window_days: Advanced configuration option. The period + over which adjustments to the score are applied, as new data is seen. + :param results_retention_days: Advanced configuration option. The period of time + (in days) that results are retained. Age is calculated relative to the timestamp + of the latest bucket result. If this property has a non-null value, once + per day at 00:30 (server time), results that are the specified number of + days older than the latest bucket result are deleted from Elasticsearch. + The default value is null, which means all results are retained. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_lazy_open is not None: + __body["allow_lazy_open"] = allow_lazy_open + if analysis_limits is not None: + __body["analysis_limits"] = analysis_limits + if background_persist_interval is not None: + __body["background_persist_interval"] = background_persist_interval + if categorization_filters is not None: + __body["categorization_filters"] = categorization_filters + if custom_settings is not None: + __body["custom_settings"] = custom_settings + if daily_model_snapshot_retention_after_days is not None: + __body[ + "daily_model_snapshot_retention_after_days" + ] = daily_model_snapshot_retention_after_days + if description is not None: + __body["description"] = description + if detectors is not None: + __body["detectors"] = detectors + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if groups is not None: + __body["groups"] = groups + if human is not None: + __query["human"] = human + if model_plot_config is not None: + __body["model_plot_config"] = model_plot_config + if model_prune_window is not None: + __body["model_prune_window"] = model_prune_window + if model_snapshot_retention_days is not None: + __body["model_snapshot_retention_days"] = model_snapshot_retention_days + if per_partition_categorization is not None: + __body["per_partition_categorization"] = per_partition_categorization + if pretty is not None: + __query["pretty"] = pretty + if renormalization_window_days is not None: + __body["renormalization_window_days"] = renormalization_window_days + if results_retention_days is not None: + __body["results_retention_days"] = results_retention_days + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/monitoring.py b/elasticsearch_serverless/_async/client/monitoring.py new file mode 100644 index 0000000..75fe1bc --- /dev/null +++ b/elasticsearch_serverless/_async/client/monitoring.py @@ -0,0 +1,87 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class MonitoringClient(NamespacedClient): + @_rewrite_parameters( + body_name="operations", + ) + async def bulk( + self, + *, + interval: t.Union["t.Literal[-1]", "t.Literal[0]", str], + operations: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + system_api_version: str, + system_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Used by the monitoring features to send monitoring data. + + ``_ + + :param interval: Collection interval (e.g., '10s' or '10000ms') of the payload + :param operations: + :param system_api_version: + :param system_id: Identifier of the monitored system + """ + if interval is None: + raise ValueError("Empty value passed for parameter 'interval'") + if operations is None: + raise ValueError("Empty value passed for parameter 'operations'") + if system_api_version is None: + raise ValueError("Empty value passed for parameter 'system_api_version'") + if system_id is None: + raise ValueError("Empty value passed for parameter 'system_id'") + __path = "/_monitoring/bulk" + __query: t.Dict[str, t.Any] = {} + if interval is not None: + __query["interval"] = interval + if system_api_version is not None: + __query["system_api_version"] = system_api_version + if system_id is not None: + __query["system_id"] = system_id + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = operations + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/nodes.py b/elasticsearch_serverless/_async/client/nodes.py new file mode 100644 index 0000000..51f7cf7 --- /dev/null +++ b/elasticsearch_serverless/_async/client/nodes.py @@ -0,0 +1,483 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse, TextApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class NodesClient(NamespacedClient): + @_rewrite_parameters() + async def clear_repositories_metering_archive( + self, + *, + node_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + max_archive_version: int, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes the archived repositories metering information present in the cluster. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. All the nodes selective options are explained [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes). + :param max_archive_version: Specifies the maximum [archive_version](https://www.elastic.co/guide/en/elasticsearch/reference/current/get-repositories-metering-api.html#get-repositories-metering-api-response-body) + to be cleared from the archive. + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + if max_archive_version in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'max_archive_version'") + __path = f"/_nodes/{_quote(node_id)}/_repositories_metering/{_quote(max_archive_version)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_repositories_metering_info( + self, + *, + node_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns cluster repositories metering information. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. All the nodes selective options are explained [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes). + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + __path = f"/_nodes/{_quote(node_id)}/_repositories_metering" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def hot_threads( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_idle_threads: t.Optional[bool] = None, + interval: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + snapshots: t.Optional[int] = None, + sort: t.Optional[ + t.Union["t.Literal['block', 'cpu', 'gpu', 'mem', 'wait']", str] + ] = None, + threads: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + type: t.Optional[ + t.Union["t.Literal['block', 'cpu', 'gpu', 'mem', 'wait']", str] + ] = None, + ) -> TextApiResponse: + """ + Returns information about hot threads on each node in the cluster. + + ``_ + + :param node_id: List of node IDs or names used to limit returned information. + :param ignore_idle_threads: If true, known idle threads (e.g. waiting in a socket + select, or to get a task from an empty queue) are filtered out. + :param interval: The interval to do the second sampling of threads. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param snapshots: Number of samples of thread stacktrace. + :param sort: The sort order for 'cpu' type (default: total) + :param threads: Specifies the number of hot threads to provide information for. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param type: The type to sample. + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/hot_threads" + else: + __path = "/_nodes/hot_threads" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_idle_threads is not None: + __query["ignore_idle_threads"] = ignore_idle_threads + if interval is not None: + __query["interval"] = interval + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if snapshots is not None: + __query["snapshots"] = snapshots + if sort is not None: + __query["sort"] = sort + if threads is not None: + __query["threads"] = threads + if timeout is not None: + __query["timeout"] = timeout + if type is not None: + __query["type"] = type + __headers = {"accept": "text/plain"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def info( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about nodes in the cluster. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. + :param metric: Limits the information returned to the specific metrics. Supports + a comma-separated list, such as http,ingest. + :param flat_settings: If true, returns settings in flat format. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/{_quote(metric)}" + elif node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}" + elif metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(metric)}" + else: + __path = "/_nodes" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def reload_secure_settings( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + secure_settings_password: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Reloads secure settings. + + ``_ + + :param node_id: A comma-separated list of node IDs to span the reload/reinit + call. Should stay empty because reloading usually involves all cluster nodes. + :param secure_settings_password: + :param timeout: Explicit operation timeout + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/reload_secure_settings" + else: + __path = "/_nodes/reload_secure_settings" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if secure_settings_password is not None: + __body["secure_settings_password"] = secure_settings_password + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def stats( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + index_metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + completion_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + fielddata_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + groups: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_segment_file_sizes: t.Optional[bool] = None, + include_unloaded_segments: t.Optional[bool] = None, + level: t.Optional[ + t.Union["t.Literal['cluster', 'indices', 'shards']", str] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + types: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns statistical information about nodes in the cluster. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. + :param metric: Limit the information returned to the specified metrics + :param index_metric: Limit the information returned for indices metric to the + specific index metrics. It can be used only if indices (or all) metric is + specified. + :param completion_fields: Comma-separated list or wildcard expressions of fields + to include in fielddata and suggest statistics. + :param fielddata_fields: Comma-separated list or wildcard expressions of fields + to include in fielddata statistics. + :param fields: Comma-separated list or wildcard expressions of fields to include + in the statistics. + :param groups: Comma-separated list of search groups to include in the search + statistics. + :param include_segment_file_sizes: If true, the call reports the aggregated disk + usage of each one of the Lucene index files (only applies if segment stats + are requested). + :param include_unloaded_segments: If set to true segment stats will include stats + for segments that are not currently loaded into memory + :param level: Indicates whether statistics are aggregated at the cluster, index, + or shard level. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param types: A comma-separated list of document types for the indexing index + metric. + """ + if ( + node_id not in SKIP_IN_PATH + and metric not in SKIP_IN_PATH + and index_metric not in SKIP_IN_PATH + ): + __path = f"/_nodes/{_quote(node_id)}/stats/{_quote(metric)}/{_quote(index_metric)}" + elif node_id not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/stats/{_quote(metric)}" + elif metric not in SKIP_IN_PATH and index_metric not in SKIP_IN_PATH: + __path = f"/_nodes/stats/{_quote(metric)}/{_quote(index_metric)}" + elif node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/stats" + elif metric not in SKIP_IN_PATH: + __path = f"/_nodes/stats/{_quote(metric)}" + else: + __path = "/_nodes/stats" + __query: t.Dict[str, t.Any] = {} + if completion_fields is not None: + __query["completion_fields"] = completion_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if fielddata_fields is not None: + __query["fielddata_fields"] = fielddata_fields + if fields is not None: + __query["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if groups is not None: + __query["groups"] = groups + if human is not None: + __query["human"] = human + if include_segment_file_sizes is not None: + __query["include_segment_file_sizes"] = include_segment_file_sizes + if include_unloaded_segments is not None: + __query["include_unloaded_segments"] = include_unloaded_segments + if level is not None: + __query["level"] = level + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if types is not None: + __query["types"] = types + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def usage( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns low-level information about REST actions usage on nodes. + + ``_ + + :param node_id: A comma-separated list of node IDs or names to limit the returned + information; use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes + :param metric: Limit the information returned to the specified metrics + :param timeout: Explicit operation timeout + """ + if node_id not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/usage/{_quote(metric)}" + elif node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/usage" + elif metric not in SKIP_IN_PATH: + __path = f"/_nodes/usage/{_quote(metric)}" + else: + __path = "/_nodes/usage" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/rollup.py b/elasticsearch_serverless/_async/client/rollup.py new file mode 100644 index 0000000..9a314db --- /dev/null +++ b/elasticsearch_serverless/_async/client/rollup.py @@ -0,0 +1,440 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class RollupClient(NamespacedClient): + @_rewrite_parameters() + async def delete_job( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing rollup job. + + ``_ + + :param id: The ID of the job to delete + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_rollup/job/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_jobs( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the configuration, stats, and status of rollup jobs. + + ``_ + + :param id: The ID of the job(s) to fetch. Accepts glob patterns, or left blank + for all jobs + """ + if id not in SKIP_IN_PATH: + __path = f"/_rollup/job/{_quote(id)}" + else: + __path = "/_rollup/job" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_rollup_caps( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the capabilities of any rollup jobs that have been configured for a specific + index or index pattern. + + ``_ + + :param id: The ID of the index to check rollup capabilities on, or left blank + for all jobs + """ + if id not in SKIP_IN_PATH: + __path = f"/_rollup/data/{_quote(id)}" + else: + __path = "/_rollup/data" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_rollup_index_caps( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the rollup capabilities of all jobs inside of a rollup index (e.g. the + index where rollup data is stored). + + ``_ + + :param index: The rollup index or index pattern to obtain rollup capabilities + from. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_rollup/data" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"headers"}, + ) + async def put_job( + self, + *, + id: str, + cron: str, + groups: t.Mapping[str, t.Any], + index_pattern: str, + page_size: int, + rollup_index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + headers: t.Optional[ + t.Mapping[str, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + human: t.Optional[bool] = None, + metrics: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a rollup job. + + ``_ + + :param id: Identifier for the rollup job. This can be any alphanumeric string + and uniquely identifies the data that is associated with the rollup job. + The ID is persistent; it is stored with the rolled up data. If you create + a job, let it run for a while, then delete the job, the data that the job + rolled up is still be associated with this job ID. You cannot create a new + job with the same ID since that could lead to problems with mismatched job + configurations. + :param cron: A cron string which defines the intervals when the rollup job should + be executed. When the interval triggers, the indexer attempts to rollup the + data in the index pattern. The cron pattern is unrelated to the time interval + of the data being rolled up. For example, you may wish to create hourly rollups + of your document but to only run the indexer on a daily basis at midnight, + as defined by the cron. The cron pattern is defined just like a Watcher cron + schedule. + :param groups: Defines the grouping fields and aggregations that are defined + for this rollup job. These fields will then be available later for aggregating + into buckets. These aggs and fields can be used in any combination. Think + of the groups configuration as defining a set of tools that can later be + used in aggregations to partition the data. Unlike raw data, we have to think + ahead to which fields and aggregations might be used. Rollups provide enough + flexibility that you simply need to determine which fields are needed, not + in what order they are needed. + :param index_pattern: The index or index pattern to roll up. Supports wildcard-style + patterns (`logstash-*`). The job attempts to rollup the entire index or index-pattern. + :param page_size: The number of bucket results that are processed on each iteration + of the rollup indexer. A larger value tends to execute faster, but requires + more memory during processing. This value has no effect on how the data is + rolled up; it is merely used for tweaking the speed or memory cost of the + indexer. + :param rollup_index: The index that contains the rollup results. The index can + be shared with other rollup jobs. The data is stored so that it doesn’t interfere + with unrelated jobs. + :param headers: + :param metrics: Defines the metrics to collect for each grouping tuple. By default, + only the doc_counts are collected for each group. To make rollup useful, + you will often add metrics like averages, mins, maxes, etc. Metrics are defined + on a per-field basis and for each field you configure which metric should + be collected. + :param timeout: Time to wait for the request to complete. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if cron is None: + raise ValueError("Empty value passed for parameter 'cron'") + if groups is None: + raise ValueError("Empty value passed for parameter 'groups'") + if index_pattern is None: + raise ValueError("Empty value passed for parameter 'index_pattern'") + if page_size is None: + raise ValueError("Empty value passed for parameter 'page_size'") + if rollup_index is None: + raise ValueError("Empty value passed for parameter 'rollup_index'") + __path = f"/_rollup/job/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if cron is not None: + __body["cron"] = cron + if groups is not None: + __body["groups"] = groups + if index_pattern is not None: + __body["index_pattern"] = index_pattern + if page_size is not None: + __body["page_size"] = page_size + if rollup_index is not None: + __body["rollup_index"] = rollup_index + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if headers is not None: + __body["headers"] = headers + if human is not None: + __query["human"] = human + if metrics is not None: + __body["metrics"] = metrics + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def rollup_search( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + size: t.Optional[int] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Enables searching rolled-up data using the standard query DSL. + + ``_ + + :param index: The indices or index-pattern(s) (containing rollup or regular data) + that should be searched + :param aggregations: + :param aggs: + :param query: + :param rest_total_hits_as_int: Indicates whether hits.total should be rendered + as an integer or an object in the rest search response + :param size: Must be zero if set, as rollups work on pre-aggregated data + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_rollup_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if size is not None: + __body["size"] = size + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def start_job( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts an existing, stopped rollup job. + + ``_ + + :param id: The ID of the job to start + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_rollup/job/{_quote(id)}/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stop_job( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops an existing, started rollup job. + + ``_ + + :param id: The ID of the job to stop + :param timeout: Block for (at maximum) the specified duration while waiting for + the job to stop. Defaults to 30s. + :param wait_for_completion: True if the API should block until the job has fully + stopped, false if should be executed async. Defaults to false. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_rollup/job/{_quote(id)}/_stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/search_application.py b/elasticsearch_serverless/_async/client/search_application.py new file mode 100644 index 0000000..500ceba --- /dev/null +++ b/elasticsearch_serverless/_async/client/search_application.py @@ -0,0 +1,348 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SearchApplicationClient(NamespacedClient): + @_rewrite_parameters() + async def delete( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a search application. + + ``_ + + :param name: The name of the search application to delete + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/search_application/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_behavioral_analytics( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Delete a behavioral analytics collection. + + ``_ + + :param name: The name of the analytics collection to be deleted + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/analytics/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the details about a search application. + + ``_ + + :param name: The name of the search application + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/search_application/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_behavioral_analytics( + self, + *, + name: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the existing behavioral analytics collections. + + ``_ + + :param name: A list of analytics collections to limit the returned information + """ + if name not in SKIP_IN_PATH: + __path = f"/_application/analytics/{_quote(name)}" + else: + __path = "/_application/analytics" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def list( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the existing search applications. + + ``_ + + :param from_: Starting offset (default: 0) + :param q: Query in the Lucene query string syntax" + :param size: specifies a max number of results to get + """ + __path = "/_application/search_application" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="search_application", + ) + async def put( + self, + *, + name: str, + search_application: t.Mapping[str, t.Any], + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a search application. + + ``_ + + :param name: The name of the search application to be created or updated + :param search_application: + :param create: If true, requires that a search application with the specified + resource_id does not already exist. (default: false) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if search_application is None: + raise ValueError("Empty value passed for parameter 'search_application'") + __path = f"/_application/search_application/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = search_application + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def put_behavioral_analytics( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a behavioral analytics collection. + + ``_ + + :param name: The name of the analytics collection to be created or updated + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/analytics/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params"}, + ) + async def search( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Perform a search against a search application + + ``_ + + :param name: The name of the search application to be searched + :param params: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/search_application/{_quote(name)}/_search" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if params is not None: + __body["params"] = params + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/searchable_snapshots.py b/elasticsearch_serverless/_async/client/searchable_snapshots.py new file mode 100644 index 0000000..e8ba046 --- /dev/null +++ b/elasticsearch_serverless/_async/client/searchable_snapshots.py @@ -0,0 +1,265 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SearchableSnapshotsClient(NamespacedClient): + @_rewrite_parameters() + async def cache_stats( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieve node-level cache statistics about searchable snapshots. + + ``_ + + :param node_id: A comma-separated list of node IDs or names to limit the returned + information; use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes + :param master_timeout: + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_searchable_snapshots/{_quote(node_id)}/cache/stats" + else: + __path = "/_searchable_snapshots/cache/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def clear_cache( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clear the cache of searchable snapshots. + + ``_ + + :param index: A comma-separated list of index names + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_searchable_snapshots/cache/clear" + else: + __path = "/_searchable_snapshots/cache/clear" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def mount( + self, + *, + repository: str, + snapshot: str, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_index_settings: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + index_settings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + renamed_index: t.Optional[str] = None, + storage: t.Optional[str] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Mount a snapshot as a searchable index. + + ``_ + + :param repository: The name of the repository containing the snapshot of the + index to mount + :param snapshot: The name of the snapshot of the index to mount + :param index: + :param ignore_index_settings: + :param index_settings: + :param master_timeout: Explicit operation timeout for connection to master node + :param renamed_index: + :param storage: Selects the kind of local storage used to accelerate searches. + Experimental, and defaults to `full_copy` + :param wait_for_completion: Should this request wait until the operation has + completed before returning + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + if index is None: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_mount" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if index is not None: + __body["index"] = index + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_index_settings is not None: + __body["ignore_index_settings"] = ignore_index_settings + if index_settings is not None: + __body["index_settings"] = index_settings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if renamed_index is not None: + __body["renamed_index"] = renamed_index + if storage is not None: + __query["storage"] = storage + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def stats( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + level: t.Optional[ + t.Union["t.Literal['cluster', 'indices', 'shards']", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieve shard-level statistics about searchable snapshots. + + ``_ + + :param index: A comma-separated list of index names + :param level: Return stats aggregated at cluster, index or shard level + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_searchable_snapshots/stats" + else: + __path = "/_searchable_snapshots/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if level is not None: + __query["level"] = level + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/security.py b/elasticsearch_serverless/_async/client/security.py new file mode 100644 index 0000000..0b5685a --- /dev/null +++ b/elasticsearch_serverless/_async/client/security.py @@ -0,0 +1,453 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SecurityClient(NamespacedClient): + @_rewrite_parameters() + async def authenticate( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Enables authentication as a user and retrieve information about the authenticated + user. + + ``_ + """ + __path = "/_security/_authenticate" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def clear_api_key_cache( + self, + *, + ids: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clear a subset or all entries from the API key cache. + + ``_ + + :param ids: Comma-separated list of API key IDs to evict from the API key cache. + To evict all API keys, use `*`. Does not support other wildcard patterns. + """ + if ids in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'ids'") + __path = f"/_security/api_key/{_quote(ids)}/_clear_cache" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def create_api_key( + self, + *, + error_trace: t.Optional[bool] = None, + expiration: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + name: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + role_descriptors: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates an API key for access without requiring basic authentication. + + ``_ + + :param expiration: Expiration time for the API key. By default, API keys never + expire. + :param metadata: Arbitrary metadata that you want to associate with the API key. + It supports nested data structure. Within the metadata object, keys beginning + with `_` are reserved for system usage. + :param name: Specifies the name for this API key. + :param refresh: If `true` (the default) then refresh the affected shards to make + this operation visible to search, if `wait_for` then wait for a refresh to + make this operation visible to search, if `false` then do nothing with refreshes. + :param role_descriptors: An array of role descriptors for this API key. This + parameter is optional. When it is not specified or is an empty array, then + the API key will have a point in time snapshot of permissions of the authenticated + user. If you supply role descriptors then the resultant permissions would + be an intersection of API keys permissions and authenticated user’s permissions + thereby limiting the access scope for API keys. The structure of role descriptor + is the same as the request for create role API. For more details, see create + or update roles API. + """ + __path = "/_security/api_key" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expiration is not None: + __body["expiration"] = expiration + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if metadata is not None: + __body["metadata"] = metadata + if name is not None: + __body["name"] = name + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if role_descriptors is not None: + __body["role_descriptors"] = role_descriptors + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def get_api_key( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + id: t.Optional[str] = None, + name: t.Optional[str] = None, + owner: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + realm_name: t.Optional[str] = None, + username: t.Optional[str] = None, + with_limited_by: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information for one or more API keys. + + ``_ + + :param id: An API key id. This parameter cannot be used with any of `name`, `realm_name` + or `username`. + :param name: An API key name. This parameter cannot be used with any of `id`, + `realm_name` or `username`. It supports prefix search with wildcard. + :param owner: A boolean flag that can be used to query API keys owned by the + currently authenticated user. The `realm_name` or `username` parameters cannot + be specified when this parameter is set to `true` as they are assumed to + be the currently authenticated ones. + :param realm_name: The name of an authentication realm. This parameter cannot + be used with either `id` or `name` or when `owner` flag is set to `true`. + :param username: The username of a user. This parameter cannot be used with either + `id` or `name` or when `owner` flag is set to `true`. + :param with_limited_by: Return the snapshot of the owner user's role descriptors + associated with the API key. An API key's actual permission is the intersection + of its assigned role descriptors and the owner user's role descriptors. + """ + __path = "/_security/api_key" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if id is not None: + __query["id"] = id + if name is not None: + __query["name"] = name + if owner is not None: + __query["owner"] = owner + if pretty is not None: + __query["pretty"] = pretty + if realm_name is not None: + __query["realm_name"] = realm_name + if username is not None: + __query["username"] = username + if with_limited_by is not None: + __query["with_limited_by"] = with_limited_by + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def invalidate_api_key( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + id: t.Optional[str] = None, + ids: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + name: t.Optional[str] = None, + owner: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + realm_name: t.Optional[str] = None, + username: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Invalidates one or more API keys. + + ``_ + + :param id: + :param ids: A list of API key ids. This parameter cannot be used with any of + `name`, `realm_name`, or `username`. + :param name: An API key name. This parameter cannot be used with any of `ids`, + `realm_name` or `username`. + :param owner: Can be used to query API keys owned by the currently authenticated + user. The `realm_name` or `username` parameters cannot be specified when + this parameter is set to `true` as they are assumed to be the currently authenticated + ones. + :param realm_name: The name of an authentication realm. This parameter cannot + be used with either `ids` or `name`, or when `owner` flag is set to `true`. + :param username: The username of a user. This parameter cannot be used with either + `ids` or `name`, or when `owner` flag is set to `true`. + """ + __path = "/_security/api_key" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if id is not None: + __body["id"] = id + if ids is not None: + __body["ids"] = ids + if name is not None: + __body["name"] = name + if owner is not None: + __body["owner"] = owner + if pretty is not None: + __query["pretty"] = pretty + if realm_name is not None: + __body["realm_name"] = realm_name + if username is not None: + __body["username"] = username + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + async def query_api_keys( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + size: t.Optional[int] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + with_limited_by: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information for API keys using a subset of query DSL + + ``_ + + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the `search_after` parameter. + :param query: A query to filter which API keys to return. The query supports + a subset of query types, including `match_all`, `bool`, `term`, `terms`, + `ids`, `prefix`, `wildcard`, and `range`. You can query all public information + associated with an API key. + :param search_after: Search after definition + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the `from` and `size` parameters. To page through + more hits, use the `search_after` parameter. + :param sort: Other than `id`, all public fields of an API key are eligible for + sorting. In addition, sort can also be applied to the `_doc` field to sort + by index order. + :param with_limited_by: Return the snapshot of the owner user's role descriptors + associated with the API key. An API key's actual permission is the intersection + of its assigned role descriptors and the owner user's role descriptors. + """ + __path = "/_security/_query/api_key" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if search_after is not None: + __body["search_after"] = search_after + if size is not None: + __body["size"] = size + if sort is not None: + __body["sort"] = sort + if with_limited_by is not None: + __query["with_limited_by"] = with_limited_by + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def update_api_key( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + role_descriptors: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates attributes of an existing API key. + + ``_ + + :param id: The ID of the API key to update. + :param metadata: Arbitrary metadata that you want to associate with the API key. + It supports nested data structure. Within the metadata object, keys beginning + with _ are reserved for system usage. + :param role_descriptors: An array of role descriptors for this API key. This + parameter is optional. When it is not specified or is an empty array, then + the API key will have a point in time snapshot of permissions of the authenticated + user. If you supply role descriptors then the resultant permissions would + be an intersection of API keys permissions and authenticated user’s permissions + thereby limiting the access scope for API keys. The structure of role descriptor + is the same as the request for create role API. For more details, see create + or update roles API. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_security/api_key/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if metadata is not None: + __body["metadata"] = metadata + if pretty is not None: + __query["pretty"] = pretty + if role_descriptors is not None: + __body["role_descriptors"] = role_descriptors + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/shutdown.py b/elasticsearch_serverless/_async/client/shutdown.py new file mode 100644 index 0000000..a19cdc3 --- /dev/null +++ b/elasticsearch_serverless/_async/client/shutdown.py @@ -0,0 +1,229 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class ShutdownClient(NamespacedClient): + @_rewrite_parameters() + async def delete_node( + self, + *, + node_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes a node from the shutdown list. Designed for indirect use by ECE/ESS and + ECK. Direct use is not supported. + + ``_ + + :param node_id: The node id of node to be removed from the shutdown state + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + __path = f"/_nodes/{_quote(node_id)}/shutdown" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_node( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieve status of a node or nodes that are currently marked as shutting down. + Designed for indirect use by ECE/ESS and ECK. Direct use is not supported. + + ``_ + + :param node_id: Which node for which to retrieve the shutdown status + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/shutdown" + else: + __path = "/_nodes/shutdown" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_node( + self, + *, + node_id: str, + reason: str, + type: t.Union["t.Literal['remove', 'replace', 'restart']", str], + allocation_delay: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + pretty: t.Optional[bool] = None, + target_node_name: t.Optional[str] = None, + timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Adds a node to be shut down. Designed for indirect use by ECE/ESS and ECK. Direct + use is not supported. + + ``_ + + :param node_id: The node id of node to be shut down + :param reason: A human-readable reason that the node is being shut down. This + field provides information for other cluster operators; it does not affect + the shut down process. + :param type: Valid values are restart, remove, or replace. Use restart when you + need to temporarily shut down a node to perform an upgrade, make configuration + changes, or perform other maintenance. Because the node is expected to rejoin + the cluster, data is not migrated off of the node. Use remove when you need + to permanently remove a node from the cluster. The node is not marked ready + for shutdown until data is migrated off of the node Use replace to do a 1:1 + replacement of a node with another node. Certain allocation decisions will + be ignored (such as disk watermarks) in the interest of true replacement + of the source node with the target node. During a replace-type shutdown, + rollover and index creation may result in unassigned shards, and shrink may + fail until the replacement is complete. + :param allocation_delay: Only valid if type is restart. Controls how long Elasticsearch + will wait for the node to restart and join the cluster before reassigning + its shards to other nodes. This works the same as delaying allocation with + the index.unassigned.node_left.delayed_timeout setting. If you specify both + a restart allocation delay and an index-level allocation delay, the longer + of the two is used. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param target_node_name: Only valid if type is replace. Specifies the name of + the node that is replacing the node being shut down. Shards from the shut + down node are only allowed to be allocated to the target node, and no other + data will be allocated to the target node. During relocation of data certain + allocation rules are ignored, such as disk watermarks or user attribute filtering + rules. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + if reason is None: + raise ValueError("Empty value passed for parameter 'reason'") + if type is None: + raise ValueError("Empty value passed for parameter 'type'") + __path = f"/_nodes/{_quote(node_id)}/shutdown" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if reason is not None: + __body["reason"] = reason + if type is not None: + __body["type"] = type + if allocation_delay is not None: + __body["allocation_delay"] = allocation_delay + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if target_node_name is not None: + __body["target_node_name"] = target_node_name + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/slm.py b/elasticsearch_serverless/_async/client/slm.py new file mode 100644 index 0000000..35b0c16 --- /dev/null +++ b/elasticsearch_serverless/_async/client/slm.py @@ -0,0 +1,377 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SlmClient(NamespacedClient): + @_rewrite_parameters() + async def delete_lifecycle( + self, + *, + policy_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing snapshot lifecycle policy. + + ``_ + + :param policy_id: The id of the snapshot lifecycle policy to remove + """ + if policy_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'policy_id'") + __path = f"/_slm/policy/{_quote(policy_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def execute_lifecycle( + self, + *, + policy_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Immediately creates a snapshot according to the lifecycle policy, without waiting + for the scheduled time. + + ``_ + + :param policy_id: The id of the snapshot lifecycle policy to be executed + """ + if policy_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'policy_id'") + __path = f"/_slm/policy/{_quote(policy_id)}/_execute" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def execute_retention( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes any snapshots that are expired according to the policy's retention rules. + + ``_ + """ + __path = "/_slm/_execute_retention" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_lifecycle( + self, + *, + policy_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves one or more snapshot lifecycle policy definitions and information about + the latest snapshot attempts. + + ``_ + + :param policy_id: Comma-separated list of snapshot lifecycle policies to retrieve + """ + if policy_id not in SKIP_IN_PATH: + __path = f"/_slm/policy/{_quote(policy_id)}" + else: + __path = "/_slm/policy" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns global and policy-level statistics about actions taken by snapshot lifecycle + management. + + ``_ + """ + __path = "/_slm/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the status of snapshot lifecycle management (SLM). + + ``_ + """ + __path = "/_slm/status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_lifecycle( + self, + *, + policy_id: str, + config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + name: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + repository: t.Optional[str] = None, + retention: t.Optional[t.Mapping[str, t.Any]] = None, + schedule: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a snapshot lifecycle policy. + + ``_ + + :param policy_id: ID for the snapshot lifecycle policy you want to create or + update. + :param config: Configuration for each snapshot created by the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param name: Name automatically assigned to each snapshot created by the policy. + Date math is supported. To prevent conflicting snapshot names, a UUID is + automatically appended to each snapshot name. + :param repository: Repository used to store snapshots created by this policy. + This repository must exist prior to the policy’s creation. You can create + a repository using the snapshot repository API. + :param retention: Retention rules used to retain and delete snapshots created + by the policy. + :param schedule: Periodic or absolute schedule at which the policy creates snapshots. + SLM applies schedule changes immediately. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if policy_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'policy_id'") + __path = f"/_slm/policy/{_quote(policy_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if config is not None: + __body["config"] = config + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if name is not None: + __body["name"] = name + if pretty is not None: + __query["pretty"] = pretty + if repository is not None: + __body["repository"] = repository + if retention is not None: + __body["retention"] = retention + if schedule is not None: + __body["schedule"] = schedule + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def start( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Turns on snapshot lifecycle management (SLM). + + ``_ + """ + __path = "/_slm/start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stop( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Turns off snapshot lifecycle management (SLM). + + ``_ + """ + __path = "/_slm/stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/snapshot.py b/elasticsearch_serverless/_async/client/snapshot.py new file mode 100644 index 0000000..9bcf81f --- /dev/null +++ b/elasticsearch_serverless/_async/client/snapshot.py @@ -0,0 +1,773 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SnapshotClient(NamespacedClient): + @_rewrite_parameters() + async def cleanup_repository( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes stale data from repository. + + ``_ + + :param name: Snapshot repository to clean up. + :param master_timeout: Period to wait for a connection to the master node. + :param timeout: Period to wait for a response. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_snapshot/{_quote(name)}/_cleanup" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def clone( + self, + *, + repository: str, + snapshot: str, + target_snapshot: str, + indices: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clones indices from one snapshot into another snapshot in the same repository. + + ``_ + + :param repository: A repository name + :param snapshot: The name of the snapshot to clone from + :param target_snapshot: The name of the cloned snapshot to create + :param indices: + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + if target_snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'target_snapshot'") + if indices is None: + raise ValueError("Empty value passed for parameter 'indices'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_clone/{_quote(target_snapshot)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if indices is not None: + __body["indices"] = indices + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def create( + self, + *, + repository: str, + snapshot: str, + error_trace: t.Optional[bool] = None, + feature_states: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_global_state: t.Optional[bool] = None, + indices: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + partial: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a snapshot in a repository. + + ``_ + + :param repository: Repository for the snapshot. + :param snapshot: Name of the snapshot. Must be unique in the repository. + :param feature_states: Feature states to include in the snapshot. Each feature + state includes one or more system indices containing related data. You can + view a list of eligible features using the get features API. If `include_global_state` + is `true`, all current feature states are included by default. If `include_global_state` + is `false`, no feature states are included by default. + :param ignore_unavailable: If `true`, the request ignores data streams and indices + in `indices` that are missing or closed. If `false`, the request returns + an error for any data stream or index that is missing or closed. + :param include_global_state: If `true`, the current cluster state is included + in the snapshot. The cluster state includes persistent cluster settings, + composable index templates, legacy index templates, ingest pipelines, and + ILM policies. It also includes data stored in system indices, such as Watches + and task records (configurable via `feature_states`). + :param indices: Data streams and indices to include in the snapshot. Supports + multi-target syntax. Includes all data streams and indices by default. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param metadata: Optional metadata for the snapshot. May have any contents. Must + be less than 1024 bytes. This map is not automatically generated by Elasticsearch. + :param partial: If `true`, allows restoring a partial snapshot of indices with + unavailable shards. Only shards that were successfully included in the snapshot + will be restored. All missing shards will be recreated as empty. If `false`, + the entire restore operation will fail if one or more indices included in + the snapshot do not have all primary shards available. + :param wait_for_completion: If `true`, the request returns a response when the + snapshot is complete. If `false`, the request returns a response when the + snapshot initializes. + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if feature_states is not None: + __body["feature_states"] = feature_states + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __body["ignore_unavailable"] = ignore_unavailable + if include_global_state is not None: + __body["include_global_state"] = include_global_state + if indices is not None: + __body["indices"] = indices + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if metadata is not None: + __body["metadata"] = metadata + if partial is not None: + __body["partial"] = partial + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def create_repository( + self, + *, + name: str, + settings: t.Mapping[str, t.Any], + type: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + repository: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + verify: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a repository. + + ``_ + + :param name: A repository name + :param settings: + :param type: + :param master_timeout: Explicit operation timeout for connection to master node + :param repository: + :param timeout: Explicit operation timeout + :param verify: Whether to verify the repository after creation + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if settings is None: + raise ValueError("Empty value passed for parameter 'settings'") + if type is None: + raise ValueError("Empty value passed for parameter 'type'") + __path = f"/_snapshot/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if settings is not None: + __body["settings"] = settings + if type is not None: + __body["type"] = type + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if repository is not None: + __body["repository"] = repository + if timeout is not None: + __query["timeout"] = timeout + if verify is not None: + __query["verify"] = verify + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def delete( + self, + *, + repository: str, + snapshot: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes one or more snapshots. + + ``_ + + :param repository: A repository name + :param snapshot: A comma-separated list of snapshot names + :param master_timeout: Explicit operation timeout for connection to master node + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_repository( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a repository. + + ``_ + + :param name: Name of the snapshot repository to unregister. Wildcard (`*`) patterns + are supported. + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: Explicit operation timeout + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_snapshot/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + repository: str, + snapshot: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + after: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_sort_value: t.Optional[str] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_repository: t.Optional[bool] = None, + index_details: t.Optional[bool] = None, + index_names: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + offset: t.Optional[int] = None, + order: t.Optional[t.Union["t.Literal['asc', 'desc']", str]] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + slm_policy_filter: t.Optional[str] = None, + sort: t.Optional[ + t.Union[ + "t.Literal['duration', 'failed_shard_count', 'index_count', 'name', 'repository', 'shard_count', 'start_time']", + str, + ] + ] = None, + verbose: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about a snapshot. + + ``_ + + :param repository: Comma-separated list of snapshot repository names used to + limit the request. Wildcard (*) expressions are supported. + :param snapshot: Comma-separated list of snapshot names to retrieve. Also accepts + wildcards (*). - To get information about all snapshots in a registered repository, + use a wildcard (*) or _all. - To get information about any snapshots that + are currently running, use _current. + :param after: Offset identifier to start pagination from as returned by the next + field in the response body. + :param from_sort_value: Value of the current sort column at which to start retrieval. + Can either be a string snapshot- or repository name when sorting by snapshot + or repository name, a millisecond time value or a number when sorting by + index- or shard count. + :param ignore_unavailable: If false, the request returns an error for any snapshots + that are unavailable. + :param include_repository: If true, returns the repository name in each snapshot. + :param index_details: If true, returns additional information about each index + in the snapshot comprising the number of shards in the index, the total size + of the index in bytes, and the maximum number of segments per shard in the + index. Defaults to false, meaning that this information is omitted. + :param index_names: If true, returns the name of each index in each snapshot. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param offset: Numeric offset to start pagination from based on the snapshots + matching this request. Using a non-zero value for this parameter is mutually + exclusive with using the after parameter. Defaults to 0. + :param order: Sort order. Valid values are asc for ascending and desc for descending + order. Defaults to asc, meaning ascending order. + :param size: Maximum number of snapshots to return. Defaults to 0 which means + return all that match the request without limit. + :param slm_policy_filter: Filter snapshots by a comma-separated list of SLM policy + names that snapshots belong to. Also accepts wildcards (*) and combinations + of wildcards followed by exclude patterns starting with -. To include snapshots + not created by an SLM policy you can use the special pattern _none that will + match all snapshots without an SLM policy. + :param sort: Allows setting a sort order for the result. Defaults to start_time, + i.e. sorting by snapshot start time stamp. + :param verbose: If true, returns additional information about each snapshot such + as the version of Elasticsearch which took the snapshot, the start and end + times of the snapshot, and the number of shards snapshotted. + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}" + __query: t.Dict[str, t.Any] = {} + if after is not None: + __query["after"] = after + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_sort_value is not None: + __query["from_sort_value"] = from_sort_value + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_repository is not None: + __query["include_repository"] = include_repository + if index_details is not None: + __query["index_details"] = index_details + if index_names is not None: + __query["index_names"] = index_names + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if offset is not None: + __query["offset"] = offset + if order is not None: + __query["order"] = order + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if slm_policy_filter is not None: + __query["slm_policy_filter"] = slm_policy_filter + if sort is not None: + __query["sort"] = sort + if verbose is not None: + __query["verbose"] = verbose + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_repository( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about a repository. + + ``_ + + :param name: A comma-separated list of repository names + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + """ + if name not in SKIP_IN_PATH: + __path = f"/_snapshot/{_quote(name)}" + else: + __path = "/_snapshot" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def restore( + self, + *, + repository: str, + snapshot: str, + error_trace: t.Optional[bool] = None, + feature_states: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_index_settings: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + ignore_unavailable: t.Optional[bool] = None, + include_aliases: t.Optional[bool] = None, + include_global_state: t.Optional[bool] = None, + index_settings: t.Optional[t.Mapping[str, t.Any]] = None, + indices: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + partial: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + rename_pattern: t.Optional[str] = None, + rename_replacement: t.Optional[str] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Restores a snapshot. + + ``_ + + :param repository: A repository name + :param snapshot: A snapshot name + :param feature_states: + :param ignore_index_settings: + :param ignore_unavailable: + :param include_aliases: + :param include_global_state: + :param index_settings: + :param indices: + :param master_timeout: Explicit operation timeout for connection to master node + :param partial: + :param rename_pattern: + :param rename_replacement: + :param wait_for_completion: Should this request wait until the operation has + completed before returning + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_restore" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if feature_states is not None: + __body["feature_states"] = feature_states + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_index_settings is not None: + __body["ignore_index_settings"] = ignore_index_settings + if ignore_unavailable is not None: + __body["ignore_unavailable"] = ignore_unavailable + if include_aliases is not None: + __body["include_aliases"] = include_aliases + if include_global_state is not None: + __body["include_global_state"] = include_global_state + if index_settings is not None: + __body["index_settings"] = index_settings + if indices is not None: + __body["indices"] = indices + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if partial is not None: + __body["partial"] = partial + if pretty is not None: + __query["pretty"] = pretty + if rename_pattern is not None: + __body["rename_pattern"] = rename_pattern + if rename_replacement is not None: + __body["rename_replacement"] = rename_replacement + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def status( + self, + *, + repository: t.Optional[str] = None, + snapshot: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about the status of a snapshot. + + ``_ + + :param repository: A repository name + :param snapshot: A comma-separated list of snapshot names + :param ignore_unavailable: Whether to ignore unavailable snapshots, defaults + to false which means a SnapshotMissingException is thrown + :param master_timeout: Explicit operation timeout for connection to master node + """ + if repository not in SKIP_IN_PATH and snapshot not in SKIP_IN_PATH: + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_status" + elif repository not in SKIP_IN_PATH: + __path = f"/_snapshot/{_quote(repository)}/_status" + else: + __path = "/_snapshot/_status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def verify_repository( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Verifies a repository. + + ``_ + + :param name: A repository name + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: Explicit operation timeout + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_snapshot/{_quote(name)}/_verify" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/sql.py b/elasticsearch_serverless/_async/client/sql.py new file mode 100644 index 0000000..cf658d9 --- /dev/null +++ b/elasticsearch_serverless/_async/client/sql.py @@ -0,0 +1,373 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SqlClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + async def clear_cursor( + self, + *, + cursor: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clears the SQL cursor + + ``_ + + :param cursor: + """ + if cursor is None: + raise ValueError("Empty value passed for parameter 'cursor'") + __path = "/_sql/close" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if cursor is not None: + __body["cursor"] = cursor + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def delete_async( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an async SQL search or a stored synchronous SQL search. If the search + is still running, the API cancels it. + + ``_ + + :param id: The async search ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_sql/async/delete/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_async( + self, + *, + id: str, + delimiter: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + human: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + pretty: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the current status and available results for an async SQL search or stored + synchronous SQL search + + ``_ + + :param id: The async search ID + :param delimiter: Separator for CSV results. The API only supports this parameter + for CSV responses. + :param format: Format for the response. You must specify a format using this + parameter or the Accept HTTP header. If you specify both, the API uses this + parameter. + :param keep_alive: Retention period for the search and its results. Defaults + to the `keep_alive` period for the original SQL search. + :param wait_for_completion_timeout: Period to wait for complete results. Defaults + to no timeout, meaning the request waits for complete search results. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_sql/async/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if delimiter is not None: + __query["delimiter"] = delimiter + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if human is not None: + __query["human"] = human + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get_async_status( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the current status of an async SQL search or a stored synchronous SQL + search + + ``_ + + :param id: The async search ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_sql/async/status/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params", "request_timeout"}, + ) + async def query( + self, + *, + catalog: t.Optional[str] = None, + columnar: t.Optional[bool] = None, + cursor: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + fetch_size: t.Optional[int] = None, + field_multi_value_leniency: t.Optional[bool] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + human: t.Optional[bool] = None, + index_using_frozen: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + keep_on_completion: t.Optional[bool] = None, + page_timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[str] = None, + request_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + time_zone: t.Optional[str] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Executes a SQL request + + ``_ + + :param catalog: Default catalog (cluster) for queries. If unspecified, the queries + execute on the data in the local cluster only. + :param columnar: If true, the results in a columnar fashion: one row represents + all the values of a certain column from the current page of results. + :param cursor: + :param fetch_size: The maximum number of rows (or entries) to return in one response + :param field_multi_value_leniency: Throw an exception when encountering multiple + values for a field (default) or be lenient and return the first value from + the list (without any guarantees of what that will be - typically the first + in natural ascending order). + :param filter: Optional Elasticsearch query DSL for additional filtering. + :param format: a short version of the Accept header, e.g. json, yaml + :param index_using_frozen: If true, the search can run on frozen indices. Defaults + to false. + :param keep_alive: Retention period for an async or saved synchronous search. + :param keep_on_completion: If true, Elasticsearch stores synchronous searches + if you also specify the wait_for_completion_timeout parameter. If false, + Elasticsearch only stores async searches that don’t finish before the wait_for_completion_timeout. + :param page_timeout: The timeout before a pagination request fails. + :param params: Values for parameters in the query. + :param query: SQL query to execute + :param request_timeout: The timeout before the request fails. + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param time_zone: Time-zone in ISO 8601 used for executing the query on the server. + More information available here. + :param wait_for_completion_timeout: Period to wait for complete results. Defaults + to no timeout, meaning the request waits for complete search results. If + the search doesn’t finish within this period, the search becomes async. + """ + __path = "/_sql" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if catalog is not None: + __body["catalog"] = catalog + if columnar is not None: + __body["columnar"] = columnar + if cursor is not None: + __body["cursor"] = cursor + if error_trace is not None: + __query["error_trace"] = error_trace + if fetch_size is not None: + __body["fetch_size"] = fetch_size + if field_multi_value_leniency is not None: + __body["field_multi_value_leniency"] = field_multi_value_leniency + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if human is not None: + __query["human"] = human + if index_using_frozen is not None: + __body["index_using_frozen"] = index_using_frozen + if keep_alive is not None: + __body["keep_alive"] = keep_alive + if keep_on_completion is not None: + __body["keep_on_completion"] = keep_on_completion + if page_timeout is not None: + __body["page_timeout"] = page_timeout + if params is not None: + __body["params"] = params + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if request_timeout is not None: + __body["request_timeout"] = request_timeout + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if time_zone is not None: + __body["time_zone"] = time_zone + if wait_for_completion_timeout is not None: + __body["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def translate( + self, + *, + query: str, + error_trace: t.Optional[bool] = None, + fetch_size: t.Optional[int] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + time_zone: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Translates SQL into Elasticsearch queries + + ``_ + + :param query: + :param fetch_size: + :param filter: + :param time_zone: + """ + if query is None: + raise ValueError("Empty value passed for parameter 'query'") + __path = "/_sql/translate" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if query is not None: + __body["query"] = query + if error_trace is not None: + __query["error_trace"] = error_trace + if fetch_size is not None: + __body["fetch_size"] = fetch_size + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if time_zone is not None: + __body["time_zone"] = time_zone + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/ssl.py b/elasticsearch_serverless/_async/client/ssl.py new file mode 100644 index 0000000..4205dee --- /dev/null +++ b/elasticsearch_serverless/_async/client/ssl.py @@ -0,0 +1,57 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class SslClient(NamespacedClient): + @_rewrite_parameters() + async def certificates( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the X.509 certificates used to encrypt communications + in the cluster. + + ``_ + """ + __path = "/_ssl/certificates" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/tasks.py b/elasticsearch_serverless/_async/client/tasks.py new file mode 100644 index 0000000..8f384d0 --- /dev/null +++ b/elasticsearch_serverless/_async/client/tasks.py @@ -0,0 +1,208 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class TasksClient(NamespacedClient): + @_rewrite_parameters() + async def cancel( + self, + *, + task_id: t.Optional[t.Union[int, str]] = None, + actions: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + nodes: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + parent_task_id: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Cancels a task, if it can be cancelled through an API. + + ``_ + + :param task_id: Cancel the task with specified task id (node_id:task_number) + :param actions: A comma-separated list of actions that should be cancelled. Leave + empty to cancel all. + :param nodes: A comma-separated list of node IDs or names to limit the returned + information; use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes + :param parent_task_id: Cancel tasks with specified parent task id (node_id:task_number). + Set to -1 to cancel all. + :param wait_for_completion: Should the request block until the cancellation of + the task and its descendant tasks is completed. Defaults to false + """ + if task_id not in SKIP_IN_PATH: + __path = f"/_tasks/{_quote(task_id)}/_cancel" + else: + __path = "/_tasks/_cancel" + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __query["actions"] = actions + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if nodes is not None: + __query["nodes"] = nodes + if parent_task_id is not None: + __query["parent_task_id"] = parent_task_id + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def get( + self, + *, + task_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about a task. + + ``_ + + :param task_id: Return the task with specified id (node_id:task_number) + :param timeout: Explicit operation timeout + :param wait_for_completion: Wait for the matching tasks to complete (default: + false) + """ + if task_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'task_id'") + __path = f"/_tasks/{_quote(task_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def list( + self, + *, + actions: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + detailed: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + group_by: t.Optional[ + t.Union["t.Literal['nodes', 'none', 'parents']", str] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + node_id: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + parent_task_id: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a list of tasks. + + ``_ + + :param actions: Comma-separated list or wildcard expression of actions used to + limit the request. + :param detailed: If `true`, the response includes detailed information about + shard recoveries. + :param group_by: Key used to group tasks in the response. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. + :param parent_task_id: Parent task ID used to limit returned information. To + return all tasks, omit this parameter or use a value of `-1`. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param wait_for_completion: If `true`, the request blocks until the operation + is complete. + """ + __path = "/_tasks" + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __query["actions"] = actions + if detailed is not None: + __query["detailed"] = detailed + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if group_by is not None: + __query["group_by"] = group_by + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if node_id is not None: + __query["node_id"] = node_id + if parent_task_id is not None: + __query["parent_task_id"] = parent_task_id + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/text_structure.py b/elasticsearch_serverless/_async/client/text_structure.py new file mode 100644 index 0000000..107ad05 --- /dev/null +++ b/elasticsearch_serverless/_async/client/text_structure.py @@ -0,0 +1,158 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class TextStructureClient(NamespacedClient): + @_rewrite_parameters( + body_name="text_files", + ) + async def find_structure( + self, + *, + text_files: t.Union[t.List[t.Any], t.Tuple[t.Any, ...]], + charset: t.Optional[str] = None, + column_names: t.Optional[str] = None, + delimiter: t.Optional[str] = None, + explain: t.Optional[bool] = None, + format: t.Optional[str] = None, + grok_pattern: t.Optional[str] = None, + has_header_row: t.Optional[bool] = None, + line_merge_size_limit: t.Optional[int] = None, + lines_to_sample: t.Optional[int] = None, + quote: t.Optional[str] = None, + should_trim_fields: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + timestamp_field: t.Optional[str] = None, + timestamp_format: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Finds the structure of a text file. The text file must contain data that is suitable + to be ingested into Elasticsearch. + + ``_ + + :param text_files: + :param charset: The text’s character set. It must be a character set that is + supported by the JVM that Elasticsearch uses. For example, UTF-8, UTF-16LE, + windows-1252, or EUC-JP. If this parameter is not specified, the structure + finder chooses an appropriate character set. + :param column_names: If you have set format to delimited, you can specify the + column names in a comma-separated list. If this parameter is not specified, + the structure finder uses the column names from the header row of the text. + If the text does not have a header role, columns are named "column1", "column2", + "column3", etc. + :param delimiter: If you have set format to delimited, you can specify the character + used to delimit the values in each row. Only a single character is supported; + the delimiter cannot have multiple characters. By default, the API considers + the following possibilities: comma, tab, semi-colon, and pipe (|). In this + default scenario, all rows must have the same number of fields for the delimited + format to be detected. If you specify a delimiter, up to 10% of the rows + can have a different number of columns than the first row. + :param explain: If this parameter is set to true, the response includes a field + named explanation, which is an array of strings that indicate how the structure + finder produced its result. + :param format: The high level structure of the text. Valid values are ndjson, + xml, delimited, and semi_structured_text. By default, the API chooses the + format. In this default scenario, all rows must have the same number of fields + for a delimited format to be detected. If the format is set to delimited + and the delimiter is not set, however, the API tolerates up to 5% of rows + that have a different number of columns than the first row. + :param grok_pattern: If you have set format to semi_structured_text, you can + specify a Grok pattern that is used to extract fields from every message + in the text. The name of the timestamp field in the Grok pattern must match + what is specified in the timestamp_field parameter. If that parameter is + not specified, the name of the timestamp field in the Grok pattern must match + "timestamp". If grok_pattern is not specified, the structure finder creates + a Grok pattern. + :param has_header_row: If you have set format to delimited, you can use this + parameter to indicate whether the column names are in the first row of the + text. If this parameter is not specified, the structure finder guesses based + on the similarity of the first row of the text to other rows. + :param line_merge_size_limit: The maximum number of characters in a message when + lines are merged to form messages while analyzing semi-structured text. If + you have extremely long messages you may need to increase this, but be aware + that this may lead to very long processing times if the way to group lines + into messages is misdetected. + :param lines_to_sample: The number of lines to include in the structural analysis, + starting from the beginning of the text. The minimum is 2; If the value of + this parameter is greater than the number of lines in the text, the analysis + proceeds (as long as there are at least two lines in the text) for all of + the lines. + :param quote: If you have set format to delimited, you can specify the character + used to quote the values in each row if they contain newlines or the delimiter + character. Only a single character is supported. If this parameter is not + specified, the default value is a double quote ("). If your delimited text + format does not use quoting, a workaround is to set this argument to a character + that does not appear anywhere in the sample. + :param should_trim_fields: If you have set format to delimited, you can specify + whether values between delimiters should have whitespace trimmed from them. + If this parameter is not specified and the delimiter is pipe (|), the default + value is true. Otherwise, the default value is false. + :param timeout: Sets the maximum amount of time that the structure analysis make + take. If the analysis is still running when the timeout expires then it will + be aborted. + :param timestamp_field: Optional parameter to specify the timestamp field in + the file + :param timestamp_format: The Java time format of the timestamp field in the text. + """ + if text_files is None: + raise ValueError("Empty value passed for parameter 'text_files'") + __path = "/_text_structure/find_structure" + __query: t.Dict[str, t.Any] = {} + if charset is not None: + __query["charset"] = charset + if column_names is not None: + __query["column_names"] = column_names + if delimiter is not None: + __query["delimiter"] = delimiter + if explain is not None: + __query["explain"] = explain + if format is not None: + __query["format"] = format + if grok_pattern is not None: + __query["grok_pattern"] = grok_pattern + if has_header_row is not None: + __query["has_header_row"] = has_header_row + if line_merge_size_limit is not None: + __query["line_merge_size_limit"] = line_merge_size_limit + if lines_to_sample is not None: + __query["lines_to_sample"] = lines_to_sample + if quote is not None: + __query["quote"] = quote + if should_trim_fields is not None: + __query["should_trim_fields"] = should_trim_fields + if timeout is not None: + __query["timeout"] = timeout + if timestamp_field is not None: + __query["timestamp_field"] = timestamp_field + if timestamp_format is not None: + __query["timestamp_format"] = timestamp_format + __body = text_files + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/transform.py b/elasticsearch_serverless/_async/client/transform.py new file mode 100644 index 0000000..90db433 --- /dev/null +++ b/elasticsearch_serverless/_async/client/transform.py @@ -0,0 +1,690 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class TransformClient(NamespacedClient): + @_rewrite_parameters() + async def delete_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing transform. + + ``_ + + :param transform_id: Identifier for the transform. + :param force: If this value is false, the transform must be stopped before it + can be deleted. If true, the transform is deleted regardless of its current + state. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_transform( + self, + *, + transform_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for transforms. + + ``_ + + :param transform_id: Identifier for the transform. It can be a transform identifier + or a wildcard expression. You can get information for all transforms by using + `_all`, by specifying `*` as the ``, or by omitting the ``. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no transforms that match. 2. Contains the _all + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. If this parameter is false, the request + returns a 404 status code when there are no matches or only partial matches. + :param exclude_generated: Excludes fields that were automatically added when + creating the transform. This allows the configuration to be in an acceptable + format to be retrieved and then added to another cluster. + :param from_: Skips the specified number of transforms. + :param size: Specifies the maximum number of transforms to obtain. + """ + if transform_id not in SKIP_IN_PATH: + __path = f"/_transform/{_quote(transform_id)}" + else: + __path = "/_transform" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def get_transform_stats( + self, + *, + transform_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for transforms. + + ``_ + + :param transform_id: Identifier for the transform. It can be a transform identifier + or a wildcard expression. You can get information for all transforms by using + `_all`, by specifying `*` as the ``, or by omitting the ``. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no transforms that match. 2. Contains the _all + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. If this parameter is false, the request + returns a 404 status code when there are no matches or only partial matches. + :param from_: Skips the specified number of transforms. + :param size: Specifies the maximum number of transforms to obtain. + :param timeout: Controls the time to wait for the stats + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def preview_transform( + self, + *, + transform_id: t.Optional[str] = None, + description: t.Optional[str] = None, + dest: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + latest: t.Optional[t.Mapping[str, t.Any]] = None, + pivot: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + retention_policy: t.Optional[t.Mapping[str, t.Any]] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + source: t.Optional[t.Mapping[str, t.Any]] = None, + sync: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Previews a transform. + + ``_ + + :param transform_id: Identifier for the transform to preview. If you specify + this path parameter, you cannot provide transform configuration details in + the request body. + :param description: Free text description of the transform. + :param dest: The destination for the transform. + :param frequency: The interval between checks for changes in the source indices + when the transform is running continuously. Also determines the retry interval + in the event of transient failures while the transform is searching or indexing. + The minimum value is 1s and the maximum is 1h. + :param latest: The latest method transforms the data by finding the latest document + for each unique key. + :param pivot: The pivot method transforms the data by aggregating and grouping + it. These objects define the group by fields and the aggregation to reduce + the data. + :param retention_policy: Defines a retention policy for the transform. Data that + meets the defined criteria is deleted from the destination index. + :param settings: Defines optional transform settings. + :param source: The source of the data for the transform. + :param sync: Defines the properties transforms require to run continuously. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id not in SKIP_IN_PATH: + __path = f"/_transform/{_quote(transform_id)}/_preview" + else: + __path = "/_transform/_preview" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if dest is not None: + __body["dest"] = dest + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if latest is not None: + __body["latest"] = latest + if pivot is not None: + __body["pivot"] = pivot + if pretty is not None: + __query["pretty"] = pretty + if retention_policy is not None: + __body["retention_policy"] = retention_policy + if settings is not None: + __body["settings"] = settings + if source is not None: + __body["source"] = source + if sync is not None: + __body["sync"] = sync + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + async def put_transform( + self, + *, + transform_id: str, + dest: t.Mapping[str, t.Any], + source: t.Mapping[str, t.Any], + defer_validation: t.Optional[bool] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + latest: t.Optional[t.Mapping[str, t.Any]] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pivot: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + retention_policy: t.Optional[t.Mapping[str, t.Any]] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + sync: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a transform. + + ``_ + + :param transform_id: Identifier for the transform. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It has a 64 character limit and must start and end with alphanumeric characters. + :param dest: The destination for the transform. + :param source: The source of the data for the transform. + :param defer_validation: When the transform is created, a series of validations + occur to ensure its success. For example, there is a check for the existence + of the source indices and a check that the destination index is not part + of the source index pattern. You can use this parameter to skip the checks, + for example when the source index does not exist until after the transform + is created. The validations are always run when you start the transform, + however, with the exception of privilege checks. + :param description: Free text description of the transform. + :param frequency: The interval between checks for changes in the source indices + when the transform is running continuously. Also determines the retry interval + in the event of transient failures while the transform is searching or indexing. + The minimum value is `1s` and the maximum is `1h`. + :param latest: The latest method transforms the data by finding the latest document + for each unique key. + :param meta: Defines optional transform metadata. + :param pivot: The pivot method transforms the data by aggregating and grouping + it. These objects define the group by fields and the aggregation to reduce + the data. + :param retention_policy: Defines a retention policy for the transform. Data that + meets the defined criteria is deleted from the destination index. + :param settings: Defines optional transform settings. + :param sync: Defines the properties transforms require to run continuously. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + if dest is None: + raise ValueError("Empty value passed for parameter 'dest'") + if source is None: + raise ValueError("Empty value passed for parameter 'source'") + __path = f"/_transform/{_quote(transform_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if dest is not None: + __body["dest"] = dest + if source is not None: + __body["source"] = source + if defer_validation is not None: + __query["defer_validation"] = defer_validation + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if latest is not None: + __body["latest"] = latest + if meta is not None: + __body["_meta"] = meta + if pivot is not None: + __body["pivot"] = pivot + if pretty is not None: + __query["pretty"] = pretty + if retention_policy is not None: + __body["retention_policy"] = retention_policy + if settings is not None: + __body["settings"] = settings + if sync is not None: + __body["sync"] = sync + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def reset_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resets an existing transform. + + ``_ + + :param transform_id: Identifier for the transform. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It has a 64 character limit and must start and end with alphanumeric characters. + :param force: If this value is `true`, the transform is reset regardless of its + current state. If it's `false`, the transform must be stopped before it can + be reset. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_reset" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def schedule_now_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Schedules now a transform. + + ``_ + + :param transform_id: Identifier for the transform. + :param timeout: Controls the time to wait for the scheduling to take place + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_schedule_now" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + async def start_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[str] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts one or more transforms. + + ``_ + + :param transform_id: Identifier for the transform. + :param from_: Restricts the set of transformed entities to those changed after + this time. Relative times like now-30d are supported. Only applicable for + continuous transforms. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stop_transform( + self, + *, + transform_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_checkpoint: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops one or more transforms. + + ``_ + + :param transform_id: Identifier for the transform. To stop multiple transforms, + use a comma-separated list or a wildcard expression. To stop all transforms, + use `_all` or `*` as the identifier. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no transforms that match; contains the `_all` string + or no identifiers and there are no matches; contains wildcard expressions + and there are only partial matches. If it is true, the API returns a successful + acknowledgement message when there are no matches. When there are only partial + matches, the API stops the appropriate transforms. If it is false, the request + returns a 404 status code when there are no matches or only partial matches. + :param force: If it is true, the API forcefully stops the transforms. + :param timeout: Period to wait for a response when `wait_for_completion` is `true`. + If no response is received before the timeout expires, the request returns + a timeout exception. However, the request continues processing and eventually + moves the transform to a STOPPED state. + :param wait_for_checkpoint: If it is true, the transform does not completely + stop until the current checkpoint is completed. If it is false, the transform + stops as soon as possible. + :param wait_for_completion: If it is true, the API blocks until the indexer state + completely stops. If it is false, the API returns immediately and the indexer + is stopped asynchronously in the background. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_stop" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_checkpoint is not None: + __query["wait_for_checkpoint"] = wait_for_checkpoint + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + async def update_transform( + self, + *, + transform_id: str, + defer_validation: t.Optional[bool] = None, + description: t.Optional[str] = None, + dest: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + retention_policy: t.Optional[t.Union[None, t.Mapping[str, t.Any]]] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + source: t.Optional[t.Mapping[str, t.Any]] = None, + sync: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of a transform. + + ``_ + + :param transform_id: Identifier for the transform. + :param defer_validation: When true, deferrable validations are not run. This + behavior may be desired if the source index does not exist until after the + transform is created. + :param description: Free text description of the transform. + :param dest: The destination for the transform. + :param frequency: The interval between checks for changes in the source indices + when the transform is running continuously. Also determines the retry interval + in the event of transient failures while the transform is searching or indexing. + The minimum value is 1s and the maximum is 1h. + :param meta: Defines optional transform metadata. + :param retention_policy: Defines a retention policy for the transform. Data that + meets the defined criteria is deleted from the destination index. + :param settings: Defines optional transform settings. + :param source: The source of the data for the transform. + :param sync: Defines the properties transforms require to run continuously. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_update" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if defer_validation is not None: + __query["defer_validation"] = defer_validation + if description is not None: + __body["description"] = description + if dest is not None: + __body["dest"] = dest + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if retention_policy is not None: + __body["retention_policy"] = retention_policy + if settings is not None: + __body["settings"] = settings + if source is not None: + __body["source"] = source + if sync is not None: + __body["sync"] = sync + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_async/client/utils.py b/elasticsearch_serverless/_async/client/utils.py new file mode 100644 index 0000000..ec0a6e4 --- /dev/null +++ b/elasticsearch_serverless/_async/client/utils.py @@ -0,0 +1,44 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from ..._sync.client.utils import ( + _TYPE_ASYNC_SNIFF_CALLBACK, + _TYPE_HOSTS, + CLIENT_META_SERVICE, + SKIP_IN_PATH, + _base64_auth_header, + _quote, + _quote_query, + _rewrite_parameters, + client_node_configs, + is_requests_http_auth, + is_requests_node_class, +) + +__all__ = [ + "CLIENT_META_SERVICE", + "_TYPE_ASYNC_SNIFF_CALLBACK", + "_base64_auth_header", + "_quote", + "_quote_query", + "_TYPE_HOSTS", + "SKIP_IN_PATH", + "client_node_configs", + "_rewrite_parameters", + "is_requests_http_auth", + "is_requests_node_class", +] diff --git a/elasticsearch_serverless/_async/client/watcher.py b/elasticsearch_serverless/_async/client/watcher.py new file mode 100644 index 0000000..7c63f5f --- /dev/null +++ b/elasticsearch_serverless/_async/client/watcher.py @@ -0,0 +1,607 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class WatcherClient(NamespacedClient): + @_rewrite_parameters() + async def ack_watch( + self, + *, + watch_id: str, + action_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Acknowledges a watch, manually throttling the execution of the watch's actions. + + ``_ + + :param watch_id: Watch ID + :param action_id: A comma-separated list of the action ids to be acked + """ + if watch_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'watch_id'") + if watch_id not in SKIP_IN_PATH and action_id not in SKIP_IN_PATH: + __path = f"/_watcher/watch/{_quote(watch_id)}/_ack/{_quote(action_id)}" + elif watch_id not in SKIP_IN_PATH: + __path = f"/_watcher/watch/{_quote(watch_id)}/_ack" + else: + raise ValueError("Couldn't find a path for the given parameters") + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def activate_watch( + self, + *, + watch_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Activates a currently inactive watch. + + ``_ + + :param watch_id: Watch ID + """ + if watch_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'watch_id'") + __path = f"/_watcher/watch/{_quote(watch_id)}/_activate" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def deactivate_watch( + self, + *, + watch_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deactivates a currently active watch. + + ``_ + + :param watch_id: Watch ID + """ + if watch_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'watch_id'") + __path = f"/_watcher/watch/{_quote(watch_id)}/_deactivate" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def delete_watch( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes a watch from Watcher. + + ``_ + + :param id: Watch ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_watcher/watch/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def execute_watch( + self, + *, + id: t.Optional[str] = None, + action_modes: t.Optional[ + t.Mapping[ + str, + t.Union[ + "t.Literal['execute', 'force_execute', 'force_simulate', 'simulate', 'skip']", + str, + ], + ] + ] = None, + alternative_input: t.Optional[t.Mapping[str, t.Any]] = None, + debug: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_condition: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + record_execution: t.Optional[bool] = None, + simulated_actions: t.Optional[t.Mapping[str, t.Any]] = None, + trigger_data: t.Optional[t.Mapping[str, t.Any]] = None, + watch: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Forces the execution of a stored watch. + + ``_ + + :param id: Identifier for the watch. + :param action_modes: Determines how to handle the watch actions as part of the + watch execution. + :param alternative_input: When present, the watch uses this object as a payload + instead of executing its own input. + :param debug: Defines whether the watch runs in debug mode. + :param ignore_condition: When set to `true`, the watch execution uses the always + condition. This can also be specified as an HTTP parameter. + :param record_execution: When set to `true`, the watch record representing the + watch execution result is persisted to the `.watcher-history` index for the + current time. In addition, the status of the watch is updated, possibly throttling + subsequent executions. This can also be specified as an HTTP parameter. + :param simulated_actions: + :param trigger_data: This structure is parsed as the data of the trigger event + that will be used during the watch execution + :param watch: When present, this watch is used instead of the one specified in + the request. This watch is not persisted to the index and record_execution + cannot be set. + """ + if id not in SKIP_IN_PATH: + __path = f"/_watcher/watch/{_quote(id)}/_execute" + else: + __path = "/_watcher/watch/_execute" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if action_modes is not None: + __body["action_modes"] = action_modes + if alternative_input is not None: + __body["alternative_input"] = alternative_input + if debug is not None: + __query["debug"] = debug + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_condition is not None: + __body["ignore_condition"] = ignore_condition + if pretty is not None: + __query["pretty"] = pretty + if record_execution is not None: + __body["record_execution"] = record_execution + if simulated_actions is not None: + __body["simulated_actions"] = simulated_actions + if trigger_data is not None: + __body["trigger_data"] = trigger_data + if watch is not None: + __body["watch"] = watch + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def get_watch( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves a watch by its ID. + + ``_ + + :param id: Watch ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_watcher/watch/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + async def put_watch( + self, + *, + id: str, + actions: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + active: t.Optional[bool] = None, + condition: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + input: t.Optional[t.Mapping[str, t.Any]] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + throttle_period: t.Optional[str] = None, + transform: t.Optional[t.Mapping[str, t.Any]] = None, + trigger: t.Optional[t.Mapping[str, t.Any]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new watch, or updates an existing one. + + ``_ + + :param id: Watch ID + :param actions: + :param active: Specify whether the watch is in/active by default + :param condition: + :param if_primary_term: only update the watch if the last operation that has + changed the watch has the specified primary term + :param if_seq_no: only update the watch if the last operation that has changed + the watch has the specified sequence number + :param input: + :param metadata: + :param throttle_period: + :param transform: + :param trigger: + :param version: Explicit version number for concurrency control + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_watcher/watch/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __body["actions"] = actions + if active is not None: + __query["active"] = active + if condition is not None: + __body["condition"] = condition + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if input is not None: + __body["input"] = input + if metadata is not None: + __body["metadata"] = metadata + if pretty is not None: + __query["pretty"] = pretty + if throttle_period is not None: + __body["throttle_period"] = throttle_period + if transform is not None: + __body["transform"] = transform + if trigger is not None: + __body["trigger"] = trigger + if version is not None: + __query["version"] = version + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + async def query_watches( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + size: t.Optional[int] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves stored watches. + + ``_ + + :param from_: The offset from the first result to fetch. Needs to be non-negative. + :param query: Optional, query filter watches to be returned. + :param search_after: Optional search After to do pagination using last hit’s + sort values. + :param size: The number of hits to return. Needs to be non-negative. + :param sort: Optional sort definition. + """ + __path = "/_watcher/_query/watches" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if search_after is not None: + __body["search_after"] = search_after + if size is not None: + __body["size"] = size + if sort is not None: + __body["sort"] = sort + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + async def start( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts Watcher if it is not already running. + + ``_ + """ + __path = "/_watcher/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stats( + self, + *, + metric: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['_all', 'current_watches', 'pending_watches', 'queued_watches']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['_all', 'current_watches', 'pending_watches', 'queued_watches']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['_all', 'current_watches', 'pending_watches', 'queued_watches']", + str, + ], + ..., + ], + ], + ] + ] = None, + emit_stacktraces: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the current Watcher metrics. + + ``_ + + :param metric: Defines which additional metrics are included in the response. + :param emit_stacktraces: Defines whether stack traces are generated for each + watch that is running. + """ + if metric not in SKIP_IN_PATH: + __path = f"/_watcher/stats/{_quote(metric)}" + else: + __path = "/_watcher/stats" + __query: t.Dict[str, t.Any] = {} + if emit_stacktraces is not None: + __query["emit_stacktraces"] = emit_stacktraces + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def stop( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops Watcher if it is running. + + ``_ + """ + __path = "/_watcher/_stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/client/xpack.py b/elasticsearch_serverless/_async/client/xpack.py new file mode 100644 index 0000000..0b5d4c3 --- /dev/null +++ b/elasticsearch_serverless/_async/client/xpack.py @@ -0,0 +1,111 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class XPackClient(NamespacedClient): + def __getattr__(self, attr_name: str) -> t.Any: + return getattr(self.client, attr_name) + + # AUTO-GENERATED-API-DEFINITIONS # + + @_rewrite_parameters() + async def info( + self, + *, + accept_enterprise: t.Optional[bool] = None, + categories: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the installed X-Pack features. + + ``_ + + :param accept_enterprise: If this param is used it must be set to true + :param categories: A comma-separated list of the information categories to include + in the response. For example, `build,license,features`. + """ + __path = "/_xpack" + __query: t.Dict[str, t.Any] = {} + if accept_enterprise is not None: + __query["accept_enterprise"] = accept_enterprise + if categories is not None: + __query["categories"] = categories + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + async def usage( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information about the installed X-Pack features. + + ``_ + + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + __path = "/_xpack/usage" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_async/helpers.py b/elasticsearch_serverless/_async/helpers.py new file mode 100644 index 0000000..bb2b9d5 --- /dev/null +++ b/elasticsearch_serverless/_async/helpers.py @@ -0,0 +1,588 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import asyncio +import logging +from typing import ( + Any, + AsyncIterable, + AsyncIterator, + Callable, + Collection, + Dict, + Iterable, + List, + MutableMapping, + Optional, + Tuple, + TypeVar, + Union, +) + +from ..exceptions import ApiError, NotFoundError, TransportError +from ..helpers.actions import ( + _TYPE_BULK_ACTION, + _TYPE_BULK_ACTION_BODY, + _TYPE_BULK_ACTION_HEADER, + _TYPE_BULK_ACTION_HEADER_AND_BODY, + _ActionChunker, + _process_bulk_chunk_error, + _process_bulk_chunk_success, + expand_action, +) +from ..helpers.errors import ScanError +from ..serializer import Serializer +from .client import AsyncElasticsearch # noqa + +logger = logging.getLogger("elasticsearch.helpers") + +T = TypeVar("T") + + +async def _chunk_actions( + actions: AsyncIterable[_TYPE_BULK_ACTION_HEADER_AND_BODY], + chunk_size: int, + max_chunk_bytes: int, + serializer: Serializer, +) -> AsyncIterable[ + Tuple[ + List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + List[bytes], + ] +]: + """ + Split actions into chunks by number or size, serialize them into strings in + the process. + """ + chunker = _ActionChunker( + chunk_size=chunk_size, max_chunk_bytes=max_chunk_bytes, serializer=serializer + ) + async for action, data in actions: + ret = chunker.feed(action, data) + if ret: + yield ret + ret = chunker.flush() + if ret: + yield ret + + +async def _process_bulk_chunk( + client: AsyncElasticsearch, + bulk_actions: List[bytes], + bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + raise_on_exception: bool = True, + raise_on_error: bool = True, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> AsyncIterable[Tuple[bool, Dict[str, Any]]]: + """ + Send a bulk request to elasticsearch and process the output. + """ + if isinstance(ignore_status, int): + ignore_status = (ignore_status,) + + try: + # send the actual request + resp = await client.bulk(*args, operations=bulk_actions, **kwargs) # type: ignore[arg-type] + except ApiError as e: + gen = _process_bulk_chunk_error( + error=e, + bulk_data=bulk_data, + ignore_status=ignore_status, + raise_on_exception=raise_on_exception, + raise_on_error=raise_on_error, + ) + else: + gen = _process_bulk_chunk_success( + resp=resp.body, + bulk_data=bulk_data, + ignore_status=ignore_status, + raise_on_error=raise_on_error, + ) + for item in gen: + yield item + + +def aiter(x: Union[Iterable[T], AsyncIterable[T]]) -> AsyncIterator[T]: + """Turns an async iterable or iterable into an async iterator""" + if hasattr(x, "__anext__"): + return x # type: ignore[return-value] + elif hasattr(x, "__aiter__"): + return x.__aiter__() + + async def f() -> AsyncIterable[T]: + nonlocal x + ix: Iterable[T] = x + for item in ix: + yield item + + return f().__aiter__() + + +async def azip( + *iterables: Union[Iterable[T], AsyncIterable[T]] +) -> AsyncIterable[Tuple[T, ...]]: + """Zips async iterables and iterables into an async iterator + with the same behavior as zip() + """ + aiters = [aiter(x) for x in iterables] + try: + while True: + yield tuple([await x.__anext__() for x in aiters]) + except StopAsyncIteration: + pass + + +async def async_streaming_bulk( + client: AsyncElasticsearch, + actions: Union[Iterable[_TYPE_BULK_ACTION], AsyncIterable[_TYPE_BULK_ACTION]], + chunk_size: int = 500, + max_chunk_bytes: int = 100 * 1024 * 1024, + raise_on_error: bool = True, + expand_action_callback: Callable[ + [_TYPE_BULK_ACTION], _TYPE_BULK_ACTION_HEADER_AND_BODY + ] = expand_action, + raise_on_exception: bool = True, + max_retries: int = 0, + initial_backoff: float = 2, + max_backoff: float = 600, + yield_ok: bool = True, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> AsyncIterable[Tuple[bool, Dict[str, Any]]]: + """ + Streaming bulk consumes actions from the iterable passed in and yields + results per action. For non-streaming usecases use + :func:`~elasticsearch.helpers.async_bulk` which is a wrapper around streaming + bulk that returns summary information about the bulk operation once the + entire input is consumed and sent. + + If you specify ``max_retries`` it will also retry any documents that were + rejected with a ``429`` status code. To do this it will wait (**by calling + asyncio.sleep**) for ``initial_backoff`` seconds and then, + every subsequent rejection for the same chunk, for double the time every + time up to ``max_backoff`` seconds. + + :arg client: instance of :class:`~elasticsearch.AsyncElasticsearch` to use + :arg actions: iterable or async iterable containing the actions to be executed + :arg chunk_size: number of docs in one chunk sent to es (default: 500) + :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) + :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) + from the execution of the last chunk when some occur. By default we raise. + :arg raise_on_exception: if ``False`` then don't propagate exceptions from + call to ``bulk`` and just report the items that failed as failed. + :arg expand_action_callback: callback executed on each action passed in, + should return a tuple containing the action line and the data line + (`None` if data line should be omitted). + :arg max_retries: maximum number of times a document will be retried when + ``429`` is received, set to 0 (default) for no retries on ``429`` + :arg initial_backoff: number of seconds we should wait before the first + retry. Any subsequent retries will be powers of ``initial_backoff * + 2**retry_number`` + :arg max_backoff: maximum number of seconds a retry will wait + :arg yield_ok: if set to False will skip successful documents in the output + :arg ignore_status: list of HTTP status code that you want to ignore + """ + + client = client.options() + client._client_meta = (("h", "bp"),) + + async def map_actions() -> AsyncIterable[_TYPE_BULK_ACTION_HEADER_AND_BODY]: + async for item in aiter(actions): + yield expand_action_callback(item) + + serializer = client.transport.serializers.get_serializer("application/json") + + bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ] + bulk_actions: List[bytes] + async for bulk_data, bulk_actions in _chunk_actions( + map_actions(), chunk_size, max_chunk_bytes, serializer + ): + for attempt in range(max_retries + 1): + to_retry: List[bytes] = [] + to_retry_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ] = [] + if attempt: + await asyncio.sleep( + min(max_backoff, initial_backoff * 2 ** (attempt - 1)) + ) + + try: + data: Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ok: bool + info: Dict[str, Any] + async for data, (ok, info) in azip( # type: ignore + bulk_data, + _process_bulk_chunk( + client, + bulk_actions, + bulk_data, + raise_on_exception, + raise_on_error, + ignore_status, + *args, + **kwargs, + ), + ): + if not ok: + action, info = info.popitem() + # retry if retries enabled, we get 429, and we are not + # in the last attempt + if ( + max_retries + and info["status"] == 429 + and (attempt + 1) <= max_retries + ): + # _process_bulk_chunk expects strings so we need to + # re-serialize the data + to_retry.extend(map(serializer.dumps, data)) + to_retry_data.append(data) + else: + yield ok, {action: info} + elif yield_ok: + yield ok, info + + except ApiError as e: + # suppress 429 errors since we will retry them + if attempt == max_retries or e.status_code != 429: + raise + else: + if not to_retry: + break + # retry only subset of documents that didn't succeed + bulk_actions, bulk_data = to_retry, to_retry_data + + +async def async_bulk( + client: AsyncElasticsearch, + actions: Union[Iterable[_TYPE_BULK_ACTION], AsyncIterable[_TYPE_BULK_ACTION]], + stats_only: bool = False, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> Tuple[int, Union[int, List[Any]]]: + """ + Helper for the :meth:`~elasticsearch.AsyncElasticsearch.bulk` api that provides + a more human friendly interface - it consumes an iterator of actions and + sends them to elasticsearch in chunks. It returns a tuple with summary + information - number of successfully executed actions and either list of + errors or number of errors if ``stats_only`` is set to ``True``. Note that + by default we raise a ``BulkIndexError`` when we encounter an error so + options like ``stats_only`` only+ apply when ``raise_on_error`` is set to + ``False``. + + When errors are being collected original document data is included in the + error dictionary which can lead to an extra high memory usage. If you need + to process a lot of data and want to ignore/collect errors please consider + using the :func:`~elasticsearch.helpers.async_streaming_bulk` helper which will + just return the errors and not store them in memory. + + + :arg client: instance of :class:`~elasticsearch.AsyncElasticsearch` to use + :arg actions: iterator containing the actions + :arg stats_only: if `True` only report number of successful/failed + operations instead of just number of successful and a list of error responses + :arg ignore_status: list of HTTP status code that you want to ignore + + Any additional keyword arguments will be passed to + :func:`~elasticsearch.helpers.async_streaming_bulk` which is used to execute + the operation, see :func:`~elasticsearch.helpers.async_streaming_bulk` for more + accepted parameters. + """ + success, failed = 0, 0 + + # list of errors to be collected is not stats_only + errors = [] + + # make streaming_bulk yield successful results so we can count them + kwargs["yield_ok"] = True + async for ok, item in async_streaming_bulk( + client, actions, ignore_status=ignore_status, *args, **kwargs # type: ignore[misc] + ): + # go through request-response pairs and detect failures + if not ok: + if not stats_only: + errors.append(item) + failed += 1 + else: + success += 1 + + return success, failed if stats_only else errors + + +async def async_scan( + client: AsyncElasticsearch, + query: Optional[Any] = None, + scroll: str = "5m", + raise_on_error: bool = True, + preserve_order: bool = False, + size: int = 1000, + request_timeout: Optional[float] = None, + clear_scroll: bool = True, + scroll_kwargs: Optional[MutableMapping[str, Any]] = None, + **kwargs: Any, +) -> AsyncIterable[Dict[str, Any]]: + """ + Simple abstraction on top of the + :meth:`~elasticsearch.AsyncElasticsearch.scroll` api - a simple iterator that + yields all hits as returned by underlining scroll requests. + + By default scan does not return results in any pre-determined order. To + have a standard order in the returned documents (either by score or + explicit sort definition) when scrolling, use ``preserve_order=True``. This + may be an expensive operation and will negate the performance benefits of + using ``scan``. + + :arg client: instance of :class:`~elasticsearch.AsyncElasticsearch` to use + :arg query: body for the :meth:`~elasticsearch.AsyncElasticsearch.search` api + :arg scroll: Specify how long a consistent view of the index should be + maintained for scrolled search + :arg raise_on_error: raises an exception (``ScanError``) if an error is + encountered (some shards fail to execute). By default we raise. + :arg preserve_order: don't set the ``search_type`` to ``scan`` - this will + cause the scroll to paginate with preserving the order. Note that this + can be an extremely expensive operation and can easily lead to + unpredictable results, use with caution. + :arg size: size (per shard) of the batch send at each iteration. + :arg request_timeout: explicit timeout for each call to ``scan`` + :arg clear_scroll: explicitly calls delete on the scroll id via the clear + scroll API at the end of the method on completion or error, defaults + to true. + :arg scroll_kwargs: additional kwargs to be passed to + :meth:`~elasticsearch.AsyncElasticsearch.scroll` + + Any additional keyword arguments will be passed to the initial + :meth:`~elasticsearch.AsyncElasticsearch.search` call: + + .. code-block:: python + + async_scan( + es, + query={"query": {"match": {"title": "python"}}}, + index="orders-*" + ) + """ + scroll_kwargs = scroll_kwargs or {} + + if not preserve_order: + query = query.copy() if query else {} + query["sort"] = "_doc" + + def pop_transport_kwargs(kw: MutableMapping[str, Any]) -> MutableMapping[str, Any]: + # Grab options that should be propagated to every + # API call within this helper instead of just 'search()' + transport_kwargs = {} + for key in ("headers", "api_key", "http_auth", "basic_auth", "bearer_auth"): + try: + value = kw.pop(key) + if key == "http_auth": + key = "basic_auth" + transport_kwargs[key] = value + except KeyError: + pass + return transport_kwargs + + client = client.options( + request_timeout=request_timeout, **pop_transport_kwargs(kwargs) + ) + client._client_meta = (("h", "s"),) + + # Setting query={"from": ...} would make 'from' be used + # as a keyword argument instead of 'from_'. We handle that here. + def normalize_from_keyword(kw: MutableMapping[str, Any]) -> None: + if "from" in kw: + kw["from_"] = kw.pop("from") + + normalize_from_keyword(kwargs) + try: + search_kwargs = query.copy() if query else {} + normalize_from_keyword(search_kwargs) + search_kwargs.update(kwargs) + search_kwargs["scroll"] = scroll + search_kwargs["size"] = size + resp = await client.search(**search_kwargs) + + # Try the old deprecated way if we fail immediately on parameters. + except TypeError: + search_kwargs = kwargs.copy() + search_kwargs["scroll"] = scroll + search_kwargs["size"] = size + resp = await client.search(body=query, **search_kwargs) # type: ignore[call-arg] + + scroll_id: Optional[str] = resp.get("_scroll_id") + scroll_transport_kwargs = pop_transport_kwargs(scroll_kwargs) + if scroll_transport_kwargs: + scroll_client = client.options(**scroll_transport_kwargs) + else: + scroll_client = client + + try: + while scroll_id and resp["hits"]["hits"]: + for hit in resp["hits"]["hits"]: + yield hit + + # Default to 0 if the value isn't included in the response + shards_info: Dict[str, int] = resp["_shards"] + shards_successful = shards_info.get("successful", 0) + shards_skipped = shards_info.get("skipped", 0) + shards_total = shards_info.get("total", 0) + + # check if we have any errors + if (shards_successful + shards_skipped) < shards_total: + shards_message = "Scroll request has only succeeded on %d (+%d skipped) shards out of %d." + logger.warning( + shards_message, + shards_successful, + shards_skipped, + shards_total, + ) + if raise_on_error: + raise ScanError( + scroll_id, + shards_message + % ( + shards_successful, + shards_skipped, + shards_total, + ), + ) + resp = await scroll_client.scroll( + scroll_id=scroll_id, scroll=scroll, **scroll_kwargs + ) + scroll_id = resp.get("_scroll_id") + + finally: + if scroll_id and clear_scroll: + await client.options(ignore_status=404).clear_scroll(scroll_id=scroll_id) + + +async def async_reindex( + client: AsyncElasticsearch, + source_index: Union[str, Collection[str]], + target_index: str, + query: Any = None, + target_client: Optional[AsyncElasticsearch] = None, + chunk_size: int = 500, + scroll: str = "5m", + op_type: Optional[str] = None, + scan_kwargs: MutableMapping[str, Any] = {}, + bulk_kwargs: MutableMapping[str, Any] = {}, +) -> Tuple[int, Union[int, List[Any]]]: + """ + Reindex all documents from one index that satisfy a given query + to another, potentially (if `target_client` is specified) on a different cluster. + If you don't specify the query you will reindex all the documents. + + Since ``2.3`` a :meth:`~elasticsearch_serverless.AsyncElasticsearch.reindex` api is + available as part of elasticsearch itself. It is recommended to use the api + instead of this helper wherever possible. The helper is here mostly for + backwards compatibility and for situations where more flexibility is + needed. + + .. note:: + + This helper doesn't transfer mappings, just the data. + + :arg client: instance of :class:`~elasticsearch_serverless.AsyncElasticsearch` to use (for + read if `target_client` is specified as well) + :arg source_index: index (or list of indices) to read documents from + :arg target_index: name of the index in the target cluster to populate + :arg query: body for the :meth:`~elasticsearch_serverless.AsyncElasticsearch.search` api + :arg target_client: optional, is specified will be used for writing (thus + enabling reindex between clusters) + :arg chunk_size: number of docs in one chunk sent to es (default: 500) + :arg scroll: Specify how long a consistent view of the index should be + maintained for scrolled search + :arg op_type: Explicit operation type. Defaults to '_index'. Data streams must + be set to 'create'. If not specified, will auto-detect if target_index is a + data stream. + :arg scan_kwargs: additional kwargs to be passed to + :func:`~elasticsearch_serverless.helpers.async_scan` + :arg bulk_kwargs: additional kwargs to be passed to + :func:`~elasticsearch_serverless.helpers.async_bulk` + """ + target_client = client if target_client is None else target_client + docs = async_scan( + client, query=query, index=source_index, scroll=scroll, **scan_kwargs + ) + + async def _change_doc_index( + hits: AsyncIterable[Dict[str, Any]], + index: str, + op_type: Optional[str], + ) -> AsyncIterable[Dict[str, Any]]: + async for h in hits: + h["_index"] = index + if op_type is not None: + h["_op_type"] = op_type + if "fields" in h: + h.update(h.pop("fields")) + yield h + + kwargs = {"stats_only": True} + kwargs.update(bulk_kwargs) + + is_data_stream = False + try: + # Verify if the target_index is data stream or index + data_streams = await target_client.indices.get_data_stream( + name=target_index, expand_wildcards="all" + ) + is_data_stream = any( + data_stream["name"] == target_index + for data_stream in data_streams["data_streams"] + ) + except (TransportError, KeyError, NotFoundError): + # If its not data stream, might be index + pass + + if is_data_stream: + if op_type not in (None, "create"): + raise ValueError("Data streams must have 'op_type' set to 'create'") + else: + op_type = "create" + + return await async_bulk( + target_client, + _change_doc_index(docs, target_index, op_type), + chunk_size=chunk_size, + **kwargs, + ) diff --git a/elasticsearch_serverless/_sync/__init__.py b/elasticsearch_serverless/_sync/__init__.py new file mode 100644 index 0000000..2a87d18 --- /dev/null +++ b/elasticsearch_serverless/_sync/__init__.py @@ -0,0 +1,16 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. diff --git a/elasticsearch_serverless/_sync/client/__init__.py b/elasticsearch_serverless/_sync/client/__init__.py new file mode 100644 index 0000000..20b6bd9 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/__init__.py @@ -0,0 +1,4056 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + + +import logging +import typing as t +import warnings + +from elastic_transport import ( + BaseNode, + HeadApiResponse, + NodeConfig, + NodePool, + NodeSelector, + ObjectApiResponse, + Serializer, + Transport, +) +from elastic_transport.client_utils import DEFAULT, DefaultType + +from ...exceptions import ApiError, TransportError +from ...serializer import DEFAULT_SERIALIZERS +from ._base import ( + BaseClient, + create_sniff_callback, + default_sniff_callback, + resolve_auth_headers, +) +from .async_search import AsyncSearchClient +from .autoscaling import AutoscalingClient +from .cat import CatClient +from .ccr import CcrClient +from .cluster import ClusterClient +from .dangling_indices import DanglingIndicesClient +from .enrich import EnrichClient +from .eql import EqlClient +from .features import FeaturesClient +from .fleet import FleetClient +from .graph import GraphClient +from .ilm import IlmClient +from .indices import IndicesClient +from .ingest import IngestClient +from .license import LicenseClient +from .logstash import LogstashClient +from .migration import MigrationClient +from .ml import MlClient +from .monitoring import MonitoringClient +from .nodes import NodesClient +from .rollup import RollupClient +from .search_application import SearchApplicationClient +from .searchable_snapshots import SearchableSnapshotsClient +from .security import SecurityClient +from .shutdown import ShutdownClient +from .slm import SlmClient +from .snapshot import SnapshotClient +from .sql import SqlClient +from .ssl import SslClient +from .tasks import TasksClient +from .text_structure import TextStructureClient +from .transform import TransformClient +from .utils import ( + _TYPE_HOSTS, + CLIENT_META_SERVICE, + SKIP_IN_PATH, + _quote, + _rewrite_parameters, + client_node_configs, + is_requests_http_auth, + is_requests_node_class, +) +from .watcher import WatcherClient +from .xpack import XPackClient + +logger = logging.getLogger("elasticsearch") + + +SelfType = t.TypeVar("SelfType", bound="Elasticsearch") + + +class Elasticsearch(BaseClient): + """ + Elasticsearch low-level client. Provides a straightforward mapping from + Python to Elasticsearch REST APIs. + + The client instance has additional attributes to update APIs in different + namespaces such as ``async_search``, ``indices``, ``security``, and more: + + .. code-block:: python + + client = Elasticsearch("http://localhost:9200") + + # Get Document API + client.get(index="*", id="1") + + # Get Index API + client.indices.get(index="*") + + Transport options can be set on the client constructor or using + the :meth:`~elasticsearch.Elasticsearch.options` method: + + .. code-block:: python + + # Set 'api_key' on the constructor + client = Elasticsearch( + "http://localhost:9200", + api_key=("id", "api_key") + ) + client.search(...) + + # Set 'api_key' per request + client.options(api_key=("id", "api_key")).search(...) + """ + + def __init__( + self, + hosts: t.Optional[_TYPE_HOSTS] = None, + *, + # API + cloud_id: t.Optional[str] = None, + api_key: t.Optional[t.Union[str, t.Tuple[str, str]]] = None, + basic_auth: t.Optional[t.Union[str, t.Tuple[str, str]]] = None, + bearer_auth: t.Optional[str] = None, + opaque_id: t.Optional[str] = None, + # Node + headers: t.Union[DefaultType, t.Mapping[str, str]] = DEFAULT, + connections_per_node: t.Union[DefaultType, int] = DEFAULT, + http_compress: t.Union[DefaultType, bool] = DEFAULT, + verify_certs: t.Union[DefaultType, bool] = DEFAULT, + ca_certs: t.Union[DefaultType, str] = DEFAULT, + client_cert: t.Union[DefaultType, str] = DEFAULT, + client_key: t.Union[DefaultType, str] = DEFAULT, + ssl_assert_hostname: t.Union[DefaultType, str] = DEFAULT, + ssl_assert_fingerprint: t.Union[DefaultType, str] = DEFAULT, + ssl_version: t.Union[DefaultType, int] = DEFAULT, + ssl_context: t.Union[DefaultType, t.Any] = DEFAULT, + ssl_show_warn: t.Union[DefaultType, bool] = DEFAULT, + # Transport + transport_class: t.Type[Transport] = Transport, + request_timeout: t.Union[DefaultType, None, float] = DEFAULT, + node_class: t.Union[DefaultType, t.Type[BaseNode]] = DEFAULT, + node_pool_class: t.Union[DefaultType, t.Type[NodePool]] = DEFAULT, + randomize_nodes_in_pool: t.Union[DefaultType, bool] = DEFAULT, + node_selector_class: t.Union[DefaultType, t.Type[NodeSelector]] = DEFAULT, + dead_node_backoff_factor: t.Union[DefaultType, float] = DEFAULT, + max_dead_node_backoff: t.Union[DefaultType, float] = DEFAULT, + serializer: t.Optional[Serializer] = None, + serializers: t.Union[DefaultType, t.Mapping[str, Serializer]] = DEFAULT, + default_mimetype: str = "application/json", + max_retries: t.Union[DefaultType, int] = DEFAULT, + retry_on_status: t.Union[DefaultType, int, t.Collection[int]] = DEFAULT, + retry_on_timeout: t.Union[DefaultType, bool] = DEFAULT, + sniff_on_start: t.Union[DefaultType, bool] = DEFAULT, + sniff_before_requests: t.Union[DefaultType, bool] = DEFAULT, + sniff_on_node_failure: t.Union[DefaultType, bool] = DEFAULT, + sniff_timeout: t.Union[DefaultType, None, float] = DEFAULT, + min_delay_between_sniffing: t.Union[DefaultType, None, float] = DEFAULT, + sniffed_node_callback: t.Optional[ + t.Callable[[t.Dict[str, t.Any], NodeConfig], t.Optional[NodeConfig]] + ] = None, + meta_header: t.Union[DefaultType, bool] = DEFAULT, + timeout: t.Union[DefaultType, None, float] = DEFAULT, + randomize_hosts: t.Union[DefaultType, bool] = DEFAULT, + host_info_callback: t.Optional[ + t.Callable[ + [t.Dict[str, t.Any], t.Dict[str, t.Union[str, int]]], + t.Optional[t.Dict[str, t.Union[str, int]]], + ] + ] = None, + sniffer_timeout: t.Union[DefaultType, None, float] = DEFAULT, + sniff_on_connection_fail: t.Union[DefaultType, bool] = DEFAULT, + http_auth: t.Union[DefaultType, t.Any] = DEFAULT, + maxsize: t.Union[DefaultType, int] = DEFAULT, + # Internal use only + _transport: t.Optional[Transport] = None, + ) -> None: + if hosts is None and cloud_id is None and _transport is None: + raise ValueError("Either 'hosts' or 'cloud_id' must be specified") + + if timeout is not DEFAULT: + if request_timeout is not DEFAULT: + raise ValueError( + "Can't specify both 'timeout' and 'request_timeout', " + "instead only specify 'request_timeout'" + ) + warnings.warn( + "The 'timeout' parameter is deprecated in favor of 'request_timeout'", + category=DeprecationWarning, + stacklevel=2, + ) + request_timeout = timeout + + if serializer is not None: + if serializers is not DEFAULT: + raise ValueError( + "Can't specify both 'serializer' and 'serializers' parameters " + "together. Instead only specify one of the other." + ) + serializers = {default_mimetype: serializer} + + if randomize_hosts is not DEFAULT: + if randomize_nodes_in_pool is not DEFAULT: + raise ValueError( + "Can't specify both 'randomize_hosts' and 'randomize_nodes_in_pool', " + "instead only specify 'randomize_nodes_in_pool'" + ) + warnings.warn( + "The 'randomize_hosts' parameter is deprecated in favor of 'randomize_nodes_in_pool'", + category=DeprecationWarning, + stacklevel=2, + ) + randomize_nodes_in_pool = randomize_hosts + + if sniffer_timeout is not DEFAULT: + if min_delay_between_sniffing is not DEFAULT: + raise ValueError( + "Can't specify both 'sniffer_timeout' and 'min_delay_between_sniffing', " + "instead only specify 'min_delay_between_sniffing'" + ) + warnings.warn( + "The 'sniffer_timeout' parameter is deprecated in favor of 'min_delay_between_sniffing'", + category=DeprecationWarning, + stacklevel=2, + ) + min_delay_between_sniffing = sniffer_timeout + + if sniff_on_connection_fail is not DEFAULT: + if sniff_on_node_failure is not DEFAULT: + raise ValueError( + "Can't specify both 'sniff_on_connection_fail' and 'sniff_on_node_failure', " + "instead only specify 'sniff_on_node_failure'" + ) + warnings.warn( + "The 'sniff_on_connection_fail' parameter is deprecated in favor of 'sniff_on_node_failure'", + category=DeprecationWarning, + stacklevel=2, + ) + sniff_on_node_failure = sniff_on_connection_fail + + if maxsize is not DEFAULT: + if connections_per_node is not DEFAULT: + raise ValueError( + "Can't specify both 'maxsize' and 'connections_per_node', " + "instead only specify 'connections_per_node'" + ) + warnings.warn( + "The 'maxsize' parameter is deprecated in favor of 'connections_per_node'", + category=DeprecationWarning, + stacklevel=2, + ) + connections_per_node = maxsize + + # Setting min_delay_between_sniffing=True implies sniff_before_requests=True + if min_delay_between_sniffing is not DEFAULT: + sniff_before_requests = True + + sniffing_options = ( + sniff_timeout, + sniff_on_start, + sniff_before_requests, + sniff_on_node_failure, + sniffed_node_callback, + min_delay_between_sniffing, + sniffed_node_callback, + ) + if cloud_id is not None and any( + x is not DEFAULT and x is not None for x in sniffing_options + ): + raise ValueError( + "Sniffing should not be enabled when connecting to Elastic Cloud" + ) + + sniff_callback = None + if host_info_callback is not None: + if sniffed_node_callback is not None: + raise ValueError( + "Can't specify both 'host_info_callback' and 'sniffed_node_callback', " + "instead only specify 'sniffed_node_callback'" + ) + warnings.warn( + "The 'host_info_callback' parameter is deprecated in favor of 'sniffed_node_callback'", + category=DeprecationWarning, + stacklevel=2, + ) + + sniff_callback = create_sniff_callback( + host_info_callback=host_info_callback + ) + elif sniffed_node_callback is not None: + sniff_callback = create_sniff_callback( + sniffed_node_callback=sniffed_node_callback + ) + elif ( + sniff_on_start is True + or sniff_before_requests is True + or sniff_on_node_failure is True + ): + sniff_callback = default_sniff_callback + + if _transport is None: + requests_session_auth = None + if http_auth is not None and http_auth is not DEFAULT: + if is_requests_http_auth(http_auth): + # If we're using custom requests authentication + # then we need to alert the user that they also + # need to use 'node_class=requests'. + if not is_requests_node_class(node_class): + raise ValueError( + "Using a custom 'requests.auth.AuthBase' class for " + "'http_auth' must be used with node_class='requests'" + ) + + # Reset 'http_auth' to DEFAULT so it's not consumed below. + requests_session_auth = http_auth + http_auth = DEFAULT + + node_configs = client_node_configs( + hosts, + cloud_id=cloud_id, + requests_session_auth=requests_session_auth, + connections_per_node=connections_per_node, + http_compress=http_compress, + verify_certs=verify_certs, + ca_certs=ca_certs, + client_cert=client_cert, + client_key=client_key, + ssl_assert_hostname=ssl_assert_hostname, + ssl_assert_fingerprint=ssl_assert_fingerprint, + ssl_version=ssl_version, + ssl_context=ssl_context, + ssl_show_warn=ssl_show_warn, + ) + transport_kwargs: t.Dict[str, t.Any] = {} + if node_class is not DEFAULT: + transport_kwargs["node_class"] = node_class + if node_pool_class is not DEFAULT: + transport_kwargs["node_pool_class"] = node_class + if randomize_nodes_in_pool is not DEFAULT: + transport_kwargs["randomize_nodes_in_pool"] = randomize_nodes_in_pool + if node_selector_class is not DEFAULT: + transport_kwargs["node_selector_class"] = node_selector_class + if dead_node_backoff_factor is not DEFAULT: + transport_kwargs["dead_node_backoff_factor"] = dead_node_backoff_factor + if max_dead_node_backoff is not DEFAULT: + transport_kwargs["max_dead_node_backoff"] = max_dead_node_backoff + if meta_header is not DEFAULT: + transport_kwargs["meta_header"] = meta_header + + transport_serializers = DEFAULT_SERIALIZERS.copy() + if serializers is not DEFAULT: + transport_serializers.update(serializers) + + # Override compatibility serializers from their non-compat mimetypes too. + # So we use the same serializer for requests and responses. + for mime_subtype in ("json", "x-ndjson"): + if f"application/{mime_subtype}" in serializers: + compat_mimetype = ( + f"application/vnd.elasticsearch+{mime_subtype}" + ) + if compat_mimetype not in serializers: + transport_serializers[compat_mimetype] = serializers[ + f"application/{mime_subtype}" + ] + + transport_kwargs["serializers"] = transport_serializers + + transport_kwargs["default_mimetype"] = default_mimetype + if sniff_on_start is not DEFAULT: + transport_kwargs["sniff_on_start"] = sniff_on_start + if sniff_before_requests is not DEFAULT: + transport_kwargs["sniff_before_requests"] = sniff_before_requests + if sniff_on_node_failure is not DEFAULT: + transport_kwargs["sniff_on_node_failure"] = sniff_on_node_failure + if sniff_timeout is not DEFAULT: + transport_kwargs["sniff_timeout"] = sniff_timeout + if min_delay_between_sniffing is not DEFAULT: + transport_kwargs[ + "min_delay_between_sniffing" + ] = min_delay_between_sniffing + + _transport = transport_class( + node_configs, + client_meta_service=CLIENT_META_SERVICE, + sniff_callback=sniff_callback, + **transport_kwargs, + ) + + super().__init__(_transport) + + # These are set per-request so are stored separately. + self._request_timeout = request_timeout + self._max_retries = max_retries + self._retry_on_timeout = retry_on_timeout + if isinstance(retry_on_status, int): + retry_on_status = (retry_on_status,) + self._retry_on_status = retry_on_status + + else: + super().__init__(_transport) + + if headers is not DEFAULT and headers is not None: + self._headers.update(headers) + if opaque_id is not DEFAULT and opaque_id is not None: # type: ignore[comparison-overlap] + self._headers["x-opaque-id"] = opaque_id + self._headers = resolve_auth_headers( + self._headers, + http_auth=http_auth, + api_key=api_key, + basic_auth=basic_auth, + bearer_auth=bearer_auth, + ) + + # namespaced clients for compatibility with API names + self.async_search = AsyncSearchClient(self) + self.autoscaling = AutoscalingClient(self) + self.cat = CatClient(self) + self.cluster = ClusterClient(self) + self.fleet = FleetClient(self) + self.features = FeaturesClient(self) + self.indices = IndicesClient(self) + self.ingest = IngestClient(self) + self.nodes = NodesClient(self) + self.snapshot = SnapshotClient(self) + self.tasks = TasksClient(self) + + self.xpack = XPackClient(self) + self.ccr = CcrClient(self) + self.dangling_indices = DanglingIndicesClient(self) + self.enrich = EnrichClient(self) + self.eql = EqlClient(self) + self.graph = GraphClient(self) + self.ilm = IlmClient(self) + self.license = LicenseClient(self) + self.logstash = LogstashClient(self) + self.migration = MigrationClient(self) + self.ml = MlClient(self) + self.monitoring = MonitoringClient(self) + self.rollup = RollupClient(self) + self.search_application = SearchApplicationClient(self) + self.searchable_snapshots = SearchableSnapshotsClient(self) + self.security = SecurityClient(self) + self.slm = SlmClient(self) + self.shutdown = ShutdownClient(self) + self.sql = SqlClient(self) + self.ssl = SslClient(self) + self.text_structure = TextStructureClient(self) + self.transform = TransformClient(self) + self.watcher = WatcherClient(self) + + def __repr__(self) -> str: + try: + # get a list of all connections + nodes = [node.base_url for node in self.transport.node_pool.all()] + # truncate to 5 if there are too many + if len(nodes) > 5: + nodes = nodes[:5] + ["..."] + return f"<{self.__class__.__name__}({nodes})>" + except Exception: + # probably operating on custom transport and connection_pool, ignore + return super().__repr__() + + def __enter__(self) -> "Elasticsearch": + try: + # All this to avoid a Mypy error when using unasync. + getattr(self.transport, "_async_call")() + except AttributeError: + pass + return self + + def __exit__(self, *_: t.Any) -> None: + self.close() + + def options( + self: SelfType, + *, + opaque_id: t.Union[DefaultType, str] = DEFAULT, + api_key: t.Union[DefaultType, str, t.Tuple[str, str]] = DEFAULT, + basic_auth: t.Union[DefaultType, str, t.Tuple[str, str]] = DEFAULT, + bearer_auth: t.Union[DefaultType, str] = DEFAULT, + headers: t.Union[DefaultType, t.Mapping[str, str]] = DEFAULT, + request_timeout: t.Union[DefaultType, t.Optional[float]] = DEFAULT, + ignore_status: t.Union[DefaultType, int, t.Collection[int]] = DEFAULT, + max_retries: t.Union[DefaultType, int] = DEFAULT, + retry_on_status: t.Union[DefaultType, int, t.Collection[int]] = DEFAULT, + retry_on_timeout: t.Union[DefaultType, bool] = DEFAULT, + ) -> SelfType: + client = type(self)(_transport=self.transport) + + resolved_headers = headers if headers is not DEFAULT else None + resolved_headers = resolve_auth_headers( + headers=resolved_headers, + api_key=api_key, + basic_auth=basic_auth, + bearer_auth=bearer_auth, + ) + resolved_opaque_id = opaque_id if opaque_id is not DEFAULT else None + if resolved_opaque_id: + resolved_headers["x-opaque-id"] = resolved_opaque_id + + if resolved_headers: + new_headers = self._headers.copy() + new_headers.update(resolved_headers) + client._headers = new_headers + else: + client._headers = self._headers.copy() + + if request_timeout is not DEFAULT: + client._request_timeout = request_timeout + else: + client._request_timeout = self._request_timeout + + if ignore_status is not DEFAULT: + if isinstance(ignore_status, int): + ignore_status = (ignore_status,) + client._ignore_status = ignore_status + else: + client._ignore_status = self._ignore_status + + if max_retries is not DEFAULT: + if not isinstance(max_retries, int): + raise TypeError("'max_retries' must be of type 'int'") + client._max_retries = max_retries + else: + client._max_retries = self._max_retries + + if retry_on_status is not DEFAULT: + if isinstance(retry_on_status, int): + retry_on_status = (retry_on_status,) + client._retry_on_status = retry_on_status + else: + client._retry_on_status = self._retry_on_status + + if retry_on_timeout is not DEFAULT: + if not isinstance(retry_on_timeout, bool): + raise TypeError("'retry_on_timeout' must be of type 'bool'") + client._retry_on_timeout = retry_on_timeout + else: + client._retry_on_timeout = self._retry_on_timeout + + return client + + def close(self) -> None: + """Closes the Transport and all internal connections""" + self.transport.close() + + @_rewrite_parameters() + def ping( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[t.List[str], str]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> bool: + """ + Returns True if a successful response returns from the info() API, + otherwise returns False. This API call can fail either at the transport + layer (due to connection errors or timeouts) or from a non-2XX HTTP response + (due to authentication or authorization issues). + + If you want to discover why the request failed you should use the ``info()`` API. + + ``_ + """ + __path = "/" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + try: + self.perform_request("HEAD", __path, params=__query, headers=__headers) + return True + except (ApiError, TransportError): + return False + + # AUTO-GENERATED-API-DEFINITIONS # + + @_rewrite_parameters( + body_name="operations", + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def bulk( + self, + *, + operations: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pipeline: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + require_alias: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to perform multiple index/update/delete operations in a single request. + + ``_ + + :param operations: + :param index: Default index for items which don't provide one + :param pipeline: The pipeline id to preprocess incoming documents with + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param require_alias: Sets require_alias for all incoming documents. Defaults + to unset (false) + :param routing: Specific routing value + :param source: True or false to return the _source field or not, or default list + of fields to return, can be overridden on each sub-request + :param source_excludes: Default list of fields to exclude from the returned _source + field, can be overridden on each sub-request + :param source_includes: Default list of fields to extract and return from the + _source field, can be overridden on each sub-request + :param timeout: Explicit operation timeout + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the bulk operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if operations is None: + raise ValueError("Empty value passed for parameter 'operations'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_bulk" + else: + __path = "/_bulk" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pipeline is not None: + __query["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if require_alias is not None: + __query["require_alias"] = require_alias + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if timeout is not None: + __query["timeout"] = timeout + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __body = operations + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def clear_scroll( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + scroll_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Explicitly clears the search context for a scroll. + + ``_ + + :param scroll_id: + """ + __path = "/_search/scroll" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if scroll_id is not None: + __body["scroll_id"] = scroll_id + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def close_point_in_time( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Close a point in time + + ``_ + + :param id: + """ + if id is None: + raise ValueError("Empty value passed for parameter 'id'") + __path = "/_pit" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if id is not None: + __body["id"] = id + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def count( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + min_score: t.Optional[float] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + routing: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns number of documents matching a query. + + ``_ + + :param index: A comma-separated list of indices to restrict the results + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param min_score: Include only documents with a specific `_score` value in the + result + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param q: Query in the Lucene query string syntax + :param query: + :param routing: A comma-separated list of specific routing values + :param terminate_after: The maximum count for each shard, upon reaching which + the query execution will terminate early + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_count" + else: + __path = "/_count" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if min_score is not None: + __query["min_score"] = min_score + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if routing is not None: + __query["routing"] = routing + if terminate_after is not None: + __query["terminate_after"] = terminate_after + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="document", + ) + def create( + self, + *, + index: str, + id: str, + document: t.Mapping[str, t.Any], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pipeline: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new document in the index. Returns a 409 response when a document with + a same ID already exists in the index. + + ``_ + + :param index: The name of the index + :param id: Document ID + :param document: + :param pipeline: The pipeline id to preprocess incoming documents with + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the index operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if document is None: + raise ValueError("Empty value passed for parameter 'document'") + __path = f"/{_quote(index)}/_create/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pipeline is not None: + __query["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __body = document + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def delete( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes a document from the index. + + ``_ + + :param index: The name of the index + :param id: The document ID + :param if_primary_term: only perform the delete operation if the last operation + that has changed the document has the specified primary term + :param if_seq_no: only perform the delete operation if the last operation that + has changed the document has the specified sequence number + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the delete operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + def delete_by_query( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + conflicts: t.Optional[t.Union["t.Literal['abort', 'proceed']", str]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + max_docs: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + refresh: t.Optional[bool] = None, + request_cache: t.Optional[bool] = None, + requests_per_second: t.Optional[float] = None, + routing: t.Optional[str] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + scroll_size: t.Optional[int] = None, + search_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + slices: t.Optional[t.Union[int, t.Union["t.Literal['auto']", str]]] = None, + sort: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[bool] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes documents matching the provided query. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param conflicts: What to do when the delete by query hits version conflicts? + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param from_: Starting offset (default: 0) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_docs: + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param q: Query in the Lucene query string syntax + :param query: + :param refresh: Should the affected indexes be refreshed? + :param request_cache: Specify if request cache should be used for this request + or not, defaults to index level setting + :param requests_per_second: The throttle for this request in sub-requests per + second. -1 means no throttle. + :param routing: A comma-separated list of specific routing values + :param scroll: Specify how long a consistent view of the index should be maintained + for scrolled search + :param scroll_size: Size on the scroll request powering the delete by query + :param search_timeout: Explicit timeout for each search request. Defaults to + no timeout. + :param search_type: Search operation type + :param slice: + :param slices: The number of slices this task should be divided into. Defaults + to 1, meaning the task isn't sliced into subtasks. Can be set to `auto`. + :param sort: A comma-separated list of : pairs + :param stats: Specific 'tag' of the request for logging and statistical purposes + :param terminate_after: The maximum number of documents to collect for each shard, + upon reaching which the query execution will terminate early. + :param timeout: Time each individual bulk request should wait for shards that + are unavailable. + :param version: Specify whether to return document version as part of a hit + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the delete by query operation. Defaults to 1, meaning + the primary shard only. Set to `all` for all shard copies, otherwise set + to any non-negative value less than or equal to the total number of copies + for the shard (number of replicas + 1) + :param wait_for_completion: Should the request should block until the delete + by query is complete. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_delete_by_query" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if conflicts is not None: + __query["conflicts"] = conflicts + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if max_docs is not None: + __body["max_docs"] = max_docs + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if refresh is not None: + __query["refresh"] = refresh + if request_cache is not None: + __query["request_cache"] = request_cache + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second + if routing is not None: + __query["routing"] = routing + if scroll is not None: + __query["scroll"] = scroll + if scroll_size is not None: + __query["scroll_size"] = scroll_size + if search_timeout is not None: + __query["search_timeout"] = search_timeout + if search_type is not None: + __query["search_type"] = search_type + if slice is not None: + __body["slice"] = slice + if slices is not None: + __query["slices"] = slices + if sort is not None: + __query["sort"] = sort + if stats is not None: + __query["stats"] = stats + if terminate_after is not None: + __query["terminate_after"] = terminate_after + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def delete_script( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a script. + + ``_ + + :param id: Script ID + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_scripts/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def exists( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a document exists in an index. + + ``_ + + :param index: The name of the index + :param id: The document ID + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param realtime: Specify whether to perform the operation in realtime or search + mode + :param refresh: Refresh the shard containing the document before performing the + operation + :param routing: Specific routing value + :param source: True or false to return the _source field or not, or a list of + fields to return + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param stored_fields: A comma-separated list of stored fields to return in the + response + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def exists_source( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a document source exists in an index. + + ``_ + + :param index: The name of the index + :param id: The document ID + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param realtime: Specify whether to perform the operation in realtime or search + mode + :param refresh: Refresh the shard containing the document before performing the + operation + :param routing: Specific routing value + :param source: True or false to return the _source field or not, or a list of + fields to return + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_source/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def field_caps( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filters: t.Optional[str] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_unmapped: t.Optional[bool] = None, + index_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + types: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the information about the capabilities of fields among multiple indices. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (*). To target all data streams + and indices, omit this parameter or use * or _all. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with foo but no index starts with bar. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. + :param fields: List of fields to retrieve capabilities for. Wildcard (`*`) expressions + are supported. + :param filters: An optional set of filters: can include +metadata,-metadata,-nested,-multifield,-parent + :param ignore_unavailable: If `true`, missing or closed indices are not included + in the response. + :param include_unmapped: If true, unmapped fields are included in the response. + :param index_filter: Allows to filter indices if the provided query rewrites + to match_none on every shard. + :param runtime_mappings: Defines ad-hoc runtime fields in the request similar + to the way it is done in search requests. These fields exist only as part + of the query and take precedence over fields defined with the same name in + the index mappings. + :param types: Only return results for fields that have one of the types in the + list + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_field_caps" + else: + __path = "/_field_caps" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if filters is not None: + __query["filters"] = filters + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_unmapped is not None: + __query["include_unmapped"] = include_unmapped + if index_filter is not None: + __body["index_filter"] = index_filter + if pretty is not None: + __query["pretty"] = pretty + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if types is not None: + __query["types"] = types + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def get( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a document. + + ``_ + + :param index: Name of the index that contains the document. + :param id: Unique identifier of the document. + :param preference: Specifies the node or shard the operation should be performed + on. Random by default. + :param realtime: Boolean) If true, the request is real-time as opposed to near-real-time. + :param refresh: If true, Elasticsearch refreshes the affected shards to make + this operation visible to search. If false, do nothing with refreshes. + :param routing: Target the specified primary shard. + :param source: True or false to return the _source field or not, or a list of + fields to return. + :param source_excludes: A comma-separated list of source fields to exclude in + the response. + :param source_includes: A comma-separated list of source fields to include in + the response. + :param stored_fields: A comma-separated list of stored fields to return in the + response + :param version: Explicit version number for concurrency control. The specified + version must match the current version of the document for the request to + succeed. + :param version_type: Specific version type: internal, external, external_gte. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_script( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a script. + + ``_ + + :param id: Script ID + :param master_timeout: Specify timeout for connection to master + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_scripts/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def get_source( + self, + *, + index: str, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the source of a document. + + ``_ + + :param index: Name of the index that contains the document. + :param id: Unique identifier of the document. + :param preference: Specifies the node or shard the operation should be performed + on. Random by default. + :param realtime: Boolean) If true, the request is real-time as opposed to near-real-time. + :param refresh: If true, Elasticsearch refreshes the affected shards to make + this operation visible to search. If false, do nothing with refreshes. + :param routing: Target the specified primary shard. + :param source: True or false to return the _source field or not, or a list of + fields to return. + :param source_excludes: A comma-separated list of source fields to exclude in + the response. + :param source_includes: A comma-separated list of source fields to include in + the response. + :param stored_fields: + :param version: Explicit version number for concurrency control. The specified + version must match the current version of the document for the request to + succeed. + :param version_type: Specific version type: internal, external, external_gte. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_source/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="document", + ) + def index( + self, + *, + index: str, + document: t.Mapping[str, t.Any], + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + op_type: t.Optional[t.Union["t.Literal['create', 'index']", str]] = None, + pipeline: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + require_alias: t.Optional[bool] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a document in an index. + + ``_ + + :param index: The name of the index + :param document: + :param id: Document ID + :param if_primary_term: only perform the index operation if the last operation + that has changed the document has the specified primary term + :param if_seq_no: only perform the index operation if the last operation that + has changed the document has the specified sequence number + :param op_type: Explicit operation type. Defaults to `index` for requests with + an explicit document ID, and to `create`for requests without an explicit + document ID + :param pipeline: The pipeline id to preprocess incoming documents with + :param refresh: If `true` then refresh the affected shards to make this operation + visible to search, if `wait_for` then wait for a refresh to make this operation + visible to search, if `false` (the default) then do nothing with refreshes. + :param require_alias: When true, requires destination to be an alias. Default + is false + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the index operation. Defaults to 1, meaning the primary + shard only. Set to `all` for all shard copies, otherwise set to any non-negative + value less than or equal to the total number of copies for the shard (number + of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if document is None: + raise ValueError("Empty value passed for parameter 'document'") + if index not in SKIP_IN_PATH and id not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_doc/{_quote(id)}" + __method = "PUT" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_doc" + __method = "POST" + else: + raise ValueError("Couldn't find a path for the given parameters") + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if op_type is not None: + __query["op_type"] = op_type + if pipeline is not None: + __query["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if require_alias is not None: + __query["require_alias"] = require_alias + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __body = document + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + __method, __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def info( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns basic information about the cluster. + + ``_ + """ + __path = "/" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def mget( + self, + *, + index: t.Optional[str] = None, + docs: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ids: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + refresh: t.Optional[bool] = None, + routing: t.Optional[str] = None, + source: t.Optional[ + t.Union[bool, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to get multiple documents in one request. + + ``_ + + :param index: Name of the index to retrieve documents from when `ids` are specified, + or when a document in the `docs` array does not specify an index. + :param docs: The documents you want to retrieve. Required if no index is specified + in the request URI. + :param ids: The IDs of the documents you want to retrieve. Allowed when the index + is specified in the request URI. + :param preference: Specifies the node or shard the operation should be performed + on. Random by default. + :param realtime: If `true`, the request is real-time as opposed to near-real-time. + :param refresh: If `true`, the request refreshes relevant shards before retrieving + documents. + :param routing: Custom value used to route operations to a specific shard. + :param source: True or false to return the `_source` field or not, or a list + of fields to return. + :param source_excludes: A comma-separated list of source fields to exclude from + the response. You can also use this parameter to exclude fields from the + subset specified in `_source_includes` query parameter. + :param source_includes: A comma-separated list of source fields to include in + the response. If this parameter is specified, only these source fields are + returned. You can exclude fields from this subset using the `_source_excludes` + query parameter. If the `_source` parameter is `false`, this parameter is + ignored. + :param stored_fields: If `true`, retrieves the document fields stored in the + index rather than the document `_source`. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_mget" + else: + __path = "/_mget" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if docs is not None: + __body["docs"] = docs + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ids is not None: + __body["ids"] = ids + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if refresh is not None: + __query["refresh"] = refresh + if routing is not None: + __query["routing"] = routing + if source is not None: + __query["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stored_fields is not None: + __query["stored_fields"] = stored_fields + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="searches", + ) + def msearch( + self, + *, + searches: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + max_concurrent_searches: t.Optional[int] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + pre_filter_shard_size: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to execute several search operations in one request. + + ``_ + + :param searches: + :param index: Comma-separated list of data streams, indices, and index aliases + to search. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param ccs_minimize_roundtrips: If true, network roundtrips between the coordinating + node and remote clusters are minimized for cross-cluster search requests. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. + :param ignore_throttled: If true, concrete, expanded or aliased indices are ignored + when frozen. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param max_concurrent_searches: Maximum number of concurrent searches the multi + search API can execute. + :param max_concurrent_shard_requests: Maximum number of concurrent shard requests + that each sub-search request executes per node. + :param pre_filter_shard_size: Defines a threshold that enforces a pre-filter + roundtrip to prefilter search shards based on query rewriting if the number + of shards the search request expands to exceeds the threshold. This filter + roundtrip can limit the number of shards significantly if for instance a + shard can not match any documents based on its rewrite method i.e., if date + filters are mandatory to match but the shard bounds and the query are disjoint. + :param rest_total_hits_as_int: If true, hits.total are returned as an integer + in the response. Defaults to false, which returns an object. + :param routing: Custom routing value used to route search operations to a specific + shard. + :param search_type: Indicates whether global term and document frequencies should + be used when scoring returned documents. + :param typed_keys: Specifies whether aggregation and suggester names should be + prefixed by their respective types in the response. + """ + if searches is None: + raise ValueError("Empty value passed for parameter 'searches'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_msearch" + else: + __path = "/_msearch" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if max_concurrent_searches is not None: + __query["max_concurrent_searches"] = max_concurrent_searches + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if search_type is not None: + __query["search_type"] = search_type + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __body = searches + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="search_templates", + ) + def msearch_template( + self, + *, + search_templates: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_concurrent_searches: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to execute several search template operations in one request. + + ``_ + + :param search_templates: + :param index: A comma-separated list of index names to use as default + :param ccs_minimize_roundtrips: Indicates whether network round-trips should + be minimized as part of cross-cluster search requests execution + :param max_concurrent_searches: Controls the maximum number of concurrent searches + the multi search api will execute + :param rest_total_hits_as_int: Indicates whether hits.total should be rendered + as an integer or an object in the rest search response + :param search_type: Search operation type + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + """ + if search_templates is None: + raise ValueError("Empty value passed for parameter 'search_templates'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_msearch/template" + else: + __path = "/_msearch/template" + __query: t.Dict[str, t.Any] = {} + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_concurrent_searches is not None: + __query["max_concurrent_searches"] = max_concurrent_searches + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if search_type is not None: + __query["search_type"] = search_type + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __body = search_templates + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def mtermvectors( + self, + *, + index: t.Optional[str] = None, + docs: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + field_statistics: t.Optional[bool] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ids: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + offsets: t.Optional[bool] = None, + payloads: t.Optional[bool] = None, + positions: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + routing: t.Optional[str] = None, + term_statistics: t.Optional[bool] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns multiple termvectors in one request. + + ``_ + + :param index: The index in which the document resides. + :param docs: + :param field_statistics: Specifies if document count, sum of document frequencies + and sum of total term frequencies should be returned. Applies to all returned + documents unless otherwise specified in body "params" or "docs". + :param fields: A comma-separated list of fields to return. Applies to all returned + documents unless otherwise specified in body "params" or "docs". + :param ids: + :param offsets: Specifies if term offsets should be returned. Applies to all + returned documents unless otherwise specified in body "params" or "docs". + :param payloads: Specifies if term payloads should be returned. Applies to all + returned documents unless otherwise specified in body "params" or "docs". + :param positions: Specifies if term positions should be returned. Applies to + all returned documents unless otherwise specified in body "params" or "docs". + :param preference: Specify the node or shard the operation should be performed + on (default: random) .Applies to all returned documents unless otherwise + specified in body "params" or "docs". + :param realtime: Specifies if requests are real-time as opposed to near-real-time + (default: true). + :param routing: Specific routing value. Applies to all returned documents unless + otherwise specified in body "params" or "docs". + :param term_statistics: Specifies if total term frequency and document frequency + should be returned. Applies to all returned documents unless otherwise specified + in body "params" or "docs". + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_mtermvectors" + else: + __path = "/_mtermvectors" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if docs is not None: + __body["docs"] = docs + if error_trace is not None: + __query["error_trace"] = error_trace + if field_statistics is not None: + __query["field_statistics"] = field_statistics + if fields is not None: + __query["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ids is not None: + __body["ids"] = ids + if offsets is not None: + __query["offsets"] = offsets + if payloads is not None: + __query["payloads"] = payloads + if positions is not None: + __query["positions"] = positions + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if routing is not None: + __query["routing"] = routing + if term_statistics is not None: + __query["term_statistics"] = term_statistics + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def open_point_in_time( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + keep_alive: t.Union["t.Literal[-1]", "t.Literal[0]", str], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + routing: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Open a point in time that can be used in subsequent searches + + ``_ + + :param index: A comma-separated list of index names to open point in time; use + `_all` or empty string to perform the operation on all indices + :param keep_alive: Specific the time to live for the point in time + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param routing: Specific routing value + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if keep_alive is None: + raise ValueError("Empty value passed for parameter 'keep_alive'") + __path = f"/{_quote(index)}/_pit" + __query: t.Dict[str, t.Any] = {} + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if routing is not None: + __query["routing"] = routing + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_script( + self, + *, + id: str, + script: t.Mapping[str, t.Any], + context: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a script. + + ``_ + + :param id: Script ID + :param script: + :param context: Script context + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if script is None: + raise ValueError("Empty value passed for parameter 'script'") + if id not in SKIP_IN_PATH and context not in SKIP_IN_PATH: + __path = f"/_scripts/{_quote(id)}/{_quote(context)}" + elif id not in SKIP_IN_PATH: + __path = f"/_scripts/{_quote(id)}" + else: + raise ValueError("Couldn't find a path for the given parameters") + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if script is not None: + __body["script"] = script + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def rank_eval( + self, + *, + requests: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + metric: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + search_type: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to evaluate the quality of ranked search results over a set of typical + search queries + + ``_ + + :param requests: A set of typical search requests, together with their provided + ratings. + :param index: Comma-separated list of data streams, indices, and index aliases + used to limit the request. Wildcard (`*`) expressions are supported. To target + all data streams and indices in a cluster, omit this parameter or use `_all` + or `*`. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with `foo` but no index starts with `bar`. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: If `true`, missing or closed indices are not included + in the response. + :param metric: Definition of the evaluation metric to calculate. + :param search_type: Search operation type + """ + if requests is None: + raise ValueError("Empty value passed for parameter 'requests'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_rank_eval" + else: + __path = "/_rank_eval" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if requests is not None: + __body["requests"] = requests + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if metric is not None: + __body["metric"] = metric + if pretty is not None: + __query["pretty"] = pretty + if search_type is not None: + __query["search_type"] = search_type + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params"}, + ) + def render_search_template( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + file: t.Optional[str] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + source: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to use the Mustache language to pre-render a search definition. + + ``_ + + :param id: The id of the stored search template + :param file: + :param params: + :param source: + """ + if id not in SKIP_IN_PATH: + __path = f"/_render/template/{_quote(id)}" + else: + __path = "/_render/template" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if file is not None: + __body["file"] = file + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if params is not None: + __body["params"] = params + if pretty is not None: + __query["pretty"] = pretty + if source is not None: + __body["source"] = source + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def scripts_painless_execute( + self, + *, + context: t.Optional[str] = None, + context_setup: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + script: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows an arbitrary script to be executed and a result to be returned + + ``_ + + :param context: + :param context_setup: + :param script: + """ + __path = "/_scripts/painless/_execute" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if context is not None: + __body["context"] = context + if context_setup is not None: + __body["context_setup"] = context_setup + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if script is not None: + __body["script"] = script + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def scroll( + self, + *, + scroll_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to retrieve a large numbers of results from a single search request. + + ``_ + + :param scroll_id: Scroll ID of the search. + :param rest_total_hits_as_int: If true, the API response’s hit.total property + is returned as an integer. If false, the API response’s hit.total property + is returned as an object. + :param scroll: Period to retain the search context for scrolling. + """ + if scroll_id is None: + raise ValueError("Empty value passed for parameter 'scroll_id'") + __path = "/_search/scroll" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if scroll_id is not None: + __body["scroll_id"] = scroll_id + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if scroll is not None: + __body["scroll"] = scroll + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + "from": "from_", + }, + ) + def search( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + batched_reduce_size: t.Optional[int] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + collapse: t.Optional[t.Mapping[str, t.Any]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + docvalue_fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + ext: t.Optional[t.Mapping[str, t.Any]] = None, + fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + highlight: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indices_boost: t.Optional[ + t.Union[t.List[t.Mapping[str, float]], t.Tuple[t.Mapping[str, float], ...]] + ] = None, + knn: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + lenient: t.Optional[bool] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + min_compatible_shard_node: t.Optional[str] = None, + min_score: t.Optional[float] = None, + pit: t.Optional[t.Mapping[str, t.Any]] = None, + post_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pre_filter_shard_size: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + rank: t.Optional[t.Mapping[str, t.Any]] = None, + request_cache: t.Optional[bool] = None, + rescore: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + seq_no_primary_term: t.Optional[bool] = None, + size: t.Optional[int] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + suggest: t.Optional[t.Mapping[str, t.Any]] = None, + suggest_field: t.Optional[str] = None, + suggest_mode: t.Optional[ + t.Union["t.Literal['always', 'missing', 'popular']", str] + ] = None, + suggest_size: t.Optional[int] = None, + suggest_text: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[str] = None, + track_scores: t.Optional[bool] = None, + track_total_hits: t.Optional[t.Union[bool, int]] = None, + typed_keys: t.Optional[bool] = None, + version: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns results matching a query. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param aggregations: + :param aggs: + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param allow_partial_search_results: Indicate if an error should be returned + if there is a partial search failure or timeout + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param batched_reduce_size: The number of shard results that should be reduced + at once on the coordinating node. This value should be used as a protection + mechanism to reduce the memory overhead per search request if the potential + number of shards in the request can be large. + :param ccs_minimize_roundtrips: Indicates whether network round-trips should + be minimized as part of cross-cluster search requests execution + :param collapse: + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc + values for field names matching these patterns in the hits.fields property + of the response. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: If true, returns detailed information about score computation + as part of a hit. + :param ext: Configuration of search extensions defined by Elasticsearch plugins. + :param fields: Array of wildcard (*) patterns. The request returns values for + field names matching these patterns in the hits.fields property of the response. + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the search_after parameter. + :param highlight: + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param indices_boost: Boosts the _score of documents from specified indices. + :param knn: Defines the approximate kNN search to run. + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_concurrent_shard_requests: The number of concurrent shard requests + per node this search executes concurrently. This value should be used to + limit the impact of the search on the cluster in order to limit the number + of concurrent shard requests + :param min_compatible_shard_node: The minimum compatible version that all shards + involved in search should have for this request to be successful + :param min_score: Minimum _score for matching documents. Documents with a lower + _score are not included in the search results. + :param pit: Limits the search to a point in time (PIT). If you provide a PIT, + you cannot specify an in the request path. + :param post_filter: + :param pre_filter_shard_size: A threshold that enforces a pre-filter roundtrip + to prefilter search shards based on query rewriting if the number of shards + the search request expands to exceeds the threshold. This filter roundtrip + can limit the number of shards significantly if for instance a shard can + not match any documents based on its rewrite method ie. if date filters are + mandatory to match but the shard bounds and the query are disjoint. + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param profile: + :param q: Query in the Lucene query string syntax + :param query: Defines the search definition using the Query DSL. + :param rank: Defines the Reciprocal Rank Fusion (RRF) to use + :param request_cache: Specify if request cache should be used for this request + or not, defaults to index level setting + :param rescore: + :param rest_total_hits_as_int: Indicates whether hits.total should be rendered + as an integer or an object in the rest search response + :param routing: A comma-separated list of specific routing values + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param script_fields: Retrieve a script evaluation (based on different fields) + for each hit. + :param scroll: Specify how long a consistent view of the index should be maintained + for scrolled search + :param search_after: + :param search_type: Search operation type + :param seq_no_primary_term: If true, returns sequence number and primary term + of the last modification of each hit. See Optimistic concurrency control. + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the from and size parameters. To page through + more hits, use the search_after parameter. + :param slice: + :param sort: + :param source: Indicates which source fields are returned for matching documents. + These fields are returned in the hits._source property of the search response. + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param stats: Stats groups to associate with the search. Each group maintains + a statistics aggregation for its associated searches. You can retrieve these + stats using the indices stats API. + :param stored_fields: List of stored fields to return as part of a hit. If no + fields are specified, no stored fields are included in the response. If this + field is specified, the _source parameter defaults to false. You can pass + _source: true to return both source fields and stored fields in the search + response. + :param suggest: + :param suggest_field: Specifies which field to use for suggestions. + :param suggest_mode: Specify suggest mode + :param suggest_size: How many suggestions to return in response + :param suggest_text: The source text for which the suggestions should be returned. + :param terminate_after: Maximum number of documents to collect for each shard. + If a query reaches this limit, Elasticsearch terminates the query early. + Elasticsearch collects documents before sorting. Defaults to 0, which does + not terminate query execution early. + :param timeout: Specifies the period of time to wait for a response from each + shard. If no response is received before the timeout expires, the request + fails and returns an error. Defaults to no timeout. + :param track_scores: If true, calculate and return document scores, even if the + scores are not used for sorting. + :param track_total_hits: Number of hits matching the query to count accurately. + If true, the exact number of hits is returned at the cost of some performance. + If false, the response does not include the total number of hits matching + the query. Defaults to 10,000 hits. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + :param version: If true, returns document version as part of a hit. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_search" + else: + __path = "/_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if batched_reduce_size is not None: + __query["batched_reduce_size"] = batched_reduce_size + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if collapse is not None: + __body["collapse"] = collapse + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if docvalue_fields is not None: + __body["docvalue_fields"] = docvalue_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if ext is not None: + __body["ext"] = ext + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if highlight is not None: + __body["highlight"] = highlight + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indices_boost is not None: + __body["indices_boost"] = indices_boost + if knn is not None: + __body["knn"] = knn + if lenient is not None: + __query["lenient"] = lenient + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if min_compatible_shard_node is not None: + __query["min_compatible_shard_node"] = min_compatible_shard_node + if min_score is not None: + __body["min_score"] = min_score + if pit is not None: + __body["pit"] = pit + if post_filter is not None: + __body["post_filter"] = post_filter + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if rank is not None: + __body["rank"] = rank + if request_cache is not None: + __query["request_cache"] = request_cache + if rescore is not None: + __body["rescore"] = rescore + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll is not None: + __query["scroll"] = scroll + if search_after is not None: + __body["search_after"] = search_after + if search_type is not None: + __query["search_type"] = search_type + if seq_no_primary_term is not None: + __body["seq_no_primary_term"] = seq_no_primary_term + if size is not None: + __body["size"] = size + if slice is not None: + __body["slice"] = slice + if sort is not None: + __body["sort"] = sort + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stats is not None: + __body["stats"] = stats + if stored_fields is not None: + __body["stored_fields"] = stored_fields + if suggest is not None: + __body["suggest"] = suggest + if suggest_field is not None: + __query["suggest_field"] = suggest_field + if suggest_mode is not None: + __query["suggest_mode"] = suggest_mode + if suggest_size is not None: + __query["suggest_size"] = suggest_size + if suggest_text is not None: + __query["suggest_text"] = suggest_text + if terminate_after is not None: + __body["terminate_after"] = terminate_after + if timeout is not None: + __body["timeout"] = timeout + if track_scores is not None: + __body["track_scores"] = track_scores + if track_total_hits is not None: + __body["track_total_hits"] = track_total_hits + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if version is not None: + __body["version"] = version + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params"}, + ) + def search_template( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + id: t.Optional[str] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + source: t.Optional[str] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to use the Mustache language to pre-render a search definition. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases to search. + Supports wildcards (*). + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param ccs_minimize_roundtrips: Indicates whether network round-trips should + be minimized as part of cross-cluster search requests execution + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: + :param id: ID of the search template to use. If no source is specified, this + parameter is required. + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param params: + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param profile: + :param rest_total_hits_as_int: If true, hits.total are rendered as an integer + in the response. + :param routing: Custom value used to route operations to a specific shard. + :param scroll: Specifies how long a consistent view of the index should be maintained + for scrolled search. + :param search_type: The type of the search operation. + :param source: An inline search template. Supports the same parameters as the + search API's request body. Also supports Mustache variables. If no id is + specified, this parameter is required. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_search/template" + else: + __path = "/_search/template" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if id is not None: + __body["id"] = id + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if params is not None: + __body["params"] = params + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if scroll is not None: + __query["scroll"] = scroll + if search_type is not None: + __query["search_type"] = search_type + if source is not None: + __body["source"] = source + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def terms_enum( + self, + *, + index: str, + field: str, + case_insensitive: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + index_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + search_after: t.Optional[str] = None, + size: t.Optional[int] = None, + string: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + The terms enum API can be used to discover terms in the index that begin with + the provided string. It is designed for low-latency look-ups used in auto-complete + scenarios. + + ``_ + + :param index: Comma-separated list of data streams, indices, and index aliases + to search. Wildcard (*) expressions are supported. + :param field: The string to match at the start of indexed terms. If not provided, + all terms in the field are considered. + :param case_insensitive: When true the provided search string is matched against + index terms without case sensitivity. + :param index_filter: Allows to filter an index shard if the provided query rewrites + to match_none. + :param search_after: + :param size: How many matching terms to return. + :param string: The string after which terms in the index should be returned. + Allows for a form of pagination if the last result from one request is passed + as the search_after parameter for a subsequent request. + :param timeout: The maximum length of time to spend collecting results. Defaults + to "1s" (one second). If the timeout is exceeded the complete flag set to + false in the response and the results may be partial or empty. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if field is None: + raise ValueError("Empty value passed for parameter 'field'") + __path = f"/{_quote(index)}/_terms_enum" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if field is not None: + __body["field"] = field + if case_insensitive is not None: + __body["case_insensitive"] = case_insensitive + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if index_filter is not None: + __body["index_filter"] = index_filter + if pretty is not None: + __query["pretty"] = pretty + if search_after is not None: + __body["search_after"] = search_after + if size is not None: + __body["size"] = size + if string is not None: + __body["string"] = string + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def termvectors( + self, + *, + index: str, + id: t.Optional[str] = None, + doc: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + field_statistics: t.Optional[bool] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + offsets: t.Optional[bool] = None, + payloads: t.Optional[bool] = None, + per_field_analyzer: t.Optional[t.Mapping[str, str]] = None, + positions: t.Optional[bool] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + realtime: t.Optional[bool] = None, + routing: t.Optional[str] = None, + term_statistics: t.Optional[bool] = None, + version: t.Optional[int] = None, + version_type: t.Optional[ + t.Union["t.Literal['external', 'external_gte', 'force', 'internal']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information and statistics about terms in the fields of a particular + document. + + ``_ + + :param index: The index in which the document resides. + :param id: The id of the document, when not specified a doc param should be supplied. + :param doc: + :param field_statistics: Specifies if document count, sum of document frequencies + and sum of total term frequencies should be returned. + :param fields: A comma-separated list of fields to return. + :param filter: + :param offsets: Specifies if term offsets should be returned. + :param payloads: Specifies if term payloads should be returned. + :param per_field_analyzer: + :param positions: Specifies if term positions should be returned. + :param preference: Specify the node or shard the operation should be performed + on (default: random). + :param realtime: Specifies if request is real-time as opposed to near-real-time + (default: true). + :param routing: Specific routing value. + :param term_statistics: Specifies if total term frequency and document frequency + should be returned. + :param version: Explicit version number for concurrency control + :param version_type: Specific version type + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if index not in SKIP_IN_PATH and id not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_termvectors/{_quote(id)}" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_termvectors" + else: + raise ValueError("Couldn't find a path for the given parameters") + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if doc is not None: + __body["doc"] = doc + if error_trace is not None: + __query["error_trace"] = error_trace + if field_statistics is not None: + __query["field_statistics"] = field_statistics + if fields is not None: + __query["fields"] = fields + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if offsets is not None: + __query["offsets"] = offsets + if payloads is not None: + __query["payloads"] = payloads + if per_field_analyzer is not None: + __body["per_field_analyzer"] = per_field_analyzer + if positions is not None: + __query["positions"] = positions + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if realtime is not None: + __query["realtime"] = realtime + if routing is not None: + __query["routing"] = routing + if term_statistics is not None: + __query["term_statistics"] = term_statistics + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + }, + ) + def update( + self, + *, + index: str, + id: str, + detect_noop: t.Optional[bool] = None, + doc: t.Optional[t.Mapping[str, t.Any]] = None, + doc_as_upsert: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + lang: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + require_alias: t.Optional[bool] = None, + retry_on_conflict: t.Optional[int] = None, + routing: t.Optional[str] = None, + script: t.Optional[t.Mapping[str, t.Any]] = None, + scripted_upsert: t.Optional[bool] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + upsert: t.Optional[t.Mapping[str, t.Any]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates a document with a script or partial document. + + ``_ + + :param index: The name of the index + :param id: Document ID + :param detect_noop: Set to false to disable setting 'result' in the response + to 'noop' if no change to the document occurred. + :param doc: A partial update to an existing document. + :param doc_as_upsert: Set to true to use the contents of 'doc' as the value of + 'upsert' + :param if_primary_term: Only perform the operation if the document has this primary + term. + :param if_seq_no: Only perform the operation if the document has this sequence + number. + :param lang: The script language. + :param refresh: If 'true', Elasticsearch refreshes the affected shards to make + this operation visible to search, if 'wait_for' then wait for a refresh to + make this operation visible to search, if 'false' do nothing with refreshes. + :param require_alias: If true, the destination must be an index alias. + :param retry_on_conflict: Specify how many times should the operation be retried + when a conflict occurs. + :param routing: Custom value used to route operations to a specific shard. + :param script: Script to execute to update the document. + :param scripted_upsert: Set to true to execute the script whether or not the + document exists. + :param source: Set to false to disable source retrieval. You can also specify + a comma-separated list of the fields you want to retrieve. + :param source_excludes: Specify the source fields you want to exclude. + :param source_includes: Specify the source fields you want to retrieve. + :param timeout: Period to wait for dynamic mapping updates and active shards. + This guarantees Elasticsearch waits for at least the timeout before failing. + The actual wait time could be longer, particularly when multiple waits occur. + :param upsert: If the document does not already exist, the contents of 'upsert' + are inserted as a new document. If the document exists, the 'script' is executed. + :param wait_for_active_shards: The number of shard copies that must be active + before proceeding with the operations. Set to 'all' or any positive integer + up to the total number of shards in the index (number_of_replicas+1). Defaults + to 1 meaning the primary shard. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/{_quote(index)}/_update/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if detect_noop is not None: + __body["detect_noop"] = detect_noop + if doc is not None: + __body["doc"] = doc + if doc_as_upsert is not None: + __body["doc_as_upsert"] = doc_as_upsert + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if lang is not None: + __query["lang"] = lang + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if require_alias is not None: + __query["require_alias"] = require_alias + if retry_on_conflict is not None: + __query["retry_on_conflict"] = retry_on_conflict + if routing is not None: + __query["routing"] = routing + if script is not None: + __body["script"] = script + if scripted_upsert is not None: + __body["scripted_upsert"] = scripted_upsert + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if timeout is not None: + __query["timeout"] = timeout + if upsert is not None: + __body["upsert"] = upsert + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + def update_by_query( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + conflicts: t.Optional[t.Union["t.Literal['abort', 'proceed']", str]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + max_docs: t.Optional[int] = None, + pipeline: t.Optional[str] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + refresh: t.Optional[bool] = None, + request_cache: t.Optional[bool] = None, + requests_per_second: t.Optional[float] = None, + routing: t.Optional[str] = None, + script: t.Optional[t.Mapping[str, t.Any]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + scroll_size: t.Optional[int] = None, + search_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + slices: t.Optional[t.Union[int, t.Union["t.Literal['auto']", str]]] = None, + sort: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[bool] = None, + version_type: t.Optional[bool] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Performs an update on every document in the index without changing the source, + for example to pick up a mapping change. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param conflicts: + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param from_: Starting offset (default: 0) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_docs: + :param pipeline: Ingest pipeline to set on index requests made by this action. + (default: none) + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param query: + :param refresh: Should the affected indexes be refreshed? + :param request_cache: Specify if request cache should be used for this request + or not, defaults to index level setting + :param requests_per_second: The throttle to set on this request in sub-requests + per second. -1 means no throttle. + :param routing: A comma-separated list of specific routing values + :param script: + :param scroll: Specify how long a consistent view of the index should be maintained + for scrolled search + :param scroll_size: Size on the scroll request powering the update by query + :param search_timeout: Explicit timeout for each search request. Defaults to + no timeout. + :param search_type: Search operation type + :param slice: + :param slices: The number of slices this task should be divided into. Defaults + to 1, meaning the task isn't sliced into subtasks. Can be set to `auto`. + :param sort: A comma-separated list of : pairs + :param stats: Specific 'tag' of the request for logging and statistical purposes + :param terminate_after: The maximum number of documents to collect for each shard, + upon reaching which the query execution will terminate early. + :param timeout: Time each individual bulk request should wait for shards that + are unavailable. + :param version: Specify whether to return document version as part of a hit + :param version_type: Should the document increment the version number (internal) + on hit or not (reindex) + :param wait_for_active_shards: Sets the number of shard copies that must be active + before proceeding with the update by query operation. Defaults to 1, meaning + the primary shard only. Set to `all` for all shard copies, otherwise set + to any non-negative value less than or equal to the total number of copies + for the shard (number of replicas + 1) + :param wait_for_completion: Should the request should block until the update + by query operation is complete. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_update_by_query" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if conflicts is not None: + __body["conflicts"] = conflicts + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if max_docs is not None: + __body["max_docs"] = max_docs + if pipeline is not None: + __query["pipeline"] = pipeline + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if refresh is not None: + __query["refresh"] = refresh + if request_cache is not None: + __query["request_cache"] = request_cache + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second + if routing is not None: + __query["routing"] = routing + if script is not None: + __body["script"] = script + if scroll is not None: + __query["scroll"] = scroll + if scroll_size is not None: + __query["scroll_size"] = scroll_size + if search_timeout is not None: + __query["search_timeout"] = search_timeout + if search_type is not None: + __query["search_type"] = search_type + if slice is not None: + __body["slice"] = slice + if slices is not None: + __query["slices"] = slices + if sort is not None: + __query["sort"] = sort + if stats is not None: + __query["stats"] = stats + if terminate_after is not None: + __query["terminate_after"] = terminate_after + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __query["version"] = version + if version_type is not None: + __query["version_type"] = version_type + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/_base.py b/elasticsearch_serverless/_sync/client/_base.py new file mode 100644 index 0000000..934ae47 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/_base.py @@ -0,0 +1,391 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import re +import warnings +from typing import ( + Any, + Callable, + Collection, + Dict, + Iterable, + List, + Mapping, + Optional, + Tuple, + Union, +) + +from elastic_transport import ( + ApiResponse, + BinaryApiResponse, + HeadApiResponse, + HttpHeaders, + ListApiResponse, + NodeConfig, + ObjectApiResponse, + SniffOptions, + TextApiResponse, + Transport, +) +from elastic_transport.client_utils import DEFAULT, DefaultType + +from ..._version import __versionstr__ +from ...compat import warn_stacklevel +from ...exceptions import ( + HTTP_EXCEPTIONS, + ApiError, + ConnectionError, + ElasticsearchWarning, + SerializationError, + UnsupportedProductError, +) +from .utils import _TYPE_SYNC_SNIFF_CALLBACK, _base64_auth_header, _quote_query + +_WARNING_RE = re.compile(r"\"([^\"]*)\"") +_COMPAT_MIMETYPE_TEMPLATE = "application/vnd.elasticsearch+%s; compatible-with=" + str( + __versionstr__.partition(".")[0] +) +_COMPAT_MIMETYPE_RE = re.compile(r"application/(json|x-ndjson|vnd\.mapbox-vector-tile)") +_COMPAT_MIMETYPE_SUB = _COMPAT_MIMETYPE_TEMPLATE % (r"\g<1>",) + + +def resolve_auth_headers( + headers: Optional[Mapping[str, str]], + http_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT, + api_key: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT, + basic_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT, + bearer_auth: Union[DefaultType, None, str] = DEFAULT, +) -> HttpHeaders: + if headers is None: + headers = HttpHeaders() + elif not isinstance(headers, HttpHeaders): + headers = HttpHeaders(headers) + + resolved_http_auth = http_auth if http_auth is not DEFAULT else None + resolved_basic_auth = basic_auth if basic_auth is not DEFAULT else None + if resolved_http_auth is not None: + if resolved_basic_auth is not None: + raise ValueError( + "Can't specify both 'http_auth' and 'basic_auth', " + "instead only specify 'basic_auth'" + ) + if isinstance(http_auth, str) or ( + isinstance(resolved_http_auth, (list, tuple)) + and all(isinstance(x, str) for x in resolved_http_auth) + ): + resolved_basic_auth = resolved_http_auth + else: + raise TypeError( + "The deprecated 'http_auth' parameter must be either 'Tuple[str, str]' or 'str'. " + "Use either the 'basic_auth' parameter instead" + ) + + warnings.warn( + "The 'http_auth' parameter is deprecated. " + "Use 'basic_auth' or 'bearer_auth' parameters instead", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + + resolved_api_key = api_key if api_key is not DEFAULT else None + resolved_bearer_auth = bearer_auth if bearer_auth is not DEFAULT else None + if resolved_api_key or resolved_basic_auth or resolved_bearer_auth: + if ( + sum( + x is not None + for x in ( + resolved_api_key, + resolved_basic_auth, + resolved_bearer_auth, + ) + ) + > 1 + ): + raise ValueError( + "Can only set one of 'api_key', 'basic_auth', and 'bearer_auth'" + ) + if headers and headers.get("authorization", None) is not None: + raise ValueError( + "Can't set 'Authorization' HTTP header with other authentication options" + ) + if resolved_api_key: + headers["authorization"] = f"ApiKey {_base64_auth_header(resolved_api_key)}" + if resolved_basic_auth: + headers[ + "authorization" + ] = f"Basic {_base64_auth_header(resolved_basic_auth)}" + if resolved_bearer_auth: + headers["authorization"] = f"Bearer {resolved_bearer_auth}" + + return headers + + +def create_sniff_callback( + host_info_callback: Optional[ + Callable[[Dict[str, Any], Dict[str, Any]], Optional[Dict[str, Any]]] + ] = None, + sniffed_node_callback: Optional[ + Callable[[Dict[str, Any], NodeConfig], Optional[NodeConfig]] + ] = None, +) -> _TYPE_SYNC_SNIFF_CALLBACK: + assert (host_info_callback is None) != (sniffed_node_callback is None) + + # Wrap the deprecated 'host_info_callback' into 'sniffed_node_callback' + if host_info_callback is not None: + + def _sniffed_node_callback( + node_info: Dict[str, Any], node_config: NodeConfig + ) -> Optional[NodeConfig]: + assert host_info_callback is not None + if ( + host_info_callback( # type ignore[misc] + node_info, {"host": node_config.host, "port": node_config.port} + ) + is None + ): + return None + return node_config + + sniffed_node_callback = _sniffed_node_callback + + def sniff_callback( + transport: Transport, sniff_options: SniffOptions + ) -> List[NodeConfig]: + for _ in transport.node_pool.all(): + try: + meta, node_infos = transport.perform_request( + "GET", + "/_nodes/_all/http", + headers={ + "accept": "application/vnd.elasticsearch+json; compatible-with=8" + }, + request_timeout=( + sniff_options.sniff_timeout + if not sniff_options.is_initial_sniff + else None + ), + ) + except (SerializationError, ConnectionError): + continue + + if not 200 <= meta.status <= 299: + continue + + node_configs = [] + for node_info in node_infos.get("nodes", {}).values(): + address = node_info.get("http", {}).get("publish_address") + if not address or ":" not in address: + continue + + if "/" in address: + # Support 7.x host/ip:port behavior where http.publish_host has been set. + fqdn, ipaddress = address.split("/", 1) + host = fqdn + _, port_str = ipaddress.rsplit(":", 1) + port = int(port_str) + else: + host, port_str = address.rsplit(":", 1) + port = int(port_str) + + assert sniffed_node_callback is not None + sniffed_node = sniffed_node_callback( + node_info, meta.node.replace(host=host, port=port) + ) + if sniffed_node is None: + continue + + # Use the node which was able to make the request as a base. + node_configs.append(sniffed_node) + + if node_configs: + return node_configs + + return [] + + return sniff_callback + + +def _default_sniffed_node_callback( + node_info: Dict[str, Any], node_config: NodeConfig +) -> Optional[NodeConfig]: + if node_info.get("roles", []) == ["master"]: + return None + return node_config + + +default_sniff_callback = create_sniff_callback( + sniffed_node_callback=_default_sniffed_node_callback +) + + +class BaseClient: + def __init__(self, _transport: Transport) -> None: + self._transport = _transport + self._client_meta: Union[DefaultType, Tuple[Tuple[str, str], ...]] = DEFAULT + self._headers = HttpHeaders() + self._request_timeout: Union[DefaultType, Optional[float]] = DEFAULT + self._ignore_status: Union[DefaultType, Collection[int]] = DEFAULT + self._max_retries: Union[DefaultType, int] = DEFAULT + self._retry_on_timeout: Union[DefaultType, bool] = DEFAULT + self._retry_on_status: Union[DefaultType, Collection[int]] = DEFAULT + self._verified_elasticsearch = False + + @property + def transport(self) -> Transport: + return self._transport + + def perform_request( + self, + method: str, + path: str, + *, + params: Optional[Mapping[str, Any]] = None, + headers: Optional[Mapping[str, str]] = None, + body: Optional[Any] = None, + ) -> ApiResponse[Any]: + if headers: + request_headers = self._headers.copy() + request_headers.update(headers) + else: + request_headers = self._headers + + def mimetype_header_to_compat(header: str) -> None: + # Converts all parts of a Accept/Content-Type headers + # from application/X -> application/vnd.elasticsearch+X + nonlocal request_headers + mimetype = request_headers.get(header, None) + if mimetype: + request_headers[header] = _COMPAT_MIMETYPE_RE.sub( + _COMPAT_MIMETYPE_SUB, mimetype + ) + + mimetype_header_to_compat("Accept") + mimetype_header_to_compat("Content-Type") + + if params: + target = f"{path}?{_quote_query(params)}" + else: + target = path + + meta, resp_body = self.transport.perform_request( + method, + target, + headers=request_headers, + body=body, + request_timeout=self._request_timeout, + max_retries=self._max_retries, + retry_on_status=self._retry_on_status, + retry_on_timeout=self._retry_on_timeout, + client_meta=self._client_meta, + ) + + # HEAD with a 404 is returned as a normal response + # since this is used as an 'exists' functionality. + if not (method == "HEAD" and meta.status == 404) and ( + not 200 <= meta.status < 299 + and ( + self._ignore_status is DEFAULT + or self._ignore_status is None + or meta.status not in self._ignore_status + ) + ): + message = str(resp_body) + + # If the response is an error response try parsing + # the raw Elasticsearch error before raising. + if isinstance(resp_body, dict): + try: + error = resp_body.get("error", message) + if isinstance(error, dict) and "type" in error: + error = error["type"] + message = error + except (ValueError, KeyError, TypeError): + pass + + raise HTTP_EXCEPTIONS.get(meta.status, ApiError)( + message=message, meta=meta, body=resp_body + ) + + # 'X-Elastic-Product: Elasticsearch' should be on every 2XX response. + if not self._verified_elasticsearch: + # If the header is set we mark the server as verified. + if meta.headers.get("x-elastic-product", "") == "Elasticsearch": + self._verified_elasticsearch = True + # Otherwise we only raise an error on 2XX responses. + elif meta.status >= 200 and meta.status < 300: + raise UnsupportedProductError( + message=( + "The client noticed that the server is not Elasticsearch " + "and we do not support this unknown product" + ), + meta=meta, + body=resp_body, + ) + + # 'Warning' headers should be reraised as 'ElasticsearchWarning' + if "warning" in meta.headers: + warning_header = (meta.headers.get("warning") or "").strip() + warning_messages: Iterable[str] = _WARNING_RE.findall(warning_header) or ( + warning_header, + ) + stacklevel = warn_stacklevel() + for warning_message in warning_messages: + warnings.warn( + warning_message, + category=ElasticsearchWarning, + stacklevel=stacklevel, + ) + + if method == "HEAD": + response = HeadApiResponse(meta=meta) + elif isinstance(resp_body, dict): + response = ObjectApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + elif isinstance(resp_body, list): + response = ListApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + elif isinstance(resp_body, str): + response = TextApiResponse( # type: ignore[assignment] + body=resp_body, + meta=meta, + ) + elif isinstance(resp_body, bytes): + response = BinaryApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + else: + response = ApiResponse(body=resp_body, meta=meta) # type: ignore[assignment] + + return response + + +class NamespacedClient(BaseClient): + def __init__(self, client: "BaseClient") -> None: + self._client = client + super().__init__(self._client.transport) + + def perform_request( + self, + method: str, + path: str, + *, + params: Optional[Mapping[str, Any]] = None, + headers: Optional[Mapping[str, str]] = None, + body: Optional[Any] = None, + ) -> ApiResponse[Any]: + # Use the internal clients .perform_request() implementation + # so we take advantage of their transport options. + return self._client.perform_request( + method, path, params=params, headers=headers, body=body + ) diff --git a/elasticsearch_serverless/_sync/client/async_search.py b/elasticsearch_serverless/_sync/client/async_search.py new file mode 100644 index 0000000..b655466 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/async_search.py @@ -0,0 +1,603 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class AsyncSearchClient(NamespacedClient): + @_rewrite_parameters() + def delete( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an async search by ID. If the search is still running, the search request + will be cancelled. Otherwise, the saved search results are deleted. + + ``_ + + :param id: A unique identifier for the async search. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_async_search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + pretty: t.Optional[bool] = None, + typed_keys: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the results of a previously submitted async search request given its + ID. + + ``_ + + :param id: A unique identifier for the async search. + :param keep_alive: Specifies how long the async search should be available in + the cluster. When not specified, the `keep_alive` set with the corresponding + submit async request will be used. Otherwise, it is possible to override + the value and extend the validity of the request. When this period expires, + the search, if still running, is cancelled. If the search is completed, its + saved results are deleted. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + :param wait_for_completion_timeout: Specifies to wait for the search to be completed + up until the provided timeout. Final results will be returned if available + before the timeout expires, otherwise the currently available results will + be returned once the timeout expires. By default no timeout is set meaning + that the currently available results will be returned without any additional + wait. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_async_search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if pretty is not None: + __query["pretty"] = pretty + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def status( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the status of a previously submitted async search request given its + ID. + + ``_ + + :param id: A unique identifier for the async search. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_async_search/status/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + "from": "from_", + }, + ) + def submit( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + batched_reduce_size: t.Optional[int] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + collapse: t.Optional[t.Mapping[str, t.Any]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + docvalue_fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + ext: t.Optional[t.Mapping[str, t.Any]] = None, + fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + highlight: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indices_boost: t.Optional[ + t.Union[t.List[t.Mapping[str, float]], t.Tuple[t.Mapping[str, float], ...]] + ] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + keep_on_completion: t.Optional[bool] = None, + knn: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + lenient: t.Optional[bool] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + min_compatible_shard_node: t.Optional[str] = None, + min_score: t.Optional[float] = None, + pit: t.Optional[t.Mapping[str, t.Any]] = None, + post_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pre_filter_shard_size: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + request_cache: t.Optional[bool] = None, + rescore: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + seq_no_primary_term: t.Optional[bool] = None, + size: t.Optional[int] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + suggest: t.Optional[t.Mapping[str, t.Any]] = None, + suggest_field: t.Optional[str] = None, + suggest_mode: t.Optional[ + t.Union["t.Literal['always', 'missing', 'popular']", str] + ] = None, + suggest_size: t.Optional[int] = None, + suggest_text: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[str] = None, + track_scores: t.Optional[bool] = None, + track_total_hits: t.Optional[t.Union[bool, int]] = None, + typed_keys: t.Optional[bool] = None, + version: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Executes a search request asynchronously. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param aggregations: + :param aggs: + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param allow_partial_search_results: Indicate if an error should be returned + if there is a partial search failure or timeout + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param batched_reduce_size: Affects how often partial results become available, + which happens whenever shard results are reduced. A partial reduction is + performed every time the coordinating node has received a certain number + of new shard responses (5 by default). + :param ccs_minimize_roundtrips: The default value is the only supported value. + :param collapse: + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc + values for field names matching these patterns in the hits.fields property + of the response. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: If true, returns detailed information about score computation + as part of a hit. + :param ext: Configuration of search extensions defined by Elasticsearch plugins. + :param fields: Array of wildcard (*) patterns. The request returns values for + field names matching these patterns in the hits.fields property of the response. + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the search_after parameter. + :param highlight: + :param ignore_throttled: Whether specified concrete, expanded or aliased indices + should be ignored when throttled + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param indices_boost: Boosts the _score of documents from specified indices. + :param keep_alive: Specifies how long the async search needs to be available. + Ongoing async searches and any saved search results are deleted after this + period. + :param keep_on_completion: If `true`, results are stored for later retrieval + when the search completes within the `wait_for_completion_timeout`. + :param knn: Defines the approximate kNN search to run. + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param max_concurrent_shard_requests: The number of concurrent shard requests + per node this search executes concurrently. This value should be used to + limit the impact of the search on the cluster in order to limit the number + of concurrent shard requests + :param min_compatible_shard_node: + :param min_score: Minimum _score for matching documents. Documents with a lower + _score are not included in the search results. + :param pit: Limits the search to a point in time (PIT). If you provide a PIT, + you cannot specify an in the request path. + :param post_filter: + :param pre_filter_shard_size: The default value cannot be changed, which enforces + the execution of a pre-filter roundtrip to retrieve statistics from each + shard so that the ones that surely don’t hold any document matching the query + get skipped. + :param preference: Specify the node or shard the operation should be performed + on (default: random) + :param profile: + :param q: Query in the Lucene query string syntax + :param query: Defines the search definition using the Query DSL. + :param request_cache: Specify if request cache should be used for this request + or not, defaults to true + :param rescore: + :param rest_total_hits_as_int: + :param routing: A comma-separated list of specific routing values + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param script_fields: Retrieve a script evaluation (based on different fields) + for each hit. + :param scroll: + :param search_after: + :param search_type: Search operation type + :param seq_no_primary_term: If true, returns sequence number and primary term + of the last modification of each hit. See Optimistic concurrency control. + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the from and size parameters. To page through + more hits, use the search_after parameter. + :param slice: + :param sort: + :param source: Indicates which source fields are returned for matching documents. + These fields are returned in the hits._source property of the search response. + :param source_excludes: A list of fields to exclude from the returned _source + field + :param source_includes: A list of fields to extract and return from the _source + field + :param stats: Stats groups to associate with the search. Each group maintains + a statistics aggregation for its associated searches. You can retrieve these + stats using the indices stats API. + :param stored_fields: List of stored fields to return as part of a hit. If no + fields are specified, no stored fields are included in the response. If this + field is specified, the _source parameter defaults to false. You can pass + _source: true to return both source fields and stored fields in the search + response. + :param suggest: + :param suggest_field: Specifies which field to use for suggestions. + :param suggest_mode: Specify suggest mode + :param suggest_size: How many suggestions to return in response + :param suggest_text: The source text for which the suggestions should be returned. + :param terminate_after: Maximum number of documents to collect for each shard. + If a query reaches this limit, Elasticsearch terminates the query early. + Elasticsearch collects documents before sorting. Defaults to 0, which does + not terminate query execution early. + :param timeout: Specifies the period of time to wait for a response from each + shard. If no response is received before the timeout expires, the request + fails and returns an error. Defaults to no timeout. + :param track_scores: If true, calculate and return document scores, even if the + scores are not used for sorting. + :param track_total_hits: Number of hits matching the query to count accurately. + If true, the exact number of hits is returned at the cost of some performance. + If false, the response does not include the total number of hits matching + the query. Defaults to 10,000 hits. + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + :param version: If true, returns document version as part of a hit. + :param wait_for_completion_timeout: Blocks and waits until the search is completed + up to a certain timeout. When the async search completes within the timeout, + the response won’t include the ID as the results are not stored in the cluster. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_async_search" + else: + __path = "/_async_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if batched_reduce_size is not None: + __query["batched_reduce_size"] = batched_reduce_size + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if collapse is not None: + __body["collapse"] = collapse + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if docvalue_fields is not None: + __body["docvalue_fields"] = docvalue_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if ext is not None: + __body["ext"] = ext + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if highlight is not None: + __body["highlight"] = highlight + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indices_boost is not None: + __body["indices_boost"] = indices_boost + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if keep_on_completion is not None: + __query["keep_on_completion"] = keep_on_completion + if knn is not None: + __body["knn"] = knn + if lenient is not None: + __query["lenient"] = lenient + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if min_compatible_shard_node is not None: + __query["min_compatible_shard_node"] = min_compatible_shard_node + if min_score is not None: + __body["min_score"] = min_score + if pit is not None: + __body["pit"] = pit + if post_filter is not None: + __body["post_filter"] = post_filter + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if request_cache is not None: + __query["request_cache"] = request_cache + if rescore is not None: + __body["rescore"] = rescore + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll is not None: + __query["scroll"] = scroll + if search_after is not None: + __body["search_after"] = search_after + if search_type is not None: + __query["search_type"] = search_type + if seq_no_primary_term is not None: + __body["seq_no_primary_term"] = seq_no_primary_term + if size is not None: + __body["size"] = size + if slice is not None: + __body["slice"] = slice + if sort is not None: + __body["sort"] = sort + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stats is not None: + __body["stats"] = stats + if stored_fields is not None: + __body["stored_fields"] = stored_fields + if suggest is not None: + __body["suggest"] = suggest + if suggest_field is not None: + __query["suggest_field"] = suggest_field + if suggest_mode is not None: + __query["suggest_mode"] = suggest_mode + if suggest_size is not None: + __query["suggest_size"] = suggest_size + if suggest_text is not None: + __query["suggest_text"] = suggest_text + if terminate_after is not None: + __body["terminate_after"] = terminate_after + if timeout is not None: + __body["timeout"] = timeout + if track_scores is not None: + __body["track_scores"] = track_scores + if track_total_hits is not None: + __body["track_total_hits"] = track_total_hits + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if version is not None: + __body["version"] = version + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/autoscaling.py b/elasticsearch_serverless/_sync/client/autoscaling.py new file mode 100644 index 0000000..be21e6f --- /dev/null +++ b/elasticsearch_serverless/_sync/client/autoscaling.py @@ -0,0 +1,175 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class AutoscalingClient(NamespacedClient): + @_rewrite_parameters() + def delete_autoscaling_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an autoscaling policy. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. + + ``_ + + :param name: the name of the autoscaling policy + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_autoscaling/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_autoscaling_capacity( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets the current autoscaling capacity based on the configured autoscaling policy. + Designed for indirect use by ECE/ESS and ECK. Direct use is not supported. + + ``_ + """ + __path = "/_autoscaling/capacity" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_autoscaling_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves an autoscaling policy. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. + + ``_ + + :param name: the name of the autoscaling policy + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_autoscaling/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="policy", + ) + def put_autoscaling_policy( + self, + *, + name: str, + policy: t.Mapping[str, t.Any], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new autoscaling policy. Designed for indirect use by ECE/ESS and ECK. + Direct use is not supported. + + ``_ + + :param name: the name of the autoscaling policy + :param policy: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if policy is None: + raise ValueError("Empty value passed for parameter 'policy'") + __path = f"/_autoscaling/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = policy + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/cat.py b/elasticsearch_serverless/_sync/client/cat.py new file mode 100644 index 0000000..80fcfa4 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/cat.py @@ -0,0 +1,1161 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse, TextApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class CatClient(NamespacedClient): + @_rewrite_parameters() + def aliases( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Shows information about currently configured aliases to indices including filter + and routing infos. + + ``_ + + :param name: A comma-separated list of aliases to retrieve. Supports wildcards + (`*`). To retrieve all aliases, omit this parameter or use `*` or `_all`. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + if name not in SKIP_IN_PATH: + __path = f"/_cat/aliases/{_quote(name)}" + else: + __path = "/_cat/aliases" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def component_templates( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Returns information about existing component_templates templates. + + ``_ + + :param name: The name of the component template. Accepts wildcard expressions. + If omitted, all component templates are returned. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + if name not in SKIP_IN_PATH: + __path = f"/_cat/component_templates/{_quote(name)}" + else: + __path = "/_cat/component_templates" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def count( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Provides quick access to the document count of the entire cluster, or individual + indices. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + if index not in SKIP_IN_PATH: + __path = f"/_cat/count/{_quote(index)}" + else: + __path = "/_cat/count" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def help( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + v: t.Optional[bool] = None, + ) -> TextApiResponse: + """ + Returns help for the Cat APIs. + + ``_ + + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param v: When set to `true` will enable verbose output. + """ + __path = "/_cat" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def indices( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + health: t.Optional[t.Union["t.Literal['green', 'red', 'yellow']", str]] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_unloaded_segments: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + pri: t.Optional[bool] = None, + s: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Returns information about indices: number of primaries and replicas, document + counts, disk size, ... + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param bytes: The unit used to display byte values. + :param expand_wildcards: The type of index that wildcard patterns can match. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: List of columns to appear in the response. Supports simple wildcards. + :param health: The health status used to limit returned indices. By default, + the response includes indices of any health status. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param include_unloaded_segments: If true, the response includes information + from segments that are not loaded into memory. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param pri: If true, the response only includes information from primary shards. + :param s: List of columns that determine how the table should be sorted. Sorting + defaults to ascending and can be changed by setting `:asc` or `:desc` as + a suffix to the column name. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if index not in SKIP_IN_PATH: + __path = f"/_cat/indices/{_quote(index)}" + else: + __path = "/_cat/indices" + __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if health is not None: + __query["health"] = health + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if include_unloaded_segments is not None: + __query["include_unloaded_segments"] = include_unloaded_segments + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if pri is not None: + __query["pri"] = pri + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def ml_data_frame_analytics( + self, + *, + id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'create_time', 'description', 'dest_index', 'failure_reason', 'id', 'model_memory_limit', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'progress', 'source_index', 'state', 'type', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + time: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about data frame analytics jobs. + + ``_ + + :param id: The ID of the data frame analytics to fetch + :param allow_no_match: Whether to ignore if a wildcard expression matches no + configs. (This includes `_all` string or when no configs have been specified) + :param bytes: The unit in which to display byte values + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param time: Unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if id not in SKIP_IN_PATH: + __path = f"/_cat/ml/data_frame/analytics/{_quote(id)}" + else: + __path = "/_cat/ml/data_frame/analytics" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def ml_datafeeds( + self, + *, + datafeed_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['ae', 'bc', 'id', 'na', 'ne', 'ni', 'nn', 's', 'sba', 'sc', 'seah', 'st']", + str, + ], + ..., + ], + ], + ] + ] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about datafeeds. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. + :param allow_no_match: Specifies what to do when the request: * Contains wildcard + expressions and there are no datafeeds that match. * Contains the `_all` + string or no identifiers and there are no matches. * Contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty datafeeds + array when there are no matches and the subset of results when there are + partial matches. If `false`, the API returns a 404 status code when there + are no matches or only partial matches. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_cat/ml/datafeeds/{_quote(datafeed_id)}" + else: + __path = "/_cat/ml/datafeeds" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def ml_jobs( + self, + *, + job_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['assignment_explanation', 'buckets.count', 'buckets.time.exp_avg', 'buckets.time.exp_avg_hour', 'buckets.time.max', 'buckets.time.min', 'buckets.time.total', 'data.buckets', 'data.earliest_record', 'data.empty_buckets', 'data.input_bytes', 'data.input_fields', 'data.input_records', 'data.invalid_dates', 'data.last', 'data.last_empty_bucket', 'data.last_sparse_bucket', 'data.latest_record', 'data.missing_fields', 'data.out_of_order_timestamps', 'data.processed_fields', 'data.processed_records', 'data.sparse_buckets', 'forecasts.memory.avg', 'forecasts.memory.max', 'forecasts.memory.min', 'forecasts.memory.total', 'forecasts.records.avg', 'forecasts.records.max', 'forecasts.records.min', 'forecasts.records.total', 'forecasts.time.avg', 'forecasts.time.max', 'forecasts.time.min', 'forecasts.time.total', 'forecasts.total', 'id', 'model.bucket_allocation_failures', 'model.by_fields', 'model.bytes', 'model.bytes_exceeded', 'model.categorization_status', 'model.categorized_doc_count', 'model.dead_category_count', 'model.failed_category_count', 'model.frequent_category_count', 'model.log_time', 'model.memory_limit', 'model.memory_status', 'model.over_fields', 'model.partition_fields', 'model.rare_category_count', 'model.timestamp', 'model.total_category_count', 'node.address', 'node.ephemeral_id', 'node.id', 'node.name', 'opened_time', 'state']", + str, + ], + ..., + ], + ], + ] + ] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param allow_no_match: Specifies what to do when the request: * Contains wildcard + expressions and there are no jobs that match. * Contains the `_all` string + or no identifiers and there are no matches. * Contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty jobs + array when there are no matches and the subset of results when there are + partial matches. If `false`, the API returns a 404 status code when there + are no matches or only partial matches. + :param bytes: The unit used to display byte values. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if job_id not in SKIP_IN_PATH: + __path = f"/_cat/ml/anomaly_detectors/{_quote(job_id)}" + else: + __path = "/_cat/ml/anomaly_detectors" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def ml_trained_models( + self, + *, + model_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union["t.Literal['b', 'gb', 'kb', 'mb', 'pb', 'tb']", str] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + from_: t.Optional[int] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['create_time', 'created_by', 'data_frame_analytics_id', 'description', 'heap_size', 'id', 'ingest.count', 'ingest.current', 'ingest.failed', 'ingest.pipelines', 'ingest.time', 'license', 'operations', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + size: t.Optional[int] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about inference trained models. + + ``_ + + :param model_id: A unique identifier for the trained model. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no models that match; contains the `_all` string + or no identifiers and there are no matches; contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty array + when there are no matches and the subset of results when there are partial + matches. If `false`, the API returns a 404 status code when there are no + matches or only partial matches. + :param bytes: The unit used to display byte values. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param from_: Skips the specified number of transforms. + :param h: A comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: A comma-separated list of column names or aliases used to sort the + response. + :param size: The maximum number of transforms to display. + :param v: When set to `true` will enable verbose output. + """ + if model_id not in SKIP_IN_PATH: + __path = f"/_cat/ml/trained_models/{_quote(model_id)}" + else: + __path = "/_cat/ml/trained_models" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if from_ is not None: + __query["from"] = from_ + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if size is not None: + __query["size"] = size + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def transforms( + self, + *, + transform_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + from_: t.Optional[int] = None, + h: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + help: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + s: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['changes_last_detection_time', 'checkpoint', 'checkpoint_duration_time_exp_avg', 'checkpoint_progress', 'create_time', 'delete_time', 'description', 'dest_index', 'docs_per_second', 'documents_deleted', 'documents_indexed', 'documents_processed', 'frequency', 'id', 'index_failure', 'index_time', 'index_total', 'indexed_documents_exp_avg', 'last_search_time', 'max_page_search_size', 'pages_processed', 'pipeline', 'processed_documents_exp_avg', 'processing_time', 'reason', 'search_failure', 'search_time', 'search_total', 'source_index', 'state', 'transform_type', 'trigger_count', 'version']", + str, + ], + ..., + ], + ], + ] + ] = None, + size: t.Optional[int] = None, + time: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + v: t.Optional[bool] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + Gets configuration and usage information about transforms. + + ``_ + + :param transform_id: A transform identifier or a wildcard expression. If you + do not specify one of these options, the API returns information for all + transforms. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no transforms that match; contains the `_all` string + or no identifiers and there are no matches; contains wildcard expressions + and there are only partial matches. If `true`, it returns an empty transforms + array when there are no matches and the subset of results when there are + partial matches. If `false`, the request returns a 404 status code when there + are no matches or only partial matches. + :param format: Specifies the format to return the columnar data in, can be set + to `text`, `json`, `cbor`, `yaml`, or `smile`. + :param from_: Skips the specified number of transforms. + :param h: Comma-separated list of column names to display. + :param help: When set to `true` will output available columns. This option can't + be combined with any other query string option. + :param local: If `true`, the request computes the list of selected nodes from + the local cluster state. If `false` the list of selected nodes are computed + from the cluster state of the master node. In both cases the coordinating + node will send requests for further information to each selected node. + :param master_timeout: Period to wait for a connection to the master node. + :param s: Comma-separated list of column names or column aliases used to sort + the response. + :param size: The maximum number of transforms to obtain. + :param time: The unit used to display time values. + :param v: When set to `true` will enable verbose output. + """ + if transform_id not in SKIP_IN_PATH: + __path = f"/_cat/transforms/{_quote(transform_id)}" + else: + __path = "/_cat/transforms" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if from_ is not None: + __query["from"] = from_ + if h is not None: + __query["h"] = h + if help is not None: + __query["help"] = help + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if s is not None: + __query["s"] = s + if size is not None: + __query["size"] = size + if time is not None: + __query["time"] = time + if v is not None: + __query["v"] = v + __headers = {"accept": "text/plain,application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/ccr.py b/elasticsearch_serverless/_sync/client/ccr.py new file mode 100644 index 0000000..1ce3f42 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/ccr.py @@ -0,0 +1,749 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class CcrClient(NamespacedClient): + @_rewrite_parameters() + def delete_auto_follow_pattern( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes auto-follow patterns. + + ``_ + + :param name: The name of the auto follow pattern. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ccr/auto_follow/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def follow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + leader_index: t.Optional[str] = None, + max_outstanding_read_requests: t.Optional[int] = None, + max_outstanding_write_requests: t.Optional[int] = None, + max_read_request_operation_count: t.Optional[int] = None, + max_read_request_size: t.Optional[str] = None, + max_retry_delay: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + max_write_buffer_count: t.Optional[int] = None, + max_write_buffer_size: t.Optional[str] = None, + max_write_request_operation_count: t.Optional[int] = None, + max_write_request_size: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + read_poll_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + remote_cluster: t.Optional[str] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new follower index configured to follow the referenced leader index. + + ``_ + + :param index: The name of the follower index + :param leader_index: + :param max_outstanding_read_requests: + :param max_outstanding_write_requests: + :param max_read_request_operation_count: + :param max_read_request_size: + :param max_retry_delay: + :param max_write_buffer_count: + :param max_write_buffer_size: + :param max_write_request_operation_count: + :param max_write_request_size: + :param read_poll_timeout: + :param remote_cluster: + :param wait_for_active_shards: Sets the number of shard copies that must be active + before returning. Defaults to 0. Set to `all` for all shard copies, otherwise + set to any non-negative value less than or equal to the total number of copies + for the shard (number of replicas + 1) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/follow" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if leader_index is not None: + __body["leader_index"] = leader_index + if max_outstanding_read_requests is not None: + __body["max_outstanding_read_requests"] = max_outstanding_read_requests + if max_outstanding_write_requests is not None: + __body["max_outstanding_write_requests"] = max_outstanding_write_requests + if max_read_request_operation_count is not None: + __body[ + "max_read_request_operation_count" + ] = max_read_request_operation_count + if max_read_request_size is not None: + __body["max_read_request_size"] = max_read_request_size + if max_retry_delay is not None: + __body["max_retry_delay"] = max_retry_delay + if max_write_buffer_count is not None: + __body["max_write_buffer_count"] = max_write_buffer_count + if max_write_buffer_size is not None: + __body["max_write_buffer_size"] = max_write_buffer_size + if max_write_request_operation_count is not None: + __body[ + "max_write_request_operation_count" + ] = max_write_request_operation_count + if max_write_request_size is not None: + __body["max_write_request_size"] = max_write_request_size + if pretty is not None: + __query["pretty"] = pretty + if read_poll_timeout is not None: + __body["read_poll_timeout"] = read_poll_timeout + if remote_cluster is not None: + __body["remote_cluster"] = remote_cluster + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def follow_info( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about all follower indices, including parameters and status + for each follower index + + ``_ + + :param index: A comma-separated list of index patterns; use `_all` to perform + the operation on all indices + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/info" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def follow_stats( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves follower stats. return shard-level stats about the following tasks + associated with each shard for the specified indices. + + ``_ + + :param index: A comma-separated list of index patterns; use `_all` to perform + the operation on all indices + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def forget_follower( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + follower_cluster: t.Optional[str] = None, + follower_index: t.Optional[str] = None, + follower_index_uuid: t.Optional[str] = None, + human: t.Optional[bool] = None, + leader_remote_cluster: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes the follower retention leases from the leader. + + ``_ + + :param index: the name of the leader index for which specified follower retention + leases should be removed + :param follower_cluster: + :param follower_index: + :param follower_index_uuid: + :param leader_remote_cluster: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/forget_follower" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if follower_cluster is not None: + __body["follower_cluster"] = follower_cluster + if follower_index is not None: + __body["follower_index"] = follower_index + if follower_index_uuid is not None: + __body["follower_index_uuid"] = follower_index_uuid + if human is not None: + __query["human"] = human + if leader_remote_cluster is not None: + __body["leader_remote_cluster"] = leader_remote_cluster + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def get_auto_follow_pattern( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets configured auto-follow patterns. Returns the specified auto-follow pattern + collection. + + ``_ + + :param name: Specifies the auto-follow pattern collection that you want to retrieve. + If you do not specify a name, the API returns information for all collections. + """ + if name not in SKIP_IN_PATH: + __path = f"/_ccr/auto_follow/{_quote(name)}" + else: + __path = "/_ccr/auto_follow" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def pause_auto_follow_pattern( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Pauses an auto-follow pattern + + ``_ + + :param name: The name of the auto follow pattern that should pause discovering + new indices to follow. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ccr/auto_follow/{_quote(name)}/pause" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def pause_follow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Pauses a follower index. The follower index will not fetch any additional operations + from the leader index. + + ``_ + + :param index: The name of the follower index that should pause following its + leader index. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/pause_follow" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_auto_follow_pattern( + self, + *, + name: str, + remote_cluster: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + follow_index_pattern: t.Optional[str] = None, + human: t.Optional[bool] = None, + leader_index_exclusion_patterns: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + leader_index_patterns: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + max_outstanding_read_requests: t.Optional[int] = None, + max_outstanding_write_requests: t.Optional[int] = None, + max_read_request_operation_count: t.Optional[int] = None, + max_read_request_size: t.Optional[t.Union[int, str]] = None, + max_retry_delay: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + max_write_buffer_count: t.Optional[int] = None, + max_write_buffer_size: t.Optional[t.Union[int, str]] = None, + max_write_request_operation_count: t.Optional[int] = None, + max_write_request_size: t.Optional[t.Union[int, str]] = None, + pretty: t.Optional[bool] = None, + read_poll_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new named collection of auto-follow patterns against a specified remote + cluster. Newly created indices on the remote cluster matching any of the specified + patterns will be automatically configured as follower indices. + + ``_ + + :param name: The name of the collection of auto-follow patterns. + :param remote_cluster: The remote cluster containing the leader indices to match + against. + :param follow_index_pattern: The name of follower index. The template {{leader_index}} + can be used to derive the name of the follower index from the name of the + leader index. When following a data stream, use {{leader_index}}; CCR does + not support changes to the names of a follower data stream’s backing indices. + :param leader_index_exclusion_patterns: An array of simple index patterns that + can be used to exclude indices from being auto-followed. Indices in the remote + cluster whose names are matching one or more leader_index_patterns and one + or more leader_index_exclusion_patterns won’t be followed. + :param leader_index_patterns: An array of simple index patterns to match against + indices in the remote cluster specified by the remote_cluster field. + :param max_outstanding_read_requests: The maximum number of outstanding reads + requests from the remote cluster. + :param max_outstanding_write_requests: The maximum number of outstanding reads + requests from the remote cluster. + :param max_read_request_operation_count: The maximum number of operations to + pull per read from the remote cluster. + :param max_read_request_size: The maximum size in bytes of per read of a batch + of operations pulled from the remote cluster. + :param max_retry_delay: The maximum time to wait before retrying an operation + that failed exceptionally. An exponential backoff strategy is employed when + retrying. + :param max_write_buffer_count: The maximum number of operations that can be queued + for writing. When this limit is reached, reads from the remote cluster will + be deferred until the number of queued operations goes below the limit. + :param max_write_buffer_size: The maximum total bytes of operations that can + be queued for writing. When this limit is reached, reads from the remote + cluster will be deferred until the total bytes of queued operations goes + below the limit. + :param max_write_request_operation_count: The maximum number of operations per + bulk write request executed on the follower. + :param max_write_request_size: The maximum total bytes of operations per bulk + write request executed on the follower. + :param read_poll_timeout: The maximum time to wait for new operations on the + remote cluster when the follower index is synchronized with the leader index. + When the timeout has elapsed, the poll for operations will return to the + follower so that it can update some statistics. Then the follower will immediately + attempt to read from the leader again. + :param settings: Settings to override from the leader index. Note that certain + settings can not be overrode (e.g., index.number_of_shards). + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if remote_cluster is None: + raise ValueError("Empty value passed for parameter 'remote_cluster'") + __path = f"/_ccr/auto_follow/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if remote_cluster is not None: + __body["remote_cluster"] = remote_cluster + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if follow_index_pattern is not None: + __body["follow_index_pattern"] = follow_index_pattern + if human is not None: + __query["human"] = human + if leader_index_exclusion_patterns is not None: + __body["leader_index_exclusion_patterns"] = leader_index_exclusion_patterns + if leader_index_patterns is not None: + __body["leader_index_patterns"] = leader_index_patterns + if max_outstanding_read_requests is not None: + __body["max_outstanding_read_requests"] = max_outstanding_read_requests + if max_outstanding_write_requests is not None: + __body["max_outstanding_write_requests"] = max_outstanding_write_requests + if max_read_request_operation_count is not None: + __body[ + "max_read_request_operation_count" + ] = max_read_request_operation_count + if max_read_request_size is not None: + __body["max_read_request_size"] = max_read_request_size + if max_retry_delay is not None: + __body["max_retry_delay"] = max_retry_delay + if max_write_buffer_count is not None: + __body["max_write_buffer_count"] = max_write_buffer_count + if max_write_buffer_size is not None: + __body["max_write_buffer_size"] = max_write_buffer_size + if max_write_request_operation_count is not None: + __body[ + "max_write_request_operation_count" + ] = max_write_request_operation_count + if max_write_request_size is not None: + __body["max_write_request_size"] = max_write_request_size + if pretty is not None: + __query["pretty"] = pretty + if read_poll_timeout is not None: + __body["read_poll_timeout"] = read_poll_timeout + if settings is not None: + __body["settings"] = settings + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def resume_auto_follow_pattern( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resumes an auto-follow pattern that has been paused + + ``_ + + :param name: The name of the auto follow pattern to resume discovering new indices + to follow. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ccr/auto_follow/{_quote(name)}/resume" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def resume_follow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_outstanding_read_requests: t.Optional[int] = None, + max_outstanding_write_requests: t.Optional[int] = None, + max_read_request_operation_count: t.Optional[int] = None, + max_read_request_size: t.Optional[str] = None, + max_retry_delay: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + max_write_buffer_count: t.Optional[int] = None, + max_write_buffer_size: t.Optional[str] = None, + max_write_request_operation_count: t.Optional[int] = None, + max_write_request_size: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + read_poll_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resumes a follower index that has been paused + + ``_ + + :param index: The name of the follow index to resume following. + :param max_outstanding_read_requests: + :param max_outstanding_write_requests: + :param max_read_request_operation_count: + :param max_read_request_size: + :param max_retry_delay: + :param max_write_buffer_count: + :param max_write_buffer_size: + :param max_write_request_operation_count: + :param max_write_request_size: + :param read_poll_timeout: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/resume_follow" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_outstanding_read_requests is not None: + __body["max_outstanding_read_requests"] = max_outstanding_read_requests + if max_outstanding_write_requests is not None: + __body["max_outstanding_write_requests"] = max_outstanding_write_requests + if max_read_request_operation_count is not None: + __body[ + "max_read_request_operation_count" + ] = max_read_request_operation_count + if max_read_request_size is not None: + __body["max_read_request_size"] = max_read_request_size + if max_retry_delay is not None: + __body["max_retry_delay"] = max_retry_delay + if max_write_buffer_count is not None: + __body["max_write_buffer_count"] = max_write_buffer_count + if max_write_buffer_size is not None: + __body["max_write_buffer_size"] = max_write_buffer_size + if max_write_request_operation_count is not None: + __body[ + "max_write_request_operation_count" + ] = max_write_request_operation_count + if max_write_request_size is not None: + __body["max_write_request_size"] = max_write_request_size + if pretty is not None: + __query["pretty"] = pretty + if read_poll_timeout is not None: + __body["read_poll_timeout"] = read_poll_timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets all stats related to cross-cluster replication. + + ``_ + """ + __path = "/_ccr/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def unfollow( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops the following task associated with a follower index and removes index metadata + and settings associated with cross-cluster replication. + + ``_ + + :param index: The name of the follower index that should be turned into a regular + index. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ccr/unfollow" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/cluster.py b/elasticsearch_serverless/_sync/client/cluster.py new file mode 100644 index 0000000..b819212 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/cluster.py @@ -0,0 +1,539 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import HeadApiResponse, ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class ClusterClient(NamespacedClient): + @_rewrite_parameters() + def delete_component_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a component template + + ``_ + + :param name: Comma-separated list or wildcard expression of component template + names used to limit the request. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_component_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def exists_component_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular component template exist + + ``_ + + :param name: Comma-separated list of component template names used to limit the + request. Wildcard (*) expressions are supported. + :param local: If true, the request retrieves information from the local node + only. Defaults to false, which means information is retrieved from the master + node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_component_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_component_template( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns one or more component templates + + ``_ + + :param name: Comma-separated list of component template names used to limit the + request. Wildcard (`*`) expressions are supported. + :param flat_settings: If `true`, returns settings in flat format. + :param include_defaults: Return all default configurations for the component + template (default: false) + :param local: If `true`, the request retrieves information from the local node + only. If `false`, information is retrieved from the master node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name not in SKIP_IN_PATH: + __path = f"/_component_template/{_quote(name)}" + else: + __path = "/_component_template" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_settings( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns cluster settings. + + ``_ + + :param flat_settings: If `true`, returns settings in flat format. + :param include_defaults: If `true`, returns default cluster settings from the + local node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + __path = "/_cluster/settings" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def info( + self, + *, + target: t.Union[ + t.Union[ + "t.Literal['_all', 'http', 'ingest', 'script', 'thread_pool']", str + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['_all', 'http', 'ingest', 'script', 'thread_pool']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['_all', 'http', 'ingest', 'script', 'thread_pool']", + str, + ], + ..., + ], + ], + ], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns different information about the cluster. + + ``_ + + :param target: Limits the information returned to the specific target. Supports + a comma-separated list, such as http,ingest. + """ + if target in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'target'") + __path = f"/_info/{_quote(target)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def pending_tasks( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a list of any cluster-level changes (e.g. create index, update mapping, + allocate or fail shard) which have not yet been executed. + + ``_ + + :param local: If `true`, the request retrieves information from the local node + only. If `false`, information is retrieved from the master node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + __path = "/_cluster/pending_tasks" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + def put_component_template( + self, + *, + name: str, + template: t.Mapping[str, t.Any], + allow_auto_create: t.Optional[bool] = None, + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a component template + + ``_ + + :param name: Name of the component template to create. Elasticsearch includes + the following built-in component templates: `logs-mappings`; 'logs-settings`; + `metrics-mappings`; `metrics-settings`;`synthetics-mapping`; `synthetics-settings`. + Elastic Agent uses these templates to configure backing indices for its data + streams. If you use Elastic Agent and want to overwrite one of these templates, + set the `version` for your replacement template higher than the current version. + If you don’t use Elastic Agent and want to disable all built-in component + and index templates, set `stack.templates.enabled` to `false` using the cluster + update settings API. + :param template: The template to be applied which includes mappings, settings, + or aliases configuration. + :param allow_auto_create: This setting overrides the value of the `action.auto_create_index` + cluster setting. If set to `true` in a template, then indices can be automatically + created using that template even if auto-creation of indices is disabled + via `actions.auto_create_index`. If set to `false` then data streams matching + the template must always be explicitly created. + :param create: If `true`, this request cannot replace or update existing component + templates. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param meta: Optional user metadata about the component template. May have any + contents. This map is not automatically generated by Elasticsearch. This + information is stored in the cluster state, so keeping it short is preferable. + To unset `_meta`, replace the template without specifying this information. + :param version: Version number used to manage component templates externally. + This number isn't automatically generated or incremented by Elasticsearch. + To unset a version, replace the template without specifying a version. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if template is None: + raise ValueError("Empty value passed for parameter 'template'") + __path = f"/_component_template/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if template is not None: + __body["template"] = template + if allow_auto_create is not None: + __body["allow_auto_create"] = allow_auto_create + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_settings( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + persistent: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + transient: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the cluster settings. + + ``_ + + :param flat_settings: Return settings in flat format (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + :param persistent: + :param timeout: Explicit operation timeout + :param transient: + """ + __path = "/_cluster/settings" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if persistent is not None: + __body["persistent"] = persistent + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if transient is not None: + __body["transient"] = transient + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def stats( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns high-level overview of cluster statistics. + + ``_ + + :param node_id: Comma-separated list of node filters used to limit returned information. + Defaults to all nodes in the cluster. + :param flat_settings: If `true`, returns settings in flat format. + :param timeout: Period to wait for each node to respond. If a node does not respond + before its timeout expires, the response does not include its stats. However, + timed out nodes are included in the response’s `_nodes.failed` property. + Defaults to no timeout. + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_cluster/stats/nodes/{_quote(node_id)}" + else: + __path = "/_cluster/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/dangling_indices.py b/elasticsearch_serverless/_sync/client/dangling_indices.py new file mode 100644 index 0000000..b742998 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/dangling_indices.py @@ -0,0 +1,162 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class DanglingIndicesClient(NamespacedClient): + @_rewrite_parameters() + def delete_dangling_index( + self, + *, + index_uuid: str, + accept_data_loss: bool, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes the specified dangling index + + ``_ + + :param index_uuid: The UUID of the dangling index + :param accept_data_loss: Must be set to true in order to delete the dangling + index + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if index_uuid in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index_uuid'") + if accept_data_loss is None: + raise ValueError("Empty value passed for parameter 'accept_data_loss'") + __path = f"/_dangling/{_quote(index_uuid)}" + __query: t.Dict[str, t.Any] = {} + if accept_data_loss is not None: + __query["accept_data_loss"] = accept_data_loss + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def import_dangling_index( + self, + *, + index_uuid: str, + accept_data_loss: bool, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Imports the specified dangling index + + ``_ + + :param index_uuid: The UUID of the dangling index + :param accept_data_loss: Must be set to true in order to import the dangling + index + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if index_uuid in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index_uuid'") + if accept_data_loss is None: + raise ValueError("Empty value passed for parameter 'accept_data_loss'") + __path = f"/_dangling/{_quote(index_uuid)}" + __query: t.Dict[str, t.Any] = {} + if accept_data_loss is not None: + __query["accept_data_loss"] = accept_data_loss + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def list_dangling_indices( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns all dangling indices. + + ``_ + """ + __path = "/_dangling" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/enrich.py b/elasticsearch_serverless/_sync/client/enrich.py new file mode 100644 index 0000000..7704a61 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/enrich.py @@ -0,0 +1,222 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class EnrichClient(NamespacedClient): + @_rewrite_parameters() + def delete_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing enrich policy and its enrich index. + + ``_ + + :param name: The name of the enrich policy + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_enrich/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def execute_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates the enrich index for an existing enrich policy. + + ``_ + + :param name: The name of the enrich policy + :param wait_for_completion: Should the request should block until the execution + is complete. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_enrich/policy/{_quote(name)}/_execute" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_policy( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets information about an enrich policy. + + ``_ + + :param name: A comma-separated list of enrich policy names + """ + if name not in SKIP_IN_PATH: + __path = f"/_enrich/policy/{_quote(name)}" + else: + __path = "/_enrich/policy" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_policy( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + geo_match: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + match: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + range: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new enrich policy. + + ``_ + + :param name: The name of the enrich policy + :param geo_match: + :param match: + :param range: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_enrich/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if geo_match is not None: + __body["geo_match"] = geo_match + if human is not None: + __query["human"] = human + if match is not None: + __body["match"] = match + if pretty is not None: + __query["pretty"] = pretty + if range is not None: + __body["range"] = range + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets enrich coordinator statistics and information about enrich policies that + are currently executing. + + ``_ + """ + __path = "/_enrich/_stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/eql.py b/elasticsearch_serverless/_sync/client/eql.py new file mode 100644 index 0000000..7a634a4 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/eql.py @@ -0,0 +1,298 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class EqlClient(NamespacedClient): + @_rewrite_parameters() + def delete( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an async EQL search by ID. If the search is still running, the search + request will be cancelled. Otherwise, the saved search results are deleted. + + ``_ + + :param id: Identifier for the search to delete. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_eql/search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + pretty: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns async results from previously executed Event Query Language (EQL) search + + ``_ + + :param id: Identifier for the search. + :param keep_alive: Period for which the search and its results are stored on + the cluster. Defaults to the keep_alive value set by the search’s EQL search + API request. + :param wait_for_completion_timeout: Timeout duration to wait for the request + to finish. Defaults to no timeout, meaning the request waits for complete + search results. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_eql/search/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_status( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the status of a previously submitted async or stored Event Query Language + (EQL) search + + ``_ + + :param id: Identifier for the search. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_eql/search/status/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def search( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + query: str, + allow_no_indices: t.Optional[bool] = None, + case_sensitive: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + event_category_field: t.Optional[str] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + fetch_size: t.Optional[int] = None, + fields: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + filter: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + keep_on_completion: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + result_position: t.Optional[t.Union["t.Literal['head', 'tail']", str]] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + size: t.Optional[int] = None, + tiebreaker_field: t.Optional[str] = None, + timestamp_field: t.Optional[str] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns results matching a query expressed in Event Query Language (EQL) + + ``_ + + :param index: The name of the index to scope the operation + :param query: EQL query you wish to run. + :param allow_no_indices: + :param case_sensitive: + :param event_category_field: Field containing the event classification, such + as process, file, or network. + :param expand_wildcards: + :param fetch_size: Maximum number of events to search at a time for sequence + queries. + :param fields: Array of wildcard (*) patterns. The response returns values for + field names matching these patterns in the fields property of each hit. + :param filter: Query, written in Query DSL, used to filter the events on which + the EQL query runs. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param keep_alive: + :param keep_on_completion: + :param result_position: + :param runtime_mappings: + :param size: For basic queries, the maximum number of matching events to return. + Defaults to 10 + :param tiebreaker_field: Field used to sort hits with the same timestamp in ascending + order + :param timestamp_field: Field containing event timestamp. Default "@timestamp" + :param wait_for_completion_timeout: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if query is None: + raise ValueError("Empty value passed for parameter 'query'") + __path = f"/{_quote(index)}/_eql/search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if query is not None: + __body["query"] = query + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if case_sensitive is not None: + __body["case_sensitive"] = case_sensitive + if error_trace is not None: + __query["error_trace"] = error_trace + if event_category_field is not None: + __body["event_category_field"] = event_category_field + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if fetch_size is not None: + __body["fetch_size"] = fetch_size + if fields is not None: + __body["fields"] = fields + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if keep_alive is not None: + __body["keep_alive"] = keep_alive + if keep_on_completion is not None: + __body["keep_on_completion"] = keep_on_completion + if pretty is not None: + __query["pretty"] = pretty + if result_position is not None: + __body["result_position"] = result_position + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if size is not None: + __body["size"] = size + if tiebreaker_field is not None: + __body["tiebreaker_field"] = tiebreaker_field + if timestamp_field is not None: + __body["timestamp_field"] = timestamp_field + if wait_for_completion_timeout is not None: + __body["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/features.py b/elasticsearch_serverless/_sync/client/features.py new file mode 100644 index 0000000..43dca35 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/features.py @@ -0,0 +1,88 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class FeaturesClient(NamespacedClient): + @_rewrite_parameters() + def get_features( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Gets a list of features which can be included in snapshots using the feature_states + field when creating a snapshot + + ``_ + """ + __path = "/_features" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def reset_features( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resets the internal state of features, usually by deleting system indices + + ``_ + """ + __path = "/_features/_reset" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/fleet.py b/elasticsearch_serverless/_sync/client/fleet.py new file mode 100644 index 0000000..20357ef --- /dev/null +++ b/elasticsearch_serverless/_sync/client/fleet.py @@ -0,0 +1,631 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class FleetClient(NamespacedClient): + @_rewrite_parameters() + def global_checkpoints( + self, + *, + index: str, + checkpoints: t.Optional[t.Union[t.List[int], t.Tuple[int, ...]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_advance: t.Optional[bool] = None, + wait_for_index: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the current global checkpoints for an index. This API is design for internal + use by the fleet server project. + + ``_ + + :param index: A single index or index alias that resolves to a single index. + :param checkpoints: A comma separated list of previous global checkpoints. When + used in combination with `wait_for_advance`, the API will only return once + the global checkpoints advances past the checkpoints. Providing an empty + list will cause Elasticsearch to immediately return the current global checkpoints. + :param timeout: Period to wait for a global checkpoints to advance past `checkpoints`. + :param wait_for_advance: A boolean value which controls whether to wait (until + the timeout) for the global checkpoints to advance past the provided `checkpoints`. + :param wait_for_index: A boolean value which controls whether to wait (until + the timeout) for the target index to exist and all primary shards be active. + Can only be true when `wait_for_advance` is true. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_fleet/global_checkpoints" + __query: t.Dict[str, t.Any] = {} + if checkpoints is not None: + __query["checkpoints"] = checkpoints + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_advance is not None: + __query["wait_for_advance"] = wait_for_advance + if wait_for_index is not None: + __query["wait_for_index"] = wait_for_index + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="searches", + ) + def msearch( + self, + *, + searches: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + index: t.Optional[str] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + max_concurrent_searches: t.Optional[int] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + pre_filter_shard_size: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + typed_keys: t.Optional[bool] = None, + wait_for_checkpoints: t.Optional[ + t.Union[t.List[int], t.Tuple[int, ...]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Multi Search API where the search will only be executed after specified checkpoints + are available due to a refresh. This API is designed for internal use by the + fleet server project. + + :param searches: + :param index: A single target to search. If the target is an index alias, it + must resolve to a single index. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param allow_partial_search_results: If true, returns partial results if there + are shard request timeouts or [shard failures](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-replication.html#shard-failures). + If false, returns an error with no partial results. Defaults to the configured + cluster setting `search.default_allow_partial_results` which is true by default. + :param ccs_minimize_roundtrips: If true, network roundtrips between the coordinating + node and remote clusters are minimized for cross-cluster search requests. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. + :param ignore_throttled: If true, concrete, expanded or aliased indices are ignored + when frozen. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param max_concurrent_searches: Maximum number of concurrent searches the multi + search API can execute. + :param max_concurrent_shard_requests: Maximum number of concurrent shard requests + that each sub-search request executes per node. + :param pre_filter_shard_size: Defines a threshold that enforces a pre-filter + roundtrip to prefilter search shards based on query rewriting if the number + of shards the search request expands to exceeds the threshold. This filter + roundtrip can limit the number of shards significantly if for instance a + shard can not match any documents based on its rewrite method i.e., if date + filters are mandatory to match but the shard bounds and the query are disjoint. + :param rest_total_hits_as_int: If true, hits.total are returned as an integer + in the response. Defaults to false, which returns an object. + :param search_type: Indicates whether global term and document frequencies should + be used when scoring returned documents. + :param typed_keys: Specifies whether aggregation and suggester names should be + prefixed by their respective types in the response. + :param wait_for_checkpoints: A comma separated list of checkpoints. When configured, + the search API will only be executed on a shard after the relevant checkpoint + has become visible for search. Defaults to an empty list which will cause + Elasticsearch to immediately execute the search. + """ + if searches is None: + raise ValueError("Empty value passed for parameter 'searches'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_fleet/_fleet_msearch" + else: + __path = "/_fleet/_fleet_msearch" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if max_concurrent_searches is not None: + __query["max_concurrent_searches"] = max_concurrent_searches + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if pretty is not None: + __query["pretty"] = pretty + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if search_type is not None: + __query["search_type"] = search_type + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if wait_for_checkpoints is not None: + __query["wait_for_checkpoints"] = wait_for_checkpoints + __body = searches + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_source": "source", + "_source_excludes": "source_excludes", + "_source_includes": "source_includes", + "from": "from_", + }, + ) + def search( + self, + *, + index: str, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + allow_partial_search_results: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + batched_reduce_size: t.Optional[int] = None, + ccs_minimize_roundtrips: t.Optional[bool] = None, + collapse: t.Optional[t.Mapping[str, t.Any]] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + docvalue_fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + ext: t.Optional[t.Mapping[str, t.Any]] = None, + fields: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + highlight: t.Optional[t.Mapping[str, t.Any]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indices_boost: t.Optional[ + t.Union[t.List[t.Mapping[str, float]], t.Tuple[t.Mapping[str, float], ...]] + ] = None, + lenient: t.Optional[bool] = None, + max_concurrent_shard_requests: t.Optional[int] = None, + min_compatible_shard_node: t.Optional[str] = None, + min_score: t.Optional[float] = None, + pit: t.Optional[t.Mapping[str, t.Any]] = None, + post_filter: t.Optional[t.Mapping[str, t.Any]] = None, + pre_filter_shard_size: t.Optional[int] = None, + preference: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + profile: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + request_cache: t.Optional[bool] = None, + rescore: t.Optional[ + t.Union[ + t.Mapping[str, t.Any], + t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + ] + ] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + routing: t.Optional[str] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + search_type: t.Optional[ + t.Union["t.Literal['dfs_query_then_fetch', 'query_then_fetch']", str] + ] = None, + seq_no_primary_term: t.Optional[bool] = None, + size: t.Optional[int] = None, + slice: t.Optional[t.Mapping[str, t.Any]] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + source: t.Optional[t.Union[bool, t.Mapping[str, t.Any]]] = None, + source_excludes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + source_includes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + stats: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + stored_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + suggest: t.Optional[t.Mapping[str, t.Any]] = None, + suggest_field: t.Optional[str] = None, + suggest_mode: t.Optional[ + t.Union["t.Literal['always', 'missing', 'popular']", str] + ] = None, + suggest_size: t.Optional[int] = None, + suggest_text: t.Optional[str] = None, + terminate_after: t.Optional[int] = None, + timeout: t.Optional[str] = None, + track_scores: t.Optional[bool] = None, + track_total_hits: t.Optional[t.Union[bool, int]] = None, + typed_keys: t.Optional[bool] = None, + version: t.Optional[bool] = None, + wait_for_checkpoints: t.Optional[ + t.Union[t.List[int], t.Tuple[int, ...]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Search API where the search will only be executed after specified checkpoints + are available due to a refresh. This API is designed for internal use by the + fleet server project. + + :param index: A single target to search. If the target is an index alias, it + must resolve to a single index. + :param aggregations: + :param aggs: + :param allow_no_indices: + :param allow_partial_search_results: If true, returns partial results if there + are shard request timeouts or [shard failures](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-replication.html#shard-failures). + If false, returns an error with no partial results. Defaults to the configured + cluster setting `search.default_allow_partial_results` which is true by default. + :param analyze_wildcard: + :param analyzer: + :param batched_reduce_size: + :param ccs_minimize_roundtrips: + :param collapse: + :param default_operator: + :param df: + :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc + values for field names matching these patterns in the hits.fields property + of the response. + :param expand_wildcards: + :param explain: If true, returns detailed information about score computation + as part of a hit. + :param ext: Configuration of search extensions defined by Elasticsearch plugins. + :param fields: Array of wildcard (*) patterns. The request returns values for + field names matching these patterns in the hits.fields property of the response. + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the search_after parameter. + :param highlight: + :param ignore_throttled: + :param ignore_unavailable: + :param indices_boost: Boosts the _score of documents from specified indices. + :param lenient: + :param max_concurrent_shard_requests: + :param min_compatible_shard_node: + :param min_score: Minimum _score for matching documents. Documents with a lower + _score are not included in the search results. + :param pit: Limits the search to a point in time (PIT). If you provide a PIT, + you cannot specify an in the request path. + :param post_filter: + :param pre_filter_shard_size: + :param preference: + :param profile: + :param q: + :param query: Defines the search definition using the Query DSL. + :param request_cache: + :param rescore: + :param rest_total_hits_as_int: + :param routing: + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param script_fields: Retrieve a script evaluation (based on different fields) + for each hit. + :param scroll: + :param search_after: + :param search_type: + :param seq_no_primary_term: If true, returns sequence number and primary term + of the last modification of each hit. See Optimistic concurrency control. + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the from and size parameters. To page through + more hits, use the search_after parameter. + :param slice: + :param sort: + :param source: Indicates which source fields are returned for matching documents. + These fields are returned in the hits._source property of the search response. + :param source_excludes: + :param source_includes: + :param stats: Stats groups to associate with the search. Each group maintains + a statistics aggregation for its associated searches. You can retrieve these + stats using the indices stats API. + :param stored_fields: List of stored fields to return as part of a hit. If no + fields are specified, no stored fields are included in the response. If this + field is specified, the _source parameter defaults to false. You can pass + _source: true to return both source fields and stored fields in the search + response. + :param suggest: + :param suggest_field: Specifies which field to use for suggestions. + :param suggest_mode: + :param suggest_size: + :param suggest_text: The source text for which the suggestions should be returned. + :param terminate_after: Maximum number of documents to collect for each shard. + If a query reaches this limit, Elasticsearch terminates the query early. + Elasticsearch collects documents before sorting. Defaults to 0, which does + not terminate query execution early. + :param timeout: Specifies the period of time to wait for a response from each + shard. If no response is received before the timeout expires, the request + fails and returns an error. Defaults to no timeout. + :param track_scores: If true, calculate and return document scores, even if the + scores are not used for sorting. + :param track_total_hits: Number of hits matching the query to count accurately. + If true, the exact number of hits is returned at the cost of some performance. + If false, the response does not include the total number of hits matching + the query. Defaults to 10,000 hits. + :param typed_keys: + :param version: If true, returns document version as part of a hit. + :param wait_for_checkpoints: A comma separated list of checkpoints. When configured, + the search API will only be executed on a shard after the relevant checkpoint + has become visible for search. Defaults to an empty list which will cause + Elasticsearch to immediately execute the search. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_fleet/_fleet_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if allow_partial_search_results is not None: + __query["allow_partial_search_results"] = allow_partial_search_results + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if batched_reduce_size is not None: + __query["batched_reduce_size"] = batched_reduce_size + if ccs_minimize_roundtrips is not None: + __query["ccs_minimize_roundtrips"] = ccs_minimize_roundtrips + if collapse is not None: + __body["collapse"] = collapse + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if docvalue_fields is not None: + __body["docvalue_fields"] = docvalue_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __body["explain"] = explain + if ext is not None: + __body["ext"] = ext + if fields is not None: + __body["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if highlight is not None: + __body["highlight"] = highlight + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indices_boost is not None: + __body["indices_boost"] = indices_boost + if lenient is not None: + __query["lenient"] = lenient + if max_concurrent_shard_requests is not None: + __query["max_concurrent_shard_requests"] = max_concurrent_shard_requests + if min_compatible_shard_node is not None: + __query["min_compatible_shard_node"] = min_compatible_shard_node + if min_score is not None: + __body["min_score"] = min_score + if pit is not None: + __body["pit"] = pit + if post_filter is not None: + __body["post_filter"] = post_filter + if pre_filter_shard_size is not None: + __query["pre_filter_shard_size"] = pre_filter_shard_size + if preference is not None: + __query["preference"] = preference + if pretty is not None: + __query["pretty"] = pretty + if profile is not None: + __body["profile"] = profile + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if request_cache is not None: + __query["request_cache"] = request_cache + if rescore is not None: + __body["rescore"] = rescore + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if routing is not None: + __query["routing"] = routing + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll is not None: + __query["scroll"] = scroll + if search_after is not None: + __body["search_after"] = search_after + if search_type is not None: + __query["search_type"] = search_type + if seq_no_primary_term is not None: + __body["seq_no_primary_term"] = seq_no_primary_term + if size is not None: + __body["size"] = size + if slice is not None: + __body["slice"] = slice + if sort is not None: + __body["sort"] = sort + if source is not None: + __body["_source"] = source + if source_excludes is not None: + __query["_source_excludes"] = source_excludes + if source_includes is not None: + __query["_source_includes"] = source_includes + if stats is not None: + __body["stats"] = stats + if stored_fields is not None: + __body["stored_fields"] = stored_fields + if suggest is not None: + __body["suggest"] = suggest + if suggest_field is not None: + __query["suggest_field"] = suggest_field + if suggest_mode is not None: + __query["suggest_mode"] = suggest_mode + if suggest_size is not None: + __query["suggest_size"] = suggest_size + if suggest_text is not None: + __query["suggest_text"] = suggest_text + if terminate_after is not None: + __body["terminate_after"] = terminate_after + if timeout is not None: + __body["timeout"] = timeout + if track_scores is not None: + __body["track_scores"] = track_scores + if track_total_hits is not None: + __body["track_total_hits"] = track_total_hits + if typed_keys is not None: + __query["typed_keys"] = typed_keys + if version is not None: + __body["version"] = version + if wait_for_checkpoints is not None: + __query["wait_for_checkpoints"] = wait_for_checkpoints + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/graph.py b/elasticsearch_serverless/_sync/client/graph.py new file mode 100644 index 0000000..b69dfc7 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/graph.py @@ -0,0 +1,96 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class GraphClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + def explore( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + connections: t.Optional[t.Mapping[str, t.Any]] = None, + controls: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + vertices: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Explore extracted and summarized information about the documents and terms in + an index. + + ``_ + + :param index: A comma-separated list of index names to search; use `_all` or + empty string to perform the operation on all indices + :param connections: + :param controls: + :param query: + :param routing: Specific routing value + :param timeout: Explicit operation timeout + :param vertices: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_graph/explore" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if connections is not None: + __body["connections"] = connections + if controls is not None: + __body["controls"] = controls + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if routing is not None: + __query["routing"] = routing + if timeout is not None: + __query["timeout"] = timeout + if vertices is not None: + __body["vertices"] = vertices + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/ilm.py b/elasticsearch_serverless/_sync/client/ilm.py new file mode 100644 index 0000000..6fa488a --- /dev/null +++ b/elasticsearch_serverless/_sync/client/ilm.py @@ -0,0 +1,543 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class IlmClient(NamespacedClient): + @_rewrite_parameters() + def delete_lifecycle( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes the specified lifecycle policy definition. A currently used policy cannot + be deleted. + + ``_ + + :param name: Identifier for the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ilm/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def explain_lifecycle( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + only_errors: t.Optional[bool] = None, + only_managed: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the index's current lifecycle state, such as the + currently executing phase, action, and step. + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases to target. + Supports wildcards (`*`). To target all data streams and indices, use `*` + or `_all`. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param only_errors: Filters the returned indices to only indices that are managed + by ILM and are in an error state, either due to an encountering an error + while executing the policy, or attempting to use a policy that does not exist. + :param only_managed: Filters the returned indices to only indices that are managed + by ILM. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ilm/explain" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if only_errors is not None: + __query["only_errors"] = only_errors + if only_managed is not None: + __query["only_managed"] = only_managed + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_lifecycle( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the specified policy definition. Includes the policy version and last + modified date. + + ``_ + + :param name: Identifier for the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name not in SKIP_IN_PATH: + __path = f"/_ilm/policy/{_quote(name)}" + else: + __path = "/_ilm/policy" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the current index lifecycle management (ILM) status. + + ``_ + """ + __path = "/_ilm/status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def migrate_to_data_tiers( + self, + *, + dry_run: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + legacy_template_to_delete: t.Optional[str] = None, + node_attribute: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Migrates the indices and ILM policies away from custom node attribute allocation + routing to data tiers routing + + ``_ + + :param dry_run: If true, simulates the migration from node attributes based allocation + filters to data tiers, but does not perform the migration. This provides + a way to retrieve the indices and ILM policies that need to be migrated. + :param legacy_template_to_delete: + :param node_attribute: + """ + __path = "/_ilm/migrate_to_data_tiers" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if dry_run is not None: + __query["dry_run"] = dry_run + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if legacy_template_to_delete is not None: + __body["legacy_template_to_delete"] = legacy_template_to_delete + if node_attribute is not None: + __body["node_attribute"] = node_attribute + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def move_to_step( + self, + *, + index: str, + current_step: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + next_step: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Manually moves an index into the specified step and executes that step. + + ``_ + + :param index: The name of the index whose lifecycle step is to change + :param current_step: + :param next_step: + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/_ilm/move/{_quote(index)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if current_step is not None: + __body["current_step"] = current_step + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if next_step is not None: + __body["next_step"] = next_step + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_lifecycle( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + policy: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a lifecycle policy + + ``_ + + :param name: Identifier for the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param policy: + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_ilm/policy/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if policy is not None: + __body["policy"] = policy + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def remove_policy( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes the assigned lifecycle policy and stops managing the specified index + + ``_ + + :param index: The name of the index to remove policy on + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ilm/remove" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def retry( + self, + *, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retries executing the policy for an index that is in the ERROR step. + + ``_ + + :param index: The name of the indices (comma-separated) whose failed lifecycle + step is to be retry + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_ilm/retry" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def start( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Start the index lifecycle management (ILM) plugin. + + ``_ + + :param master_timeout: + :param timeout: + """ + __path = "/_ilm/start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stop( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Halts all lifecycle management operations and stops the index lifecycle management + (ILM) plugin + + ``_ + + :param master_timeout: + :param timeout: + """ + __path = "/_ilm/stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/indices.py b/elasticsearch_serverless/_sync/client/indices.py new file mode 100644 index 0000000..e0bedbe --- /dev/null +++ b/elasticsearch_serverless/_sync/client/indices.py @@ -0,0 +1,2795 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import HeadApiResponse, ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class IndicesClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + def analyze( + self, + *, + index: t.Optional[str] = None, + analyzer: t.Optional[str] = None, + attributes: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + char_filter: t.Optional[ + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ] + ] = None, + error_trace: t.Optional[bool] = None, + explain: t.Optional[bool] = None, + field: t.Optional[str] = None, + filter: t.Optional[ + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + normalizer: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + text: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + tokenizer: t.Optional[t.Union[str, t.Mapping[str, t.Any]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Performs the analysis process on a text and return the tokens breakdown of the + text. + + ``_ + + :param index: The name of the index to scope the operation + :param analyzer: + :param attributes: + :param char_filter: + :param explain: + :param field: + :param filter: + :param normalizer: + :param text: + :param tokenizer: + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_analyze" + else: + __path = "/_analyze" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analyzer is not None: + __body["analyzer"] = analyzer + if attributes is not None: + __body["attributes"] = attributes + if char_filter is not None: + __body["char_filter"] = char_filter + if error_trace is not None: + __query["error_trace"] = error_trace + if explain is not None: + __body["explain"] = explain + if field is not None: + __body["field"] = field + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if normalizer is not None: + __body["normalizer"] = normalizer + if pretty is not None: + __query["pretty"] = pretty + if text is not None: + __body["text"] = text + if tokenizer is not None: + __body["tokenizer"] = tokenizer + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def create( + self, + *, + index: str, + aliases: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + mappings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates an index with optional settings and mappings. + + ``_ + + :param index: The name of the index + :param aliases: + :param mappings: Mapping for fields in the index. If specified, this mapping + can include: - Field names - Field data types - Mapping parameters + :param master_timeout: Specify timeout for connection to master + :param settings: + :param timeout: Explicit operation timeout + :param wait_for_active_shards: Set the number of active shards to wait for before + the operation returns. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aliases is not None: + __body["aliases"] = aliases + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if mappings is not None: + __body["mappings"] = mappings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if settings is not None: + __body["settings"] = settings + if timeout is not None: + __query["timeout"] = timeout + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def create_data_stream( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a data stream + + ``_ + + :param name: Name of the data stream, which must meet the following criteria: + Lowercase only; Cannot include `\\`, `/`, `*`, `?`, `"`, `<`, `>`, `|`, `,`, + `#`, `:`, or a space character; Cannot start with `-`, `_`, `+`, or `.ds-`; + Cannot be `.` or `..`; Cannot be longer than 255 bytes. Multi-byte characters + count towards this limit faster. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def data_streams_stats( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Provides statistics on operations happening in a data stream. + + ``_ + + :param name: A comma-separated list of data stream names; use `_all` or empty + string to perform the operation on all data streams + :param expand_wildcards: + """ + if name not in SKIP_IN_PATH: + __path = f"/_data_stream/{_quote(name)}/_stats" + else: + __path = "/_data_stream/_stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an index. + + ``_ + + :param index: A comma-separated list of indices to delete; use `_all` or `*` + string to delete all indices + :param allow_no_indices: Ignore if a wildcard expression resolves to no concrete + indices (default: false) + :param expand_wildcards: Whether wildcard expressions should get expanded to + open, closed, or hidden indices + :param ignore_unavailable: Ignore unavailable indexes (default: false) + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_alias( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an alias. + + ``_ + + :param index: A comma-separated list of index names (supports wildcards); use + `_all` for all indices + :param name: A comma-separated list of aliases to delete (supports wildcards); + use `_all` to delete all aliases for the specified indices. + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit timestamp for the document + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_data_lifecycle( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes the data lifecycle of the selected data streams. + + ``_ + + :param name: A comma-separated list of data streams of which the data lifecycle + will be deleted; use `*` to get all data streams + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit timestamp for the document + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}/_lifecycle" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_data_stream( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a data stream. + + ``_ + + :param name: Comma-separated list of data streams to delete. Wildcard (`*`) expressions + are supported. + :param expand_wildcards: Type of data stream that wildcard patterns can match. + Supports comma-separated values,such as `open,hidden`. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_index_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an index template. + + ``_ + + :param name: Comma-separated list of index template names used to limit the request. + Wildcard (*) expressions are supported. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_template( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an index template. + + ``_ + + :param name: The name of the template + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit operation timeout + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def disk_usage( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flush: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + run_expensive_tasks: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Analyzes the disk usage of each field of an index or data stream + + ``_ + + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. It’s recommended to execute this API with a single + index (or the latest backing index of a data stream) as the API consumes + resources significantly. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as open,hidden. + :param flush: If true, the API performs a flush before analysis. If false, the + response may not include uncommitted data. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. + :param run_expensive_tasks: Analyzing field disk usage is resource-intensive. + To use the API, this parameter must be set to true. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_disk_usage" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flush is not None: + __query["flush"] = flush + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if pretty is not None: + __query["pretty"] = pretty + if run_expensive_tasks is not None: + __query["run_expensive_tasks"] = run_expensive_tasks + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def exists( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular index exists. + + ``_ + + :param index: A comma-separated list of index names + :param allow_no_indices: Ignore if a wildcard expression resolves to no concrete + indices (default: false) + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param flat_settings: Return settings in flat format (default: false) + :param ignore_unavailable: Ignore unavailable indexes (default: false) + :param include_defaults: Whether to return all default setting for each of the + indices. + :param local: Return local information, do not retrieve the state from master + node (default: false) + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def exists_alias( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular alias exists. + + ``_ + + :param name: A comma-separated list of alias names to return + :param index: A comma-separated list of index names to filter aliases + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param local: Return local information, do not retrieve the state from master + node (default: false) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + elif name not in SKIP_IN_PATH: + __path = f"/_alias/{_quote(name)}" + else: + raise ValueError("Couldn't find a path for the given parameters") + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def exists_index_template( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular index template exists. + + ``_ + + :param name: Comma-separated list of index template names used to limit the request. + Wildcard (*) expressions are supported. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def exists_template( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> HeadApiResponse: + """ + Returns information about whether a particular index template exists. + + ``_ + + :param name: The comma separated names of the index templates + :param flat_settings: Return settings in flat format (default: false) + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_template/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "HEAD", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def explain_data_lifecycle( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the index's current DLM lifecycle, such as any potential + encountered error, time since creation etc. + + ``_ + + :param index: The name of the index to explain + :param include_defaults: indicates if the API should return the default values + the system uses for the index's lifecycle + :param master_timeout: Specify timeout for connection to master + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_lifecycle/explain" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + features: t.Optional[ + t.Union[ + t.Union["t.Literal['aliases', 'mappings', 'settings']", str], + t.Union[ + t.List[ + t.Union["t.Literal['aliases', 'mappings', 'settings']", str] + ], + t.Tuple[ + t.Union["t.Literal['aliases', 'mappings', 'settings']", str], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about one or more indices. + + ``_ + + :param index: Comma-separated list of data streams, indices, and index aliases + used to limit the request. Wildcard expressions (*) are supported. + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as open,hidden. + :param features: Return only information on specified index features + :param flat_settings: If true, returns settings in flat format. + :param ignore_unavailable: If false, requests that target a missing index return + an error. + :param include_defaults: If true, return all default settings in the response. + :param local: If true, the request retrieves information from the local node + only. Defaults to false, which means information is retrieved from the master + node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if features is not None: + __query["features"] = features + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_alias( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns an alias. + + ``_ + + :param index: A comma-separated list of index names to filter aliases + :param name: A comma-separated list of alias names to return + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param local: Return local information, do not retrieve the state from master + node (default: false) + """ + if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_alias" + elif name not in SKIP_IN_PATH: + __path = f"/_alias/{_quote(name)}" + else: + __path = "/_alias" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_data_lifecycle( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the data lifecycle of the selected data streams. + + ``_ + + :param name: A comma-separated list of data streams to get; use `*` to get all + data streams + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param include_defaults: Return all relevant default configurations for the data + stream (default: false) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}/_lifecycle" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_data_stream( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns data streams. + + ``_ + + :param name: Comma-separated list of data stream names used to limit the request. + Wildcard (`*`) expressions are supported. If omitted, all data streams are + returned. + :param expand_wildcards: Type of data stream that wildcard patterns can match. + Supports comma-separated values, such as `open,hidden`. + :param include_defaults: If true, returns all relevant default configurations + for the index template. + """ + if name not in SKIP_IN_PATH: + __path = f"/_data_stream/{_quote(name)}" + else: + __path = "/_data_stream" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_index_template( + self, + *, + name: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns an index template. + + ``_ + + :param name: Comma-separated list of index template names used to limit the request. + Wildcard (*) expressions are supported. + :param flat_settings: If true, returns settings in flat format. + :param include_defaults: If true, returns all relevant default configurations + for the index template. + :param local: If true, the request retrieves information from the local node + only. Defaults to false, which means information is retrieved from the master + node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + if name not in SKIP_IN_PATH: + __path = f"/_index_template/{_quote(name)}" + else: + __path = "/_index_template" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_mapping( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns mappings for one or more indices. + + ``_ + + :param index: A comma-separated list of index names + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Specify timeout for connection to master + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_mapping" + else: + __path = "/_mapping" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_settings( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns settings for one or more indices. + + ``_ + + :param index: A comma-separated list of index names; use `_all` or empty string + to perform the operation on all indices + :param name: The name of the settings that should be included + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param flat_settings: Return settings in flat format (default: false) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param include_defaults: Whether to return all default setting for each of the + indices. + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Specify timeout for connection to master + """ + if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_settings/{_quote(name)}" + elif index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_settings" + elif name not in SKIP_IN_PATH: + __path = f"/_settings/{_quote(name)}" + else: + __path = "/_settings" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_template( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns an index template. + + ``_ + + :param name: The comma separated names of the index templates + :param flat_settings: Return settings in flat format (default: false) + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + """ + if name not in SKIP_IN_PATH: + __path = f"/_template/{_quote(name)}" + else: + __path = "/_template" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def migrate_to_data_stream( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Migrates an alias to a data stream + + ``_ + + :param name: The name of the alias to migrate + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/_migrate/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def modify_data_stream( + self, + *, + actions: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Modifies a data stream + + ``_ + + :param actions: Actions to perform. + """ + if actions is None: + raise ValueError("Empty value passed for parameter 'actions'") + __path = "/_data_stream/_modify" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __body["actions"] = actions + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_alias( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + name: str, + error_trace: t.Optional[bool] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + index_routing: t.Optional[str] = None, + is_write_index: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + routing: t.Optional[str] = None, + search_routing: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates an alias. + + ``_ + + :param index: A comma-separated list of index names the alias should point to + (supports wildcards); use `_all` to perform the operation on all indices. + :param name: The name of the alias to be created or updated + :param filter: + :param index_routing: + :param is_write_index: + :param master_timeout: Specify timeout for connection to master + :param routing: + :param search_routing: + :param timeout: Explicit timestamp for the document + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/{_quote(index)}/_alias/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if index_routing is not None: + __body["index_routing"] = index_routing + if is_write_index is not None: + __body["is_write_index"] = is_write_index + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if routing is not None: + __body["routing"] = routing + if search_routing is not None: + __body["search_routing"] = search_routing + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_data_lifecycle( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + data_retention: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the data lifecycle of the selected data streams. + + ``_ + + :param name: A comma-separated list of data streams whose lifecycle will be updated; + use `*` to set the lifecycle to all data streams + :param data_retention: + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + :param master_timeout: Specify timeout for connection to master + :param timeout: Explicit timestamp for the document + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_data_stream/{_quote(name)}/_lifecycle" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if data_retention is not None: + __body["data_retention"] = data_retention + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + def put_index_template( + self, + *, + name: str, + composed_of: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + create: t.Optional[bool] = None, + data_stream: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + index_patterns: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + priority: t.Optional[int] = None, + template: t.Optional[t.Mapping[str, t.Any]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates an index template. + + ``_ + + :param name: Index or template name + :param composed_of: + :param create: Whether the index template should only be added if new or can + also replace an existing one + :param data_stream: + :param index_patterns: + :param meta: + :param priority: + :param template: + :param version: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if composed_of is not None: + __body["composed_of"] = composed_of + if create is not None: + __query["create"] = create + if data_stream is not None: + __body["data_stream"] = data_stream + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if index_patterns is not None: + __body["index_patterns"] = index_patterns + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if priority is not None: + __body["priority"] = priority + if template is not None: + __body["template"] = template + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={ + "_field_names": "field_names", + "_meta": "meta", + "_routing": "routing", + "_source": "source", + }, + ) + def put_mapping( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_indices: t.Optional[bool] = None, + date_detection: t.Optional[bool] = None, + dynamic: t.Optional[ + t.Union["t.Literal['false', 'runtime', 'strict', 'true']", str] + ] = None, + dynamic_date_formats: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + dynamic_templates: t.Optional[ + t.Union[ + t.Mapping[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Mapping[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Mapping[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + field_names: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + numeric_detection: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + properties: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + routing: t.Optional[t.Mapping[str, t.Any]] = None, + runtime: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + source: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + write_index_only: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the index mappings. + + ``_ + + :param index: A comma-separated list of index names the mapping should be added + to (supports wildcards); use `_all` or omit to add the mapping on all indices. + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param date_detection: Controls whether dynamic date detection is enabled. + :param dynamic: Controls whether new fields are added dynamically. + :param dynamic_date_formats: If date detection is enabled then new string fields + are checked against 'dynamic_date_formats' and if the value matches then + a new date field is added instead of string. + :param dynamic_templates: Specify dynamic templates for the mapping. + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param field_names: Control whether field names are enabled for the index. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param master_timeout: Specify timeout for connection to master + :param meta: A mapping type can have custom meta data associated with it. These + are not used at all by Elasticsearch, but can be used to store application-specific + metadata. + :param numeric_detection: Automatically map strings into numeric data types for + all fields. + :param properties: Mapping for a field. For new fields, this mapping can include: + - Field name - Field data type - Mapping parameters + :param routing: Enable making a routing value required on indexed documents. + :param runtime: Mapping of runtime fields for the index. + :param source: Control whether the _source field is enabled on the index. + :param timeout: Explicit operation timeout + :param write_index_only: When true, applies mappings only to the write index + of an alias or data stream + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_mapping" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if date_detection is not None: + __body["date_detection"] = date_detection + if dynamic is not None: + __body["dynamic"] = dynamic + if dynamic_date_formats is not None: + __body["dynamic_date_formats"] = dynamic_date_formats + if dynamic_templates is not None: + __body["dynamic_templates"] = dynamic_templates + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if field_names is not None: + __body["_field_names"] = field_names + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if numeric_detection is not None: + __body["numeric_detection"] = numeric_detection + if pretty is not None: + __query["pretty"] = pretty + if properties is not None: + __body["properties"] = properties + if routing is not None: + __body["_routing"] = routing + if runtime is not None: + __body["runtime"] = runtime + if source is not None: + __body["_source"] = source + if timeout is not None: + __query["timeout"] = timeout + if write_index_only is not None: + __query["write_index_only"] = write_index_only + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="settings", + ) + def put_settings( + self, + *, + settings: t.Mapping[str, t.Any], + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + preserve_existing: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the index settings. + + ``_ + + :param settings: + :param index: A comma-separated list of index names; use `_all` or empty string + to perform the operation on all indices + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param flat_settings: Return settings in flat format (default: false) + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param master_timeout: Specify timeout for connection to master + :param preserve_existing: Whether to update existing settings. If set to `true` + existing settings on an index remain unchanged, the default is `false` + :param timeout: Explicit operation timeout + """ + if settings is None: + raise ValueError("Empty value passed for parameter 'settings'") + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_settings" + else: + __path = "/_settings" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if preserve_existing is not None: + __query["preserve_existing"] = preserve_existing + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __body = settings + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_template( + self, + *, + name: str, + aliases: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + index_patterns: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + mappings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + order: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates an index template. + + ``_ + + :param name: The name of the template + :param aliases: Aliases for the index. + :param create: If true, this request cannot replace or update existing index + templates. + :param flat_settings: + :param index_patterns: Array of wildcard expressions used to match the names + of indices during creation. + :param mappings: Mapping for fields in the index. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param order: Order in which Elasticsearch applies this template if index matches + multiple templates. Templates with lower 'order' values are merged first. + Templates with higher 'order' values are merged later, overriding templates + with lower values. + :param settings: Configuration options for the index. + :param timeout: + :param version: Version number used to manage index templates externally. This + number is not automatically generated by Elasticsearch. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_template/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aliases is not None: + __body["aliases"] = aliases + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if index_patterns is not None: + __body["index_patterns"] = index_patterns + if mappings is not None: + __body["mappings"] = mappings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if order is not None: + __body["order"] = order + if pretty is not None: + __query["pretty"] = pretty + if settings is not None: + __body["settings"] = settings + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def resolve_index( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about any matching indices, aliases, and data streams + + ``_ + + :param name: A comma-separated list of names or wildcard expressions + :param expand_wildcards: Whether wildcard expressions should get expanded to + open or closed indices (default: open) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_resolve/index/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def rollover( + self, + *, + alias: str, + new_index: t.Optional[str] = None, + aliases: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + conditions: t.Optional[t.Mapping[str, t.Any]] = None, + dry_run: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + mappings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_active_shards: t.Optional[ + t.Union[int, t.Union["t.Literal['all', 'index-setting']", str]] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates an alias to point to a new index when the existing index is considered + to be too large or too old. + + ``_ + + :param alias: The name of the alias to rollover + :param new_index: The name of the rollover index + :param aliases: + :param conditions: + :param dry_run: If set to true the rollover action will only be validated but + not actually performed even if a condition matches. The default is false + :param mappings: + :param master_timeout: Specify timeout for connection to master + :param settings: + :param timeout: Explicit operation timeout + :param wait_for_active_shards: Set the number of active shards to wait for on + the newly created rollover index before the operation returns. + """ + if alias in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'alias'") + if alias not in SKIP_IN_PATH and new_index not in SKIP_IN_PATH: + __path = f"/{_quote(alias)}/_rollover/{_quote(new_index)}" + elif alias not in SKIP_IN_PATH: + __path = f"/{_quote(alias)}/_rollover" + else: + raise ValueError("Couldn't find a path for the given parameters") + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aliases is not None: + __body["aliases"] = aliases + if conditions is not None: + __body["conditions"] = conditions + if dry_run is not None: + __query["dry_run"] = dry_run + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if mappings is not None: + __body["mappings"] = mappings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if settings is not None: + __body["settings"] = settings + if timeout is not None: + __query["timeout"] = timeout + if wait_for_active_shards is not None: + __query["wait_for_active_shards"] = wait_for_active_shards + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + def simulate_index_template( + self, + *, + name: str, + allow_auto_create: t.Optional[bool] = None, + composed_of: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + create: t.Optional[bool] = None, + data_stream: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + index_patterns: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + priority: t.Optional[int] = None, + template: t.Optional[t.Mapping[str, t.Any]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Simulate matching the given index name against the index templates in the system + + ``_ + + :param name: Index or template name to simulate + :param allow_auto_create: + :param composed_of: + :param create: If `true`, the template passed in the body is only used if no + existing templates match the same index patterns. If `false`, the simulation + uses the template with the highest priority. Note that the template is not + permanently added or updated in either case; it is only used for the simulation. + :param data_stream: + :param include_defaults: If true, returns all relevant default configurations + for the index template. + :param index_patterns: + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param meta: + :param priority: + :param template: + :param version: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_index_template/_simulate_index/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_auto_create is not None: + __body["allow_auto_create"] = allow_auto_create + if composed_of is not None: + __body["composed_of"] = composed_of + if create is not None: + __query["create"] = create + if data_stream is not None: + __body["data_stream"] = data_stream + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if index_patterns is not None: + __body["index_patterns"] = index_patterns + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if priority is not None: + __body["priority"] = priority + if template is not None: + __body["template"] = template + if version is not None: + __body["version"] = version + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_name="template", + ) + def simulate_template( + self, + *, + name: t.Optional[str] = None, + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + include_defaults: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + template: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Simulate resolving the given template name or body + + ``_ + + :param name: Name of the index template to simulate. To test a template configuration + before you add it to the cluster, omit this parameter and specify the template + configuration in the request body. + :param create: If true, the template passed in the body is only used if no existing + templates match the same index patterns. If false, the simulation uses the + template with the highest priority. Note that the template is not permanently + added or updated in either case; it is only used for the simulation. + :param include_defaults: If true, returns all relevant default configurations + for the index template. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param template: + """ + if name not in SKIP_IN_PATH: + __path = f"/_index_template/_simulate/{_quote(name)}" + else: + __path = "/_index_template/_simulate" + __query: t.Dict[str, t.Any] = {} + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if include_defaults is not None: + __query["include_defaults"] = include_defaults + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __body = template + if not __body: + __body = None + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def update_aliases( + self, + *, + actions: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates index aliases. + + ``_ + + :param actions: + :param master_timeout: Specify timeout for connection to master + :param timeout: Request timeout + """ + __path = "/_aliases" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __body["actions"] = actions + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def validate_query( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + all_shards: t.Optional[bool] = None, + allow_no_indices: t.Optional[bool] = None, + analyze_wildcard: t.Optional[bool] = None, + analyzer: t.Optional[str] = None, + default_operator: t.Optional[t.Union["t.Literal['and', 'or']", str]] = None, + df: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + explain: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + lenient: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + rewrite: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows a user to validate a potentially expensive query without executing it. + + ``_ + + :param index: A comma-separated list of index names to restrict the operation; + use `_all` or empty string to perform the operation on all indices + :param all_shards: Execute validation on all shards instead of one random shard + per index + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param analyze_wildcard: Specify whether wildcard and prefix queries should be + analyzed (default: false) + :param analyzer: The analyzer to use for the query string + :param default_operator: The default operator for query string query (AND or + OR) + :param df: The field to use as default where no field prefix is given in the + query string + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param explain: Return detailed information about the error + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + :param lenient: Specify whether format-based query failures (such as providing + text to a numeric field) should be ignored + :param q: Query in the Lucene query string syntax + :param query: + :param rewrite: Provide a more detailed explanation showing the actual Lucene + query that will be executed. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_validate/query" + else: + __path = "/_validate/query" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if all_shards is not None: + __query["all_shards"] = all_shards + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if analyze_wildcard is not None: + __query["analyze_wildcard"] = analyze_wildcard + if analyzer is not None: + __query["analyzer"] = analyzer + if default_operator is not None: + __query["default_operator"] = default_operator + if df is not None: + __query["df"] = df + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if explain is not None: + __query["explain"] = explain + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if lenient is not None: + __query["lenient"] = lenient + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if query is not None: + __body["query"] = query + if rewrite is not None: + __query["rewrite"] = rewrite + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/ingest.py b/elasticsearch_serverless/_sync/client/ingest.py new file mode 100644 index 0000000..05b6443 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/ingest.py @@ -0,0 +1,295 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class IngestClient(NamespacedClient): + @_rewrite_parameters() + def delete_pipeline( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a pipeline. + + ``_ + + :param id: Pipeline ID + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: Explicit operation timeout + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ingest/pipeline/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_pipeline( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + summary: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a pipeline. + + ``_ + + :param id: Comma separated list of pipeline ids. Wildcards supported + :param master_timeout: Explicit operation timeout for connection to master node + :param summary: Return pipelines without their definitions (default: false) + """ + if id not in SKIP_IN_PATH: + __path = f"/_ingest/pipeline/{_quote(id)}" + else: + __path = "/_ingest/pipeline" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if summary is not None: + __query["summary"] = summary + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def processor_grok( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a list of the built-in patterns. + + ``_ + """ + __path = "/_ingest/processor/grok" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + def put_pipeline( + self, + *, + id: str, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_version: t.Optional[int] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + on_failure: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + pretty: t.Optional[bool] = None, + processors: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a pipeline. + + ``_ + + :param id: ID of the ingest pipeline to create or update. + :param description: Description of the ingest pipeline. + :param if_version: Required version for optimistic concurrency control for pipeline + updates + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param meta: Optional metadata about the ingest pipeline. May have any contents. + This map is not automatically generated by Elasticsearch. + :param on_failure: Processors to run immediately after a processor failure. Each + processor supports a processor-level `on_failure` value. If a processor without + an `on_failure` value fails, Elasticsearch uses this pipeline-level parameter + as a fallback. The processors in this parameter run sequentially in the order + specified. Elasticsearch will not attempt to run the pipeline's remaining + processors. + :param processors: Processors used to perform transformations on documents before + indexing. Processors run sequentially in the order specified. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param version: Version number used by external systems to track ingest pipelines. + This parameter is intended for external systems only. Elasticsearch does + not use or validate pipeline version numbers. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ingest/pipeline/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_version is not None: + __query["if_version"] = if_version + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if meta is not None: + __body["_meta"] = meta + if on_failure is not None: + __body["on_failure"] = on_failure + if pretty is not None: + __query["pretty"] = pretty + if processors is not None: + __body["processors"] = processors + if timeout is not None: + __query["timeout"] = timeout + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def simulate( + self, + *, + id: t.Optional[str] = None, + docs: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pipeline: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + verbose: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Allows to simulate a pipeline with example documents. + + ``_ + + :param id: Pipeline ID + :param docs: + :param pipeline: + :param verbose: Verbose mode. Display data output for each processor in executed + pipeline + """ + if id not in SKIP_IN_PATH: + __path = f"/_ingest/pipeline/{_quote(id)}/_simulate" + else: + __path = "/_ingest/pipeline/_simulate" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if docs is not None: + __body["docs"] = docs + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pipeline is not None: + __body["pipeline"] = pipeline + if pretty is not None: + __query["pretty"] = pretty + if verbose is not None: + __query["verbose"] = verbose + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/license.py b/elasticsearch_serverless/_sync/client/license.py new file mode 100644 index 0000000..9c4fd63 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/license.py @@ -0,0 +1,294 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class LicenseClient(NamespacedClient): + @_rewrite_parameters() + def delete( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes licensing information for the cluster + + ``_ + """ + __path = "/_license" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + accept_enterprise: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves licensing information for the cluster + + ``_ + + :param accept_enterprise: If `true`, this parameter returns enterprise for Enterprise + license types. If `false`, this parameter returns platinum for both platinum + and enterprise license types. This behavior is maintained for backwards compatibility. + This parameter is deprecated and will always be set to true in 8.x. + :param local: Specifies whether to retrieve local information. The default value + is `false`, which means the information is retrieved from the master node. + """ + __path = "/_license" + __query: t.Dict[str, t.Any] = {} + if accept_enterprise is not None: + __query["accept_enterprise"] = accept_enterprise + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_basic_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the status of the basic license. + + ``_ + """ + __path = "/_license/basic_status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_trial_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the status of the trial license. + + ``_ + """ + __path = "/_license/trial_status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def post( + self, + *, + acknowledge: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + license: t.Optional[t.Mapping[str, t.Any]] = None, + licenses: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the license for the cluster. + + ``_ + + :param acknowledge: Specifies whether you acknowledge the license changes. + :param license: + :param licenses: A sequence of one or more JSON documents containing the license + information. + """ + __path = "/_license" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if acknowledge is not None: + __query["acknowledge"] = acknowledge + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if license is not None: + __body["license"] = license + if licenses is not None: + __body["licenses"] = licenses + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def post_start_basic( + self, + *, + acknowledge: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts an indefinite basic license. + + ``_ + + :param acknowledge: whether the user has acknowledged acknowledge messages (default: + false) + """ + __path = "/_license/start_basic" + __query: t.Dict[str, t.Any] = {} + if acknowledge is not None: + __query["acknowledge"] = acknowledge + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def post_start_trial( + self, + *, + acknowledge: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + type_query_string: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + starts a limited time trial license. + + ``_ + + :param acknowledge: whether the user has acknowledged acknowledge messages (default: + false) + :param type_query_string: + """ + __path = "/_license/start_trial" + __query: t.Dict[str, t.Any] = {} + if acknowledge is not None: + __query["acknowledge"] = acknowledge + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if type_query_string is not None: + __query["type_query_string"] = type_query_string + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/logstash.py b/elasticsearch_serverless/_sync/client/logstash.py new file mode 100644 index 0000000..af2c146 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/logstash.py @@ -0,0 +1,143 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class LogstashClient(NamespacedClient): + @_rewrite_parameters() + def delete_pipeline( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes Logstash Pipelines used by Central Management + + ``_ + + :param id: The ID of the Pipeline + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_logstash/pipeline/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_pipeline( + self, + *, + id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves Logstash Pipelines used by Central Management + + ``_ + + :param id: A comma-separated list of Pipeline IDs + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if id not in SKIP_IN_PATH: + __path = f"/_logstash/pipeline/{_quote(id)}" + else: + __path = "/_logstash/pipeline" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="pipeline", + ) + def put_pipeline( + self, + *, + id: str, + pipeline: t.Mapping[str, t.Any], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Adds and updates Logstash Pipelines used for Central Management + + ``_ + + :param id: The ID of the Pipeline + :param pipeline: + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if pipeline is None: + raise ValueError("Empty value passed for parameter 'pipeline'") + __path = f"/_logstash/pipeline/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = pipeline + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/migration.py b/elasticsearch_serverless/_sync/client/migration.py new file mode 100644 index 0000000..2ce226c --- /dev/null +++ b/elasticsearch_serverless/_sync/client/migration.py @@ -0,0 +1,127 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class MigrationClient(NamespacedClient): + @_rewrite_parameters() + def deprecations( + self, + *, + index: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about different cluster, node, and index level settings + that use deprecated features that will be removed or changed in the next major + version. + + ``_ + + :param index: Comma-separate list of data streams or indices to check. Wildcard + (*) expressions are supported. + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_migration/deprecations" + else: + __path = "/_migration/deprecations" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_feature_upgrade_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Find out whether system features need to be upgraded or not + + ``_ + """ + __path = "/_migration/system_features" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def post_feature_upgrade( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Begin upgrades for system features + + ``_ + """ + __path = "/_migration/system_features" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/ml.py b/elasticsearch_serverless/_sync/client/ml.py new file mode 100644 index 0000000..e32c397 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/ml.py @@ -0,0 +1,3306 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class MlClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + def close_job( + self, + *, + job_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Closes one or more anomaly detection jobs. A job can be opened and closed multiple + times throughout its lifecycle. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, or a wildcard expression. You can close multiple anomaly detection + jobs in a single API request by using a group name, a comma-separated list + of jobs, or a wildcard expression. You can close all jobs by using `_all` + or by specifying `*` as the job identifier. + :param allow_no_match: Refer to the description for the `allow_no_match` query + parameter. + :param force: Refer to the descriptiion for the `force` query parameter. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_close" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __body["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def delete_calendar( + self, + *, + calendar_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_calendar_event( + self, + *, + calendar_id: str, + event_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes scheduled events from a calendar. + + ``_ + + :param calendar_id: The ID of the calendar to modify + :param event_id: The ID of the event to remove from the calendar + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if event_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'event_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/events/{_quote(event_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_calendar_job( + self, + *, + calendar_id: str, + job_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes anomaly detection jobs from a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param job_id: An identifier for the anomaly detection jobs. It can be a job + identifier, a group name, or a comma-separated list of jobs or groups. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/jobs/{_quote(job_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_data_frame_analytics( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. + :param force: If `true`, it deletes a job that is not stopped; this method is + quicker than stopping and deleting the job. + :param timeout: The time to wait for the job to be deleted. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_datafeed( + self, + *, + datafeed_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param force: Use to forcefully delete a started datafeed; this method is quicker + than stopping and deleting the datafeed. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_filter( + self, + *, + filter_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a filter. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + """ + if filter_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'filter_id'") + __path = f"/_ml/filters/{_quote(filter_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_job( + self, + *, + job_id: str, + delete_user_annotations: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing anomaly detection job. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param delete_user_annotations: Specifies whether annotations that have been + added by the user should be deleted along with any auto-generated annotations + when the job is reset. + :param force: Use to forcefully delete an opened job; this method is quicker + than closing and deleting the job. + :param wait_for_completion: Specifies whether the request should return immediately + or wait until the job deletion completes. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}" + __query: t.Dict[str, t.Any] = {} + if delete_user_annotations is not None: + __query["delete_user_annotations"] = delete_user_annotations + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_trained_model( + self, + *, + model_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing trained inference model that is currently not referenced + by an ingest pipeline. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param force: Forcefully deletes a trained model that is referenced by ingest + pipelines or has a started deployment. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_trained_model_alias( + self, + *, + model_id: str, + model_alias: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a model alias that refers to the trained model + + ``_ + + :param model_id: The trained model ID to which the model alias refers. + :param model_alias: The model alias to delete. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if model_alias in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_alias'") + __path = f"/_ml/trained_models/{_quote(model_id)}/model_aliases/{_quote(model_alias)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def estimate_model_memory( + self, + *, + analysis_config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_bucket_cardinality: t.Optional[t.Mapping[str, int]] = None, + overall_cardinality: t.Optional[t.Mapping[str, int]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Estimates the model memory + + ``_ + + :param analysis_config: For a list of the properties that you can specify in + the `analysis_config` component of the body of this API. + :param max_bucket_cardinality: Estimates of the highest cardinality in a single + bucket that is observed for influencer fields over the time period that the + job analyzes data. To produce a good answer, values must be provided for + all influencer fields. Providing values for fields that are not listed as + `influencers` has no effect on the estimation. + :param overall_cardinality: Estimates of the cardinality that is observed for + fields over the whole time period that the job analyzes data. To produce + a good answer, values must be provided for fields referenced in the `by_field_name`, + `over_field_name` and `partition_field_name` of any detectors. Providing + values for other fields has no effect on the estimation. It can be omitted + from the request if no detectors have a `by_field_name`, `over_field_name` + or `partition_field_name`. + """ + __path = "/_ml/anomaly_detectors/_estimate_model_memory" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analysis_config is not None: + __body["analysis_config"] = analysis_config + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_bucket_cardinality is not None: + __body["max_bucket_cardinality"] = max_bucket_cardinality + if overall_cardinality is not None: + __body["overall_cardinality"] = overall_cardinality + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def evaluate_data_frame( + self, + *, + evaluation: t.Mapping[str, t.Any], + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Evaluates the data frame analytics for an annotated index. + + ``_ + + :param evaluation: Defines the type of evaluation you want to perform. + :param index: Defines the `index` in which the evaluation will be performed. + :param query: A query clause that retrieves a subset of data from the source + index. + """ + if evaluation is None: + raise ValueError("Empty value passed for parameter 'evaluation'") + if index is None: + raise ValueError("Empty value passed for parameter 'index'") + __path = "/_ml/data_frame/_evaluate" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if evaluation is not None: + __body["evaluation"] = evaluation + if index is not None: + __body["index"] = index + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def flush_job( + self, + *, + job_id: str, + advance_time: t.Optional[t.Union[str, t.Any]] = None, + calc_interim: t.Optional[bool] = None, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + skip_time: t.Optional[t.Union[str, t.Any]] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Forces any buffered data to be processed by the job. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param advance_time: Refer to the description for the `advance_time` query parameter. + :param calc_interim: Refer to the description for the `calc_interim` query parameter. + :param end: Refer to the description for the `end` query parameter. + :param skip_time: Refer to the description for the `skip_time` query parameter. + :param start: Refer to the description for the `start` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_flush" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if advance_time is not None: + __body["advance_time"] = advance_time + if calc_interim is not None: + __body["calc_interim"] = calc_interim + if end is not None: + __body["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if skip_time is not None: + __body["skip_time"] = skip_time + if start is not None: + __body["start"] = start + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_calendar_events( + self, + *, + calendar_id: str, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + job_id: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the scheduled events in calendars. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. You can get + information for multiple calendars by using a comma-separated list of ids + or a wildcard expression. You can get information for all calendars by using + `_all` or `*` or by omitting the calendar identifier. + :param end: Specifies to get events with timestamps earlier than this time. + :param from_: Skips the specified number of events. + :param job_id: Specifies to get events for a specific anomaly detection job identifier + or job group. It must be used with a calendar identifier of `_all` or `*`. + :param size: Specifies the maximum number of events to obtain. + :param start: Specifies to get events with timestamps after this time. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/events" + __query: t.Dict[str, t.Any] = {} + if end is not None: + __query["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if job_id is not None: + __query["job_id"] = job_id + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if start is not None: + __query["start"] = start + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + def get_calendars( + self, + *, + calendar_id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + page: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for calendars. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. You can get + information for multiple calendars by using a comma-separated list of ids + or a wildcard expression. You can get information for all calendars by using + `_all` or `*` or by omitting the calendar identifier. + :param from_: Skips the specified number of calendars. This parameter is supported + only when you omit the calendar identifier. + :param page: This object is supported only when you omit the calendar identifier. + :param size: Specifies the maximum number of calendars to obtain. This parameter + is supported only when you omit the calendar identifier. + """ + if calendar_id not in SKIP_IN_PATH: + __path = f"/_ml/calendars/{_quote(calendar_id)}" + else: + __path = "/_ml/calendars" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if page is not None: + __body["page"] = page + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_data_frame_analytics( + self, + *, + id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for data frame analytics jobs. + + ``_ + + :param id: Identifier for the data frame analytics job. If you do not specify + this option, the API returns information for the first hundred data frame + analytics jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no data frame analytics jobs that match. 2. Contains + the `_all` string or no identifiers and there are no matches. 3. Contains + wildcard expressions and there are only partial matches. The default value + returns an empty data_frame_analytics array when there are no matches and + the subset of results when there are partial matches. If this parameter is + `false`, the request returns a 404 status code when there are no matches + or only partial matches. + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + :param from_: Skips the specified number of data frame analytics jobs. + :param size: Specifies the maximum number of data frame analytics jobs to obtain. + """ + if id not in SKIP_IN_PATH: + __path = f"/_ml/data_frame/analytics/{_quote(id)}" + else: + __path = "/_ml/data_frame/analytics" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_data_frame_analytics_stats( + self, + *, + id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + verbose: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for data frame analytics jobs. + + ``_ + + :param id: Identifier for the data frame analytics job. If you do not specify + this option, the API returns information for the first hundred data frame + analytics jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no data frame analytics jobs that match. 2. Contains + the `_all` string or no identifiers and there are no matches. 3. Contains + wildcard expressions and there are only partial matches. The default value + returns an empty data_frame_analytics array when there are no matches and + the subset of results when there are partial matches. If this parameter is + `false`, the request returns a 404 status code when there are no matches + or only partial matches. + :param from_: Skips the specified number of data frame analytics jobs. + :param size: Specifies the maximum number of data frame analytics jobs to obtain. + :param verbose: Defines whether the stats response should be verbose. + """ + if id not in SKIP_IN_PATH: + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_stats" + else: + __path = "/_ml/data_frame/analytics/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if verbose is not None: + __query["verbose"] = verbose + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_datafeed_stats( + self, + *, + datafeed_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for datafeeds. + + ``_ + + :param datafeed_id: Identifier for the datafeed. It can be a datafeed identifier + or a wildcard expression. If you do not specify one of these options, the + API returns information about all datafeeds. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no datafeeds that match. 2. Contains the `_all` + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. The default value is `true`, which returns + an empty `datafeeds` array when there are no matches and the subset of results + when there are partial matches. If this parameter is `false`, the request + returns a `404` status code when there are no matches or only partial matches. + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_stats" + else: + __path = "/_ml/datafeeds/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_datafeeds( + self, + *, + datafeed_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for datafeeds. + + ``_ + + :param datafeed_id: Identifier for the datafeed. It can be a datafeed identifier + or a wildcard expression. If you do not specify one of these options, the + API returns information about all datafeeds. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no datafeeds that match. 2. Contains the `_all` + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. The default value is `true`, which returns + an empty `datafeeds` array when there are no matches and the subset of results + when there are partial matches. If this parameter is `false`, the request + returns a `404` status code when there are no matches or only partial matches. + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}" + else: + __path = "/_ml/datafeeds" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_filters( + self, + *, + filter_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves filters. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + :param from_: Skips the specified number of filters. + :param size: Specifies the maximum number of filters to obtain. + """ + if filter_id not in SKIP_IN_PATH: + __path = f"/_ml/filters/{_quote(filter_id)}" + else: + __path = "/_ml/filters" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_job_stats( + self, + *, + job_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, a comma-separated list of jobs, or a wildcard expression. If + you do not specify one of these options, the API returns information for + all anomaly detection jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no jobs that match. 2. Contains the _all string + or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. If `true`, the API returns an empty `jobs` + array when there are no matches and the subset of results when there are + partial matches. If `false`, the API returns a `404` status code when there + are no matches or only partial matches. + """ + if job_id not in SKIP_IN_PATH: + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_stats" + else: + __path = "/_ml/anomaly_detectors/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_jobs( + self, + *, + job_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, or a wildcard expression. If you do not specify one of these + options, the API returns information for all anomaly detection jobs. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no jobs that match. 2. Contains the _all string + or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. The default value is `true`, which returns + an empty `jobs` array when there are no matches and the subset of results + when there are partial matches. If this parameter is `false`, the request + returns a `404` status code when there are no matches or only partial matches. + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + """ + if job_id not in SKIP_IN_PATH: + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}" + else: + __path = "/_ml/anomaly_detectors" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def get_overall_buckets( + self, + *, + job_id: str, + allow_no_match: t.Optional[bool] = None, + bucket_span: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + exclude_interim: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + overall_score: t.Optional[t.Union[float, str]] = None, + pretty: t.Optional[bool] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + top_n: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves overall bucket results that summarize the bucket results of multiple + anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. It can be a job identifier, + a group name, a comma-separated list of jobs or groups, or a wildcard expression. + You can summarize the bucket results for all anomaly detection jobs by using + `_all` or by specifying `*` as the ``. + :param allow_no_match: Refer to the description for the `allow_no_match` query + parameter. + :param bucket_span: Refer to the description for the `bucket_span` query parameter. + :param end: Refer to the description for the `end` query parameter. + :param exclude_interim: Refer to the description for the `exclude_interim` query + parameter. + :param overall_score: Refer to the description for the `overall_score` query + parameter. + :param start: Refer to the description for the `start` query parameter. + :param top_n: Refer to the description for the `top_n` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/results/overall_buckets" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if bucket_span is not None: + __body["bucket_span"] = bucket_span + if end is not None: + __body["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_interim is not None: + __body["exclude_interim"] = exclude_interim + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if overall_score is not None: + __body["overall_score"] = overall_score + if pretty is not None: + __query["pretty"] = pretty + if start is not None: + __body["start"] = start + if top_n is not None: + __body["top_n"] = top_n + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_trained_models( + self, + *, + model_id: t.Optional[str] = None, + allow_no_match: t.Optional[bool] = None, + decompress_definition: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + include: t.Optional[ + t.Union[ + "t.Literal['definition', 'definition_status', 'feature_importance_baseline', 'hyperparameters', 'total_feature_importance']", + str, + ] + ] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + tags: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for a trained inference model. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param allow_no_match: Specifies what to do when the request: - Contains wildcard + expressions and there are no models that match. - Contains the _all string + or no identifiers and there are no matches. - Contains wildcard expressions + and there are only partial matches. If true, it returns an empty array when + there are no matches and the subset of results when there are partial matches. + :param decompress_definition: Specifies whether the included model definition + should be returned as a JSON map (true) or in a custom compressed format + (false). + :param exclude_generated: Indicates if certain fields should be removed from + the configuration on retrieval. This allows the configuration to be in an + acceptable format to be retrieved and then added to another cluster. + :param from_: Skips the specified number of models. + :param include: A comma delimited string of optional fields to include in the + response body. + :param size: Specifies the maximum number of models to obtain. + :param tags: A comma delimited string of tags. A trained model can have many + tags, or none. When supplied, only trained models that contain all the supplied + tags are returned. + """ + if model_id not in SKIP_IN_PATH: + __path = f"/_ml/trained_models/{_quote(model_id)}" + else: + __path = "/_ml/trained_models" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if decompress_definition is not None: + __query["decompress_definition"] = decompress_definition + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if include is not None: + __query["include"] = include + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if tags is not None: + __query["tags"] = tags + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_trained_models_stats( + self, + *, + model_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for trained inference models. + + ``_ + + :param model_id: The unique identifier of the trained model or a model alias. + It can be a comma-separated list or a wildcard expression. + :param allow_no_match: Specifies what to do when the request: - Contains wildcard + expressions and there are no models that match. - Contains the _all string + or no identifiers and there are no matches. - Contains wildcard expressions + and there are only partial matches. If true, it returns an empty array when + there are no matches and the subset of results when there are partial matches. + :param from_: Skips the specified number of models. + :param size: Specifies the maximum number of models to obtain. + """ + if model_id not in SKIP_IN_PATH: + __path = f"/_ml/trained_models/{_quote(model_id)}/_stats" + else: + __path = "/_ml/trained_models/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def open_job( + self, + *, + job_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Opens one or more anomaly detection jobs. + + ``_ + + :param job_id: Identifier for the anomaly detection job. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_open" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def post_calendar_events( + self, + *, + calendar_id: str, + events: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Posts scheduled events in a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param events: A list of one of more scheduled events. The event’s start and + end times can be specified as integer milliseconds since the epoch or as + a string in ISO 8601 format. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if events is None: + raise ValueError("Empty value passed for parameter 'events'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/events" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if events is not None: + __body["events"] = events + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def preview_data_frame_analytics( + self, + *, + id: t.Optional[str] = None, + config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Previews that will be analyzed given a data frame analytics config. + + ``_ + + :param id: Identifier for the data frame analytics job. + :param config: A data frame analytics config as described in create data frame + analytics jobs. Note that `id` and `dest` don’t need to be provided in the + context of this API. + """ + if id not in SKIP_IN_PATH: + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_preview" + else: + __path = "/_ml/data_frame/analytics/_preview" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if config is not None: + __body["config"] = config + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def preview_datafeed( + self, + *, + datafeed_id: t.Optional[str] = None, + datafeed_config: t.Optional[t.Mapping[str, t.Any]] = None, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + job_config: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Previews a datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. NOTE: If you use this path parameter, you cannot provide datafeed + or anomaly detection job configuration details in the request body. + :param datafeed_config: The datafeed definition to preview. + :param end: The end time when the datafeed preview should stop + :param job_config: The configuration details for the anomaly detection job that + is associated with the datafeed. If the `datafeed_config` object does not + include a `job_id` that references an existing anomaly detection job, you + must supply this `job_config` object. If you include both a `job_id` and + a `job_config`, the latter information is used. You cannot specify a `job_config` + object unless you also supply a `datafeed_config` object. + :param start: The start time from where the datafeed preview should begin + """ + if datafeed_id not in SKIP_IN_PATH: + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_preview" + else: + __path = "/_ml/datafeeds/_preview" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if datafeed_config is not None: + __body["datafeed_config"] = datafeed_config + if end is not None: + __query["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if job_config is not None: + __body["job_config"] = job_config + if pretty is not None: + __query["pretty"] = pretty + if start is not None: + __query["start"] = start + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_calendar( + self, + *, + calendar_id: str, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + job_ids: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param description: A description of the calendar. + :param job_ids: An array of anomaly detection job identifiers. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if job_ids is not None: + __body["job_ids"] = job_ids + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def put_calendar_job( + self, + *, + calendar_id: str, + job_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Adds an anomaly detection job to a calendar. + + ``_ + + :param calendar_id: A string that uniquely identifies a calendar. + :param job_id: An identifier for the anomaly detection jobs. It can be a job + identifier, a group name, or a comma-separated list of jobs or groups. + """ + if calendar_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'calendar_id'") + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/calendars/{_quote(calendar_id)}/jobs/{_quote(job_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"headers"}, + ) + def put_data_frame_analytics( + self, + *, + id: str, + analysis: t.Mapping[str, t.Any], + dest: t.Mapping[str, t.Any], + source: t.Mapping[str, t.Any], + allow_lazy_start: t.Optional[bool] = None, + analyzed_fields: t.Optional[t.Mapping[str, t.Any]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + headers: t.Optional[ + t.Mapping[str, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + human: t.Optional[bool] = None, + max_num_threads: t.Optional[int] = None, + model_memory_limit: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + version: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param analysis: The analysis configuration, which contains the information necessary + to perform one of the following types of analysis: classification, outlier + detection, or regression. + :param dest: The destination configuration. + :param source: The configuration of how to source the analysis data. + :param allow_lazy_start: Specifies whether this job can start when there is insufficient + machine learning node capacity for it to be immediately assigned to a node. + If set to `false` and a machine learning node with capacity to run the job + cannot be immediately found, the API returns an error. If set to `true`, + the API does not return an error; the job waits in the `starting` state until + sufficient machine learning node capacity is available. This behavior is + also affected by the cluster-wide `xpack.ml.max_lazy_ml_nodes` setting. + :param analyzed_fields: Specifies `includes` and/or `excludes` patterns to select + which fields will be included in the analysis. The patterns specified in + `excludes` are applied last, therefore `excludes` takes precedence. In other + words, if the same field is specified in both `includes` and `excludes`, + then the field will not be included in the analysis. If `analyzed_fields` + is not set, only the relevant fields will be included. For example, all the + numeric fields for outlier detection. The supported fields vary for each + type of analysis. Outlier detection requires numeric or `boolean` data to + analyze. The algorithms don’t support missing values therefore fields that + have data types other than numeric or boolean are ignored. Documents where + included fields contain missing values, null values, or an array are also + ignored. Therefore the `dest` index may contain documents that don’t have + an outlier score. Regression supports fields that are numeric, `boolean`, + `text`, `keyword`, and `ip` data types. It is also tolerant of missing values. + Fields that are supported are included in the analysis, other fields are + ignored. Documents where included fields contain an array with two or more + values are also ignored. Documents in the `dest` index that don’t contain + a results field are not included in the regression analysis. Classification + supports fields that are numeric, `boolean`, `text`, `keyword`, and `ip` + data types. It is also tolerant of missing values. Fields that are supported + are included in the analysis, other fields are ignored. Documents where included + fields contain an array with two or more values are also ignored. Documents + in the `dest` index that don’t contain a results field are not included in + the classification analysis. Classification analysis can be improved by mapping + ordinal variable values to a single number. For example, in case of age ranges, + you can model the values as `0-14 = 0`, `15-24 = 1`, `25-34 = 2`, and so + on. + :param description: A description of the job. + :param headers: + :param max_num_threads: The maximum number of threads to be used by the analysis. + Using more threads may decrease the time necessary to complete the analysis + at the cost of using more CPU. Note that the process may use additional threads + for operational functionality other than the analysis itself. + :param model_memory_limit: The approximate maximum amount of memory resources + that are permitted for analytical processing. If your `elasticsearch.yml` + file contains an `xpack.ml.max_model_memory_limit` setting, an error occurs + when you try to create data frame analytics jobs that have `model_memory_limit` + values greater than that setting. + :param version: + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if analysis is None: + raise ValueError("Empty value passed for parameter 'analysis'") + if dest is None: + raise ValueError("Empty value passed for parameter 'dest'") + if source is None: + raise ValueError("Empty value passed for parameter 'source'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analysis is not None: + __body["analysis"] = analysis + if dest is not None: + __body["dest"] = dest + if source is not None: + __body["source"] = source + if allow_lazy_start is not None: + __body["allow_lazy_start"] = allow_lazy_start + if analyzed_fields is not None: + __body["analyzed_fields"] = analyzed_fields + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if headers is not None: + __body["headers"] = headers + if human is not None: + __query["human"] = human + if max_num_threads is not None: + __body["max_num_threads"] = max_num_threads + if model_memory_limit is not None: + __body["model_memory_limit"] = model_memory_limit + if pretty is not None: + __query["pretty"] = pretty + if version is not None: + __body["version"] = version + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"headers"}, + ) + def put_datafeed( + self, + *, + datafeed_id: str, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + chunking_config: t.Optional[t.Mapping[str, t.Any]] = None, + delayed_data_check_config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + headers: t.Optional[ + t.Mapping[str, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indexes: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + indices: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + indices_options: t.Optional[t.Mapping[str, t.Any]] = None, + job_id: t.Optional[str] = None, + max_empty_searches: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + query_delay: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll_size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param aggregations: If set, the datafeed performs aggregation searches. Support + for aggregations is limited and should be used only with low cardinality + data. + :param allow_no_indices: If true, wildcard indices expressions that resolve into + no concrete indices are ignored. This includes the `_all` string or when + no indices are specified. + :param chunking_config: Datafeeds might be required to search over long time + periods, for several months or years. This search is split into time chunks + in order to ensure the load on Elasticsearch is managed. Chunking configuration + controls how the size of these time chunks are calculated; it is an advanced + configuration option. + :param delayed_data_check_config: Specifies whether the datafeed checks for missing + data and the size of the window. The datafeed can optionally search over + indices that have already been read in an effort to determine whether any + data has subsequently been added to the index. If missing data is found, + it is a good indication that the `query_delay` is set too low and the data + is being indexed after the datafeed has passed that moment in time. This + check runs only on real-time datafeeds. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values. + :param frequency: The interval at which scheduled queries are made while the + datafeed runs in real time. The default value is either the bucket span for + short bucket spans, or, for longer bucket spans, a sensible fraction of the + bucket span. When `frequency` is shorter than the bucket span, interim results + for the last (partial) bucket are written then eventually overwritten by + the full bucket results. If the datafeed uses aggregations, this value must + be divisible by the interval of the date histogram aggregation. + :param headers: + :param ignore_throttled: If true, concrete, expanded, or aliased indices are + ignored when frozen. + :param ignore_unavailable: If true, unavailable indices (missing or closed) are + ignored. + :param indexes: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices_options: Specifies index expansion options that are used during + search + :param job_id: Identifier for the anomaly detection job. + :param max_empty_searches: If a real-time datafeed has never seen any data (including + during any initial training period), it automatically stops and closes the + associated job after this many real-time searches return no documents. In + other words, it stops after `frequency` times `max_empty_searches` of real-time + operation. If not set, a datafeed with no end time that sees no data remains + started until it is explicitly stopped. By default, it is not set. + :param query: The Elasticsearch query domain-specific language (DSL). This value + corresponds to the query object in an Elasticsearch search POST body. All + the options that are supported by Elasticsearch can be used, as this object + is passed verbatim to Elasticsearch. + :param query_delay: The number of seconds behind real time that data is queried. + For example, if data from 10:04 a.m. might not be searchable in Elasticsearch + until 10:06 a.m., set this property to 120 seconds. The default value is + randomly selected between `60s` and `120s`. This randomness improves the + query performance when there are multiple jobs running on the same node. + :param runtime_mappings: Specifies runtime fields for the datafeed search. + :param script_fields: Specifies scripts that evaluate custom expressions and + returns script fields to the datafeed. The detector configuration objects + in a job can contain functions that use these script fields. + :param scroll_size: The size parameter that is used in Elasticsearch searches + when the datafeed does not use aggregations. The maximum value is the value + of `index.max_result_window`, which is 10,000 by default. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aggregations is not None: + __body["aggregations"] = aggregations + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if chunking_config is not None: + __body["chunking_config"] = chunking_config + if delayed_data_check_config is not None: + __body["delayed_data_check_config"] = delayed_data_check_config + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if headers is not None: + __body["headers"] = headers + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indexes is not None: + __body["indexes"] = indexes + if indices is not None: + __body["indices"] = indices + if indices_options is not None: + __body["indices_options"] = indices_options + if job_id is not None: + __body["job_id"] = job_id + if max_empty_searches is not None: + __body["max_empty_searches"] = max_empty_searches + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if query_delay is not None: + __body["query_delay"] = query_delay + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll_size is not None: + __body["scroll_size"] = scroll_size + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_filter( + self, + *, + filter_id: str, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + items: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a filter. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + :param description: A description of the filter. + :param items: The items of the filter. A wildcard `*` can be used at the beginning + or the end of an item. Up to 10000 items are allowed in each filter. + """ + if filter_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'filter_id'") + __path = f"/_ml/filters/{_quote(filter_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if items is not None: + __body["items"] = items + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_job( + self, + *, + job_id: str, + analysis_config: t.Mapping[str, t.Any], + data_description: t.Mapping[str, t.Any], + allow_lazy_open: t.Optional[bool] = None, + analysis_limits: t.Optional[t.Mapping[str, t.Any]] = None, + background_persist_interval: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + custom_settings: t.Optional[t.Any] = None, + daily_model_snapshot_retention_after_days: t.Optional[int] = None, + datafeed_config: t.Optional[t.Mapping[str, t.Any]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + groups: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + human: t.Optional[bool] = None, + model_plot_config: t.Optional[t.Mapping[str, t.Any]] = None, + model_snapshot_retention_days: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + renormalization_window_days: t.Optional[int] = None, + results_index_name: t.Optional[str] = None, + results_retention_days: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates an anomaly detection job. + + ``_ + + :param job_id: The identifier for the anomaly detection job. This identifier + can contain lowercase alphanumeric characters (a-z and 0-9), hyphens, and + underscores. It must start and end with alphanumeric characters. + :param analysis_config: Specifies how to analyze the data. After you create a + job, you cannot change the analysis configuration; all the properties are + informational. + :param data_description: Defines the format of the input data when you send data + to the job by using the post data API. Note that when configure a datafeed, + these properties are automatically set. When data is received via the post + data API, it is not stored in Elasticsearch. Only the results for anomaly + detection are retained. + :param allow_lazy_open: Advanced configuration option. Specifies whether this + job can open when there is insufficient machine learning node capacity for + it to be immediately assigned to a node. By default, if a machine learning + node with capacity to run the job cannot immediately be found, the open anomaly + detection jobs API returns an error. However, this is also subject to the + cluster-wide `xpack.ml.max_lazy_ml_nodes` setting. If this option is set + to true, the open anomaly detection jobs API does not return an error and + the job waits in the opening state until sufficient machine learning node + capacity is available. + :param analysis_limits: Limits can be applied for the resources required to hold + the mathematical models in memory. These limits are approximate and can be + set per job. They do not control the memory used by other processes, for + example the Elasticsearch Java processes. + :param background_persist_interval: Advanced configuration option. The time between + each periodic persistence of the model. The default value is a randomized + value between 3 to 4 hours, which avoids all jobs persisting at exactly the + same time. The smallest allowed value is 1 hour. For very large models (several + GB), persistence could take 10-20 minutes, so do not set the `background_persist_interval` + value too low. + :param custom_settings: Advanced configuration option. Contains custom meta data + about the job. + :param daily_model_snapshot_retention_after_days: Advanced configuration option, + which affects the automatic removal of old model snapshots for this job. + It specifies a period of time (in days) after which only the first snapshot + per day is retained. This period is relative to the timestamp of the most + recent snapshot for this job. Valid values range from 0 to `model_snapshot_retention_days`. + :param datafeed_config: Defines a datafeed for the anomaly detection job. If + Elasticsearch security features are enabled, your datafeed remembers which + roles the user who created it had at the time of creation and runs the query + using those same roles. If you provide secondary authorization headers, those + credentials are used instead. + :param description: A description of the job. + :param groups: A list of job groups. A job can belong to no groups or many. + :param model_plot_config: This advanced configuration option stores model information + along with the results. It provides a more detailed view into anomaly detection. + If you enable model plot it can add considerable overhead to the performance + of the system; it is not feasible for jobs with many entities. Model plot + provides a simplified and indicative view of the model and its bounds. It + does not display complex features such as multivariate correlations or multimodal + data. As such, anomalies may occasionally be reported which cannot be seen + in the model plot. Model plot config can be configured when the job is created + or updated later. It must be disabled if performance issues are experienced. + :param model_snapshot_retention_days: Advanced configuration option, which affects + the automatic removal of old model snapshots for this job. It specifies the + maximum period of time (in days) that snapshots are retained. This period + is relative to the timestamp of the most recent snapshot for this job. By + default, snapshots ten days older than the newest snapshot are deleted. + :param renormalization_window_days: Advanced configuration option. The period + over which adjustments to the score are applied, as new data is seen. The + default value is the longer of 30 days or 100 bucket spans. + :param results_index_name: A text string that affects the name of the machine + learning results index. By default, the job generates an index named `.ml-anomalies-shared`. + :param results_retention_days: Advanced configuration option. The period of time + (in days) that results are retained. Age is calculated relative to the timestamp + of the latest bucket result. If this property has a non-null value, once + per day at 00:30 (server time), results that are the specified number of + days older than the latest bucket result are deleted from Elasticsearch. + The default value is null, which means all results are retained. Annotations + generated by the system also count as results for retention purposes; they + are deleted after the same number of days as results. Annotations added by + users are retained forever. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + if analysis_config is None: + raise ValueError("Empty value passed for parameter 'analysis_config'") + if data_description is None: + raise ValueError("Empty value passed for parameter 'data_description'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if analysis_config is not None: + __body["analysis_config"] = analysis_config + if data_description is not None: + __body["data_description"] = data_description + if allow_lazy_open is not None: + __body["allow_lazy_open"] = allow_lazy_open + if analysis_limits is not None: + __body["analysis_limits"] = analysis_limits + if background_persist_interval is not None: + __body["background_persist_interval"] = background_persist_interval + if custom_settings is not None: + __body["custom_settings"] = custom_settings + if daily_model_snapshot_retention_after_days is not None: + __body[ + "daily_model_snapshot_retention_after_days" + ] = daily_model_snapshot_retention_after_days + if datafeed_config is not None: + __body["datafeed_config"] = datafeed_config + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if groups is not None: + __body["groups"] = groups + if human is not None: + __query["human"] = human + if model_plot_config is not None: + __body["model_plot_config"] = model_plot_config + if model_snapshot_retention_days is not None: + __body["model_snapshot_retention_days"] = model_snapshot_retention_days + if pretty is not None: + __query["pretty"] = pretty + if renormalization_window_days is not None: + __body["renormalization_window_days"] = renormalization_window_days + if results_index_name is not None: + __body["results_index_name"] = results_index_name + if results_retention_days is not None: + __body["results_retention_days"] = results_retention_days + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_trained_model( + self, + *, + model_id: str, + compressed_definition: t.Optional[str] = None, + defer_definition_decompression: t.Optional[bool] = None, + definition: t.Optional[t.Mapping[str, t.Any]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + inference_config: t.Optional[t.Mapping[str, t.Any]] = None, + input: t.Optional[t.Mapping[str, t.Any]] = None, + metadata: t.Optional[t.Any] = None, + model_size_bytes: t.Optional[int] = None, + model_type: t.Optional[ + t.Union["t.Literal['lang_ident', 'pytorch', 'tree_ensemble']", str] + ] = None, + pretty: t.Optional[bool] = None, + tags: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates an inference trained model. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param compressed_definition: The compressed (GZipped and Base64 encoded) inference + definition of the model. If compressed_definition is specified, then definition + cannot be specified. + :param defer_definition_decompression: If set to `true` and a `compressed_definition` + is provided, the request defers definition decompression and skips relevant + validations. + :param definition: The inference definition for the model. If definition is specified, + then compressed_definition cannot be specified. + :param description: A human-readable description of the inference trained model. + :param inference_config: The default configuration for inference. This can be + either a regression or classification configuration. It must match the underlying + definition.trained_model's target_type. For pre-packaged models such as ELSER + the config is not required. + :param input: The input field names for the model definition. + :param metadata: An object map that contains metadata about the model. + :param model_size_bytes: The estimated memory usage in bytes to keep the trained + model in memory. This property is supported only if defer_definition_decompression + is true or the model definition is not supplied. + :param model_type: The model type. + :param tags: An array of tags to organize the model. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if compressed_definition is not None: + __body["compressed_definition"] = compressed_definition + if defer_definition_decompression is not None: + __query["defer_definition_decompression"] = defer_definition_decompression + if definition is not None: + __body["definition"] = definition + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if inference_config is not None: + __body["inference_config"] = inference_config + if input is not None: + __body["input"] = input + if metadata is not None: + __body["metadata"] = metadata + if model_size_bytes is not None: + __body["model_size_bytes"] = model_size_bytes + if model_type is not None: + __body["model_type"] = model_type + if pretty is not None: + __query["pretty"] = pretty + if tags is not None: + __body["tags"] = tags + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def put_trained_model_alias( + self, + *, + model_id: str, + model_alias: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + reassign: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new model alias (or reassigns an existing one) to refer to the trained + model + + ``_ + + :param model_id: The identifier for the trained model that the alias refers to. + :param model_alias: The alias to create or update. This value cannot end in numbers. + :param reassign: Specifies whether the alias gets reassigned to the specified + trained model if it is already assigned to a different model. If the alias + is already assigned and this parameter is false, the API returns an error. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if model_alias in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_alias'") + __path = f"/_ml/trained_models/{_quote(model_id)}/model_aliases/{_quote(model_alias)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if reassign is not None: + __query["reassign"] = reassign + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_trained_model_definition_part( + self, + *, + model_id: str, + part: int, + definition: str, + total_definition_length: int, + total_parts: int, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates part of a trained model definition + + ``_ + + :param model_id: The unique identifier of the trained model. + :param part: The definition part number. When the definition is loaded for inference + the definition parts are streamed in the order of their part number. The + first part must be `0` and the final part must be `total_parts - 1`. + :param definition: The definition part for the model. Must be a base64 encoded + string. + :param total_definition_length: The total uncompressed definition length in bytes. + Not base64 encoded. + :param total_parts: The total number of parts that will be uploaded. Must be + greater than 0. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if part in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'part'") + if definition is None: + raise ValueError("Empty value passed for parameter 'definition'") + if total_definition_length is None: + raise ValueError( + "Empty value passed for parameter 'total_definition_length'" + ) + if total_parts is None: + raise ValueError("Empty value passed for parameter 'total_parts'") + __path = f"/_ml/trained_models/{_quote(model_id)}/definition/{_quote(part)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if definition is not None: + __body["definition"] = definition + if total_definition_length is not None: + __body["total_definition_length"] = total_definition_length + if total_parts is not None: + __body["total_parts"] = total_parts + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_trained_model_vocabulary( + self, + *, + model_id: str, + vocabulary: t.Union[t.List[str], t.Tuple[str, ...]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + merges: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a trained model vocabulary + + ``_ + + :param model_id: The unique identifier of the trained model. + :param vocabulary: The model vocabulary, which must not be empty. + :param merges: The optional model merges if required by the tokenizer. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + if vocabulary is None: + raise ValueError("Empty value passed for parameter 'vocabulary'") + __path = f"/_ml/trained_models/{_quote(model_id)}/vocabulary" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if vocabulary is not None: + __body["vocabulary"] = vocabulary + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if merges is not None: + __body["merges"] = merges + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def reset_job( + self, + *, + job_id: str, + delete_user_annotations: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resets an existing anomaly detection job. + + ``_ + + :param job_id: The ID of the job to reset. + :param delete_user_annotations: Specifies whether annotations that have been + added by the user should be deleted along with any auto-generated annotations + when the job is reset. + :param wait_for_completion: Should this request wait until the operation has + completed before returning. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_reset" + __query: t.Dict[str, t.Any] = {} + if delete_user_annotations is not None: + __query["delete_user_annotations"] = delete_user_annotations + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def start_data_frame_analytics( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts a data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param timeout: Controls the amount of time to wait until the data frame analytics + job starts. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def start_datafeed( + self, + *, + datafeed_id: str, + end: t.Optional[t.Union[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + start: t.Optional[t.Union[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts one or more datafeeds. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param end: Refer to the description for the `end` query parameter. + :param start: Refer to the description for the `start` query parameter. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_start" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if end is not None: + __body["end"] = end + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if start is not None: + __body["start"] = start + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def start_trained_model_deployment( + self, + *, + model_id: str, + cache_size: t.Optional[t.Union[int, str]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + number_of_allocations: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + priority: t.Optional[t.Union["t.Literal['low', 'normal']", str]] = None, + queue_capacity: t.Optional[int] = None, + threads_per_allocation: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for: t.Optional[ + t.Union["t.Literal['fully_allocated', 'started', 'starting']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Start a trained model deployment. + + ``_ + + :param model_id: The unique identifier of the trained model. Currently, only + PyTorch models are supported. + :param cache_size: The inference cache size (in memory outside the JVM heap) + per node for the model. The default value is the same size as the `model_size_bytes`. + To disable the cache, `0b` can be provided. + :param number_of_allocations: The number of model allocations on each node where + the model is deployed. All allocations on a node share the same copy of the + model in memory but use a separate set of threads to evaluate the model. + Increasing this value generally increases the throughput. If this setting + is greater than the number of hardware threads it will automatically be changed + to a value less than the number of hardware threads. + :param priority: The deployment priority. + :param queue_capacity: Specifies the number of inference requests that are allowed + in the queue. After the number of requests exceeds this value, new requests + are rejected with a 429 error. + :param threads_per_allocation: Sets the number of threads used by each model + allocation during inference. This generally increases the inference speed. + The inference process is a compute-bound process; any number greater than + the number of available hardware threads on the machine does not increase + the inference speed. If this setting is greater than the number of hardware + threads it will automatically be changed to a value less than the number + of hardware threads. + :param timeout: Specifies the amount of time to wait for the model to deploy. + :param wait_for: Specifies the allocation status to wait for before returning. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}/deployment/_start" + __query: t.Dict[str, t.Any] = {} + if cache_size is not None: + __query["cache_size"] = cache_size + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if number_of_allocations is not None: + __query["number_of_allocations"] = number_of_allocations + if pretty is not None: + __query["pretty"] = pretty + if priority is not None: + __query["priority"] = priority + if queue_capacity is not None: + __query["queue_capacity"] = queue_capacity + if threads_per_allocation is not None: + __query["threads_per_allocation"] = threads_per_allocation + if timeout is not None: + __query["timeout"] = timeout + if wait_for is not None: + __query["wait_for"] = wait_for + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stop_data_frame_analytics( + self, + *, + id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops one or more data frame analytics jobs. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no data frame analytics jobs that match. 2. Contains + the _all string or no identifiers and there are no matches. 3. Contains wildcard + expressions and there are only partial matches. The default value is true, + which returns an empty data_frame_analytics array when there are no matches + and the subset of results when there are partial matches. If this parameter + is false, the request returns a 404 status code when there are no matches + or only partial matches. + :param force: If true, the data frame analytics job is stopped forcefully. + :param timeout: Controls the amount of time to wait until the data frame analytics + job stops. Defaults to 20 seconds. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_stop" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def stop_datafeed( + self, + *, + datafeed_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops one or more datafeeds. + + ``_ + + :param datafeed_id: Identifier for the datafeed. You can stop multiple datafeeds + in a single API request by using a comma-separated list of datafeeds or a + wildcard expression. You can close all datafeeds by using `_all` or by specifying + `*` as the identifier. + :param allow_no_match: Refer to the description for the `allow_no_match` query + parameter. + :param force: Refer to the description for the `force` query parameter. + :param timeout: Refer to the description for the `timeout` query parameter. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_stop" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __body["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def stop_trained_model_deployment( + self, + *, + model_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stop a trained model deployment. + + ``_ + + :param model_id: The unique identifier of the trained model. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no deployments that match; contains the `_all` + string or no identifiers and there are no matches; or contains wildcard expressions + and there are only partial matches. By default, it returns an empty array + when there are no matches and the subset of results when there are partial + matches. If `false`, the request returns a 404 status code when there are + no matches or only partial matches. + :param force: Forcefully stops the deployment, even if it is used by ingest pipelines. + You can't use these pipelines until you restart the model deployment. + """ + if model_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'model_id'") + __path = f"/_ml/trained_models/{_quote(model_id)}/deployment/_stop" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def update_data_frame_analytics( + self, + *, + id: str, + allow_lazy_start: t.Optional[bool] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + max_num_threads: t.Optional[int] = None, + model_memory_limit: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of a data frame analytics job. + + ``_ + + :param id: Identifier for the data frame analytics job. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It must start and end with alphanumeric characters. + :param allow_lazy_start: Specifies whether this job can start when there is insufficient + machine learning node capacity for it to be immediately assigned to a node. + :param description: A description of the job. + :param max_num_threads: The maximum number of threads to be used by the analysis. + Using more threads may decrease the time necessary to complete the analysis + at the cost of using more CPU. Note that the process may use additional threads + for operational functionality other than the analysis itself. + :param model_memory_limit: The approximate maximum amount of memory resources + that are permitted for analytical processing. If your `elasticsearch.yml` + file contains an `xpack.ml.max_model_memory_limit` setting, an error occurs + when you try to create data frame analytics jobs that have `model_memory_limit` + values greater than that setting. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_ml/data_frame/analytics/{_quote(id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_lazy_start is not None: + __body["allow_lazy_start"] = allow_lazy_start + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if max_num_threads is not None: + __body["max_num_threads"] = max_num_threads + if model_memory_limit is not None: + __body["model_memory_limit"] = model_memory_limit + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def update_datafeed( + self, + *, + datafeed_id: str, + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + allow_no_indices: t.Optional[bool] = None, + chunking_config: t.Optional[t.Mapping[str, t.Any]] = None, + delayed_data_check_config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + indexes: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + indices: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + indices_options: t.Optional[t.Mapping[str, t.Any]] = None, + job_id: t.Optional[str] = None, + max_empty_searches: t.Optional[int] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + query_delay: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + script_fields: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + scroll_size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of a datafeed. + + ``_ + + :param datafeed_id: A numerical character string that uniquely identifies the + datafeed. This identifier can contain lowercase alphanumeric characters (a-z + and 0-9), hyphens, and underscores. It must start and end with alphanumeric + characters. + :param aggregations: If set, the datafeed performs aggregation searches. Support + for aggregations is limited and should be used only with low cardinality + data. + :param allow_no_indices: If `true`, wildcard indices expressions that resolve + into no concrete indices are ignored. This includes the `_all` string or + when no indices are specified. + :param chunking_config: Datafeeds might search over long time periods, for several + months or years. This search is split into time chunks in order to ensure + the load on Elasticsearch is managed. Chunking configuration controls how + the size of these time chunks are calculated; it is an advanced configuration + option. + :param delayed_data_check_config: Specifies whether the datafeed checks for missing + data and the size of the window. The datafeed can optionally search over + indices that have already been read in an effort to determine whether any + data has subsequently been added to the index. If missing data is found, + it is a good indication that the `query_delay` is set too low and the data + is being indexed after the datafeed has passed that moment in time. This + check runs only on real-time datafeeds. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values. Valid + values are: * `all`: Match any data stream or index, including hidden ones. + * `closed`: Match closed, non-hidden indices. Also matches any non-hidden + data stream. Data streams cannot be closed. * `hidden`: Match hidden data + streams and hidden indices. Must be combined with `open`, `closed`, or both. + * `none`: Wildcard patterns are not accepted. * `open`: Match open, non-hidden + indices. Also matches any non-hidden data stream. + :param frequency: The interval at which scheduled queries are made while the + datafeed runs in real time. The default value is either the bucket span for + short bucket spans, or, for longer bucket spans, a sensible fraction of the + bucket span. When `frequency` is shorter than the bucket span, interim results + for the last (partial) bucket are written then eventually overwritten by + the full bucket results. If the datafeed uses aggregations, this value must + be divisible by the interval of the date histogram aggregation. + :param ignore_throttled: If `true`, concrete, expanded or aliased indices are + ignored when frozen. + :param ignore_unavailable: If `true`, unavailable indices (missing or closed) + are ignored. + :param indexes: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices: An array of index names. Wildcards are supported. If any of the + indices are in remote clusters, the machine learning nodes must have the + `remote_cluster_client` role. + :param indices_options: Specifies index expansion options that are used during + search. + :param job_id: + :param max_empty_searches: If a real-time datafeed has never seen any data (including + during any initial training period), it automatically stops and closes the + associated job after this many real-time searches return no documents. In + other words, it stops after `frequency` times `max_empty_searches` of real-time + operation. If not set, a datafeed with no end time that sees no data remains + started until it is explicitly stopped. By default, it is not set. + :param query: The Elasticsearch query domain-specific language (DSL). This value + corresponds to the query object in an Elasticsearch search POST body. All + the options that are supported by Elasticsearch can be used, as this object + is passed verbatim to Elasticsearch. Note that if you change the query, the + analyzed data is also changed. Therefore, the time required to learn might + be long and the understandability of the results is unpredictable. If you + want to make significant changes to the source data, it is recommended that + you clone the job and datafeed and make the amendments in the clone. Let + both run in parallel and close one when you are satisfied with the results + of the job. + :param query_delay: The number of seconds behind real time that data is queried. + For example, if data from 10:04 a.m. might not be searchable in Elasticsearch + until 10:06 a.m., set this property to 120 seconds. The default value is + randomly selected between `60s` and `120s`. This randomness improves the + query performance when there are multiple jobs running on the same node. + :param runtime_mappings: Specifies runtime fields for the datafeed search. + :param script_fields: Specifies scripts that evaluate custom expressions and + returns script fields to the datafeed. The detector configuration objects + in a job can contain functions that use these script fields. + :param scroll_size: The size parameter that is used in Elasticsearch searches + when the datafeed does not use aggregations. The maximum value is the value + of `index.max_result_window`. + """ + if datafeed_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'datafeed_id'") + __path = f"/_ml/datafeeds/{_quote(datafeed_id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aggregations is not None: + __body["aggregations"] = aggregations + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if chunking_config is not None: + __body["chunking_config"] = chunking_config + if delayed_data_check_config is not None: + __body["delayed_data_check_config"] = delayed_data_check_config + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if indexes is not None: + __body["indexes"] = indexes + if indices is not None: + __body["indices"] = indices + if indices_options is not None: + __body["indices_options"] = indices_options + if job_id is not None: + __body["job_id"] = job_id + if max_empty_searches is not None: + __body["max_empty_searches"] = max_empty_searches + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if query_delay is not None: + __body["query_delay"] = query_delay + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if script_fields is not None: + __body["script_fields"] = script_fields + if scroll_size is not None: + __body["scroll_size"] = scroll_size + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def update_filter( + self, + *, + filter_id: str, + add_items: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + remove_items: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates the description of a filter, adds items, or removes items. + + ``_ + + :param filter_id: A string that uniquely identifies a filter. + :param add_items: The items to add to the filter. + :param description: A description for the filter. + :param remove_items: The items to remove from the filter. + """ + if filter_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'filter_id'") + __path = f"/_ml/filters/{_quote(filter_id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if add_items is not None: + __body["add_items"] = add_items + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if remove_items is not None: + __body["remove_items"] = remove_items + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def update_job( + self, + *, + job_id: str, + allow_lazy_open: t.Optional[bool] = None, + analysis_limits: t.Optional[t.Mapping[str, t.Any]] = None, + background_persist_interval: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + categorization_filters: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + custom_settings: t.Optional[t.Mapping[str, t.Any]] = None, + daily_model_snapshot_retention_after_days: t.Optional[int] = None, + description: t.Optional[str] = None, + detectors: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + groups: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + human: t.Optional[bool] = None, + model_plot_config: t.Optional[t.Mapping[str, t.Any]] = None, + model_prune_window: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + model_snapshot_retention_days: t.Optional[int] = None, + per_partition_categorization: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + renormalization_window_days: t.Optional[int] = None, + results_retention_days: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of an anomaly detection job. + + ``_ + + :param job_id: Identifier for the job. + :param allow_lazy_open: Advanced configuration option. Specifies whether this + job can open when there is insufficient machine learning node capacity for + it to be immediately assigned to a node. If `false` and a machine learning + node with capacity to run the job cannot immediately be found, the open anomaly + detection jobs API returns an error. However, this is also subject to the + cluster-wide `xpack.ml.max_lazy_ml_nodes` setting. If this option is set + to `true`, the open anomaly detection jobs API does not return an error and + the job waits in the opening state until sufficient machine learning node + capacity is available. + :param analysis_limits: + :param background_persist_interval: Advanced configuration option. The time between + each periodic persistence of the model. The default value is a randomized + value between 3 to 4 hours, which avoids all jobs persisting at exactly the + same time. The smallest allowed value is 1 hour. For very large models (several + GB), persistence could take 10-20 minutes, so do not set the value too low. + If the job is open when you make the update, you must stop the datafeed, + close the job, then reopen the job and restart the datafeed for the changes + to take effect. + :param categorization_filters: + :param custom_settings: Advanced configuration option. Contains custom meta data + about the job. For example, it can contain custom URL information as shown + in Adding custom URLs to machine learning results. + :param daily_model_snapshot_retention_after_days: Advanced configuration option, + which affects the automatic removal of old model snapshots for this job. + It specifies a period of time (in days) after which only the first snapshot + per day is retained. This period is relative to the timestamp of the most + recent snapshot for this job. Valid values range from 0 to `model_snapshot_retention_days`. + For jobs created before version 7.8.0, the default value matches `model_snapshot_retention_days`. + :param description: A description of the job. + :param detectors: An array of detector update objects. + :param groups: A list of job groups. A job can belong to no groups or many. + :param model_plot_config: + :param model_prune_window: + :param model_snapshot_retention_days: Advanced configuration option, which affects + the automatic removal of old model snapshots for this job. It specifies the + maximum period of time (in days) that snapshots are retained. This period + is relative to the timestamp of the most recent snapshot for this job. + :param per_partition_categorization: Settings related to how categorization interacts + with partition fields. + :param renormalization_window_days: Advanced configuration option. The period + over which adjustments to the score are applied, as new data is seen. + :param results_retention_days: Advanced configuration option. The period of time + (in days) that results are retained. Age is calculated relative to the timestamp + of the latest bucket result. If this property has a non-null value, once + per day at 00:30 (server time), results that are the specified number of + days older than the latest bucket result are deleted from Elasticsearch. + The default value is null, which means all results are retained. + """ + if job_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'job_id'") + __path = f"/_ml/anomaly_detectors/{_quote(job_id)}/_update" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if allow_lazy_open is not None: + __body["allow_lazy_open"] = allow_lazy_open + if analysis_limits is not None: + __body["analysis_limits"] = analysis_limits + if background_persist_interval is not None: + __body["background_persist_interval"] = background_persist_interval + if categorization_filters is not None: + __body["categorization_filters"] = categorization_filters + if custom_settings is not None: + __body["custom_settings"] = custom_settings + if daily_model_snapshot_retention_after_days is not None: + __body[ + "daily_model_snapshot_retention_after_days" + ] = daily_model_snapshot_retention_after_days + if description is not None: + __body["description"] = description + if detectors is not None: + __body["detectors"] = detectors + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if groups is not None: + __body["groups"] = groups + if human is not None: + __query["human"] = human + if model_plot_config is not None: + __body["model_plot_config"] = model_plot_config + if model_prune_window is not None: + __body["model_prune_window"] = model_prune_window + if model_snapshot_retention_days is not None: + __body["model_snapshot_retention_days"] = model_snapshot_retention_days + if per_partition_categorization is not None: + __body["per_partition_categorization"] = per_partition_categorization + if pretty is not None: + __query["pretty"] = pretty + if renormalization_window_days is not None: + __body["renormalization_window_days"] = renormalization_window_days + if results_retention_days is not None: + __body["results_retention_days"] = results_retention_days + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/monitoring.py b/elasticsearch_serverless/_sync/client/monitoring.py new file mode 100644 index 0000000..1b65a06 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/monitoring.py @@ -0,0 +1,87 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class MonitoringClient(NamespacedClient): + @_rewrite_parameters( + body_name="operations", + ) + def bulk( + self, + *, + interval: t.Union["t.Literal[-1]", "t.Literal[0]", str], + operations: t.Union[ + t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...] + ], + system_api_version: str, + system_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Used by the monitoring features to send monitoring data. + + ``_ + + :param interval: Collection interval (e.g., '10s' or '10000ms') of the payload + :param operations: + :param system_api_version: + :param system_id: Identifier of the monitored system + """ + if interval is None: + raise ValueError("Empty value passed for parameter 'interval'") + if operations is None: + raise ValueError("Empty value passed for parameter 'operations'") + if system_api_version is None: + raise ValueError("Empty value passed for parameter 'system_api_version'") + if system_id is None: + raise ValueError("Empty value passed for parameter 'system_id'") + __path = "/_monitoring/bulk" + __query: t.Dict[str, t.Any] = {} + if interval is not None: + __query["interval"] = interval + if system_api_version is not None: + __query["system_api_version"] = system_api_version + if system_id is not None: + __query["system_id"] = system_id + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = operations + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/nodes.py b/elasticsearch_serverless/_sync/client/nodes.py new file mode 100644 index 0000000..e38240f --- /dev/null +++ b/elasticsearch_serverless/_sync/client/nodes.py @@ -0,0 +1,483 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse, TextApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class NodesClient(NamespacedClient): + @_rewrite_parameters() + def clear_repositories_metering_archive( + self, + *, + node_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + max_archive_version: int, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes the archived repositories metering information present in the cluster. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. All the nodes selective options are explained [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes). + :param max_archive_version: Specifies the maximum [archive_version](https://www.elastic.co/guide/en/elasticsearch/reference/current/get-repositories-metering-api.html#get-repositories-metering-api-response-body) + to be cleared from the archive. + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + if max_archive_version in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'max_archive_version'") + __path = f"/_nodes/{_quote(node_id)}/_repositories_metering/{_quote(max_archive_version)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_repositories_metering_info( + self, + *, + node_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns cluster repositories metering information. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. All the nodes selective options are explained [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes). + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + __path = f"/_nodes/{_quote(node_id)}/_repositories_metering" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def hot_threads( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_idle_threads: t.Optional[bool] = None, + interval: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + snapshots: t.Optional[int] = None, + sort: t.Optional[ + t.Union["t.Literal['block', 'cpu', 'gpu', 'mem', 'wait']", str] + ] = None, + threads: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + type: t.Optional[ + t.Union["t.Literal['block', 'cpu', 'gpu', 'mem', 'wait']", str] + ] = None, + ) -> TextApiResponse: + """ + Returns information about hot threads on each node in the cluster. + + ``_ + + :param node_id: List of node IDs or names used to limit returned information. + :param ignore_idle_threads: If true, known idle threads (e.g. waiting in a socket + select, or to get a task from an empty queue) are filtered out. + :param interval: The interval to do the second sampling of threads. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param snapshots: Number of samples of thread stacktrace. + :param sort: The sort order for 'cpu' type (default: total) + :param threads: Specifies the number of hot threads to provide information for. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param type: The type to sample. + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/hot_threads" + else: + __path = "/_nodes/hot_threads" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_idle_threads is not None: + __query["ignore_idle_threads"] = ignore_idle_threads + if interval is not None: + __query["interval"] = interval + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if snapshots is not None: + __query["snapshots"] = snapshots + if sort is not None: + __query["sort"] = sort + if threads is not None: + __query["threads"] = threads + if timeout is not None: + __query["timeout"] = timeout + if type is not None: + __query["type"] = type + __headers = {"accept": "text/plain"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def info( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + flat_settings: t.Optional[bool] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about nodes in the cluster. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. + :param metric: Limits the information returned to the specific metrics. Supports + a comma-separated list, such as http,ingest. + :param flat_settings: If true, returns settings in flat format. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/{_quote(metric)}" + elif node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}" + elif metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(metric)}" + else: + __path = "/_nodes" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if flat_settings is not None: + __query["flat_settings"] = flat_settings + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def reload_secure_settings( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + secure_settings_password: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Reloads secure settings. + + ``_ + + :param node_id: A comma-separated list of node IDs to span the reload/reinit + call. Should stay empty because reloading usually involves all cluster nodes. + :param secure_settings_password: + :param timeout: Explicit operation timeout + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/reload_secure_settings" + else: + __path = "/_nodes/reload_secure_settings" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if secure_settings_password is not None: + __body["secure_settings_password"] = secure_settings_password + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def stats( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + index_metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + completion_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + fielddata_fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + fields: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + groups: t.Optional[bool] = None, + human: t.Optional[bool] = None, + include_segment_file_sizes: t.Optional[bool] = None, + include_unloaded_segments: t.Optional[bool] = None, + level: t.Optional[ + t.Union["t.Literal['cluster', 'indices', 'shards']", str] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + types: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns statistical information about nodes in the cluster. + + ``_ + + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. + :param metric: Limit the information returned to the specified metrics + :param index_metric: Limit the information returned for indices metric to the + specific index metrics. It can be used only if indices (or all) metric is + specified. + :param completion_fields: Comma-separated list or wildcard expressions of fields + to include in fielddata and suggest statistics. + :param fielddata_fields: Comma-separated list or wildcard expressions of fields + to include in fielddata statistics. + :param fields: Comma-separated list or wildcard expressions of fields to include + in the statistics. + :param groups: Comma-separated list of search groups to include in the search + statistics. + :param include_segment_file_sizes: If true, the call reports the aggregated disk + usage of each one of the Lucene index files (only applies if segment stats + are requested). + :param include_unloaded_segments: If set to true segment stats will include stats + for segments that are not currently loaded into memory + :param level: Indicates whether statistics are aggregated at the cluster, index, + or shard level. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param types: A comma-separated list of document types for the indexing index + metric. + """ + if ( + node_id not in SKIP_IN_PATH + and metric not in SKIP_IN_PATH + and index_metric not in SKIP_IN_PATH + ): + __path = f"/_nodes/{_quote(node_id)}/stats/{_quote(metric)}/{_quote(index_metric)}" + elif node_id not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/stats/{_quote(metric)}" + elif metric not in SKIP_IN_PATH and index_metric not in SKIP_IN_PATH: + __path = f"/_nodes/stats/{_quote(metric)}/{_quote(index_metric)}" + elif node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/stats" + elif metric not in SKIP_IN_PATH: + __path = f"/_nodes/stats/{_quote(metric)}" + else: + __path = "/_nodes/stats" + __query: t.Dict[str, t.Any] = {} + if completion_fields is not None: + __query["completion_fields"] = completion_fields + if error_trace is not None: + __query["error_trace"] = error_trace + if fielddata_fields is not None: + __query["fielddata_fields"] = fielddata_fields + if fields is not None: + __query["fields"] = fields + if filter_path is not None: + __query["filter_path"] = filter_path + if groups is not None: + __query["groups"] = groups + if human is not None: + __query["human"] = human + if include_segment_file_sizes is not None: + __query["include_segment_file_sizes"] = include_segment_file_sizes + if include_unloaded_segments is not None: + __query["include_unloaded_segments"] = include_unloaded_segments + if level is not None: + __query["level"] = level + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if types is not None: + __query["types"] = types + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def usage( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + metric: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns low-level information about REST actions usage on nodes. + + ``_ + + :param node_id: A comma-separated list of node IDs or names to limit the returned + information; use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes + :param metric: Limit the information returned to the specified metrics + :param timeout: Explicit operation timeout + """ + if node_id not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/usage/{_quote(metric)}" + elif node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/usage" + elif metric not in SKIP_IN_PATH: + __path = f"/_nodes/usage/{_quote(metric)}" + else: + __path = "/_nodes/usage" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/rollup.py b/elasticsearch_serverless/_sync/client/rollup.py new file mode 100644 index 0000000..c54e0c0 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/rollup.py @@ -0,0 +1,440 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class RollupClient(NamespacedClient): + @_rewrite_parameters() + def delete_job( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing rollup job. + + ``_ + + :param id: The ID of the job to delete + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_rollup/job/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_jobs( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the configuration, stats, and status of rollup jobs. + + ``_ + + :param id: The ID of the job(s) to fetch. Accepts glob patterns, or left blank + for all jobs + """ + if id not in SKIP_IN_PATH: + __path = f"/_rollup/job/{_quote(id)}" + else: + __path = "/_rollup/job" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_rollup_caps( + self, + *, + id: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the capabilities of any rollup jobs that have been configured for a specific + index or index pattern. + + ``_ + + :param id: The ID of the index to check rollup capabilities on, or left blank + for all jobs + """ + if id not in SKIP_IN_PATH: + __path = f"/_rollup/data/{_quote(id)}" + else: + __path = "/_rollup/data" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_rollup_index_caps( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the rollup capabilities of all jobs inside of a rollup index (e.g. the + index where rollup data is stored). + + ``_ + + :param index: The rollup index or index pattern to obtain rollup capabilities + from. + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_rollup/data" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"headers"}, + ) + def put_job( + self, + *, + id: str, + cron: str, + groups: t.Mapping[str, t.Any], + index_pattern: str, + page_size: int, + rollup_index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + headers: t.Optional[ + t.Mapping[str, t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] + ] = None, + human: t.Optional[bool] = None, + metrics: t.Optional[ + t.Union[t.List[t.Mapping[str, t.Any]], t.Tuple[t.Mapping[str, t.Any], ...]] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a rollup job. + + ``_ + + :param id: Identifier for the rollup job. This can be any alphanumeric string + and uniquely identifies the data that is associated with the rollup job. + The ID is persistent; it is stored with the rolled up data. If you create + a job, let it run for a while, then delete the job, the data that the job + rolled up is still be associated with this job ID. You cannot create a new + job with the same ID since that could lead to problems with mismatched job + configurations. + :param cron: A cron string which defines the intervals when the rollup job should + be executed. When the interval triggers, the indexer attempts to rollup the + data in the index pattern. The cron pattern is unrelated to the time interval + of the data being rolled up. For example, you may wish to create hourly rollups + of your document but to only run the indexer on a daily basis at midnight, + as defined by the cron. The cron pattern is defined just like a Watcher cron + schedule. + :param groups: Defines the grouping fields and aggregations that are defined + for this rollup job. These fields will then be available later for aggregating + into buckets. These aggs and fields can be used in any combination. Think + of the groups configuration as defining a set of tools that can later be + used in aggregations to partition the data. Unlike raw data, we have to think + ahead to which fields and aggregations might be used. Rollups provide enough + flexibility that you simply need to determine which fields are needed, not + in what order they are needed. + :param index_pattern: The index or index pattern to roll up. Supports wildcard-style + patterns (`logstash-*`). The job attempts to rollup the entire index or index-pattern. + :param page_size: The number of bucket results that are processed on each iteration + of the rollup indexer. A larger value tends to execute faster, but requires + more memory during processing. This value has no effect on how the data is + rolled up; it is merely used for tweaking the speed or memory cost of the + indexer. + :param rollup_index: The index that contains the rollup results. The index can + be shared with other rollup jobs. The data is stored so that it doesn’t interfere + with unrelated jobs. + :param headers: + :param metrics: Defines the metrics to collect for each grouping tuple. By default, + only the doc_counts are collected for each group. To make rollup useful, + you will often add metrics like averages, mins, maxes, etc. Metrics are defined + on a per-field basis and for each field you configure which metric should + be collected. + :param timeout: Time to wait for the request to complete. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + if cron is None: + raise ValueError("Empty value passed for parameter 'cron'") + if groups is None: + raise ValueError("Empty value passed for parameter 'groups'") + if index_pattern is None: + raise ValueError("Empty value passed for parameter 'index_pattern'") + if page_size is None: + raise ValueError("Empty value passed for parameter 'page_size'") + if rollup_index is None: + raise ValueError("Empty value passed for parameter 'rollup_index'") + __path = f"/_rollup/job/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if cron is not None: + __body["cron"] = cron + if groups is not None: + __body["groups"] = groups + if index_pattern is not None: + __body["index_pattern"] = index_pattern + if page_size is not None: + __body["page_size"] = page_size + if rollup_index is not None: + __body["rollup_index"] = rollup_index + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if headers is not None: + __body["headers"] = headers + if human is not None: + __query["human"] = human + if metrics is not None: + __body["metrics"] = metrics + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __body["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def rollup_search( + self, + *, + index: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + aggregations: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + aggs: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + rest_total_hits_as_int: t.Optional[bool] = None, + size: t.Optional[int] = None, + typed_keys: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Enables searching rolled-up data using the standard query DSL. + + ``_ + + :param index: The indices or index-pattern(s) (containing rollup or regular data) + that should be searched + :param aggregations: + :param aggs: + :param query: + :param rest_total_hits_as_int: Indicates whether hits.total should be rendered + as an integer or an object in the rest search response + :param size: Must be zero if set, as rollups work on pre-aggregated data + :param typed_keys: Specify whether aggregation and suggester names should be + prefixed by their respective types in the response + """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/{_quote(index)}/_rollup_search" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if aggregations is not None: + __body["aggregations"] = aggregations + if aggs is not None: + __body["aggs"] = aggs + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if rest_total_hits_as_int is not None: + __query["rest_total_hits_as_int"] = rest_total_hits_as_int + if size is not None: + __body["size"] = size + if typed_keys is not None: + __query["typed_keys"] = typed_keys + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def start_job( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts an existing, stopped rollup job. + + ``_ + + :param id: The ID of the job to start + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_rollup/job/{_quote(id)}/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stop_job( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops an existing, started rollup job. + + ``_ + + :param id: The ID of the job to stop + :param timeout: Block for (at maximum) the specified duration while waiting for + the job to stop. Defaults to 30s. + :param wait_for_completion: True if the API should block until the job has fully + stopped, false if should be executed async. Defaults to false. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_rollup/job/{_quote(id)}/_stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/search_application.py b/elasticsearch_serverless/_sync/client/search_application.py new file mode 100644 index 0000000..4f16837 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/search_application.py @@ -0,0 +1,348 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SearchApplicationClient(NamespacedClient): + @_rewrite_parameters() + def delete( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a search application. + + ``_ + + :param name: The name of the search application to delete + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/search_application/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_behavioral_analytics( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Delete a behavioral analytics collection. + + ``_ + + :param name: The name of the analytics collection to be deleted + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/analytics/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the details about a search application. + + ``_ + + :param name: The name of the search application + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/search_application/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_behavioral_analytics( + self, + *, + name: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the existing behavioral analytics collections. + + ``_ + + :param name: A list of analytics collections to limit the returned information + """ + if name not in SKIP_IN_PATH: + __path = f"/_application/analytics/{_quote(name)}" + else: + __path = "/_application/analytics" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def list( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + q: t.Optional[str] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the existing search applications. + + ``_ + + :param from_: Starting offset (default: 0) + :param q: Query in the Lucene query string syntax" + :param size: specifies a max number of results to get + """ + __path = "/_application/search_application" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if q is not None: + __query["q"] = q + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_name="search_application", + ) + def put( + self, + *, + name: str, + search_application: t.Mapping[str, t.Any], + create: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a search application. + + ``_ + + :param name: The name of the search application to be created or updated + :param search_application: + :param create: If true, requires that a search application with the specified + resource_id does not already exist. (default: false) + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if search_application is None: + raise ValueError("Empty value passed for parameter 'search_application'") + __path = f"/_application/search_application/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if create is not None: + __query["create"] = create + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __body = search_application + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def put_behavioral_analytics( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a behavioral analytics collection. + + ``_ + + :param name: The name of the analytics collection to be created or updated + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/analytics/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params"}, + ) + def search( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Perform a search against a search application + + ``_ + + :param name: The name of the search application to be searched + :param params: + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_application/search_application/{_quote(name)}/_search" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if params is not None: + __body["params"] = params + if pretty is not None: + __query["pretty"] = pretty + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/searchable_snapshots.py b/elasticsearch_serverless/_sync/client/searchable_snapshots.py new file mode 100644 index 0000000..17cc31e --- /dev/null +++ b/elasticsearch_serverless/_sync/client/searchable_snapshots.py @@ -0,0 +1,265 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SearchableSnapshotsClient(NamespacedClient): + @_rewrite_parameters() + def cache_stats( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieve node-level cache statistics about searchable snapshots. + + ``_ + + :param node_id: A comma-separated list of node IDs or names to limit the returned + information; use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes + :param master_timeout: + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_searchable_snapshots/{_quote(node_id)}/cache/stats" + else: + __path = "/_searchable_snapshots/cache/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def clear_cache( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + allow_no_indices: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Union["t.Literal['all', 'closed', 'hidden', 'none', 'open']", str], + t.Union[ + t.List[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['all', 'closed', 'hidden', 'none', 'open']", str + ], + ..., + ], + ], + ] + ] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clear the cache of searchable snapshots. + + ``_ + + :param index: A comma-separated list of index names + :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves + into no concrete indices. (This includes `_all` string or when no indices + have been specified) + :param expand_wildcards: Whether to expand wildcard expression to concrete indices + that are open, closed or both. + :param ignore_unavailable: Whether specified concrete indices should be ignored + when unavailable (missing or closed) + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_searchable_snapshots/cache/clear" + else: + __path = "/_searchable_snapshots/cache/clear" + __query: t.Dict[str, t.Any] = {} + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices + if error_trace is not None: + __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def mount( + self, + *, + repository: str, + snapshot: str, + index: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_index_settings: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + index_settings: t.Optional[t.Mapping[str, t.Any]] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + renamed_index: t.Optional[str] = None, + storage: t.Optional[str] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Mount a snapshot as a searchable index. + + ``_ + + :param repository: The name of the repository containing the snapshot of the + index to mount + :param snapshot: The name of the snapshot of the index to mount + :param index: + :param ignore_index_settings: + :param index_settings: + :param master_timeout: Explicit operation timeout for connection to master node + :param renamed_index: + :param storage: Selects the kind of local storage used to accelerate searches. + Experimental, and defaults to `full_copy` + :param wait_for_completion: Should this request wait until the operation has + completed before returning + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + if index is None: + raise ValueError("Empty value passed for parameter 'index'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_mount" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if index is not None: + __body["index"] = index + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_index_settings is not None: + __body["ignore_index_settings"] = ignore_index_settings + if index_settings is not None: + __body["index_settings"] = index_settings + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if renamed_index is not None: + __body["renamed_index"] = renamed_index + if storage is not None: + __query["storage"] = storage + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def stats( + self, + *, + index: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + level: t.Optional[ + t.Union["t.Literal['cluster', 'indices', 'shards']", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieve shard-level statistics about searchable snapshots. + + ``_ + + :param index: A comma-separated list of index names + :param level: Return stats aggregated at cluster, index or shard level + """ + if index not in SKIP_IN_PATH: + __path = f"/{_quote(index)}/_searchable_snapshots/stats" + else: + __path = "/_searchable_snapshots/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if level is not None: + __query["level"] = level + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/security.py b/elasticsearch_serverless/_sync/client/security.py new file mode 100644 index 0000000..6734b6a --- /dev/null +++ b/elasticsearch_serverless/_sync/client/security.py @@ -0,0 +1,453 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SecurityClient(NamespacedClient): + @_rewrite_parameters() + def authenticate( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Enables authentication as a user and retrieve information about the authenticated + user. + + ``_ + """ + __path = "/_security/_authenticate" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def clear_api_key_cache( + self, + *, + ids: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clear a subset or all entries from the API key cache. + + ``_ + + :param ids: Comma-separated list of API key IDs to evict from the API key cache. + To evict all API keys, use `*`. Does not support other wildcard patterns. + """ + if ids in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'ids'") + __path = f"/_security/api_key/{_quote(ids)}/_clear_cache" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def create_api_key( + self, + *, + error_trace: t.Optional[bool] = None, + expiration: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + name: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + refresh: t.Optional[ + t.Union["t.Literal['false', 'true', 'wait_for']", bool, str] + ] = None, + role_descriptors: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates an API key for access without requiring basic authentication. + + ``_ + + :param expiration: Expiration time for the API key. By default, API keys never + expire. + :param metadata: Arbitrary metadata that you want to associate with the API key. + It supports nested data structure. Within the metadata object, keys beginning + with `_` are reserved for system usage. + :param name: Specifies the name for this API key. + :param refresh: If `true` (the default) then refresh the affected shards to make + this operation visible to search, if `wait_for` then wait for a refresh to + make this operation visible to search, if `false` then do nothing with refreshes. + :param role_descriptors: An array of role descriptors for this API key. This + parameter is optional. When it is not specified or is an empty array, then + the API key will have a point in time snapshot of permissions of the authenticated + user. If you supply role descriptors then the resultant permissions would + be an intersection of API keys permissions and authenticated user’s permissions + thereby limiting the access scope for API keys. The structure of role descriptor + is the same as the request for create role API. For more details, see create + or update roles API. + """ + __path = "/_security/api_key" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if expiration is not None: + __body["expiration"] = expiration + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if metadata is not None: + __body["metadata"] = metadata + if name is not None: + __body["name"] = name + if pretty is not None: + __query["pretty"] = pretty + if refresh is not None: + __query["refresh"] = refresh + if role_descriptors is not None: + __body["role_descriptors"] = role_descriptors + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def get_api_key( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + id: t.Optional[str] = None, + name: t.Optional[str] = None, + owner: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + realm_name: t.Optional[str] = None, + username: t.Optional[str] = None, + with_limited_by: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information for one or more API keys. + + ``_ + + :param id: An API key id. This parameter cannot be used with any of `name`, `realm_name` + or `username`. + :param name: An API key name. This parameter cannot be used with any of `id`, + `realm_name` or `username`. It supports prefix search with wildcard. + :param owner: A boolean flag that can be used to query API keys owned by the + currently authenticated user. The `realm_name` or `username` parameters cannot + be specified when this parameter is set to `true` as they are assumed to + be the currently authenticated ones. + :param realm_name: The name of an authentication realm. This parameter cannot + be used with either `id` or `name` or when `owner` flag is set to `true`. + :param username: The username of a user. This parameter cannot be used with either + `id` or `name` or when `owner` flag is set to `true`. + :param with_limited_by: Return the snapshot of the owner user's role descriptors + associated with the API key. An API key's actual permission is the intersection + of its assigned role descriptors and the owner user's role descriptors. + """ + __path = "/_security/api_key" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if id is not None: + __query["id"] = id + if name is not None: + __query["name"] = name + if owner is not None: + __query["owner"] = owner + if pretty is not None: + __query["pretty"] = pretty + if realm_name is not None: + __query["realm_name"] = realm_name + if username is not None: + __query["username"] = username + if with_limited_by is not None: + __query["with_limited_by"] = with_limited_by + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def invalidate_api_key( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + id: t.Optional[str] = None, + ids: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + name: t.Optional[str] = None, + owner: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + realm_name: t.Optional[str] = None, + username: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Invalidates one or more API keys. + + ``_ + + :param id: + :param ids: A list of API key ids. This parameter cannot be used with any of + `name`, `realm_name`, or `username`. + :param name: An API key name. This parameter cannot be used with any of `ids`, + `realm_name` or `username`. + :param owner: Can be used to query API keys owned by the currently authenticated + user. The `realm_name` or `username` parameters cannot be specified when + this parameter is set to `true` as they are assumed to be the currently authenticated + ones. + :param realm_name: The name of an authentication realm. This parameter cannot + be used with either `ids` or `name`, or when `owner` flag is set to `true`. + :param username: The username of a user. This parameter cannot be used with either + `ids` or `name`, or when `owner` flag is set to `true`. + """ + __path = "/_security/api_key" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if id is not None: + __body["id"] = id + if ids is not None: + __body["ids"] = ids + if name is not None: + __body["name"] = name + if owner is not None: + __body["owner"] = owner + if pretty is not None: + __query["pretty"] = pretty + if realm_name is not None: + __body["realm_name"] = realm_name + if username is not None: + __body["username"] = username + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + def query_api_keys( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + size: t.Optional[int] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + with_limited_by: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information for API keys using a subset of query DSL + + ``_ + + :param from_: Starting document offset. By default, you cannot page through more + than 10,000 hits using the from and size parameters. To page through more + hits, use the `search_after` parameter. + :param query: A query to filter which API keys to return. The query supports + a subset of query types, including `match_all`, `bool`, `term`, `terms`, + `ids`, `prefix`, `wildcard`, and `range`. You can query all public information + associated with an API key. + :param search_after: Search after definition + :param size: The number of hits to return. By default, you cannot page through + more than 10,000 hits using the `from` and `size` parameters. To page through + more hits, use the `search_after` parameter. + :param sort: Other than `id`, all public fields of an API key are eligible for + sorting. In addition, sort can also be applied to the `_doc` field to sort + by index order. + :param with_limited_by: Return the snapshot of the owner user's role descriptors + associated with the API key. An API key's actual permission is the intersection + of its assigned role descriptors and the owner user's role descriptors. + """ + __path = "/_security/_query/api_key" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if search_after is not None: + __body["search_after"] = search_after + if size is not None: + __body["size"] = size + if sort is not None: + __body["sort"] = sort + if with_limited_by is not None: + __query["with_limited_by"] = with_limited_by + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def update_api_key( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + role_descriptors: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates attributes of an existing API key. + + ``_ + + :param id: The ID of the API key to update. + :param metadata: Arbitrary metadata that you want to associate with the API key. + It supports nested data structure. Within the metadata object, keys beginning + with _ are reserved for system usage. + :param role_descriptors: An array of role descriptors for this API key. This + parameter is optional. When it is not specified or is an empty array, then + the API key will have a point in time snapshot of permissions of the authenticated + user. If you supply role descriptors then the resultant permissions would + be an intersection of API keys permissions and authenticated user’s permissions + thereby limiting the access scope for API keys. The structure of role descriptor + is the same as the request for create role API. For more details, see create + or update roles API. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_security/api_key/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if metadata is not None: + __body["metadata"] = metadata + if pretty is not None: + __query["pretty"] = pretty + if role_descriptors is not None: + __body["role_descriptors"] = role_descriptors + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/shutdown.py b/elasticsearch_serverless/_sync/client/shutdown.py new file mode 100644 index 0000000..fef109e --- /dev/null +++ b/elasticsearch_serverless/_sync/client/shutdown.py @@ -0,0 +1,229 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class ShutdownClient(NamespacedClient): + @_rewrite_parameters() + def delete_node( + self, + *, + node_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes a node from the shutdown list. Designed for indirect use by ECE/ESS and + ECK. Direct use is not supported. + + ``_ + + :param node_id: The node id of node to be removed from the shutdown state + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + __path = f"/_nodes/{_quote(node_id)}/shutdown" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_node( + self, + *, + node_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieve status of a node or nodes that are currently marked as shutting down. + Designed for indirect use by ECE/ESS and ECK. Direct use is not supported. + + ``_ + + :param node_id: Which node for which to retrieve the shutdown status + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id not in SKIP_IN_PATH: + __path = f"/_nodes/{_quote(node_id)}/shutdown" + else: + __path = "/_nodes/shutdown" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_node( + self, + *, + node_id: str, + reason: str, + type: t.Union["t.Literal['remove', 'replace', 'restart']", str], + allocation_delay: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + pretty: t.Optional[bool] = None, + target_node_name: t.Optional[str] = None, + timeout: t.Optional[ + t.Union["t.Literal['d', 'h', 'm', 'micros', 'ms', 'nanos', 's']", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Adds a node to be shut down. Designed for indirect use by ECE/ESS and ECK. Direct + use is not supported. + + ``_ + + :param node_id: The node id of node to be shut down + :param reason: A human-readable reason that the node is being shut down. This + field provides information for other cluster operators; it does not affect + the shut down process. + :param type: Valid values are restart, remove, or replace. Use restart when you + need to temporarily shut down a node to perform an upgrade, make configuration + changes, or perform other maintenance. Because the node is expected to rejoin + the cluster, data is not migrated off of the node. Use remove when you need + to permanently remove a node from the cluster. The node is not marked ready + for shutdown until data is migrated off of the node Use replace to do a 1:1 + replacement of a node with another node. Certain allocation decisions will + be ignored (such as disk watermarks) in the interest of true replacement + of the source node with the target node. During a replace-type shutdown, + rollover and index creation may result in unassigned shards, and shrink may + fail until the replacement is complete. + :param allocation_delay: Only valid if type is restart. Controls how long Elasticsearch + will wait for the node to restart and join the cluster before reassigning + its shards to other nodes. This works the same as delaying allocation with + the index.unassigned.node_left.delayed_timeout setting. If you specify both + a restart allocation delay and an index-level allocation delay, the longer + of the two is used. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param target_node_name: Only valid if type is replace. Specifies the name of + the node that is replacing the node being shut down. Shards from the shut + down node are only allowed to be allocated to the target node, and no other + data will be allocated to the target node. During relocation of data certain + allocation rules are ignored, such as disk watermarks or user attribute filtering + rules. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if node_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'node_id'") + if reason is None: + raise ValueError("Empty value passed for parameter 'reason'") + if type is None: + raise ValueError("Empty value passed for parameter 'type'") + __path = f"/_nodes/{_quote(node_id)}/shutdown" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if reason is not None: + __body["reason"] = reason + if type is not None: + __body["type"] = type + if allocation_delay is not None: + __body["allocation_delay"] = allocation_delay + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if target_node_name is not None: + __body["target_node_name"] = target_node_name + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/slm.py b/elasticsearch_serverless/_sync/client/slm.py new file mode 100644 index 0000000..671b1c7 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/slm.py @@ -0,0 +1,377 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SlmClient(NamespacedClient): + @_rewrite_parameters() + def delete_lifecycle( + self, + *, + policy_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing snapshot lifecycle policy. + + ``_ + + :param policy_id: The id of the snapshot lifecycle policy to remove + """ + if policy_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'policy_id'") + __path = f"/_slm/policy/{_quote(policy_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def execute_lifecycle( + self, + *, + policy_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Immediately creates a snapshot according to the lifecycle policy, without waiting + for the scheduled time. + + ``_ + + :param policy_id: The id of the snapshot lifecycle policy to be executed + """ + if policy_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'policy_id'") + __path = f"/_slm/policy/{_quote(policy_id)}/_execute" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def execute_retention( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes any snapshots that are expired according to the policy's retention rules. + + ``_ + """ + __path = "/_slm/_execute_retention" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_lifecycle( + self, + *, + policy_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves one or more snapshot lifecycle policy definitions and information about + the latest snapshot attempts. + + ``_ + + :param policy_id: Comma-separated list of snapshot lifecycle policies to retrieve + """ + if policy_id not in SKIP_IN_PATH: + __path = f"/_slm/policy/{_quote(policy_id)}" + else: + __path = "/_slm/policy" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns global and policy-level statistics about actions taken by snapshot lifecycle + management. + + ``_ + """ + __path = "/_slm/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the status of snapshot lifecycle management (SLM). + + ``_ + """ + __path = "/_slm/status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_lifecycle( + self, + *, + policy_id: str, + config: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + name: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + repository: t.Optional[str] = None, + retention: t.Optional[t.Mapping[str, t.Any]] = None, + schedule: t.Optional[str] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates or updates a snapshot lifecycle policy. + + ``_ + + :param policy_id: ID for the snapshot lifecycle policy you want to create or + update. + :param config: Configuration for each snapshot created by the policy. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param name: Name automatically assigned to each snapshot created by the policy. + Date math is supported. To prevent conflicting snapshot names, a UUID is + automatically appended to each snapshot name. + :param repository: Repository used to store snapshots created by this policy. + This repository must exist prior to the policy’s creation. You can create + a repository using the snapshot repository API. + :param retention: Retention rules used to retain and delete snapshots created + by the policy. + :param schedule: Periodic or absolute schedule at which the policy creates snapshots. + SLM applies schedule changes immediately. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if policy_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'policy_id'") + __path = f"/_slm/policy/{_quote(policy_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if config is not None: + __body["config"] = config + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if name is not None: + __body["name"] = name + if pretty is not None: + __query["pretty"] = pretty + if repository is not None: + __body["repository"] = repository + if retention is not None: + __body["retention"] = retention + if schedule is not None: + __body["schedule"] = schedule + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def start( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Turns on snapshot lifecycle management (SLM). + + ``_ + """ + __path = "/_slm/start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stop( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Turns off snapshot lifecycle management (SLM). + + ``_ + """ + __path = "/_slm/stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/snapshot.py b/elasticsearch_serverless/_sync/client/snapshot.py new file mode 100644 index 0000000..20cba3f --- /dev/null +++ b/elasticsearch_serverless/_sync/client/snapshot.py @@ -0,0 +1,773 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SnapshotClient(NamespacedClient): + @_rewrite_parameters() + def cleanup_repository( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes stale data from repository. + + ``_ + + :param name: Snapshot repository to clean up. + :param master_timeout: Period to wait for a connection to the master node. + :param timeout: Period to wait for a response. + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_snapshot/{_quote(name)}/_cleanup" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def clone( + self, + *, + repository: str, + snapshot: str, + target_snapshot: str, + indices: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clones indices from one snapshot into another snapshot in the same repository. + + ``_ + + :param repository: A repository name + :param snapshot: The name of the snapshot to clone from + :param target_snapshot: The name of the cloned snapshot to create + :param indices: + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + if target_snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'target_snapshot'") + if indices is None: + raise ValueError("Empty value passed for parameter 'indices'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_clone/{_quote(target_snapshot)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if indices is not None: + __body["indices"] = indices + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def create( + self, + *, + repository: str, + snapshot: str, + error_trace: t.Optional[bool] = None, + feature_states: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_global_state: t.Optional[bool] = None, + indices: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + partial: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a snapshot in a repository. + + ``_ + + :param repository: Repository for the snapshot. + :param snapshot: Name of the snapshot. Must be unique in the repository. + :param feature_states: Feature states to include in the snapshot. Each feature + state includes one or more system indices containing related data. You can + view a list of eligible features using the get features API. If `include_global_state` + is `true`, all current feature states are included by default. If `include_global_state` + is `false`, no feature states are included by default. + :param ignore_unavailable: If `true`, the request ignores data streams and indices + in `indices` that are missing or closed. If `false`, the request returns + an error for any data stream or index that is missing or closed. + :param include_global_state: If `true`, the current cluster state is included + in the snapshot. The cluster state includes persistent cluster settings, + composable index templates, legacy index templates, ingest pipelines, and + ILM policies. It also includes data stored in system indices, such as Watches + and task records (configurable via `feature_states`). + :param indices: Data streams and indices to include in the snapshot. Supports + multi-target syntax. Includes all data streams and indices by default. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param metadata: Optional metadata for the snapshot. May have any contents. Must + be less than 1024 bytes. This map is not automatically generated by Elasticsearch. + :param partial: If `true`, allows restoring a partial snapshot of indices with + unavailable shards. Only shards that were successfully included in the snapshot + will be restored. All missing shards will be recreated as empty. If `false`, + the entire restore operation will fail if one or more indices included in + the snapshot do not have all primary shards available. + :param wait_for_completion: If `true`, the request returns a response when the + snapshot is complete. If `false`, the request returns a response when the + snapshot initializes. + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if feature_states is not None: + __body["feature_states"] = feature_states + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __body["ignore_unavailable"] = ignore_unavailable + if include_global_state is not None: + __body["include_global_state"] = include_global_state + if indices is not None: + __body["indices"] = indices + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if metadata is not None: + __body["metadata"] = metadata + if partial is not None: + __body["partial"] = partial + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def create_repository( + self, + *, + name: str, + settings: t.Mapping[str, t.Any], + type: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + repository: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + verify: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a repository. + + ``_ + + :param name: A repository name + :param settings: + :param type: + :param master_timeout: Explicit operation timeout for connection to master node + :param repository: + :param timeout: Explicit operation timeout + :param verify: Whether to verify the repository after creation + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + if settings is None: + raise ValueError("Empty value passed for parameter 'settings'") + if type is None: + raise ValueError("Empty value passed for parameter 'type'") + __path = f"/_snapshot/{_quote(name)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if settings is not None: + __body["settings"] = settings + if type is not None: + __body["type"] = type + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if repository is not None: + __body["repository"] = repository + if timeout is not None: + __query["timeout"] = timeout + if verify is not None: + __query["verify"] = verify + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def delete( + self, + *, + repository: str, + snapshot: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes one or more snapshots. + + ``_ + + :param repository: A repository name + :param snapshot: A comma-separated list of snapshot names + :param master_timeout: Explicit operation timeout for connection to master node + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_repository( + self, + *, + name: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes a repository. + + ``_ + + :param name: Name of the snapshot repository to unregister. Wildcard (`*`) patterns + are supported. + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: Explicit operation timeout + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_snapshot/{_quote(name)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + repository: str, + snapshot: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + after: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_sort_value: t.Optional[str] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + include_repository: t.Optional[bool] = None, + index_details: t.Optional[bool] = None, + index_names: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + offset: t.Optional[int] = None, + order: t.Optional[t.Union["t.Literal['asc', 'desc']", str]] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + slm_policy_filter: t.Optional[str] = None, + sort: t.Optional[ + t.Union[ + "t.Literal['duration', 'failed_shard_count', 'index_count', 'name', 'repository', 'shard_count', 'start_time']", + str, + ] + ] = None, + verbose: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about a snapshot. + + ``_ + + :param repository: Comma-separated list of snapshot repository names used to + limit the request. Wildcard (*) expressions are supported. + :param snapshot: Comma-separated list of snapshot names to retrieve. Also accepts + wildcards (*). - To get information about all snapshots in a registered repository, + use a wildcard (*) or _all. - To get information about any snapshots that + are currently running, use _current. + :param after: Offset identifier to start pagination from as returned by the next + field in the response body. + :param from_sort_value: Value of the current sort column at which to start retrieval. + Can either be a string snapshot- or repository name when sorting by snapshot + or repository name, a millisecond time value or a number when sorting by + index- or shard count. + :param ignore_unavailable: If false, the request returns an error for any snapshots + that are unavailable. + :param include_repository: If true, returns the repository name in each snapshot. + :param index_details: If true, returns additional information about each index + in the snapshot comprising the number of shards in the index, the total size + of the index in bytes, and the maximum number of segments per shard in the + index. Defaults to false, meaning that this information is omitted. + :param index_names: If true, returns the name of each index in each snapshot. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param offset: Numeric offset to start pagination from based on the snapshots + matching this request. Using a non-zero value for this parameter is mutually + exclusive with using the after parameter. Defaults to 0. + :param order: Sort order. Valid values are asc for ascending and desc for descending + order. Defaults to asc, meaning ascending order. + :param size: Maximum number of snapshots to return. Defaults to 0 which means + return all that match the request without limit. + :param slm_policy_filter: Filter snapshots by a comma-separated list of SLM policy + names that snapshots belong to. Also accepts wildcards (*) and combinations + of wildcards followed by exclude patterns starting with -. To include snapshots + not created by an SLM policy you can use the special pattern _none that will + match all snapshots without an SLM policy. + :param sort: Allows setting a sort order for the result. Defaults to start_time, + i.e. sorting by snapshot start time stamp. + :param verbose: If true, returns additional information about each snapshot such + as the version of Elasticsearch which took the snapshot, the start and end + times of the snapshot, and the number of shards snapshotted. + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}" + __query: t.Dict[str, t.Any] = {} + if after is not None: + __query["after"] = after + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_sort_value is not None: + __query["from_sort_value"] = from_sort_value + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if include_repository is not None: + __query["include_repository"] = include_repository + if index_details is not None: + __query["index_details"] = index_details + if index_names is not None: + __query["index_names"] = index_names + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if offset is not None: + __query["offset"] = offset + if order is not None: + __query["order"] = order + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if slm_policy_filter is not None: + __query["slm_policy_filter"] = slm_policy_filter + if sort is not None: + __query["sort"] = sort + if verbose is not None: + __query["verbose"] = verbose + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_repository( + self, + *, + name: t.Optional[t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + local: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about a repository. + + ``_ + + :param name: A comma-separated list of repository names + :param local: Return local information, do not retrieve the state from master + node (default: false) + :param master_timeout: Explicit operation timeout for connection to master node + """ + if name not in SKIP_IN_PATH: + __path = f"/_snapshot/{_quote(name)}" + else: + __path = "/_snapshot" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if local is not None: + __query["local"] = local + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def restore( + self, + *, + repository: str, + snapshot: str, + error_trace: t.Optional[bool] = None, + feature_states: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_index_settings: t.Optional[ + t.Union[t.List[str], t.Tuple[str, ...]] + ] = None, + ignore_unavailable: t.Optional[bool] = None, + include_aliases: t.Optional[bool] = None, + include_global_state: t.Optional[bool] = None, + index_settings: t.Optional[t.Mapping[str, t.Any]] = None, + indices: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + partial: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + rename_pattern: t.Optional[str] = None, + rename_replacement: t.Optional[str] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Restores a snapshot. + + ``_ + + :param repository: A repository name + :param snapshot: A snapshot name + :param feature_states: + :param ignore_index_settings: + :param ignore_unavailable: + :param include_aliases: + :param include_global_state: + :param index_settings: + :param indices: + :param master_timeout: Explicit operation timeout for connection to master node + :param partial: + :param rename_pattern: + :param rename_replacement: + :param wait_for_completion: Should this request wait until the operation has + completed before returning + """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'repository'") + if snapshot in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'snapshot'") + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_restore" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if feature_states is not None: + __body["feature_states"] = feature_states + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_index_settings is not None: + __body["ignore_index_settings"] = ignore_index_settings + if ignore_unavailable is not None: + __body["ignore_unavailable"] = ignore_unavailable + if include_aliases is not None: + __body["include_aliases"] = include_aliases + if include_global_state is not None: + __body["include_global_state"] = include_global_state + if index_settings is not None: + __body["index_settings"] = index_settings + if indices is not None: + __body["indices"] = indices + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if partial is not None: + __body["partial"] = partial + if pretty is not None: + __query["pretty"] = pretty + if rename_pattern is not None: + __body["rename_pattern"] = rename_pattern + if rename_replacement is not None: + __body["rename_replacement"] = rename_replacement + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def status( + self, + *, + repository: t.Optional[str] = None, + snapshot: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about the status of a snapshot. + + ``_ + + :param repository: A repository name + :param snapshot: A comma-separated list of snapshot names + :param ignore_unavailable: Whether to ignore unavailable snapshots, defaults + to false which means a SnapshotMissingException is thrown + :param master_timeout: Explicit operation timeout for connection to master node + """ + if repository not in SKIP_IN_PATH and snapshot not in SKIP_IN_PATH: + __path = f"/_snapshot/{_quote(repository)}/{_quote(snapshot)}/_status" + elif repository not in SKIP_IN_PATH: + __path = f"/_snapshot/{_quote(repository)}/_status" + else: + __path = "/_snapshot/_status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def verify_repository( + self, + *, + name: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Verifies a repository. + + ``_ + + :param name: A repository name + :param master_timeout: Explicit operation timeout for connection to master node + :param timeout: Explicit operation timeout + """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'name'") + __path = f"/_snapshot/{_quote(name)}/_verify" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/sql.py b/elasticsearch_serverless/_sync/client/sql.py new file mode 100644 index 0000000..c69186a --- /dev/null +++ b/elasticsearch_serverless/_sync/client/sql.py @@ -0,0 +1,373 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class SqlClient(NamespacedClient): + @_rewrite_parameters( + body_fields=True, + ) + def clear_cursor( + self, + *, + cursor: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Clears the SQL cursor + + ``_ + + :param cursor: + """ + if cursor is None: + raise ValueError("Empty value passed for parameter 'cursor'") + __path = "/_sql/close" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if cursor is not None: + __body["cursor"] = cursor + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def delete_async( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an async SQL search or a stored synchronous SQL search. If the search + is still running, the API cancels it. + + ``_ + + :param id: The async search ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_sql/async/delete/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_async( + self, + *, + id: str, + delimiter: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + human: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + pretty: t.Optional[bool] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the current status and available results for an async SQL search or stored + synchronous SQL search + + ``_ + + :param id: The async search ID + :param delimiter: Separator for CSV results. The API only supports this parameter + for CSV responses. + :param format: Format for the response. You must specify a format using this + parameter or the Accept HTTP header. If you specify both, the API uses this + parameter. + :param keep_alive: Retention period for the search and its results. Defaults + to the `keep_alive` period for the original SQL search. + :param wait_for_completion_timeout: Period to wait for complete results. Defaults + to no timeout, meaning the request waits for complete search results. + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_sql/async/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if delimiter is not None: + __query["delimiter"] = delimiter + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if human is not None: + __query["human"] = human + if keep_alive is not None: + __query["keep_alive"] = keep_alive + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion_timeout is not None: + __query["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get_async_status( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns the current status of an async SQL search or a stored synchronous SQL + search + + ``_ + + :param id: The async search ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_sql/async/status/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ignore_deprecated_options={"params", "request_timeout"}, + ) + def query( + self, + *, + catalog: t.Optional[str] = None, + columnar: t.Optional[bool] = None, + cursor: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + fetch_size: t.Optional[int] = None, + field_multi_value_leniency: t.Optional[bool] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + format: t.Optional[str] = None, + human: t.Optional[bool] = None, + index_using_frozen: t.Optional[bool] = None, + keep_alive: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + keep_on_completion: t.Optional[bool] = None, + page_timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + params: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[str] = None, + request_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + time_zone: t.Optional[str] = None, + wait_for_completion_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Executes a SQL request + + ``_ + + :param catalog: Default catalog (cluster) for queries. If unspecified, the queries + execute on the data in the local cluster only. + :param columnar: If true, the results in a columnar fashion: one row represents + all the values of a certain column from the current page of results. + :param cursor: + :param fetch_size: The maximum number of rows (or entries) to return in one response + :param field_multi_value_leniency: Throw an exception when encountering multiple + values for a field (default) or be lenient and return the first value from + the list (without any guarantees of what that will be - typically the first + in natural ascending order). + :param filter: Optional Elasticsearch query DSL for additional filtering. + :param format: a short version of the Accept header, e.g. json, yaml + :param index_using_frozen: If true, the search can run on frozen indices. Defaults + to false. + :param keep_alive: Retention period for an async or saved synchronous search. + :param keep_on_completion: If true, Elasticsearch stores synchronous searches + if you also specify the wait_for_completion_timeout parameter. If false, + Elasticsearch only stores async searches that don’t finish before the wait_for_completion_timeout. + :param page_timeout: The timeout before a pagination request fails. + :param params: Values for parameters in the query. + :param query: SQL query to execute + :param request_timeout: The timeout before the request fails. + :param runtime_mappings: Defines one or more runtime fields in the search request. + These fields take precedence over mapped fields with the same name. + :param time_zone: Time-zone in ISO 8601 used for executing the query on the server. + More information available here. + :param wait_for_completion_timeout: Period to wait for complete results. Defaults + to no timeout, meaning the request waits for complete search results. If + the search doesn’t finish within this period, the search becomes async. + """ + __path = "/_sql" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if catalog is not None: + __body["catalog"] = catalog + if columnar is not None: + __body["columnar"] = columnar + if cursor is not None: + __body["cursor"] = cursor + if error_trace is not None: + __query["error_trace"] = error_trace + if fetch_size is not None: + __body["fetch_size"] = fetch_size + if field_multi_value_leniency is not None: + __body["field_multi_value_leniency"] = field_multi_value_leniency + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if format is not None: + __query["format"] = format + if human is not None: + __query["human"] = human + if index_using_frozen is not None: + __body["index_using_frozen"] = index_using_frozen + if keep_alive is not None: + __body["keep_alive"] = keep_alive + if keep_on_completion is not None: + __body["keep_on_completion"] = keep_on_completion + if page_timeout is not None: + __body["page_timeout"] = page_timeout + if params is not None: + __body["params"] = params + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if request_timeout is not None: + __body["request_timeout"] = request_timeout + if runtime_mappings is not None: + __body["runtime_mappings"] = runtime_mappings + if time_zone is not None: + __body["time_zone"] = time_zone + if wait_for_completion_timeout is not None: + __body["wait_for_completion_timeout"] = wait_for_completion_timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + ) + def translate( + self, + *, + query: str, + error_trace: t.Optional[bool] = None, + fetch_size: t.Optional[int] = None, + filter: t.Optional[t.Mapping[str, t.Any]] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + time_zone: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Translates SQL into Elasticsearch queries + + ``_ + + :param query: + :param fetch_size: + :param filter: + :param time_zone: + """ + if query is None: + raise ValueError("Empty value passed for parameter 'query'") + __path = "/_sql/translate" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if query is not None: + __body["query"] = query + if error_trace is not None: + __query["error_trace"] = error_trace + if fetch_size is not None: + __body["fetch_size"] = fetch_size + if filter is not None: + __body["filter"] = filter + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if time_zone is not None: + __body["time_zone"] = time_zone + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/ssl.py b/elasticsearch_serverless/_sync/client/ssl.py new file mode 100644 index 0000000..99c1926 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/ssl.py @@ -0,0 +1,57 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class SslClient(NamespacedClient): + @_rewrite_parameters() + def certificates( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the X.509 certificates used to encrypt communications + in the cluster. + + ``_ + """ + __path = "/_ssl/certificates" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/tasks.py b/elasticsearch_serverless/_sync/client/tasks.py new file mode 100644 index 0000000..00418a6 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/tasks.py @@ -0,0 +1,208 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class TasksClient(NamespacedClient): + @_rewrite_parameters() + def cancel( + self, + *, + task_id: t.Optional[t.Union[int, str]] = None, + actions: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + nodes: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + parent_task_id: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Cancels a task, if it can be cancelled through an API. + + ``_ + + :param task_id: Cancel the task with specified task id (node_id:task_number) + :param actions: A comma-separated list of actions that should be cancelled. Leave + empty to cancel all. + :param nodes: A comma-separated list of node IDs or names to limit the returned + information; use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes + :param parent_task_id: Cancel tasks with specified parent task id (node_id:task_number). + Set to -1 to cancel all. + :param wait_for_completion: Should the request block until the cancellation of + the task and its descendant tasks is completed. Defaults to false + """ + if task_id not in SKIP_IN_PATH: + __path = f"/_tasks/{_quote(task_id)}/_cancel" + else: + __path = "/_tasks/_cancel" + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __query["actions"] = actions + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if nodes is not None: + __query["nodes"] = nodes + if parent_task_id is not None: + __query["parent_task_id"] = parent_task_id + if pretty is not None: + __query["pretty"] = pretty + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def get( + self, + *, + task_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns information about a task. + + ``_ + + :param task_id: Return the task with specified id (node_id:task_number) + :param timeout: Explicit operation timeout + :param wait_for_completion: Wait for the matching tasks to complete (default: + false) + """ + if task_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'task_id'") + __path = f"/_tasks/{_quote(task_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def list( + self, + *, + actions: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + detailed: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + group_by: t.Optional[ + t.Union["t.Literal['nodes', 'none', 'parents']", str] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + node_id: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + parent_task_id: t.Optional[str] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Returns a list of tasks. + + ``_ + + :param actions: Comma-separated list or wildcard expression of actions used to + limit the request. + :param detailed: If `true`, the response includes detailed information about + shard recoveries. + :param group_by: Key used to group tasks in the response. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param node_id: Comma-separated list of node IDs or names used to limit returned + information. + :param parent_task_id: Parent task ID used to limit returned information. To + return all tasks, omit this parameter or use a value of `-1`. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + :param wait_for_completion: If `true`, the request blocks until the operation + is complete. + """ + __path = "/_tasks" + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __query["actions"] = actions + if detailed is not None: + __query["detailed"] = detailed + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if group_by is not None: + __query["group_by"] = group_by + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if node_id is not None: + __query["node_id"] = node_id + if parent_task_id is not None: + __query["parent_task_id"] = parent_task_id + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/text_structure.py b/elasticsearch_serverless/_sync/client/text_structure.py new file mode 100644 index 0000000..5172c25 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/text_structure.py @@ -0,0 +1,158 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class TextStructureClient(NamespacedClient): + @_rewrite_parameters( + body_name="text_files", + ) + def find_structure( + self, + *, + text_files: t.Union[t.List[t.Any], t.Tuple[t.Any, ...]], + charset: t.Optional[str] = None, + column_names: t.Optional[str] = None, + delimiter: t.Optional[str] = None, + explain: t.Optional[bool] = None, + format: t.Optional[str] = None, + grok_pattern: t.Optional[str] = None, + has_header_row: t.Optional[bool] = None, + line_merge_size_limit: t.Optional[int] = None, + lines_to_sample: t.Optional[int] = None, + quote: t.Optional[str] = None, + should_trim_fields: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + timestamp_field: t.Optional[str] = None, + timestamp_format: t.Optional[str] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Finds the structure of a text file. The text file must contain data that is suitable + to be ingested into Elasticsearch. + + ``_ + + :param text_files: + :param charset: The text’s character set. It must be a character set that is + supported by the JVM that Elasticsearch uses. For example, UTF-8, UTF-16LE, + windows-1252, or EUC-JP. If this parameter is not specified, the structure + finder chooses an appropriate character set. + :param column_names: If you have set format to delimited, you can specify the + column names in a comma-separated list. If this parameter is not specified, + the structure finder uses the column names from the header row of the text. + If the text does not have a header role, columns are named "column1", "column2", + "column3", etc. + :param delimiter: If you have set format to delimited, you can specify the character + used to delimit the values in each row. Only a single character is supported; + the delimiter cannot have multiple characters. By default, the API considers + the following possibilities: comma, tab, semi-colon, and pipe (|). In this + default scenario, all rows must have the same number of fields for the delimited + format to be detected. If you specify a delimiter, up to 10% of the rows + can have a different number of columns than the first row. + :param explain: If this parameter is set to true, the response includes a field + named explanation, which is an array of strings that indicate how the structure + finder produced its result. + :param format: The high level structure of the text. Valid values are ndjson, + xml, delimited, and semi_structured_text. By default, the API chooses the + format. In this default scenario, all rows must have the same number of fields + for a delimited format to be detected. If the format is set to delimited + and the delimiter is not set, however, the API tolerates up to 5% of rows + that have a different number of columns than the first row. + :param grok_pattern: If you have set format to semi_structured_text, you can + specify a Grok pattern that is used to extract fields from every message + in the text. The name of the timestamp field in the Grok pattern must match + what is specified in the timestamp_field parameter. If that parameter is + not specified, the name of the timestamp field in the Grok pattern must match + "timestamp". If grok_pattern is not specified, the structure finder creates + a Grok pattern. + :param has_header_row: If you have set format to delimited, you can use this + parameter to indicate whether the column names are in the first row of the + text. If this parameter is not specified, the structure finder guesses based + on the similarity of the first row of the text to other rows. + :param line_merge_size_limit: The maximum number of characters in a message when + lines are merged to form messages while analyzing semi-structured text. If + you have extremely long messages you may need to increase this, but be aware + that this may lead to very long processing times if the way to group lines + into messages is misdetected. + :param lines_to_sample: The number of lines to include in the structural analysis, + starting from the beginning of the text. The minimum is 2; If the value of + this parameter is greater than the number of lines in the text, the analysis + proceeds (as long as there are at least two lines in the text) for all of + the lines. + :param quote: If you have set format to delimited, you can specify the character + used to quote the values in each row if they contain newlines or the delimiter + character. Only a single character is supported. If this parameter is not + specified, the default value is a double quote ("). If your delimited text + format does not use quoting, a workaround is to set this argument to a character + that does not appear anywhere in the sample. + :param should_trim_fields: If you have set format to delimited, you can specify + whether values between delimiters should have whitespace trimmed from them. + If this parameter is not specified and the delimiter is pipe (|), the default + value is true. Otherwise, the default value is false. + :param timeout: Sets the maximum amount of time that the structure analysis make + take. If the analysis is still running when the timeout expires then it will + be aborted. + :param timestamp_field: Optional parameter to specify the timestamp field in + the file + :param timestamp_format: The Java time format of the timestamp field in the text. + """ + if text_files is None: + raise ValueError("Empty value passed for parameter 'text_files'") + __path = "/_text_structure/find_structure" + __query: t.Dict[str, t.Any] = {} + if charset is not None: + __query["charset"] = charset + if column_names is not None: + __query["column_names"] = column_names + if delimiter is not None: + __query["delimiter"] = delimiter + if explain is not None: + __query["explain"] = explain + if format is not None: + __query["format"] = format + if grok_pattern is not None: + __query["grok_pattern"] = grok_pattern + if has_header_row is not None: + __query["has_header_row"] = has_header_row + if line_merge_size_limit is not None: + __query["line_merge_size_limit"] = line_merge_size_limit + if lines_to_sample is not None: + __query["lines_to_sample"] = lines_to_sample + if quote is not None: + __query["quote"] = quote + if should_trim_fields is not None: + __query["should_trim_fields"] = should_trim_fields + if timeout is not None: + __query["timeout"] = timeout + if timestamp_field is not None: + __query["timestamp_field"] = timestamp_field + if timestamp_format is not None: + __query["timestamp_format"] = timestamp_format + __body = text_files + __headers = { + "accept": "application/json", + "content-type": "application/x-ndjson", + } + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/transform.py b/elasticsearch_serverless/_sync/client/transform.py new file mode 100644 index 0000000..12c95c4 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/transform.py @@ -0,0 +1,690 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class TransformClient(NamespacedClient): + @_rewrite_parameters() + def delete_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deletes an existing transform. + + ``_ + + :param transform_id: Identifier for the transform. + :param force: If this value is false, the transform must be stopped before it + can be deleted. If true, the transform is deleted regardless of its current + state. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_transform( + self, + *, + transform_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + exclude_generated: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves configuration information for transforms. + + ``_ + + :param transform_id: Identifier for the transform. It can be a transform identifier + or a wildcard expression. You can get information for all transforms by using + `_all`, by specifying `*` as the ``, or by omitting the ``. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no transforms that match. 2. Contains the _all + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. If this parameter is false, the request + returns a 404 status code when there are no matches or only partial matches. + :param exclude_generated: Excludes fields that were automatically added when + creating the transform. This allows the configuration to be in an acceptable + format to be retrieved and then added to another cluster. + :param from_: Skips the specified number of transforms. + :param size: Specifies the maximum number of transforms to obtain. + """ + if transform_id not in SKIP_IN_PATH: + __path = f"/_transform/{_quote(transform_id)}" + else: + __path = "/_transform" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if exclude_generated is not None: + __query["exclude_generated"] = exclude_generated + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def get_transform_stats( + self, + *, + transform_id: t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]], + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + size: t.Optional[int] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information for transforms. + + ``_ + + :param transform_id: Identifier for the transform. It can be a transform identifier + or a wildcard expression. You can get information for all transforms by using + `_all`, by specifying `*` as the ``, or by omitting the ``. + :param allow_no_match: Specifies what to do when the request: 1. Contains wildcard + expressions and there are no transforms that match. 2. Contains the _all + string or no identifiers and there are no matches. 3. Contains wildcard expressions + and there are only partial matches. If this parameter is false, the request + returns a 404 status code when there are no matches or only partial matches. + :param from_: Skips the specified number of transforms. + :param size: Specifies the maximum number of transforms to obtain. + :param timeout: Controls the time to wait for the stats + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_stats" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if size is not None: + __query["size"] = size + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def preview_transform( + self, + *, + transform_id: t.Optional[str] = None, + description: t.Optional[str] = None, + dest: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + latest: t.Optional[t.Mapping[str, t.Any]] = None, + pivot: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + retention_policy: t.Optional[t.Mapping[str, t.Any]] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + source: t.Optional[t.Mapping[str, t.Any]] = None, + sync: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Previews a transform. + + ``_ + + :param transform_id: Identifier for the transform to preview. If you specify + this path parameter, you cannot provide transform configuration details in + the request body. + :param description: Free text description of the transform. + :param dest: The destination for the transform. + :param frequency: The interval between checks for changes in the source indices + when the transform is running continuously. Also determines the retry interval + in the event of transient failures while the transform is searching or indexing. + The minimum value is 1s and the maximum is 1h. + :param latest: The latest method transforms the data by finding the latest document + for each unique key. + :param pivot: The pivot method transforms the data by aggregating and grouping + it. These objects define the group by fields and the aggregation to reduce + the data. + :param retention_policy: Defines a retention policy for the transform. Data that + meets the defined criteria is deleted from the destination index. + :param settings: Defines optional transform settings. + :param source: The source of the data for the transform. + :param sync: Defines the properties transforms require to run continuously. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id not in SKIP_IN_PATH: + __path = f"/_transform/{_quote(transform_id)}/_preview" + else: + __path = "/_transform/_preview" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if description is not None: + __body["description"] = description + if dest is not None: + __body["dest"] = dest + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if latest is not None: + __body["latest"] = latest + if pivot is not None: + __body["pivot"] = pivot + if pretty is not None: + __query["pretty"] = pretty + if retention_policy is not None: + __body["retention_policy"] = retention_policy + if settings is not None: + __body["settings"] = settings + if source is not None: + __body["source"] = source + if sync is not None: + __body["sync"] = sync + if timeout is not None: + __query["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + def put_transform( + self, + *, + transform_id: str, + dest: t.Mapping[str, t.Any], + source: t.Mapping[str, t.Any], + defer_validation: t.Optional[bool] = None, + description: t.Optional[str] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + latest: t.Optional[t.Mapping[str, t.Any]] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pivot: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + retention_policy: t.Optional[t.Mapping[str, t.Any]] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + sync: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Instantiates a transform. + + ``_ + + :param transform_id: Identifier for the transform. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It has a 64 character limit and must start and end with alphanumeric characters. + :param dest: The destination for the transform. + :param source: The source of the data for the transform. + :param defer_validation: When the transform is created, a series of validations + occur to ensure its success. For example, there is a check for the existence + of the source indices and a check that the destination index is not part + of the source index pattern. You can use this parameter to skip the checks, + for example when the source index does not exist until after the transform + is created. The validations are always run when you start the transform, + however, with the exception of privilege checks. + :param description: Free text description of the transform. + :param frequency: The interval between checks for changes in the source indices + when the transform is running continuously. Also determines the retry interval + in the event of transient failures while the transform is searching or indexing. + The minimum value is `1s` and the maximum is `1h`. + :param latest: The latest method transforms the data by finding the latest document + for each unique key. + :param meta: Defines optional transform metadata. + :param pivot: The pivot method transforms the data by aggregating and grouping + it. These objects define the group by fields and the aggregation to reduce + the data. + :param retention_policy: Defines a retention policy for the transform. Data that + meets the defined criteria is deleted from the destination index. + :param settings: Defines optional transform settings. + :param sync: Defines the properties transforms require to run continuously. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + if dest is None: + raise ValueError("Empty value passed for parameter 'dest'") + if source is None: + raise ValueError("Empty value passed for parameter 'source'") + __path = f"/_transform/{_quote(transform_id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if dest is not None: + __body["dest"] = dest + if source is not None: + __body["source"] = source + if defer_validation is not None: + __query["defer_validation"] = defer_validation + if description is not None: + __body["description"] = description + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if latest is not None: + __body["latest"] = latest + if meta is not None: + __body["_meta"] = meta + if pivot is not None: + __body["pivot"] = pivot + if pretty is not None: + __query["pretty"] = pretty + if retention_policy is not None: + __body["retention_policy"] = retention_policy + if settings is not None: + __body["settings"] = settings + if sync is not None: + __body["sync"] = sync + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def reset_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Resets an existing transform. + + ``_ + + :param transform_id: Identifier for the transform. This identifier can contain + lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores. + It has a 64 character limit and must start and end with alphanumeric characters. + :param force: If this value is `true`, the transform is reset regardless of its + current state. If it's `false`, the transform must be stopped before it can + be reset. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_reset" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def schedule_now_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Schedules now a transform. + + ``_ + + :param transform_id: Identifier for the transform. + :param timeout: Controls the time to wait for the scheduling to take place + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_schedule_now" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + parameter_aliases={"from": "from_"}, + ) + def start_transform( + self, + *, + transform_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[str] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts one or more transforms. + + ``_ + + :param transform_id: Identifier for the transform. + :param from_: Restricts the set of transformed entities to those changed after + this time. Relative times like now-30d are supported. Only applicable for + continuous transforms. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __query["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stop_transform( + self, + *, + transform_id: str, + allow_no_match: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + force: t.Optional[bool] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + wait_for_checkpoint: t.Optional[bool] = None, + wait_for_completion: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops one or more transforms. + + ``_ + + :param transform_id: Identifier for the transform. To stop multiple transforms, + use a comma-separated list or a wildcard expression. To stop all transforms, + use `_all` or `*` as the identifier. + :param allow_no_match: Specifies what to do when the request: contains wildcard + expressions and there are no transforms that match; contains the `_all` string + or no identifiers and there are no matches; contains wildcard expressions + and there are only partial matches. If it is true, the API returns a successful + acknowledgement message when there are no matches. When there are only partial + matches, the API stops the appropriate transforms. If it is false, the request + returns a 404 status code when there are no matches or only partial matches. + :param force: If it is true, the API forcefully stops the transforms. + :param timeout: Period to wait for a response when `wait_for_completion` is `true`. + If no response is received before the timeout expires, the request returns + a timeout exception. However, the request continues processing and eventually + moves the transform to a STOPPED state. + :param wait_for_checkpoint: If it is true, the transform does not completely + stop until the current checkpoint is completed. If it is false, the transform + stops as soon as possible. + :param wait_for_completion: If it is true, the API blocks until the indexer state + completely stops. If it is false, the API returns immediately and the indexer + is stopped asynchronously in the background. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_stop" + __query: t.Dict[str, t.Any] = {} + if allow_no_match is not None: + __query["allow_no_match"] = allow_no_match + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if force is not None: + __query["force"] = force + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if wait_for_checkpoint is not None: + __query["wait_for_checkpoint"] = wait_for_checkpoint + if wait_for_completion is not None: + __query["wait_for_completion"] = wait_for_completion + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"_meta": "meta"}, + ) + def update_transform( + self, + *, + transform_id: str, + defer_validation: t.Optional[bool] = None, + description: t.Optional[str] = None, + dest: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + frequency: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + human: t.Optional[bool] = None, + meta: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + retention_policy: t.Optional[t.Union[None, t.Mapping[str, t.Any]]] = None, + settings: t.Optional[t.Mapping[str, t.Any]] = None, + source: t.Optional[t.Mapping[str, t.Any]] = None, + sync: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union["t.Literal[-1]", "t.Literal[0]", str]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Updates certain properties of a transform. + + ``_ + + :param transform_id: Identifier for the transform. + :param defer_validation: When true, deferrable validations are not run. This + behavior may be desired if the source index does not exist until after the + transform is created. + :param description: Free text description of the transform. + :param dest: The destination for the transform. + :param frequency: The interval between checks for changes in the source indices + when the transform is running continuously. Also determines the retry interval + in the event of transient failures while the transform is searching or indexing. + The minimum value is 1s and the maximum is 1h. + :param meta: Defines optional transform metadata. + :param retention_policy: Defines a retention policy for the transform. Data that + meets the defined criteria is deleted from the destination index. + :param settings: Defines optional transform settings. + :param source: The source of the data for the transform. + :param sync: Defines the properties transforms require to run continuously. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. + """ + if transform_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'transform_id'") + __path = f"/_transform/{_quote(transform_id)}/_update" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + if defer_validation is not None: + __query["defer_validation"] = defer_validation + if description is not None: + __body["description"] = description + if dest is not None: + __body["dest"] = dest + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if frequency is not None: + __body["frequency"] = frequency + if human is not None: + __query["human"] = human + if meta is not None: + __body["_meta"] = meta + if pretty is not None: + __query["pretty"] = pretty + if retention_policy is not None: + __body["retention_policy"] = retention_policy + if settings is not None: + __body["settings"] = settings + if source is not None: + __body["source"] = source + if sync is not None: + __body["sync"] = sync + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) diff --git a/elasticsearch_serverless/_sync/client/utils.py b/elasticsearch_serverless/_sync/client/utils.py new file mode 100644 index 0000000..1444adf --- /dev/null +++ b/elasticsearch_serverless/_sync/client/utils.py @@ -0,0 +1,443 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import base64 +import inspect +import warnings +from datetime import date, datetime +from functools import wraps +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Collection, + Dict, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from elastic_transport import ( + AsyncTransport, + HttpHeaders, + NodeConfig, + RequestsHttpNode, + SniffOptions, + Transport, +) +from elastic_transport.client_utils import ( + DEFAULT, + client_meta_version, + create_user_agent, + parse_cloud_id, + percent_encode, + url_to_node_config, +) + +from ..._version import __versionstr__ +from ...compat import to_bytes, to_str, warn_stacklevel + +if TYPE_CHECKING: + from ._base import NamespacedClient + +# parts of URL to be omitted +SKIP_IN_PATH: Collection[Any] = (None, "", b"", [], ()) + +# To be passed to 'client_meta_service' on the Transport +CLIENT_META_SERVICE = ("es", client_meta_version(__versionstr__)) + +# Default User-Agent used by the client +USER_AGENT = create_user_agent("elasticsearch-py", __versionstr__) + +_TYPE_HOSTS = Union[str, List[Union[str, Mapping[str, Union[str, int]], NodeConfig]]] + +_TYPE_ASYNC_SNIFF_CALLBACK = Callable[ + [AsyncTransport, SniffOptions], Awaitable[List[NodeConfig]] +] +_TYPE_SYNC_SNIFF_CALLBACK = Callable[[Transport, SniffOptions], List[NodeConfig]] + +_TRANSPORT_OPTIONS = { + "api_key", + "http_auth", + "request_timeout", + "opaque_id", + "headers", + "ignore", +} + +F = TypeVar("F", bound=Callable[..., Any]) + + +def client_node_configs( + hosts: Optional[_TYPE_HOSTS], + cloud_id: Optional[str], + requests_session_auth: Optional[Any] = None, + **kwargs: Any, +) -> List[NodeConfig]: + if cloud_id is not None: + if hosts is not None: + raise ValueError( + "The 'cloud_id' and 'hosts' parameters are mutually exclusive" + ) + node_configs = cloud_id_to_node_configs(cloud_id) + else: + assert hosts is not None + node_configs = hosts_to_node_configs(hosts) + + # Remove all values which are 'DEFAULT' to avoid overwriting actual defaults. + node_options = {k: v for k, v in kwargs.items() if v is not DEFAULT} + + # Set the 'User-Agent' default header. + headers = HttpHeaders(node_options.pop("headers", ())) + headers.setdefault("user-agent", USER_AGENT) + node_options["headers"] = headers + + # If a custom Requests AuthBase is passed we set that via '_extras'. + if requests_session_auth is not None: + node_options.setdefault("_extras", {})[ + "requests.session.auth" + ] = requests_session_auth + + def apply_node_options(node_config: NodeConfig) -> NodeConfig: + """Needs special handling of headers since .replace() wipes out existing headers""" + nonlocal node_options + headers = node_config.headers.copy() # type: ignore[attr-defined] + + headers_to_add = node_options.pop("headers", ()) + if headers_to_add: + headers.update(headers_to_add) + + headers.setdefault("user-agent", USER_AGENT) + headers.freeze() + node_options["headers"] = headers + return node_config.replace(**node_options) + + return [apply_node_options(node_config) for node_config in node_configs] + + +def hosts_to_node_configs(hosts: _TYPE_HOSTS) -> List[NodeConfig]: + """Transforms the many formats of 'hosts' into NodeConfigs""" + + # To make the logic here simpler we reroute everything to be List[X] + if not isinstance(hosts, (tuple, list)): + return hosts_to_node_configs([hosts]) + + node_configs: List[NodeConfig] = [] + for host in hosts: + if isinstance(host, NodeConfig): + node_configs.append(host) + + elif isinstance(host, str): + node_configs.append(url_to_node_config(host)) + + elif isinstance(host, Mapping): + node_configs.append(host_mapping_to_node_config(host)) + else: + raise ValueError( + "'hosts' must be a list of URLs, NodeConfigs, or dictionaries" + ) + + return node_configs + + +def host_mapping_to_node_config(host: Mapping[str, Union[str, int]]) -> NodeConfig: + """Converts an old-style dictionary host specification to a NodeConfig""" + + allow_hosts_keys = { + "scheme", + "host", + "port", + "path_prefix", + "url_prefix", + "use_ssl", + } + disallowed_keys = set(host.keys()).difference(allow_hosts_keys) + if disallowed_keys: + bad_keys_used = "', '".join(sorted(disallowed_keys)) + allowed_keys = "', '".join(sorted(allow_hosts_keys)) + raise ValueError( + f"Can't specify the options '{bad_keys_used}' via a " + f"dictionary in 'hosts', only '{allowed_keys}' options " + "are allowed" + ) + + options = dict(host) + + # Handle the deprecated option 'use_ssl' + if "use_ssl" in options: + use_ssl = options.pop("use_ssl") + if not isinstance(use_ssl, bool): + raise TypeError("'use_ssl' must be of type 'bool'") + + # Ensure the user isn't specifying scheme=http use_ssl=True or vice-versa + if "scheme" in options and (options["scheme"] == "https") != use_ssl: + raise ValueError( + f"Cannot specify conflicting options 'scheme={options['scheme']}' " + f"and 'use_ssl={use_ssl}'. Use 'scheme' only instead" + ) + + warnings.warn( + "The 'use_ssl' option is no longer needed as specifying a 'scheme' is now required", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + options.setdefault("scheme", "https" if use_ssl else "http") + + # Handle the deprecated option 'url_prefix' + if "url_prefix" in options: + if "path_prefix" in options: + raise ValueError( + "Cannot specify conflicting options 'url_prefix' and " + "'path_prefix'. Use 'path_prefix' only instead" + ) + + warnings.warn( + "The 'url_prefix' option is deprecated in favor of 'path_prefix'", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + options["path_prefix"] = options.pop("url_prefix") + + return NodeConfig(**options) # type: ignore + + +def cloud_id_to_node_configs(cloud_id: str) -> List[NodeConfig]: + """Transforms an Elastic Cloud ID into a NodeConfig""" + es_addr = parse_cloud_id(cloud_id).es_address + if es_addr is None or not all(es_addr): + raise ValueError("Cloud ID missing host and port information for Elasticsearch") + host, port = es_addr + return [ + NodeConfig( + scheme="https", + host=host, + port=port, + http_compress=True, + ) + ] + + +def _base64_auth_header(auth_value: Union[str, List[str], Tuple[str, str]]) -> str: + """Takes either a 2-tuple or a base64-encoded string + and returns a base64-encoded string to be used + as an HTTP authorization header. + """ + if isinstance(auth_value, (list, tuple)): + return base64.b64encode(to_bytes(":".join(auth_value))).decode("ascii") + return to_str(auth_value) + + +def _escape(value: Any) -> str: + """ + Escape a single value of a URL string or a query parameter. If it is a list + or tuple, turn it into a comma-separated string first. + """ + + # make sequences into comma-separated stings + if isinstance(value, (list, tuple)): + value = ",".join([_escape(item) for item in value]) + + # dates and datetimes into isoformat + elif isinstance(value, (date, datetime)): + value = value.isoformat() + + # make bools into true/false strings + elif isinstance(value, bool): + value = str(value).lower() + + elif isinstance(value, bytes): + return value.decode("utf-8", "surrogatepass") + + if not isinstance(value, str): + return str(value) + return value + + +def _quote(value: Any) -> str: + return percent_encode(_escape(value), ",*") + + +def _quote_query(query: Mapping[str, Any]) -> str: + return "&".join([f"{k}={_quote(v)}" for k, v in query.items()]) + + +def _merge_kwargs_no_duplicates(kwargs: Dict[str, Any], values: Dict[str, Any]) -> None: + for key, val in values.items(): + if key in kwargs: + raise ValueError( + f"Received multiple values for '{key}', specify parameters " + "directly instead of using 'body' or 'params'" + ) + kwargs[key] = val + + +def _rewrite_parameters( + body_name: Optional[str] = None, + body_fields: bool = False, + parameter_aliases: Optional[Dict[str, str]] = None, + ignore_deprecated_options: Optional[Set[str]] = None, +) -> Callable[[F], F]: + def wrapper(api: F) -> F: + @wraps(api) + def wrapped(*args: Any, **kwargs: Any) -> Any: + nonlocal api, body_name, body_fields + + # Let's give a nicer error message when users pass positional arguments. + if len(args) >= 2: + raise TypeError( + "Positional arguments can't be used with Elasticsearch API methods. " + "Instead only use keyword arguments." + ) + + # We merge 'params' first as transport options can be specified using params. + if "params" in kwargs and ( + not ignore_deprecated_options + or "params" not in ignore_deprecated_options + ): + params = kwargs.pop("params") + if params: + if not hasattr(params, "items"): + raise ValueError( + "Couldn't merge 'params' with other parameters as it wasn't a mapping. " + "Instead of using 'params' use individual API parameters" + ) + warnings.warn( + "The 'params' parameter is deprecated and will be removed " + "in a future version. Instead use individual parameters.", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + _merge_kwargs_no_duplicates(kwargs, params) + + maybe_transport_options = _TRANSPORT_OPTIONS.intersection(kwargs) + if maybe_transport_options: + transport_options = {} + for option in maybe_transport_options: + if ( + ignore_deprecated_options + and option in ignore_deprecated_options + ): + continue + try: + option_rename = option + if option == "ignore": + option_rename = "ignore_status" + transport_options[option_rename] = kwargs.pop(option) + except KeyError: + pass + if transport_options: + warnings.warn( + "Passing transport options in the API method is deprecated. Use 'Elasticsearch.options()' instead.", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + client = args[0] + + # Namespaced clients need to unwrapped. + namespaced_client: Optional[Type["NamespacedClient"]] = None + if hasattr(client, "_client"): + namespaced_client = type(client) + client = client._client + + client = client.options(**transport_options) + + # Re-wrap the client if we unwrapped due to being namespaced. + if namespaced_client is not None: + client = namespaced_client(client) + args = (client,) + args[1:] + + if "body" in kwargs and ( + not ignore_deprecated_options or "body" not in ignore_deprecated_options + ): + body = kwargs.pop("body") + if body is not None: + if body_name: + if body_name in kwargs: + raise TypeError( + f"Can't use '{body_name}' and 'body' parameters together because '{body_name}' " + "is an alias for 'body'. Instead you should only use the " + f"'{body_name}' parameter. See https://github.com/elastic/elasticsearch-py/" + "issues/1698 for more information" + ) + + warnings.warn( + "The 'body' parameter is deprecated and will be removed " + f"in a future version. Instead use the '{body_name}' parameter. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 " + "for more information", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + kwargs[body_name] = body + + elif body_fields: + if not hasattr(body, "items"): + raise ValueError( + "Couldn't merge 'body' with other parameters as it wasn't a mapping. " + "Instead of using 'body' use individual API parameters" + ) + warnings.warn( + "The 'body' parameter is deprecated and will be removed " + "in a future version. Instead use individual parameters.", + category=DeprecationWarning, + stacklevel=warn_stacklevel(), + ) + _merge_kwargs_no_duplicates(kwargs, body) + + if parameter_aliases: + for alias, rename_to in parameter_aliases.items(): + try: + kwargs[rename_to] = kwargs.pop(alias) + except KeyError: + pass + + return api(*args, **kwargs) + + return wrapped # type: ignore[return-value] + + return wrapper + + +def is_requests_http_auth(http_auth: Any) -> bool: + """Detect if an http_auth value is a custom Requests auth object""" + try: + from requests.auth import AuthBase + + return isinstance(http_auth, AuthBase) + except ImportError: + pass + return False + + +def is_requests_node_class(node_class: Any) -> bool: + """Detect if 'RequestsHttpNode' would be used given the setting of 'node_class'""" + return ( + node_class is not None + and node_class is not DEFAULT + and ( + node_class == "requests" + or ( + inspect.isclass(node_class) and issubclass(node_class, RequestsHttpNode) + ) + ) + ) diff --git a/elasticsearch_serverless/_sync/client/watcher.py b/elasticsearch_serverless/_sync/client/watcher.py new file mode 100644 index 0000000..2e1d866 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/watcher.py @@ -0,0 +1,607 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters + + +class WatcherClient(NamespacedClient): + @_rewrite_parameters() + def ack_watch( + self, + *, + watch_id: str, + action_id: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Acknowledges a watch, manually throttling the execution of the watch's actions. + + ``_ + + :param watch_id: Watch ID + :param action_id: A comma-separated list of the action ids to be acked + """ + if watch_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'watch_id'") + if watch_id not in SKIP_IN_PATH and action_id not in SKIP_IN_PATH: + __path = f"/_watcher/watch/{_quote(watch_id)}/_ack/{_quote(action_id)}" + elif watch_id not in SKIP_IN_PATH: + __path = f"/_watcher/watch/{_quote(watch_id)}/_ack" + else: + raise ValueError("Couldn't find a path for the given parameters") + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def activate_watch( + self, + *, + watch_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Activates a currently inactive watch. + + ``_ + + :param watch_id: Watch ID + """ + if watch_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'watch_id'") + __path = f"/_watcher/watch/{_quote(watch_id)}/_activate" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def deactivate_watch( + self, + *, + watch_id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Deactivates a currently active watch. + + ``_ + + :param watch_id: Watch ID + """ + if watch_id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'watch_id'") + __path = f"/_watcher/watch/{_quote(watch_id)}/_deactivate" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def delete_watch( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Removes a watch from Watcher. + + ``_ + + :param id: Watch ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_watcher/watch/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "DELETE", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def execute_watch( + self, + *, + id: t.Optional[str] = None, + action_modes: t.Optional[ + t.Mapping[ + str, + t.Union[ + "t.Literal['execute', 'force_execute', 'force_simulate', 'simulate', 'skip']", + str, + ], + ] + ] = None, + alternative_input: t.Optional[t.Mapping[str, t.Any]] = None, + debug: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + ignore_condition: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + record_execution: t.Optional[bool] = None, + simulated_actions: t.Optional[t.Mapping[str, t.Any]] = None, + trigger_data: t.Optional[t.Mapping[str, t.Any]] = None, + watch: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Forces the execution of a stored watch. + + ``_ + + :param id: Identifier for the watch. + :param action_modes: Determines how to handle the watch actions as part of the + watch execution. + :param alternative_input: When present, the watch uses this object as a payload + instead of executing its own input. + :param debug: Defines whether the watch runs in debug mode. + :param ignore_condition: When set to `true`, the watch execution uses the always + condition. This can also be specified as an HTTP parameter. + :param record_execution: When set to `true`, the watch record representing the + watch execution result is persisted to the `.watcher-history` index for the + current time. In addition, the status of the watch is updated, possibly throttling + subsequent executions. This can also be specified as an HTTP parameter. + :param simulated_actions: + :param trigger_data: This structure is parsed as the data of the trigger event + that will be used during the watch execution + :param watch: When present, this watch is used instead of the one specified in + the request. This watch is not persisted to the index and record_execution + cannot be set. + """ + if id not in SKIP_IN_PATH: + __path = f"/_watcher/watch/{_quote(id)}/_execute" + else: + __path = "/_watcher/watch/_execute" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if action_modes is not None: + __body["action_modes"] = action_modes + if alternative_input is not None: + __body["alternative_input"] = alternative_input + if debug is not None: + __query["debug"] = debug + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if ignore_condition is not None: + __body["ignore_condition"] = ignore_condition + if pretty is not None: + __query["pretty"] = pretty + if record_execution is not None: + __body["record_execution"] = record_execution + if simulated_actions is not None: + __body["simulated_actions"] = simulated_actions + if trigger_data is not None: + __body["trigger_data"] = trigger_data + if watch is not None: + __body["watch"] = watch + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def get_watch( + self, + *, + id: str, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves a watch by its ID. + + ``_ + + :param id: Watch ID + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_watcher/watch/{_quote(id)}" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters( + body_fields=True, + ) + def put_watch( + self, + *, + id: str, + actions: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, + active: t.Optional[bool] = None, + condition: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + if_primary_term: t.Optional[int] = None, + if_seq_no: t.Optional[int] = None, + input: t.Optional[t.Mapping[str, t.Any]] = None, + metadata: t.Optional[t.Mapping[str, t.Any]] = None, + pretty: t.Optional[bool] = None, + throttle_period: t.Optional[str] = None, + transform: t.Optional[t.Mapping[str, t.Any]] = None, + trigger: t.Optional[t.Mapping[str, t.Any]] = None, + version: t.Optional[int] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Creates a new watch, or updates an existing one. + + ``_ + + :param id: Watch ID + :param actions: + :param active: Specify whether the watch is in/active by default + :param condition: + :param if_primary_term: only update the watch if the last operation that has + changed the watch has the specified primary term + :param if_seq_no: only update the watch if the last operation that has changed + the watch has the specified sequence number + :param input: + :param metadata: + :param throttle_period: + :param transform: + :param trigger: + :param version: Explicit version number for concurrency control + """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'id'") + __path = f"/_watcher/watch/{_quote(id)}" + __body: t.Dict[str, t.Any] = {} + __query: t.Dict[str, t.Any] = {} + if actions is not None: + __body["actions"] = actions + if active is not None: + __query["active"] = active + if condition is not None: + __body["condition"] = condition + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if if_primary_term is not None: + __query["if_primary_term"] = if_primary_term + if if_seq_no is not None: + __query["if_seq_no"] = if_seq_no + if input is not None: + __body["input"] = input + if metadata is not None: + __body["metadata"] = metadata + if pretty is not None: + __query["pretty"] = pretty + if throttle_period is not None: + __body["throttle_period"] = throttle_period + if transform is not None: + __body["transform"] = transform + if trigger is not None: + __body["trigger"] = trigger + if version is not None: + __query["version"] = version + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "PUT", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters( + body_fields=True, + parameter_aliases={"from": "from_"}, + ) + def query_watches( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + from_: t.Optional[int] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + query: t.Optional[t.Mapping[str, t.Any]] = None, + search_after: t.Optional[ + t.Union[ + t.List[t.Union[None, bool, float, int, str, t.Any]], + t.Tuple[t.Union[None, bool, float, int, str, t.Any], ...], + ] + ] = None, + size: t.Optional[int] = None, + sort: t.Optional[ + t.Union[ + t.Union[str, t.Mapping[str, t.Any]], + t.Union[ + t.List[t.Union[str, t.Mapping[str, t.Any]]], + t.Tuple[t.Union[str, t.Mapping[str, t.Any]], ...], + ], + ] + ] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves stored watches. + + ``_ + + :param from_: The offset from the first result to fetch. Needs to be non-negative. + :param query: Optional, query filter watches to be returned. + :param search_after: Optional search After to do pagination using last hit’s + sort values. + :param size: The number of hits to return. Needs to be non-negative. + :param sort: Optional sort definition. + """ + __path = "/_watcher/_query/watches" + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = {} + # The 'sort' parameter with a colon can't be encoded to the body. + if sort is not None and ( + (isinstance(sort, str) and ":" in sort) + or ( + isinstance(sort, (list, tuple)) + and all(isinstance(_x, str) for _x in sort) + and any(":" in _x for _x in sort) + ) + ): + __query["sort"] = sort + sort = None + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if from_ is not None: + __body["from"] = from_ + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if query is not None: + __body["query"] = query + if search_after is not None: + __body["search_after"] = search_after + if size is not None: + __body["size"] = size + if sort is not None: + __body["sort"] = sort + if not __body: + __body = None # type: ignore[assignment] + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers, body=__body + ) + + @_rewrite_parameters() + def start( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Starts Watcher if it is not already running. + + ``_ + """ + __path = "/_watcher/_start" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stats( + self, + *, + metric: t.Optional[ + t.Union[ + t.Union[ + "t.Literal['_all', 'current_watches', 'pending_watches', 'queued_watches']", + str, + ], + t.Union[ + t.List[ + t.Union[ + "t.Literal['_all', 'current_watches', 'pending_watches', 'queued_watches']", + str, + ] + ], + t.Tuple[ + t.Union[ + "t.Literal['_all', 'current_watches', 'pending_watches', 'queued_watches']", + str, + ], + ..., + ], + ], + ] + ] = None, + emit_stacktraces: t.Optional[bool] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves the current Watcher metrics. + + ``_ + + :param metric: Defines which additional metrics are included in the response. + :param emit_stacktraces: Defines whether stack traces are generated for each + watch that is running. + """ + if metric not in SKIP_IN_PATH: + __path = f"/_watcher/stats/{_quote(metric)}" + else: + __path = "/_watcher/stats" + __query: t.Dict[str, t.Any] = {} + if emit_stacktraces is not None: + __query["emit_stacktraces"] = emit_stacktraces + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def stop( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Stops Watcher if it is running. + + ``_ + """ + __path = "/_watcher/_stop" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "POST", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_sync/client/xpack.py b/elasticsearch_serverless/_sync/client/xpack.py new file mode 100644 index 0000000..b1fbdd5 --- /dev/null +++ b/elasticsearch_serverless/_sync/client/xpack.py @@ -0,0 +1,111 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import _rewrite_parameters + + +class XPackClient(NamespacedClient): + def __getattr__(self, attr_name: str) -> t.Any: + return getattr(self.client, attr_name) + + # AUTO-GENERATED-API-DEFINITIONS # + + @_rewrite_parameters() + def info( + self, + *, + accept_enterprise: t.Optional[bool] = None, + categories: t.Optional[t.Union[t.List[str], t.Tuple[str, ...]]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves information about the installed X-Pack features. + + ``_ + + :param accept_enterprise: If this param is used it must be set to true + :param categories: A comma-separated list of the information categories to include + in the response. For example, `build,license,features`. + """ + __path = "/_xpack" + __query: t.Dict[str, t.Any] = {} + if accept_enterprise is not None: + __query["accept_enterprise"] = accept_enterprise + if categories is not None: + __query["categories"] = categories + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) + + @_rewrite_parameters() + def usage( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[ + t.Union[str, t.Union[t.List[str], t.Tuple[str, ...]]] + ] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[ + t.Union["t.Literal[-1]", "t.Literal[0]", str] + ] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + Retrieves usage information about the installed X-Pack features. + + ``_ + + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + __path = "/_xpack/usage" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", __path, params=__query, headers=__headers + ) diff --git a/elasticsearch_serverless/_utils.py b/elasticsearch_serverless/_utils.py new file mode 100644 index 0000000..fae2157 --- /dev/null +++ b/elasticsearch_serverless/_utils.py @@ -0,0 +1,34 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import re +from typing import Any, Dict + + +def fixup_module_metadata(module_name: str, namespace: Dict[str, Any]) -> None: + # Yoinked from python-trio/outcome, thanks Nathaniel! License: MIT + def fix_one(obj: Any) -> None: + mod = getattr(obj, "__module__", None) + if mod is not None and re.match(r"^elasticsearch[0-9]*\.", mod) is not None: + obj.__module__ = module_name + if isinstance(obj, type): + for attr_value in obj.__dict__.values(): + fix_one(attr_value) + + for objname in namespace["__all__"]: + obj = namespace[objname] + fix_one(obj) diff --git a/elasticsearch_serverless/_version.py b/elasticsearch_serverless/_version.py new file mode 100644 index 0000000..36d1fe9 --- /dev/null +++ b/elasticsearch_serverless/_version.py @@ -0,0 +1,18 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +__versionstr__ = "8.10.0" diff --git a/elasticsearch_serverless/client.py b/elasticsearch_serverless/client.py new file mode 100644 index 0000000..a140c9b --- /dev/null +++ b/elasticsearch_serverless/client.py @@ -0,0 +1,112 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import warnings + +from ._sync.client import Elasticsearch as Elasticsearch # noqa: F401 +from ._sync.client.async_search import ( # noqa: F401 + AsyncSearchClient as AsyncSearchClient, +) +from ._sync.client.autoscaling import ( # noqa: F401 + AutoscalingClient as AutoscalingClient, +) +from ._sync.client.cat import CatClient as CatClient # noqa: F401 +from ._sync.client.ccr import CcrClient as CcrClient # noqa: F401 +from ._sync.client.cluster import ClusterClient as ClusterClient # noqa: F401 +from ._sync.client.dangling_indices import ( # noqa: F401 + DanglingIndicesClient as DanglingIndicesClient, +) +from ._sync.client.enrich import EnrichClient as EnrichClient # noqa: F401 +from ._sync.client.eql import EqlClient as EqlClient # noqa: F401 +from ._sync.client.features import FeaturesClient as FeaturesClient # noqa: F401 +from ._sync.client.fleet import FleetClient as FleetClient # noqa: F401 +from ._sync.client.graph import GraphClient as GraphClient # noqa: F401 +from ._sync.client.ilm import IlmClient as IlmClient # noqa: F401 +from ._sync.client.indices import IndicesClient as IndicesClient # noqa: F401 +from ._sync.client.ingest import IngestClient as IngestClient # noqa: F401 +from ._sync.client.license import LicenseClient as LicenseClient # noqa: F401 +from ._sync.client.logstash import LogstashClient as LogstashClient # noqa: F401 +from ._sync.client.migration import MigrationClient as MigrationClient # noqa: F401 +from ._sync.client.ml import MlClient as MlClient # noqa: F401 +from ._sync.client.monitoring import MonitoringClient as MonitoringClient # noqa: F401 +from ._sync.client.nodes import NodesClient as NodesClient # noqa: F401 +from ._sync.client.rollup import RollupClient as RollupClient # noqa: F401 +from ._sync.client.searchable_snapshots import ( # noqa: F401 + SearchableSnapshotsClient as SearchableSnapshotsClient, +) +from ._sync.client.security import SecurityClient as SecurityClient # noqa: F401 +from ._sync.client.shutdown import ShutdownClient as ShutdownClient # noqa: F401 +from ._sync.client.slm import SlmClient as SlmClient # noqa: F401 +from ._sync.client.snapshot import SnapshotClient as SnapshotClient # noqa: F401 +from ._sync.client.sql import SqlClient as SqlClient # noqa: F401 +from ._sync.client.ssl import SslClient as SslClient # noqa: F401 +from ._sync.client.tasks import TasksClient as TasksClient # noqa: F401 +from ._sync.client.text_structure import ( # noqa: F401 + TextStructureClient as TextStructureClient, +) +from ._sync.client.transform import TransformClient as TransformClient # noqa: F401 +from ._sync.client.watcher import WatcherClient as WatcherClient # noqa: F401 +from ._sync.client.xpack import XPackClient as XPackClient # noqa: F401 +from ._utils import fixup_module_metadata + +# This file exists for backwards compatibility. +warnings.warn( + "Importing from the 'elasticsearch.client' module is deprecated. " + "Instead use 'elasticsearch' module for importing the client.", + category=DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "AsyncSearchClient", + "AutoscalingClient", + "CatClient", + "CcrClient", + "ClusterClient", + "DanglingIndicesClient", + "Elasticsearch", + "EnrichClient", + "EqlClient", + "FeaturesClient", + "FleetClient", + "GraphClient", + "IlmClient", + "IndicesClient", + "IngestClient", + "LicenseClient", + "LogstashClient", + "MigrationClient", + "MlClient", + "MonitoringClient", + "NodesClient", + "RollupClient", + "SearchableSnapshotsClient", + "SecurityClient", + "ShutdownClient", + "SlmClient", + "SnapshotClient", + "SqlClient", + "SslClient", + "TasksClient", + "TextStructureClient", + "TransformClient", + "WatcherClient", + "XPackClient", +] + +fixup_module_metadata(__name__, globals()) +del fixup_module_metadata diff --git a/elasticsearch_serverless/compat.py b/elasticsearch_serverless/compat.py new file mode 100644 index 0000000..7639fd2 --- /dev/null +++ b/elasticsearch_serverless/compat.py @@ -0,0 +1,79 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import inspect +import sys +from pathlib import Path +from typing import Tuple, Type, Union + +string_types: Tuple[Type[str], Type[bytes]] = (str, bytes) + + +def to_str(x: Union[str, bytes], encoding: str = "ascii") -> str: + if not isinstance(x, str): + return x.decode(encoding) + return x + + +def to_bytes(x: Union[str, bytes], encoding: str = "ascii") -> bytes: + if not isinstance(x, bytes): + return x.encode(encoding) + return x + + +def warn_stacklevel() -> int: + """Dynamically determine warning stacklevel for warnings based on the call stack""" + try: + # Grab the root module from the current module '__name__' + module_name = __name__.partition(".")[0] + module_path = Path(sys.modules[module_name].__file__) # type: ignore[arg-type] + + # If the module is a folder we're looking at + # subdirectories, otherwise we're looking for + # an exact match. + module_is_folder = module_path.name == "__init__.py" + if module_is_folder: + module_path = module_path.parent + + # Look through frames until we find a file that + # isn't a part of our module, then return that stacklevel. + for level, frame in enumerate(inspect.stack()): + # Garbage collecting frames + frame_filename = Path(frame.filename) + del frame + + if ( + # If the module is a folder we look at subdirectory + module_is_folder + and module_path not in frame_filename.parents + ) or ( + # Otherwise we're looking for an exact match. + not module_is_folder + and module_path != frame_filename + ): + return level + except KeyError: + pass + return 0 + + +__all__ = [ + "string_types", + "to_str", + "to_bytes", + "warn_stacklevel", +] diff --git a/elasticsearch_serverless/exceptions.py b/elasticsearch_serverless/exceptions.py new file mode 100644 index 0000000..f587067 --- /dev/null +++ b/elasticsearch_serverless/exceptions.py @@ -0,0 +1,129 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from typing import Any, Dict, Type + +from elastic_transport import ApiError as _ApiError +from elastic_transport import ConnectionError as ConnectionError +from elastic_transport import ConnectionTimeout as ConnectionTimeout +from elastic_transport import SerializationError as SerializationError +from elastic_transport import TlsError as SSLError +from elastic_transport import TransportError as TransportError +from elastic_transport import TransportWarning + +__all__ = [ + "SerializationError", + "TransportError", + "ConnectionError", + "SSLError", + "ConnectionTimeout", + "AuthorizationException", + "AuthenticationException", + "NotFoundError", + "ConflictError", + "BadRequestError", +] + + +class ApiError(_ApiError): + @property + def status_code(self) -> int: + """Backwards-compatible way to access ``self.meta.status``""" + return self.meta.status + + @property + def error(self) -> str: + """Backwards-compatible way to access ``self.message``""" + return self.message + + @property + def info(self) -> Any: + """Backwards-compatible way to access ``self.body``""" + return self.body + + def __str__(self) -> str: + cause = "" + try: + if self.body and isinstance(self.body, dict) and "error" in self.body: + if isinstance(self.body["error"], dict): + root_cause = self.body["error"]["root_cause"][0] + cause = ", ".join( + filter( + None, + [ + repr(root_cause["reason"]), + root_cause.get("resource.id"), + root_cause.get("resource.type"), + ], + ) + ) + + else: + cause = repr(self.body["error"]) + except LookupError: + pass + msg = ", ".join(filter(None, [str(self.status_code), repr(self.error), cause])) + return f"{self.__class__.__name__}({msg})" + + +class UnsupportedProductError(ApiError): + """Error which is raised when the client detects + it's not connected to a supported product. + """ + + def __str__(self) -> str: + return self.message + + +class NotFoundError(ApiError): + """Exception representing a 404 status code.""" + + +class ConflictError(ApiError): + """Exception representing a 409 status code.""" + + +class BadRequestError(ApiError): + """Exception representing a 400 status code.""" + + +class AuthenticationException(ApiError): + """Exception representing a 401 status code.""" + + +class AuthorizationException(ApiError): + """Exception representing a 403 status code.""" + + +class ElasticsearchWarning(TransportWarning): + """Warning that is raised when a deprecated option + or incorrect usage is flagged via the 'Warning' HTTP header. + """ + + +# Aliases for backwards compatibility +ElasticsearchDeprecationWarning = ElasticsearchWarning +RequestError = BadRequestError + + +HTTP_EXCEPTIONS: Dict[int, Type[ApiError]] = { + 400: BadRequestError, + 401: AuthenticationException, + 403: AuthorizationException, + 404: NotFoundError, + 409: ConflictError, +} diff --git a/elasticsearch_serverless/helpers/__init__.py b/elasticsearch_serverless/helpers/__init__.py new file mode 100644 index 0000000..6767693 --- /dev/null +++ b/elasticsearch_serverless/helpers/__init__.py @@ -0,0 +1,41 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from .._async.helpers import async_bulk, async_reindex, async_scan, async_streaming_bulk +from .._utils import fixup_module_metadata +from .actions import _chunk_actions # noqa: F401 +from .actions import _process_bulk_chunk # noqa: F401 +from .actions import bulk, expand_action, parallel_bulk, reindex, scan, streaming_bulk +from .errors import BulkIndexError, ScanError + +__all__ = [ + "BulkIndexError", + "ScanError", + "expand_action", + "streaming_bulk", + "bulk", + "parallel_bulk", + "scan", + "reindex", + "async_scan", + "async_bulk", + "async_reindex", + "async_streaming_bulk", +] + +fixup_module_metadata(__name__, globals()) +del fixup_module_metadata diff --git a/elasticsearch_serverless/helpers/actions.py b/elasticsearch_serverless/helpers/actions.py new file mode 100644 index 0000000..a218c5e --- /dev/null +++ b/elasticsearch_serverless/helpers/actions.py @@ -0,0 +1,845 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import logging +import time +from operator import methodcaller +from queue import Queue +from typing import ( + Any, + Callable, + Collection, + Dict, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + Optional, + Tuple, + Union, +) + +from .. import Elasticsearch +from ..compat import to_bytes +from ..exceptions import ApiError, NotFoundError, TransportError +from ..serializer import Serializer +from .errors import BulkIndexError, ScanError + +logger = logging.getLogger("elasticsearch.helpers") + +_TYPE_BULK_ACTION = Union[bytes, str, Dict[str, Any]] +_TYPE_BULK_ACTION_HEADER = Dict[str, Any] +_TYPE_BULK_ACTION_BODY = Union[None, bytes, Dict[str, Any]] +_TYPE_BULK_ACTION_HEADER_AND_BODY = Tuple[ + _TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY +] + + +def expand_action(data: _TYPE_BULK_ACTION) -> _TYPE_BULK_ACTION_HEADER_AND_BODY: + """ + From one document or action definition passed in by the user extract the + action/data lines needed for elasticsearch's + :meth:`~elasticsearch.Elasticsearch.bulk` api. + """ + # when given a string, assume user wants to index raw json + if isinstance(data, (bytes, str)): + return {"index": {}}, to_bytes(data, "utf-8") + + # make sure we don't alter the action + data = data.copy() + op_type: str = data.pop("_op_type", "index") + action: Dict[str, Any] = {op_type: {}} + + # If '_source' is a dict use it for source + # otherwise if op_type == 'update' then + # '_source' should be in the metadata. + if ( + op_type == "update" + and "_source" in data + and not isinstance(data["_source"], Mapping) + ): + action[op_type]["_source"] = data.pop("_source") + + for key in ( + "_id", + "_index", + "_if_seq_no", + "_if_primary_term", + "_parent", + "_percolate", + "_retry_on_conflict", + "_routing", + "_timestamp", + "_type", + "_version", + "_version_type", + "if_seq_no", + "if_primary_term", + "parent", + "pipeline", + "retry_on_conflict", + "routing", + "version", + "version_type", + ): + if key in data: + if key in { + "_if_seq_no", + "_if_primary_term", + "_parent", + "_retry_on_conflict", + "_routing", + "_version", + "_version_type", + }: + action[op_type][key[1:]] = data.pop(key) + else: + action[op_type][key] = data.pop(key) + + # no data payload for delete + if op_type == "delete": + return action, None + + return action, data.get("_source", data) + + +class _ActionChunker: + def __init__( + self, chunk_size: int, max_chunk_bytes: int, serializer: Serializer + ) -> None: + self.chunk_size = chunk_size + self.max_chunk_bytes = max_chunk_bytes + self.serializer = serializer + + self.size = 0 + self.action_count = 0 + self.bulk_actions: List[bytes] = [] + self.bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ] = [] + + def feed( + self, action: _TYPE_BULK_ACTION_HEADER, data: _TYPE_BULK_ACTION_BODY + ) -> Optional[ + Tuple[ + List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + List[bytes], + ] + ]: + ret = None + raw_action = action + raw_data = data + action_bytes = to_bytes(self.serializer.dumps(action), "utf-8") + # +1 to account for the trailing new line character + cur_size = len(action_bytes) + 1 + + data_bytes: Optional[bytes] + if data is not None: + data_bytes = to_bytes(self.serializer.dumps(data), "utf-8") + cur_size += len(data_bytes) + 1 + else: + data_bytes = None + + # full chunk, send it and start a new one + if self.bulk_actions and ( + self.size + cur_size > self.max_chunk_bytes + or self.action_count == self.chunk_size + ): + ret = (self.bulk_data, self.bulk_actions) + self.bulk_actions = [] + self.bulk_data = [] + self.size = 0 + self.action_count = 0 + + self.bulk_actions.append(action_bytes) + if data_bytes is not None: + self.bulk_actions.append(data_bytes) + self.bulk_data.append((raw_action, raw_data)) + else: + self.bulk_data.append((raw_action,)) + + self.size += cur_size + self.action_count += 1 + return ret + + def flush( + self, + ) -> Optional[ + Tuple[ + List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + List[bytes], + ] + ]: + ret = None + if self.bulk_actions: + ret = (self.bulk_data, self.bulk_actions) + self.bulk_actions = [] + self.bulk_data = [] + return ret + + +def _chunk_actions( + actions: Iterable[_TYPE_BULK_ACTION_HEADER_AND_BODY], + chunk_size: int, + max_chunk_bytes: int, + serializer: Serializer, +) -> Iterable[ + Tuple[ + List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + List[bytes], + ] +]: + """ + Split actions into chunks by number or size, serialize them into strings in + the process. + """ + chunker = _ActionChunker( + chunk_size=chunk_size, max_chunk_bytes=max_chunk_bytes, serializer=serializer + ) + for action, data in actions: + ret = chunker.feed(action, data) + if ret: + yield ret + ret = chunker.flush() + if ret: + yield ret + + +def _process_bulk_chunk_success( + resp: Dict[str, Any], + bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + ignore_status: Collection[int], + raise_on_error: bool = True, +) -> Iterator[Tuple[bool, Dict[str, Any]]]: + # if raise on error is set, we need to collect errors per chunk before raising them + errors = [] + + # go through request-response pairs and detect failures + for data, (op_type, item) in zip( + bulk_data, map(methodcaller("popitem"), resp["items"]) + ): + status_code = item.get("status", 500) + + ok = 200 <= status_code < 300 + if not ok and raise_on_error and status_code not in ignore_status: + # include original document source + if len(data) > 1: + item["data"] = data[1] # type: ignore[misc] + errors.append({op_type: item}) + + if ok or not errors: + # if we are not just recording all errors to be able to raise + # them all at once, yield items individually + yield ok, {op_type: item} + + if errors: + raise BulkIndexError(f"{len(errors)} document(s) failed to index.", errors) + + +def _process_bulk_chunk_error( + error: ApiError, + bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + ignore_status: Collection[int], + raise_on_exception: bool = True, + raise_on_error: bool = True, +) -> Iterable[Tuple[bool, Dict[str, Any]]]: + # default behavior - just propagate exception + if raise_on_exception and error.status_code not in ignore_status: + raise error + + # if we are not propagating, mark all actions in current chunk as failed + err_message = str(error) + exc_errors = [] + + for data in bulk_data: + # collect all the information about failed actions + op_type, action = data[0].copy().popitem() + info = {"error": err_message, "status": error.status_code, "exception": error} + if op_type != "delete" and len(data) > 1: + info["data"] = data[1] # type: ignore[misc] + info.update(action) + exc_errors.append({op_type: info}) + + # emulate standard behavior for failed actions + if raise_on_error and error.status_code not in ignore_status: + raise BulkIndexError( + f"{len(exc_errors)} document(s) failed to index.", exc_errors + ) + else: + for err in exc_errors: + yield False, err + + +def _process_bulk_chunk( + client: Elasticsearch, + bulk_actions: List[bytes], + bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ], + raise_on_exception: bool = True, + raise_on_error: bool = True, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> Iterable[Tuple[bool, Dict[str, Any]]]: + """ + Send a bulk request to elasticsearch and process the output. + """ + if isinstance(ignore_status, int): + ignore_status = (ignore_status,) + + try: + # send the actual request + resp = client.bulk(*args, operations=bulk_actions, **kwargs) # type: ignore[arg-type] + except ApiError as e: + gen = _process_bulk_chunk_error( + error=e, + bulk_data=bulk_data, + ignore_status=ignore_status, + raise_on_exception=raise_on_exception, + raise_on_error=raise_on_error, + ) + else: + gen = _process_bulk_chunk_success( + resp=resp.body, + bulk_data=bulk_data, + ignore_status=ignore_status, + raise_on_error=raise_on_error, + ) + yield from gen + + +def streaming_bulk( + client: Elasticsearch, + actions: Iterable[_TYPE_BULK_ACTION], + chunk_size: int = 500, + max_chunk_bytes: int = 100 * 1024 * 1024, + raise_on_error: bool = True, + expand_action_callback: Callable[ + [_TYPE_BULK_ACTION], _TYPE_BULK_ACTION_HEADER_AND_BODY + ] = expand_action, + raise_on_exception: bool = True, + max_retries: int = 0, + initial_backoff: float = 2, + max_backoff: float = 600, + yield_ok: bool = True, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> Iterable[Tuple[bool, Dict[str, Any]]]: + """ + Streaming bulk consumes actions from the iterable passed in and yields + results per action. For non-streaming usecases use + :func:`~elasticsearch.helpers.bulk` which is a wrapper around streaming + bulk that returns summary information about the bulk operation once the + entire input is consumed and sent. + + If you specify ``max_retries`` it will also retry any documents that were + rejected with a ``429`` status code. To do this it will wait (**by calling + time.sleep which will block**) for ``initial_backoff`` seconds and then, + every subsequent rejection for the same chunk, for double the time every + time up to ``max_backoff`` seconds. + + :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use + :arg actions: iterable containing the actions to be executed + :arg chunk_size: number of docs in one chunk sent to es (default: 500) + :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) + :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) + from the execution of the last chunk when some occur. By default we raise. + :arg raise_on_exception: if ``False`` then don't propagate exceptions from + call to ``bulk`` and just report the items that failed as failed. + :arg expand_action_callback: callback executed on each action passed in, + should return a tuple containing the action line and the data line + (`None` if data line should be omitted). + :arg max_retries: maximum number of times a document will be retried when + ``429`` is received, set to 0 (default) for no retries on ``429`` + :arg initial_backoff: number of seconds we should wait before the first + retry. Any subsequent retries will be powers of ``initial_backoff * + 2**retry_number`` + :arg max_backoff: maximum number of seconds a retry will wait + :arg yield_ok: if set to False will skip successful documents in the output + :arg ignore_status: list of HTTP status code that you want to ignore + """ + client = client.options() + client._client_meta = (("h", "bp"),) + + serializer = client.transport.serializers.get_serializer("application/json") + + bulk_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ] + bulk_actions: List[bytes] + for bulk_data, bulk_actions in _chunk_actions( + map(expand_action_callback, actions), chunk_size, max_chunk_bytes, serializer + ): + for attempt in range(max_retries + 1): + to_retry: List[bytes] = [] + to_retry_data: List[ + Union[ + Tuple[_TYPE_BULK_ACTION_HEADER], + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + ] + ] = [] + if attempt: + time.sleep(min(max_backoff, initial_backoff * 2 ** (attempt - 1))) + + try: + for data, (ok, info) in zip( + bulk_data, + _process_bulk_chunk( + client, + bulk_actions, + bulk_data, + raise_on_exception, + raise_on_error, + ignore_status, + *args, + **kwargs, + ), + ): + if not ok: + action, info = info.popitem() + # retry if retries enabled, we get 429, and we are not + # in the last attempt + if ( + max_retries + and info["status"] == 429 + and (attempt + 1) <= max_retries + ): + # _process_bulk_chunk expects bytes so we need to + # re-serialize the data + to_retry.extend(map(serializer.dumps, data)) + to_retry_data.append(data) + else: + yield ok, {action: info} + elif yield_ok: + yield ok, info + + except ApiError as e: + # suppress 429 errors since we will retry them + if attempt == max_retries or e.status_code != 429: + raise + else: + if not to_retry: + break + # retry only subset of documents that didn't succeed + bulk_actions, bulk_data = to_retry, to_retry_data + + +def bulk( + client: Elasticsearch, + actions: Iterable[_TYPE_BULK_ACTION], + stats_only: bool = False, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> Tuple[int, Union[int, List[Dict[str, Any]]]]: + """ + Helper for the :meth:`~elasticsearch.Elasticsearch.bulk` api that provides + a more human friendly interface - it consumes an iterator of actions and + sends them to elasticsearch in chunks. It returns a tuple with summary + information - number of successfully executed actions and either list of + errors or number of errors if ``stats_only`` is set to ``True``. Note that + by default we raise a ``BulkIndexError`` when we encounter an error so + options like ``stats_only`` only apply when ``raise_on_error`` is set to + ``False``. + + When errors are being collected original document data is included in the + error dictionary which can lead to an extra high memory usage. If you need + to process a lot of data and want to ignore/collect errors please consider + using the :func:`~elasticsearch.helpers.streaming_bulk` helper which will + just return the errors and not store them in memory. + + + :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use + :arg actions: iterator containing the actions + :arg stats_only: if `True` only report number of successful/failed + operations instead of just number of successful and a list of error responses + :arg ignore_status: list of HTTP status code that you want to ignore + + Any additional keyword arguments will be passed to + :func:`~elasticsearch.helpers.streaming_bulk` which is used to execute + the operation, see :func:`~elasticsearch.helpers.streaming_bulk` for more + accepted parameters. + """ + success, failed = 0, 0 + + # list of errors to be collected is not stats_only + errors = [] + + # make streaming_bulk yield successful results so we can count them + kwargs["yield_ok"] = True + for ok, item in streaming_bulk( + client, actions, ignore_status=ignore_status, *args, **kwargs # type: ignore[misc] + ): + # go through request-response pairs and detect failures + if not ok: + if not stats_only: + errors.append(item) + failed += 1 + else: + success += 1 + + return success, failed if stats_only else errors + + +def parallel_bulk( + client: Elasticsearch, + actions: Iterable[_TYPE_BULK_ACTION], + thread_count: int = 4, + chunk_size: int = 500, + max_chunk_bytes: int = 100 * 1024 * 1024, + queue_size: int = 4, + expand_action_callback: Callable[ + [_TYPE_BULK_ACTION], _TYPE_BULK_ACTION_HEADER_AND_BODY + ] = expand_action, + ignore_status: Union[int, Collection[int]] = (), + *args: Any, + **kwargs: Any, +) -> Iterable[Tuple[bool, Any]]: + """ + Parallel version of the bulk helper run in multiple threads at once. + + :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use + :arg actions: iterator containing the actions + :arg thread_count: size of the threadpool to use for the bulk requests + :arg chunk_size: number of docs in one chunk sent to es (default: 500) + :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) + :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) + from the execution of the last chunk when some occur. By default we raise. + :arg raise_on_exception: if ``False`` then don't propagate exceptions from + call to ``bulk`` and just report the items that failed as failed. + :arg expand_action_callback: callback executed on each action passed in, + should return a tuple containing the action line and the data line + (`None` if data line should be omitted). + :arg queue_size: size of the task queue between the main thread (producing + chunks to send) and the processing threads. + :arg ignore_status: list of HTTP status code that you want to ignore + """ + # Avoid importing multiprocessing unless parallel_bulk is used + # to avoid exceptions on restricted environments like App Engine + from multiprocessing.pool import ThreadPool + + expanded_actions = map(expand_action_callback, actions) + serializer = client.transport.serializers.get_serializer("application/json") + + class BlockingPool(ThreadPool): + def _setup_queues(self) -> None: + super()._setup_queues() # type: ignore + # The queue must be at least the size of the number of threads to + # prevent hanging when inserting sentinel values during teardown. + self._inqueue: Queue[ + Tuple[ + List[ + Union[ + Tuple[Dict[str, Any]], Tuple[Dict[str, Any], Dict[str, Any]] + ] + ], + List[bytes], + ] + ] = Queue(max(queue_size, thread_count)) + self._quick_put = self._inqueue.put + + pool = BlockingPool(thread_count) + + try: + for result in pool.imap( + lambda bulk_chunk: list( + _process_bulk_chunk( + client, + bulk_chunk[1], + bulk_chunk[0], + ignore_status=ignore_status, # type: ignore[misc] + *args, + **kwargs, + ) + ), + _chunk_actions(expanded_actions, chunk_size, max_chunk_bytes, serializer), + ): + yield from result + + finally: + pool.close() + pool.join() + + +def scan( + client: Elasticsearch, + query: Optional[Any] = None, + scroll: str = "5m", + raise_on_error: bool = True, + preserve_order: bool = False, + size: int = 1000, + request_timeout: Optional[float] = None, + clear_scroll: bool = True, + scroll_kwargs: Optional[MutableMapping[str, Any]] = None, + **kwargs: Any, +) -> Iterable[Dict[str, Any]]: + """ + Simple abstraction on top of the + :meth:`~elasticsearch.Elasticsearch.scroll` api - a simple iterator that + yields all hits as returned by underlining scroll requests. + + By default scan does not return results in any pre-determined order. To + have a standard order in the returned documents (either by score or + explicit sort definition) when scrolling, use ``preserve_order=True``. This + may be an expensive operation and will negate the performance benefits of + using ``scan``. + + :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use + :arg query: body for the :meth:`~elasticsearch.Elasticsearch.search` api + :arg scroll: Specify how long a consistent view of the index should be + maintained for scrolled search + :arg raise_on_error: raises an exception (``ScanError``) if an error is + encountered (some shards fail to execute). By default we raise. + :arg preserve_order: don't set the ``search_type`` to ``scan`` - this will + cause the scroll to paginate with preserving the order. Note that this + can be an extremely expensive operation and can easily lead to + unpredictable results, use with caution. + :arg size: size (per shard) of the batch send at each iteration. + :arg request_timeout: explicit timeout for each call to ``scan`` + :arg clear_scroll: explicitly calls delete on the scroll id via the clear + scroll API at the end of the method on completion or error, defaults + to true. + :arg scroll_kwargs: additional kwargs to be passed to + :meth:`~elasticsearch.Elasticsearch.scroll` + + Any additional keyword arguments will be passed to the initial + :meth:`~elasticsearch.Elasticsearch.search` call:: + + scan(es, + query={"query": {"match": {"title": "python"}}}, + index="orders-*", + doc_type="books" + ) + + """ + scroll_kwargs = scroll_kwargs or {} + if not preserve_order: + query = query.copy() if query else {} + query["sort"] = "_doc" + + def pop_transport_kwargs(kw: MutableMapping[str, Any]) -> Dict[str, Any]: + # Grab options that should be propagated to every + # API call within this helper instead of just 'search()' + transport_kwargs = {} + for key in ("headers", "api_key", "http_auth", "basic_auth", "bearer_auth"): + try: + value = kw.pop(key) + if key == "http_auth": + key = "basic_auth" + transport_kwargs[key] = value + except KeyError: + pass + return transport_kwargs + + client = client.options( + request_timeout=request_timeout, **pop_transport_kwargs(kwargs) + ) + client._client_meta = (("h", "s"),) + + # Setting query={"from": ...} would make 'from' be used + # as a keyword argument instead of 'from_'. We handle that here. + def normalize_from_keyword(kw: MutableMapping[str, Any]) -> None: + if "from" in kw: + kw["from_"] = kw.pop("from") + + normalize_from_keyword(kwargs) + try: + search_kwargs = query.copy() if query else {} + normalize_from_keyword(search_kwargs) + search_kwargs.update(kwargs) + search_kwargs["scroll"] = scroll + search_kwargs["size"] = size + resp = client.search(**search_kwargs) + + # Try the old deprecated way if we fail immediately on parameters. + except TypeError: + search_kwargs = kwargs.copy() + search_kwargs["scroll"] = scroll + search_kwargs["size"] = size + resp = client.search(body=query, **search_kwargs) # type: ignore[call-arg] + + scroll_id = resp.get("_scroll_id") + scroll_transport_kwargs = pop_transport_kwargs(scroll_kwargs) + if scroll_transport_kwargs: + scroll_client = client.options(**scroll_transport_kwargs) + else: + scroll_client = client + + try: + while scroll_id and resp["hits"]["hits"]: + yield from resp["hits"]["hits"] + + # Default to 0 if the value isn't included in the response + shards_info: Dict[str, int] = resp["_shards"] + shards_successful = shards_info.get("successful", 0) + shards_skipped = shards_info.get("skipped", 0) + shards_total = shards_info.get("total", 0) + + # check if we have any errors + if (shards_successful + shards_skipped) < shards_total: + shards_message = "Scroll request has only succeeded on %d (+%d skipped) shards out of %d." + logger.warning( + shards_message, + shards_successful, + shards_skipped, + shards_total, + ) + if raise_on_error: + raise ScanError( + scroll_id, + shards_message + % ( + shards_successful, + shards_skipped, + shards_total, + ), + ) + resp = scroll_client.scroll( + scroll_id=scroll_id, scroll=scroll, **scroll_kwargs + ) + scroll_id = resp.get("_scroll_id") + + finally: + if scroll_id and clear_scroll: + client.options(ignore_status=404).clear_scroll(scroll_id=scroll_id) + + +def reindex( + client: Elasticsearch, + source_index: Union[str, Collection[str]], + target_index: str, + query: Optional[Any] = None, + target_client: Optional[Elasticsearch] = None, + chunk_size: int = 500, + scroll: str = "5m", + op_type: Optional[str] = None, + scan_kwargs: MutableMapping[str, Any] = {}, + bulk_kwargs: MutableMapping[str, Any] = {}, +) -> Tuple[int, Union[int, List[Dict[str, Any]]]]: + """ + Reindex all documents from one index that satisfy a given query + to another, potentially (if `target_client` is specified) on a different cluster. + If you don't specify the query you will reindex all the documents. + + Since ``2.3`` a :meth:`~elasticsearch.Elasticsearch.reindex` api is + available as part of elasticsearch itself. It is recommended to use the api + instead of this helper wherever possible. The helper is here mostly for + backwards compatibility and for situations where more flexibility is + needed. + + .. note:: + + This helper doesn't transfer mappings, just the data. + + :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use (for + read if `target_client` is specified as well) + :arg source_index: index (or list of indices) to read documents from + :arg target_index: name of the index in the target cluster to populate + :arg query: body for the :meth:`~elasticsearch.Elasticsearch.search` api + :arg target_client: optional, is specified will be used for writing (thus + enabling reindex between clusters) + :arg chunk_size: number of docs in one chunk sent to es (default: 500) + :arg scroll: Specify how long a consistent view of the index should be + maintained for scrolled search + :arg op_type: Explicit operation type. Defaults to '_index'. Data streams must + be set to 'create'. If not specified, will auto-detect if target_index is a + data stream. + :arg scan_kwargs: additional kwargs to be passed to + :func:`~elasticsearch.helpers.scan` + :arg bulk_kwargs: additional kwargs to be passed to + :func:`~elasticsearch.helpers.bulk` + """ + target_client = client if target_client is None else target_client + docs = scan(client, query=query, index=source_index, scroll=scroll, **scan_kwargs) + + def _change_doc_index( + hits: Iterable[Dict[str, Any]], index: str, op_type: Optional[str] + ) -> Iterable[Dict[str, Any]]: + for h in hits: + h["_index"] = index + if op_type is not None: + h["_op_type"] = op_type + if "fields" in h: + h.update(h.pop("fields")) + yield h + + kwargs = {"stats_only": True} + kwargs.update(bulk_kwargs) + + is_data_stream = False + try: + # Verify if the target_index is data stream or index + data_streams = target_client.indices.get_data_stream( + name=target_index, expand_wildcards="all" + ) + is_data_stream = any( + data_stream["name"] == target_index + for data_stream in data_streams["data_streams"] + ) + except (TransportError, KeyError, NotFoundError): + # If its not data stream, might be index + pass + + if is_data_stream: + if op_type not in (None, "create"): + raise ValueError("Data streams must have 'op_type' set to 'create'") + else: + op_type = "create" + + return bulk( + target_client, + _change_doc_index(docs, target_index, op_type), + chunk_size=chunk_size, + **kwargs, + ) diff --git a/elasticsearch_serverless/helpers/errors.py b/elasticsearch_serverless/helpers/errors.py new file mode 100644 index 0000000..359fe87 --- /dev/null +++ b/elasticsearch_serverless/helpers/errors.py @@ -0,0 +1,32 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from typing import Any, Dict, List + + +class BulkIndexError(Exception): + def __init__(self, message: Any, errors: List[Dict[str, Any]]): + super().__init__(message) + self.errors: List[Dict[str, Any]] = errors + + +class ScanError(Exception): + scroll_id: str + + def __init__(self, scroll_id: str, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.scroll_id = scroll_id diff --git a/elasticsearch_serverless/py.typed b/elasticsearch_serverless/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/elasticsearch_serverless/serializer.py b/elasticsearch_serverless/serializer.py new file mode 100644 index 0000000..758c6b7 --- /dev/null +++ b/elasticsearch_serverless/serializer.py @@ -0,0 +1,203 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import uuid +from datetime import date, datetime +from decimal import Decimal +from typing import Any, ClassVar, Dict, Tuple + +from elastic_transport import JsonSerializer as _JsonSerializer +from elastic_transport import NdjsonSerializer as _NdjsonSerializer +from elastic_transport import Serializer as Serializer +from elastic_transport import TextSerializer as TextSerializer + +from .exceptions import SerializationError + +INTEGER_TYPES = () +FLOAT_TYPES = (Decimal,) +TIME_TYPES = (date, datetime) + +__all__ = [ + "Serializer", + "JsonSerializer", + "TextSerializer", + "NdjsonSerializer", + "CompatibilityModeJsonSerializer", + "CompatibilityModeNdjsonSerializer", + "MapboxVectorTileSerializer", +] + + +class JsonSerializer(_JsonSerializer): + mimetype: ClassVar[str] = "application/json" + + def default(self, data: Any) -> Any: + if isinstance(data, TIME_TYPES): + # Little hack to avoid importing pandas but to not + # return 'NaT' string for pd.NaT as that's not a valid + # Elasticsearch date. + formatted_data = data.isoformat() + if formatted_data != "NaT": + return formatted_data + + if isinstance(data, uuid.UUID): + return str(data) + elif isinstance(data, FLOAT_TYPES): + return float(data) + + # This is kept for backwards compatibility even + # if 'INTEGER_TYPES' isn't used by default anymore. + elif INTEGER_TYPES and isinstance(data, INTEGER_TYPES): + return int(data) + + # Special cases for numpy and pandas types + # These are expensive to import so we try them last. + serialized, value = _attempt_serialize_numpy_or_pandas(data) + if serialized: + return value + + raise TypeError(f"Unable to serialize {data!r} (type: {type(data)})") + + +class NdjsonSerializer(JsonSerializer, _NdjsonSerializer): + mimetype: ClassVar[str] = "application/x-ndjson" + + def default(self, data: Any) -> Any: + return JsonSerializer.default(self, data) + + +class CompatibilityModeJsonSerializer(JsonSerializer): + mimetype: ClassVar[str] = "application/vnd.elasticsearch+json" + + +class CompatibilityModeNdjsonSerializer(NdjsonSerializer): + mimetype: ClassVar[str] = "application/vnd.elasticsearch+x-ndjson" + + +class MapboxVectorTileSerializer(Serializer): + mimetype: ClassVar[str] = "application/vnd.mapbox-vector-tile" + + def loads(self, data: bytes) -> bytes: + return data + + def dumps(self, data: bytes) -> bytes: + if isinstance(data, bytes): + return data + raise SerializationError(f"Cannot serialize {data!r} into a MapBox vector tile") + + +DEFAULT_SERIALIZERS: Dict[str, Serializer] = { + JsonSerializer.mimetype: JsonSerializer(), + MapboxVectorTileSerializer.mimetype: MapboxVectorTileSerializer(), + NdjsonSerializer.mimetype: NdjsonSerializer(), + CompatibilityModeJsonSerializer.mimetype: CompatibilityModeJsonSerializer(), + CompatibilityModeNdjsonSerializer.mimetype: CompatibilityModeNdjsonSerializer(), +} + +# Alias for backwards compatibility +JSONSerializer = JsonSerializer + + +def _attempt_serialize_numpy_or_pandas(data: Any) -> Tuple[bool, Any]: + """Attempts to serialize a value from the numpy or pandas libraries. + This function is separate from JSONSerializer because the inner functions + are rewritten to be no-ops if either library isn't available to avoid + attempting to import and raising an ImportError over and over again. + + Returns a tuple of (bool, Any) where the bool corresponds to whether + the second value contains a properly serialized value and thus + should be returned by JSONSerializer.default(). + """ + serialized, value = _attempt_serialize_numpy(data) + if serialized: + return serialized, value + + serialized, value = _attempt_serialize_pandas(data) + if serialized: + return serialized, value + + return False, None + + +def _attempt_serialize_numpy(data: Any) -> Tuple[bool, Any]: + global _attempt_serialize_numpy + try: + import numpy as np + + if isinstance( + data, + ( + np.int_, + np.intc, + np.int8, + np.int16, + np.int32, + np.int64, + np.uint8, + np.uint16, + np.uint32, + np.uint64, + ), + ): + return True, int(data) + elif isinstance( + data, + ( + np.float_, + np.float16, + np.float32, + np.float64, + ), + ): + return True, float(data) + elif isinstance(data, np.bool_): + return True, bool(data) + elif isinstance(data, np.datetime64): + return True, data.item().isoformat() + elif isinstance(data, np.ndarray): + return True, data.tolist() + + except ImportError: + # Since we failed to import 'numpy' we don't want to try again. + _attempt_serialize_numpy = _attempt_serialize_noop + + return False, None + + +def _attempt_serialize_pandas(data: Any) -> Tuple[bool, Any]: + global _attempt_serialize_pandas + try: + import pandas as pd + + if isinstance(data, (pd.Series, pd.Categorical)): + return True, data.tolist() + elif isinstance(data, pd.Timestamp) and data is not getattr(pd, "NaT", None): + return True, data.isoformat() + elif data is getattr(pd, "NA", None): + return True, None + + except ImportError: + # Since we failed to import 'pandas' we don't want to try again. + _attempt_serialize_pandas = _attempt_serialize_noop + + return False, None + + +def _attempt_serialize_noop(data: Any) -> Tuple[bool, Any]: # noqa + # Short-circuit if the above functions can't import + # the corresponding library on the first attempt. + return False, None diff --git a/elasticsearch_serverless/transport.py b/elasticsearch_serverless/transport.py new file mode 100644 index 0000000..5d84866 --- /dev/null +++ b/elasticsearch_serverless/transport.py @@ -0,0 +1,57 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import warnings +from typing import Any, Dict, Optional, Union + +from elastic_transport import AsyncTransport, Transport # noqa: F401 + +# This file exists for backwards compatibility. +warnings.warn( + "Importing from the 'elasticsearch.transport' module is deprecated. " + "Instead import from 'elastic_transport'", + category=DeprecationWarning, + stacklevel=2, +) + + +def get_host_info( + node_info: Dict[str, Any], host: Dict[str, Union[int, str]] +) -> Optional[Dict[str, Union[int, str]]]: + """ + Simple callback that takes the node info from `/_cluster/nodes` and a + parsed connection information and return the connection information. If + `None` is returned this node will be skipped. + Useful for filtering nodes (by proximity for example) or if additional + information needs to be provided for the :class:`~elasticsearch.Connection` + class. By default master only nodes are filtered out since they shouldn't + typically be used for API operations. + :arg node_info: node information from `/_cluster/nodes` + :arg host: connection information (host, port) extracted from the node info + """ + + warnings.warn( + "The 'get_host_info' function is deprecated. Instead " + "use the 'sniff_node_callback' parameter on the client", + category=DeprecationWarning, + stacklevel=2, + ) + + # ignore master only nodes + if node_info.get("roles", []) == ["master"]: + return None + return host diff --git a/noxfile.py b/noxfile.py index 2c82811..f37a9ff 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,7 +36,9 @@ def test(session): session.install("-r", "dev-requirements.txt") python_version = tuple(int(x) for x in session.python.split(".")) - junit_xml = os.path.join(SOURCE_DIR, "junit", "elasticsearch-serverless-python-junit.xml") + junit_xml = os.path.join( + SOURCE_DIR, "junit", "elasticsearch-serverless-python-junit.xml" + ) pytest_argv = [ "pytest", "--cov-report=term-missing", diff --git a/setup.py b/setup.py index 476fff5..c2ce690 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ r"__versionstr__\s+=\s+[\"\']([^\"\']+)[\"\']", f.read() ).group(1) -with open(join(base_dir, "README.md")) as f: +with open(join(base_dir, "README.rst")) as f: # Remove reST raw directive from README as they're not allowed on PyPI # Those blocks start with a newline and continue until the next newline mode = None diff --git a/test_elasticsearch_serverless/test_types/README.md b/test_elasticsearch_serverless/test_types/README.md new file mode 100644 index 0000000..0fc3182 --- /dev/null +++ b/test_elasticsearch_serverless/test_types/README.md @@ -0,0 +1,6 @@ +# Type Hints + +All of these scripts are used to test the type hinting +distributed with the `elasticsearch` package. +These scripts simulate normal usage of the client and are run +through `mypy --strict` as a part of continuous integration. diff --git a/test_elasticsearch_serverless/test_types/aliased_types.py b/test_elasticsearch_serverless/test_types/aliased_types.py new file mode 100644 index 0000000..0b5c97f --- /dev/null +++ b/test_elasticsearch_serverless/test_types/aliased_types.py @@ -0,0 +1,169 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from typing import Any, AsyncGenerator, Dict, Generator + +from elastic_transport import Transport +from elasticsearch8 import AsyncElasticsearch, Elasticsearch +from elasticsearch8.helpers import ( + async_bulk, + async_reindex, + async_scan, + async_streaming_bulk, + bulk, + reindex, + scan, + streaming_bulk, +) + +es = Elasticsearch( + [{"host": "localhost", "port": 9443}], + transport_class=Transport, + sniff_on_start=True, + sniffer_timeout=0.1, + sniff_timeout=1, + sniff_on_connection_fail=False, + max_retries=1, + retry_on_status={100, 400, 503}, + retry_on_timeout=True, +) + + +def sync_gen() -> Generator[Dict[Any, Any], None, None]: + yield {} + + +def scan_types() -> None: + for _ in scan( + es, + query={"query": {"match_all": {}}}, + request_timeout=10, + clear_scroll=True, + scroll_kwargs={"request_timeout": 10}, + ): + pass + for _ in scan( + es, + raise_on_error=False, + preserve_order=False, + scroll="10m", + size=10, + request_timeout=10.0, + ): + pass + + +def streaming_bulk_types() -> None: + for _ in streaming_bulk(es, sync_gen()): + pass + for _ in streaming_bulk(es, sync_gen().__iter__()): + pass + for _ in streaming_bulk(es, [{"key": "value"}]): + pass + + +def bulk_types() -> None: + _, _ = bulk(es, sync_gen()) + _, _ = bulk(es, sync_gen().__iter__()) + _, _ = bulk(es, [{"key": "value"}]) + + +def reindex_types() -> None: + _, _ = reindex( + es, "src-index", "target-index", query={"query": {"match": {"key": "val"}}} + ) + _, _ = reindex( + es, source_index="src-index", target_index="target-index", target_client=es + ) + _, _ = reindex( + es, + "src-index", + "target-index", + chunk_size=1, + scroll="10m", + scan_kwargs={"request_timeout": 10}, + bulk_kwargs={"request_timeout": 10}, + ) + + +es2 = AsyncElasticsearch( + [{"host": "localhost", "port": 9443}], + sniff_on_start=True, + sniffer_timeout=0.1, + sniff_timeout=1, + sniff_on_connection_fail=False, + max_retries=1, + retry_on_status={100, 400, 503}, + retry_on_timeout=True, +) + + +async def async_gen() -> AsyncGenerator[Dict[Any, Any], None]: + yield {} + + +async def async_scan_types() -> None: + async for _ in async_scan( + es2, + query={"query": {"match_all": {}}}, + request_timeout=10, + clear_scroll=True, + scroll_kwargs={"request_timeout": 10}, + ): + pass + async for _ in async_scan( + es2, + raise_on_error=False, + preserve_order=False, + scroll="10m", + size=10, + request_timeout=10.0, + ): + pass + + +async def async_streaming_bulk_types() -> None: + async for _ in async_streaming_bulk(es2, async_gen()): + pass + async for _ in async_streaming_bulk(es2, async_gen().__aiter__()): + pass + async for _ in async_streaming_bulk(es2, [{"key": "value"}]): + pass + + +async def async_bulk_types() -> None: + _, _ = await async_bulk(es2, async_gen()) + _, _ = await async_bulk(es2, async_gen().__aiter__()) + _, _ = await async_bulk(es2, [{}]) + + +async def async_reindex_types() -> None: + _, _ = await async_reindex( + es2, "src-index", "target-index", query={"query": {"match": {"key": "val"}}} + ) + _, _ = await async_reindex( + es2, source_index="src-index", target_index="target-index", target_client=es2 + ) + _, _ = await async_reindex( + es2, + "src-index", + "target-index", + chunk_size=1, + scroll="10m", + scan_kwargs={"request_timeout": 10}, + bulk_kwargs={"request_timeout": 10}, + ) diff --git a/test_elasticsearch_serverless/test_types/async_types.py b/test_elasticsearch_serverless/test_types/async_types.py new file mode 100644 index 0000000..ac7eed1 --- /dev/null +++ b/test_elasticsearch_serverless/test_types/async_types.py @@ -0,0 +1,105 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from typing import Any, AsyncGenerator, Dict + +from elasticsearch import AsyncElasticsearch +from elasticsearch.helpers import ( + async_bulk, + async_reindex, + async_scan, + async_streaming_bulk, +) + +es = AsyncElasticsearch( + [{"host": "localhost", "port": 9443}], + sniff_on_start=True, + sniffer_timeout=0.1, + sniff_timeout=1, + sniff_on_connection_fail=False, + max_retries=1, + retry_on_status={100, 400, 503}, + retry_on_timeout=True, +) + + +async def main() -> None: + await es.options( + request_timeout=1.0, max_retries=0, api_key="api-key-example" + ).search(index="test-index") + + await es.options( + request_timeout=1.0, max_retries=0, api_key="api-key-example" + ).indices.exists(index="test-index") + + +async def async_gen() -> AsyncGenerator[Dict[Any, Any], None]: + yield {} + + +async def async_scan_types() -> None: + async for _ in async_scan( + es, + query={"query": {"match_all": {}}}, + request_timeout=10, + clear_scroll=True, + scroll_kwargs={"request_timeout": 10}, + ): + pass + async for _ in async_scan( + es, + raise_on_error=False, + preserve_order=False, + scroll="10m", + size=10, + request_timeout=10.0, + ): + pass + + +async def async_streaming_bulk_types() -> None: + async for _ in async_streaming_bulk(es, async_gen()): + pass + async for _ in async_streaming_bulk(es, async_gen().__aiter__()): + pass + async for _ in async_streaming_bulk(es, [{}]): + pass + + +async def async_bulk_types() -> None: + _, _ = await async_bulk(es, async_gen()) + _, _ = await async_bulk(es, async_gen().__aiter__()) + _, _ = await async_bulk(es, [{}]) + _, _ = await async_bulk(es, ({"key": "value"},)) + + +async def async_reindex_types() -> None: + _, _ = await async_reindex( + es, "src-index", "target-index", query={"query": {"match": {"key": "val"}}} + ) + _, _ = await async_reindex( + es, source_index="src-index", target_index="target-index", target_client=es + ) + _, _ = await async_reindex( + es, + "src-index", + "target-index", + chunk_size=1, + scroll="10m", + scan_kwargs={"request_timeout": 10}, + bulk_kwargs={"request_timeout": 10}, + ) diff --git a/test_elasticsearch_serverless/test_types/sync_types.py b/test_elasticsearch_serverless/test_types/sync_types.py new file mode 100644 index 0000000..f4582dd --- /dev/null +++ b/test_elasticsearch_serverless/test_types/sync_types.py @@ -0,0 +1,98 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +from typing import Any, Dict, Generator + +from elasticsearch import Elasticsearch +from elasticsearch.helpers import bulk, reindex, scan, streaming_bulk + +es = Elasticsearch( + [{"host": "localhost", "port": 9443}], + sniff_on_start=True, + sniffer_timeout=0.1, + sniff_timeout=1, + sniff_on_connection_fail=False, + max_retries=1, + retry_on_status={100, 400, 503}, + retry_on_timeout=True, +) + +es.options(request_timeout=1.0, max_retries=0, api_key="api-key-example").search( + index="test-index" +) + +es.options( + request_timeout=1.0, max_retries=0, api_key="api-key-example" +).indices.exists(index="test-index") + + +def sync_gen() -> Generator[Dict[Any, Any], None, None]: + yield {} + + +def scan_types() -> None: + for _ in scan( + es, + query={"query": {"match_all": {}}}, + request_timeout=10, + clear_scroll=True, + scroll_kwargs={"request_timeout": 10}, + ): + pass + for _ in scan( + es, + raise_on_error=False, + preserve_order=False, + scroll="10m", + size=10, + request_timeout=10.0, + ): + pass + + +def streaming_bulk_types() -> None: + for _ in streaming_bulk(es, sync_gen()): + pass + for _ in streaming_bulk(es, sync_gen().__iter__()): + pass + for _ in streaming_bulk(es, [{}]): + pass + + +def bulk_types() -> None: + _, _ = bulk(es, sync_gen()) + _, _ = bulk(es, sync_gen().__iter__()) + _, _ = bulk(es, [{}]) + _, _ = bulk(es, ({"key": "value"},)) + + +def reindex_types() -> None: + _, _ = reindex( + es, "src-index", "target-index", query={"query": {"match": {"key": "val"}}} + ) + _, _ = reindex( + es, source_index="src-index", target_index="target-index", target_client=es + ) + _, _ = reindex( + es, + "src-index", + "target-index", + chunk_size=1, + scroll="10m", + scan_kwargs={"request_timeout": 10}, + bulk_kwargs={"request_timeout": 10}, + ) diff --git a/utils/build-dists.py b/utils/build-dists.py new file mode 100644 index 0000000..d858960 --- /dev/null +++ b/utils/build-dists.py @@ -0,0 +1,296 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +"""A command line tool for building and verifying releases +Can be used for building both 'elasticsearch' and 'elasticsearchX' dists. +Only requires 'name' in 'setup.py' and the directory to be changed. +""" + +import contextlib +import os +import re +import shlex +import shutil +import sys +import tempfile + +base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +tmp_dir = None + + +@contextlib.contextmanager +def set_tmp_dir(): + global tmp_dir + tmp_dir = tempfile.mkdtemp() + yield tmp_dir + shutil.rmtree(tmp_dir) + tmp_dir = None + + +def run(*argv, expect_exit_code=0): + global tmp_dir + try: + prev_dir = os.getcwd() + if tmp_dir is None: + os.chdir(base_dir) + else: + os.chdir(tmp_dir) + + cmd = " ".join(shlex.quote(x) for x in argv) + print("$ " + cmd) + exit_code = os.system(cmd) + if exit_code != expect_exit_code: + print( + "Command exited incorrectly: should have been %d was %d" + % (expect_exit_code, exit_code) + ) + exit(exit_code or 1) + finally: + os.chdir(prev_dir) + + +def test_dist(dist): + with set_tmp_dir() as tmp_dir: + dist_name = re.match(r"^(elasticsearch\d*)-", os.path.basename(dist)).group(1) + + # Build the venv and install the dist + run("python", "-m", "venv", os.path.join(tmp_dir, "venv")) + venv_python = os.path.join(tmp_dir, "venv/bin/python") + run( + venv_python, + "-m", + "pip", + "install", + "-U", + "pip", + "mypy", + "numpy", + "pandas-stubs", + ) + run(venv_python, "-m", "pip", "install", dist) + + # Test the sync namespaces + run(venv_python, "-c", f"from {dist_name} import Elasticsearch") + run( + venv_python, + "-c", + f"from {dist_name}.helpers import scan, bulk, streaming_bulk, reindex", + ) + run( + venv_python, + "-c", + f"from {dist_name} import Elasticsearch, AsyncElasticsearch", + ) + run( + venv_python, + "-c", + f"from {dist_name}.helpers import scan, bulk, streaming_bulk, reindex, async_scan, async_bulk, async_streaming_bulk, async_reindex", + ) + + # Install aiohttp and see that async is now available + run(venv_python, "-m", "pip", "install", "aiohttp") + run(venv_python, "-c", f"from {dist_name} import AsyncElasticsearch") + run( + venv_python, + "-c", + f"from {dist_name}.helpers import async_scan, async_bulk, async_streaming_bulk, async_reindex", + ) + + # Only need to test 'async_types' for non-aliased package + # since 'aliased_types' tests both async and sync. + if dist_name == "elasticsearch_serverless": + run( + venv_python, + "-m", + "mypy", + "--strict", + "--install-types", + "--non-interactive", + os.path.join( + base_dir, "test_elasticsearch_serverless/test_types/async_types.py" + ), + ) + + # Ensure that the namespaces are correct for the dist + for suffix in ("", "1", "2", "5", "6", "7", "8", "9", "10"): + distx_name = f"elasticsearch{suffix}" + run( + venv_python, + "-c", + f"import {distx_name}", + expect_exit_code=256 if distx_name != dist_name else 0, + ) + + # Check that sync types work for 'elasticsearch_serverless' and + # that aliased types work for 'elasticsearchX' + if dist_name == "elasticsearch_serverless": + run( + venv_python, + "-m", + "mypy", + "--strict", + "--install-types", + "--non-interactive", + os.path.join( + base_dir, "test_elasticsearch_serverless/test_types/sync_types.py" + ), + ) + else: + run( + venv_python, + "-m", + "mypy", + "--strict", + "--install-types", + "--non-interactive", + os.path.join( + base_dir, + "test_elasticsearch_serverless/test_types/aliased_types.py", + ), + ) + + # Uninstall the dist, see that we can't import things anymore + run(venv_python, "-m", "pip", "uninstall", "--yes", dist_name) + run( + venv_python, + "-c", + f"from {dist_name} import Elasticsearch", + expect_exit_code=256, + ) + + +def main(): + run("git", "checkout", "--", "setup.py", "elasticsearch_serverless/") + run("rm", "-rf", "build/", "dist/*", "*.egg-info", ".eggs") + + # Grab the major version to be used as a suffix. + version_path = os.path.join(base_dir, "elasticsearch_serverless/_version.py") + with open(version_path) as f: + version = re.search( + r"^__versionstr__\s+=\s+[\"\']([^\"\']+)[\"\']", f.read(), re.M + ).group(1) + major_version = version.split(".")[0] + + # If we're handed a version from the build manager we + # should check that the version is correct or write + # a new one. + if len(sys.argv) >= 2: + # 'build_version' is what the release manager wants, + # 'expect_version' is what we're expecting to compare + # the package version to before building the dists. + build_version = expect_version = sys.argv[1] + + # Any prefixes in the version specifier mean we're making + # a pre-release which will modify __versionstr__ locally + # and not produce a git tag. + if any(x in build_version for x in ("-SNAPSHOT", "-rc", "-alpha", "-beta")): + # If a snapshot, then we add '+dev' + if "-SNAPSHOT" in build_version: + version = version + "+dev" + # alpha/beta/rc -> aN/bN/rcN + else: + pre_number = re.search(r"-(a|b|rc)(?:lpha|eta|)(\d+)$", expect_version) + version = version + pre_number.group(1) + pre_number.group(2) + + expect_version = re.sub( + r"(?:-(?:SNAPSHOT|alpha\d+|beta\d+|rc\d+))+$", "", expect_version + ) + if expect_version.endswith(".x"): + expect_version = expect_version[:-1] + + # For snapshots we ensure that the version in the package + # at least *starts* with the version. This is to support + # build_version='7.x-SNAPSHOT'. + if not version.startswith(expect_version): + print( + "Version of package (%s) didn't match the " + "expected release version (%s)" % (version, build_version) + ) + exit(1) + + # A release that will be tagged, we want + # there to be no '+dev', etc. + elif expect_version != version: + print( + "Version of package (%s) didn't match the " + "expected release version (%s)" % (version, build_version) + ) + exit(1) + + for suffix in ("", major_version): + run("rm", "-rf", "build/", "*.egg-info", ".eggs") + + # Rename the module to fit the suffix. + shutil.move( + os.path.join(base_dir, "elasticsearch_serverless"), + os.path.join(base_dir, f"elasticsearch{suffix}"), + ) + + # Ensure that the version within 'elasticsearch_serverless/_version.py' is correct. + version_path = os.path.join(base_dir, f"elasticsearch{suffix}/_version.py") + with open(version_path) as f: + version_data = f.read() + version_data = re.sub( + r"__versionstr__ = \"[^\"]+\"", + f'__versionstr__ = "{version}"', + version_data, + ) + with open(version_path, "w") as f: + f.truncate() + f.write(version_data) + + # Rewrite setup.py with the new name. + setup_py_path = os.path.join(base_dir, "setup.py") + with open(setup_py_path) as f: + setup_py = f.read() + with open(setup_py_path, "w") as f: + f.truncate() + assert 'package_name = "elasticsearch_serverless"' in setup_py + f.write( + setup_py.replace( + 'package_name = "elasticsearch_serverless"', + f'package_name = "elasticsearch{suffix}"', + ) + ) + + # Build the sdist/wheels + run("python", "-m", "build") + + # Clean up everything. + run("git", "checkout", "--", "setup.py", "elasticsearch_serverless/") + if suffix: + run("rm", "-rf", f"elasticsearch{suffix}/") + + # Test everything that got created + dists = os.listdir(os.path.join(base_dir, "dist")) + assert len(dists) == 4 + for dist in dists: + test_dist(os.path.join(base_dir, "dist", dist)) + os.system('bash -c "chmod a+w dist/*"') + + # After this run 'python -m twine upload dist/*' + print( + "\n\n" + "===============================\n\n" + " * Releases are ready! *\n\n" + "$ python -m twine upload dist/*\n\n" + "===============================" + ) + + +if __name__ == "__main__": + main() diff --git a/utils/bump-version.py b/utils/bump-version.py new file mode 100644 index 0000000..a5ff868 --- /dev/null +++ b/utils/bump-version.py @@ -0,0 +1,85 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +"""Command line tool which changes the branch to be +ready to build and test the given Elastic stack version. +""" + +import re +import sys +from pathlib import Path + +SOURCE_DIR = Path(__file__).absolute().parent.parent + + +def find_and_replace(path, pattern, replace): + # Does a find and replace within a file path and complains + # if the given pattern isn't found in the file. + with open(path, "r") as f: + old_data = f.read() + + if re.search(pattern, old_data, flags=re.MULTILINE) is None: + print(f"Didn't find the pattern {pattern!r} in {path!s}") + exit(1) + + new_data = re.sub(pattern, replace, old_data, flags=re.MULTILINE) + with open(path, "w") as f: + f.truncate() + f.write(new_data) + + +def main(): + if len(sys.argv) != 2: + print("usage: utils/bump-version.py [stack version]") + exit(1) + + stack_version = sys.argv[1] + try: + python_version = re.search(r"^([0-9][0-9\.]*[0-9]+)", stack_version).group(1) + except AttributeError: + print(f"Couldn't match the given stack version {stack_version!r}") + exit(1) + + # Pad the version value with .0 until there + # we have the major, minor, and patch. + for _ in range(3): + if len(python_version.split(".")) >= 3: + break + python_version += ".0" + + find_and_replace( + path=SOURCE_DIR / "elasticsearch_serverless/_version.py", + pattern=r"__versionstr__ = \"[0-9]+[0-9\.]*[0-9](?:\+dev)?\"", + replace=f'__versionstr__ = "{python_version}"', + ) + + # These values should always be the 'major.minor-SNAPSHOT' + major_minor_version = ".".join(python_version.split(".")[:2]) + find_and_replace( + path=SOURCE_DIR / ".ci/test-matrix.yml", + pattern=r'STACK_VERSION:\s+\- "[0-9]+[0-9\.]*[0-9](?:\-SNAPSHOT)?"', + replace=f'STACK_VERSION:\n - "{major_minor_version}.0-SNAPSHOT"', + ) + find_and_replace( + path=SOURCE_DIR / ".github/workflows/unified-release.yml", + pattern=r'STACK_VERSION:\s+"[0-9]+[0-9\.]*[0-9](?:\-SNAPSHOT)?"', + replace=f'STACK_VERSION: "{major_minor_version}-SNAPSHOT"', + ) + + +if __name__ == "__main__": + main() diff --git a/utils/generate-examples.py b/utils/generate-examples.py new file mode 100644 index 0000000..d5a3199 --- /dev/null +++ b/utils/generate-examples.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + + +import collections +import json +import tempfile +from pathlib import Path + +import black +from click.testing import CliRunner +from jinja2 import Environment, FileSystemLoader + +code_root = Path(__file__).absolute().parent.parent +asciidocs_dir = code_root / "docs/examples" +flight_recorder_dir = code_root.parent / "clients-flight-recorder" +report_path = flight_recorder_dir / "recordings/docs/parsed-alternative-report.json" +substitutions = {"type": "doc_type", "from": "from_"} + +jinja_env = Environment( + loader=FileSystemLoader([code_root / "utils" / "templates"]), + trim_blocks=True, + lstrip_blocks=True, +) + +files_to_generate = [ + "search/request-body.asciidoc", + "mapping.asciidoc", + "query-dsl.asciidoc", + "query-dsl/query-string-query.asciidoc", + "getting-started.asciidoc", + "query-dsl/query_filter_context.asciidoc", + "query-dsl/bool-query.asciidoc", + "query-dsl/match-query.asciidoc", + "indices/create-index.asciidoc", + "docs/index_.asciidoc", + "aggregations/bucket/terms-aggregation.asciidoc", + "query-dsl/range-query.asciidoc", + "/search/search.asciidoc", + "query-dsl/multi-match-query.asciidoc", + "docs/bulk.asciidoc", + "indices/delete-index.asciidoc", + "indices/put-mapping.asciidoc", + "docs/reindex.asciidoc", + "query-dsl/term-query.asciidoc", + "indices/templates.asciidoc", + "getting-started.asciidoc", + "docs/update.asciidoc", + "query-dsl/match-all-query.asciidoc", + "docs/get.asciidoc", + "query-dsl/wildcard-query.asciidoc", + "query-dsl/exists-query.asciidoc", + "docs/delete-by-query.asciidoc", + "mapping/params/format.asciidoc", + "mapping/types/nested.asciidoc", + "query-dsl/terms-query.asciidoc", + "search/request/sort.asciidoc", + "mapping/types/date.asciidoc", + "indices/update-settings.asciidoc", + "indices/aliases.asciidoc", + "setup/install/check-running.asciidoc", + "query-dsl/regexp-query.asciidoc", + "query-dsl/function-score-query.asciidoc", + "search/request/from-size.asciidoc", + "cluster/health.asciidoc", + "query-dsl/nested-query.asciidoc", + "mapping/types/array.asciidoc", + "mapping/params/fielddata.asciidoc", + "search/count.asciidoc", + "mapping/types/keyword.asciidoc", + "docs/update-by-query.asciidoc", + "search/suggesters.asciidoc", + "api-conventions.asciidoc", + "cat/indices.asciidoc", + "query-dsl/match-phrase-query.asciidoc", + "indices/get-index.asciidoc", + "setup/logging-config.asciidoc", + "docs/delete.asciidoc", + "aggregations/metrics/valuecount-aggregation.asciidoc", + "indices/get-mapping.asciidoc", + "aggregations/bucket/filter-aggregation.asciidoc", + "aggregations/bucket/datehistogram-aggregation.asciidoc", + "mapping/types/numeric.asciidoc", + "search/request/scroll.asciidoc", + "mapping/fields/id-field.asciidoc", + "search.asciidoc", + "mapping/params/multi-fields.asciidoc", +] + + +ParsedSource = collections.namedtuple("ParsedSource", ["api", "params", "body"]) + + +def blacken(filename): + runner = CliRunner() + result = runner.invoke( + black.main, [str(filename), "--line-length=75", "--target-version=py27"] + ) + assert result.exit_code == 0, result.output + + +def main(): + for filepath in asciidocs_dir.iterdir(): + if filepath.name.endswith(".asciidoc"): + filepath.unlink() + + if not flight_recorder_dir.exists() or not report_path.exists(): + raise RuntimeError( + f"clients-flight-recorder repository not checked out at {flight_recorder_dir}" + ) + + with report_path.open() as f: + report = json.loads(f.read()) + + t = jinja_env.get_template("example") + + for exm in report: + if exm["lang"] != "console": + continue + if exm["source_location"]["file"] not in files_to_generate: + continue + + parsed_sources = [] + for src in exm["parsed_source"]: + params = (src.get("params") or {}).copy() + params.update(src.get("query") or {}) + params = { + k: (list(v.split(",")) if isinstance(v, str) and "," in v else v) + for k, v in params.items() + } + + parsed_sources.append( + ParsedSource( + api=src["api"], + params={ + substitutions.get(k, k): repr(v) for k, v in params.items() + }, + body=src.get("body", None) or None, + ) + ) + + tmp_path = Path(tempfile.mktemp()) + with tmp_path.open(mode="w") as f: + f.write(t.render(parsed_sources=parsed_sources)) + + blacken(tmp_path) + + with tmp_path.open(mode="r") as f: + data = f.read() + data = data.rstrip().replace(",)", ")") + tmp_path.unlink() + + with (asciidocs_dir / f"{exm['digest']}.asciidoc").open(mode="w") as f: + f.truncate() + f.write( + f"""// {exm['source_location']['file']}:{exm['source_location']['line']} + +[source, python] +---- +{data} +----""" + ) + + +if __name__ == "__main__": + main() diff --git a/utils/license-headers.py b/utils/license-headers.py new file mode 100644 index 0000000..b4fc743 --- /dev/null +++ b/utils/license-headers.py @@ -0,0 +1,123 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +"""Script which verifies that all source files have a license header. +Has two modes: 'fix' and 'check'. 'fix' fixes problems, 'check' will +error out if 'fix' would have changed the file. +""" + +import os +import re +import sys +from itertools import chain +from typing import Iterator, List + +lines_to_keep = ["# -*- coding: utf-8 -*-\n", "#!/usr/bin/env python\n"] +license_header_lines = [ + "# Licensed to Elasticsearch B.V. under one or more contributor\n", + "# license agreements. See the NOTICE file distributed with\n", + "# this work for additional information regarding copyright\n", + "# ownership. Elasticsearch B.V. licenses this file to you under\n", + '# the Apache License, Version 2.0 (the "License"); you may\n', + "# not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing,\n", + "# software distributed under the License is distributed on an\n", + '# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n', + "# KIND, either express or implied. See the License for the\n", + "# specific language governing permissions and limitations\n", + "# under the License.\n", + "\n", +] + + +def find_files_to_fix(sources: List[str]) -> Iterator[str]: + """Iterates over all files and dirs in 'sources' and returns + only the filepaths that need fixing. + """ + for source in sources: + if os.path.isfile(source) and does_file_need_fix(source): + yield source + elif os.path.isdir(source): + for root, _, filenames in os.walk(source): + for filename in filenames: + filepath = os.path.join(root, filename) + if does_file_need_fix(filepath): + yield filepath + + +def does_file_need_fix(filepath: str) -> bool: + if not re.search(r"\.pyi?$", filepath): + return False + with open(filepath, mode="r") as f: + first_license_line = None + for line in f: + if line == license_header_lines[0]: + first_license_line = line + break + elif line not in lines_to_keep: + return True + for header_line, line in zip( + license_header_lines, chain((first_license_line,), f) + ): + if line != header_line: + return True + return False + + +def add_header_to_file(filepath: str) -> None: + with open(filepath, mode="r") as f: + lines = list(f) + i = 0 + for i, line in enumerate(lines): + if line not in lines_to_keep: + break + lines = lines[:i] + license_header_lines + lines[i:] + with open(filepath, mode="w") as f: + f.truncate() + f.write("".join(lines)) + print(f"Fixed {os.path.relpath(filepath, os.getcwd())}") + + +def main(): + mode = sys.argv[1] + assert mode in ("fix", "check") + sources = [os.path.abspath(x) for x in sys.argv[2:]] + files_to_fix = find_files_to_fix(sources) + + if mode == "fix": + for filepath in files_to_fix: + add_header_to_file(filepath) + else: + no_license_headers = list(files_to_fix) + if no_license_headers: + print("No license header found in:") + cwd = os.getcwd() + [ + print(f" - {os.path.relpath(filepath, cwd)}") + for filepath in no_license_headers + ] + sys.exit(1) + else: + print("All files had license header") + + +if __name__ == "__main__": + main() diff --git a/utils/run-black.py b/utils/run-black.py new file mode 100644 index 0000000..76c62a1 --- /dev/null +++ b/utils/run-black.py @@ -0,0 +1,88 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import os +import re +import subprocess +import sys +import time + + +def sleep_if_hot() -> None: + def is_hot() -> bool: + try: + return ( + max( + map( + int, + re.findall( + r"\s\+([0-9]{2})\.[0-9]", + subprocess.check_output( + "sensors", + shell=True, + stderr=subprocess.STDOUT, + ).decode("utf-8"), + ), + ) + ) + > 80 + ) + except subprocess.CalledProcessError: + return False + + while is_hot(): + time.sleep(0.1) + + +def run_on_directory(dir: str, check: bool) -> None: + for root, _, filenames in os.walk(dir): + for filename in filenames: + if filename.endswith(".py") or filename.endswith(".pyi"): + run_on_file(os.path.join(root, filename), check) + + +def run_on_file(file: str, check: bool) -> None: + try: + subprocess.check_call( + f"black --target-version=py36 {'--check ' if check else ''}{file}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + print(e.output) + exit(e.returncode) + sleep_if_hot() + + +def main() -> None: + cwd = os.getcwd() + check = ["--check"] == sys.argv[1:2] + targets = [ + os.path.expanduser(os.path.join(cwd, target)) + for target in sys.argv[1:] + if target != "--check" + ] + for target in targets: + if os.path.isdir(target): + run_on_directory(target, check) + else: + run_on_file(target, check) + + +if __name__ == "__main__": + main() diff --git a/utils/run-unasync.py b/utils/run-unasync.py new file mode 100644 index 0000000..149d872 --- /dev/null +++ b/utils/run-unasync.py @@ -0,0 +1,58 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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. + +import os +from pathlib import Path + +import unasync + + +def main(): + # Unasync all the generated async code + additional_replacements = { + # We want to rewrite to 'Transport' instead of 'SyncTransport', etc + "AsyncTransport": "Transport", + "AsyncElasticsearch": "Elasticsearch", + # We don't want to rewrite this class + "AsyncSearchClient": "AsyncSearchClient", + # Handling typing.Awaitable[...] isn't done yet by unasync. + "_TYPE_ASYNC_SNIFF_CALLBACK": "_TYPE_SYNC_SNIFF_CALLBACK", + } + rules = [ + unasync.Rule( + fromdir="/elasticsearch_serverless/_async/client/", + todir="/elasticsearch_serverless/_sync/client/", + additional_replacements=additional_replacements, + ), + ] + + filepaths = [] + for root, _, filenames in os.walk( + Path(__file__).absolute().parent.parent / "elasticsearch_serverless/_async" + ): + for filename in filenames: + if filename.rpartition(".")[-1] in ( + "py", + "pyi", + ) and not filename.startswith("utils.py"): + filepaths.append(os.path.join(root, filename)) + + unasync.unasync_files(filepaths, rules) + + +if __name__ == "__main__": + main() From 353538e69076c4f512e1455724e1e5ee632879f6 Mon Sep 17 00:00:00 2001 From: Josh Mock Date: Mon, 17 Jul 2023 13:20:10 -0500 Subject: [PATCH 2/2] Latest codegen updates --- .../_async/client/__init__.py | 235 +++++++++++------- .../_async/client/indices.py | 82 +++--- .../_sync/client/__init__.py | 235 +++++++++++------- .../_sync/client/indices.py | 82 +++--- 4 files changed, 388 insertions(+), 246 deletions(-) diff --git a/elasticsearch_serverless/_async/client/__init__.py b/elasticsearch_serverless/_async/client/__init__.py index 315092e..a67e68b 100644 --- a/elasticsearch_serverless/_async/client/__init__.py +++ b/elasticsearch_serverless/_async/client/__init__.py @@ -3058,129 +3058,186 @@ async def search( ``_ - :param index: A comma-separated list of index names to search; use `_all` or - empty string to perform the operation on all indices - :param aggregations: - :param aggs: - :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves - into no concrete indices. (This includes `_all` string or when no indices - have been specified) - :param allow_partial_search_results: Indicate if an error should be returned - if there is a partial search failure or timeout - :param analyze_wildcard: Specify whether wildcard and prefix queries should be - analyzed (default: false) - :param analyzer: The analyzer to use for the query string + :param index: Comma-separated list of data streams, indices, and aliases to search. + Supports wildcards (`*`). To search all data streams and indices, omit this + parameter or use `*` or `_all`. + :param aggregations: Defines the aggregations that are run as part of the search + request. + :param aggs: Defines the aggregations that are run as part of the search request. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with `foo` but no index starts with `bar`. + :param allow_partial_search_results: If true, returns partial results if there + are shard request timeouts or shard failures. If false, returns an error + with no partial results. + :param analyze_wildcard: If true, wildcard and prefix queries are analyzed. This + parameter can only be used when the q query string parameter is specified. + :param analyzer: Analyzer to use for the query string. This parameter can only + be used when the q query string parameter is specified. :param batched_reduce_size: The number of shard results that should be reduced at once on the coordinating node. This value should be used as a protection mechanism to reduce the memory overhead per search request if the potential number of shards in the request can be large. - :param ccs_minimize_roundtrips: Indicates whether network round-trips should - be minimized as part of cross-cluster search requests execution - :param collapse: - :param default_operator: The default operator for query string query (AND or - OR) - :param df: The field to use as default where no field prefix is given in the - query string - :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc - values for field names matching these patterns in the hits.fields property + :param ccs_minimize_roundtrips: If true, network round-trips between the coordinating + node and the remote clusters are minimized when executing cross-cluster search + (CCS) requests. + :param collapse: Collapses search results the values of the specified field. + :param default_operator: The default operator for query string query: AND or + OR. This parameter can only be used when the `q` query string parameter is + specified. + :param df: Field to use as default where no field prefix is given in the query + string. This parameter can only be used when the q query string parameter + is specified. + :param docvalue_fields: Array of wildcard (`*`) patterns. The request returns + doc values for field names matching these patterns in the `hits.fields` property of the response. - :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. :param explain: If true, returns detailed information about score computation as part of a hit. :param ext: Configuration of search extensions defined by Elasticsearch plugins. - :param fields: Array of wildcard (*) patterns. The request returns values for - field names matching these patterns in the hits.fields property of the response. - :param from_: Starting document offset. By default, you cannot page through more - than 10,000 hits using the from and size parameters. To page through more - hits, use the search_after parameter. - :param highlight: - :param ignore_throttled: Whether specified concrete, expanded or aliased indices - should be ignored when throttled - :param ignore_unavailable: Whether specified concrete indices should be ignored - when unavailable (missing or closed) + :param fields: Array of wildcard (`*`) patterns. The request returns values for + field names matching these patterns in the `hits.fields` property of the + response. + :param from_: Starting document offset. Needs to be non-negative. By default, + you cannot page through more than 10,000 hits using the `from` and `size` + parameters. To page through more hits, use the `search_after` parameter. + :param highlight: Specifies the highlighter to use for retrieving highlighted + snippets from one or more fields in your search results. + :param ignore_throttled: If `true`, concrete, expanded or aliased indices will + be ignored when frozen. + :param ignore_unavailable: If `false`, the request returns an error if it targets + a missing or closed index. :param indices_boost: Boosts the _score of documents from specified indices. :param knn: Defines the approximate kNN search to run. - :param lenient: Specify whether format-based query failures (such as providing - text to a numeric field) should be ignored - :param max_concurrent_shard_requests: The number of concurrent shard requests - per node this search executes concurrently. This value should be used to - limit the impact of the search on the cluster in order to limit the number - of concurrent shard requests - :param min_compatible_shard_node: The minimum compatible version that all shards - involved in search should have for this request to be successful - :param min_score: Minimum _score for matching documents. Documents with a lower - _score are not included in the search results. + :param lenient: If `true`, format-based query failures (such as providing text + to a numeric field) in the query string will be ignored. This parameter can + only be used when the `q` query string parameter is specified. + :param max_concurrent_shard_requests: Defines the number of concurrent shard + requests per node this search executes concurrently. This value should be + used to limit the impact of the search on the cluster in order to limit the + number of concurrent shard requests. + :param min_compatible_shard_node: The minimum version of the node that can handle + the request Any handling node with a lower version will fail the request. + :param min_score: Minimum `_score` for matching documents. Documents with a lower + `_score` are not included in the search results. :param pit: Limits the search to a point in time (PIT). If you provide a PIT, - you cannot specify an in the request path. - :param post_filter: - :param pre_filter_shard_size: A threshold that enforces a pre-filter roundtrip - to prefilter search shards based on query rewriting if the number of shards - the search request expands to exceeds the threshold. This filter roundtrip - can limit the number of shards significantly if for instance a shard can - not match any documents based on its rewrite method ie. if date filters are - mandatory to match but the shard bounds and the query are disjoint. - :param preference: Specify the node or shard the operation should be performed - on (default: random) - :param profile: - :param q: Query in the Lucene query string syntax + you cannot specify an `` in the request path. + :param post_filter: Use the `post_filter` parameter to filter search results. + The search hits are filtered after the aggregations are calculated. A post + filter has no impact on the aggregation results. + :param pre_filter_shard_size: Defines a threshold that enforces a pre-filter + roundtrip to prefilter search shards based on query rewriting if the number + of shards the search request expands to exceeds the threshold. This filter + roundtrip can limit the number of shards significantly if for instance a + shard can not match any documents based on its rewrite method (if date filters + are mandatory to match but the shard bounds and the query are disjoint). + When unspecified, the pre-filter phase is executed if any of these conditions + is met: the request targets more than 128 shards; the request targets one + or more read-only index; the primary sort of the query targets an indexed + field. + :param preference: Nodes and shards used for the search. By default, Elasticsearch + selects from eligible nodes and shards using adaptive replica selection, + accounting for allocation awareness. Valid values are: `_only_local` to run + the search only on shards on the local node; `_local` to, if possible, run + the search on shards on the local node, or if not, select shards using the + default method; `_only_nodes:,` to run the search on only + the specified nodes IDs, where, if suitable shards exist on more than one + selected node, use shards on those nodes using the default method, or if + none of the specified nodes are available, select shards from any available + node using the default method; `_prefer_nodes:,` to if + possible, run the search on the specified nodes IDs, or if not, select shards + using the default method; `_shards:,` to run the search only + on the specified shards; `` (any string that does not start + with `_`) to route searches with the same `` to the same shards + in the same order. + :param profile: Set to `true` to return detailed timing information about the + execution of individual components in a search request. NOTE: This is a debugging + tool and adds significant overhead to search execution. + :param q: Query in the Lucene query string syntax using query parameter search. + Query parameter searches do not support the full Elasticsearch Query DSL + but are handy for testing. :param query: Defines the search definition using the Query DSL. - :param rank: Defines the Reciprocal Rank Fusion (RRF) to use - :param request_cache: Specify if request cache should be used for this request - or not, defaults to index level setting - :param rescore: - :param rest_total_hits_as_int: Indicates whether hits.total should be rendered - as an integer or an object in the rest search response - :param routing: A comma-separated list of specific routing values + :param rank: Defines the Reciprocal Rank Fusion (RRF) to use. + :param request_cache: If `true`, the caching of search results is enabled for + requests where `size` is `0`. Defaults to index level settings. + :param rescore: Can be used to improve precision by reordering just the top (for + example 100 - 500) documents returned by the `query` and `post_filter` phases. + :param rest_total_hits_as_int: Indicates whether `hits.total` should be rendered + as an integer or an object in the rest search response. + :param routing: Custom value used to route operations to a specific shard. :param runtime_mappings: Defines one or more runtime fields in the search request. These fields take precedence over mapped fields with the same name. :param script_fields: Retrieve a script evaluation (based on different fields) for each hit. - :param scroll: Specify how long a consistent view of the index should be maintained - for scrolled search - :param search_after: - :param search_type: Search operation type - :param seq_no_primary_term: If true, returns sequence number and primary term - of the last modification of each hit. See Optimistic concurrency control. + :param scroll: Period to retain the search context for scrolling. See Scroll + search results. By default, this value cannot exceed `1d` (24 hours). You + can change this limit using the `search.max_keep_alive` cluster-level setting. + :param search_after: Used to retrieve the next page of hits using a set of sort + values from the previous page. + :param search_type: How distributed term frequencies are calculated for relevance + scoring. + :param seq_no_primary_term: If `true`, returns sequence number and primary term + of the last modification of each hit. :param size: The number of hits to return. By default, you cannot page through - more than 10,000 hits using the from and size parameters. To page through - more hits, use the search_after parameter. - :param slice: - :param sort: + more than 10,000 hits using the `from` and `size` parameters. To page through + more hits, use the `search_after` parameter. + :param slice: Can be used to split a scrolled search into multiple slices that + can be consumed independently. + :param sort: A comma-separated list of : pairs. :param source: Indicates which source fields are returned for matching documents. These fields are returned in the hits._source property of the search response. - :param source_excludes: A list of fields to exclude from the returned _source - field - :param source_includes: A list of fields to extract and return from the _source - field + :param source_excludes: A comma-separated list of source fields to exclude from + the response. You can also use this parameter to exclude fields from the + subset specified in `_source_includes` query parameter. If the `_source` + parameter is `false`, this parameter is ignored. + :param source_includes: A comma-separated list of source fields to include in + the response. If this parameter is specified, only these source fields are + returned. You can exclude fields from this subset using the `_source_excludes` + query parameter. If the `_source` parameter is `false`, this parameter is + ignored. :param stats: Stats groups to associate with the search. Each group maintains a statistics aggregation for its associated searches. You can retrieve these stats using the indices stats API. :param stored_fields: List of stored fields to return as part of a hit. If no fields are specified, no stored fields are included in the response. If this - field is specified, the _source parameter defaults to false. You can pass - _source: true to return both source fields and stored fields in the search - response. - :param suggest: + field is specified, the `_source` parameter defaults to `false`. You can + pass `_source: true` to return both source fields and stored fields in the + search response. + :param suggest: Defines a suggester that provides similar looking terms based + on a provided text. :param suggest_field: Specifies which field to use for suggestions. - :param suggest_mode: Specify suggest mode - :param suggest_size: How many suggestions to return in response + :param suggest_mode: Specifies the suggest mode. This parameter can only be used + when the `suggest_field` and `suggest_text` query string parameters are specified. + :param suggest_size: Number of suggestions to return. This parameter can only + be used when the `suggest_field` and `suggest_text` query string parameters + are specified. :param suggest_text: The source text for which the suggestions should be returned. + This parameter can only be used when the `suggest_field` and `suggest_text` + query string parameters are specified. :param terminate_after: Maximum number of documents to collect for each shard. If a query reaches this limit, Elasticsearch terminates the query early. - Elasticsearch collects documents before sorting. Defaults to 0, which does - not terminate query execution early. + Elasticsearch collects documents before sorting. Use with caution. Elasticsearch + applies this parameter to each shard handling the request. When possible, + let Elasticsearch perform early termination automatically. Avoid specifying + this parameter for requests that target data streams with backing indices + across multiple data tiers. If set to `0` (default), the query does not terminate + early. :param timeout: Specifies the period of time to wait for a response from each shard. If no response is received before the timeout expires, the request fails and returns an error. Defaults to no timeout. :param track_scores: If true, calculate and return document scores, even if the scores are not used for sorting. :param track_total_hits: Number of hits matching the query to count accurately. - If true, the exact number of hits is returned at the cost of some performance. - If false, the response does not include the total number of hits matching - the query. Defaults to 10,000 hits. - :param typed_keys: Specify whether aggregation and suggester names should be - prefixed by their respective types in the response + If `true`, the exact number of hits is returned at the cost of some performance. + If `false`, the response does not include the total number of hits matching + the query. + :param typed_keys: If `true`, aggregation and suggester names are be prefixed + by their respective types in the response. :param version: If true, returns document version as part of a hit. """ if index not in SKIP_IN_PATH: diff --git a/elasticsearch_serverless/_async/client/indices.py b/elasticsearch_serverless/_async/client/indices.py index 639109c..4d0fe96 100644 --- a/elasticsearch_serverless/_async/client/indices.py +++ b/elasticsearch_serverless/_async/client/indices.py @@ -263,9 +263,11 @@ async def data_streams_stats( ``_ - :param name: A comma-separated list of data stream names; use `_all` or empty - string to perform the operation on all data streams - :param expand_wildcards: + :param name: Comma-separated list of data streams used to limit the request. + Wildcard expressions (`*`) are supported. To target all data streams in a + cluster, omit this parameter or use `*`. + :param expand_wildcards: Type of data stream that wildcard patterns can match. + Supports comma-separated values, such as `open,hidden`. """ if name not in SKIP_IN_PATH: __path = f"/_data_stream/{_quote(name)}/_stats" @@ -1543,22 +1545,28 @@ async def get_settings( ``_ - :param index: A comma-separated list of index names; use `_all` or empty string - to perform the operation on all indices - :param name: The name of the settings that should be included - :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves - into no concrete indices. (This includes `_all` string or when no indices - have been specified) - :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. - :param flat_settings: Return settings in flat format (default: false) - :param ignore_unavailable: Whether specified concrete indices should be ignored - when unavailable (missing or closed) - :param include_defaults: Whether to return all default setting for each of the - indices. - :param local: Return local information, do not retrieve the state from master - node (default: false) - :param master_timeout: Specify timeout for connection to master + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param name: Comma-separated list or wildcard expression of settings to retrieve. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with foo but no index starts with `bar`. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. + :param flat_settings: If `true`, returns settings in flat format. + :param ignore_unavailable: If `false`, the request returns an error if it targets + a missing or closed index. + :param include_defaults: If `true`, return all default settings in the response. + :param local: If `true`, the request retrieves information from the local node + only. If `false`, information is retrieved from the master node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. """ if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: __path = f"/{_quote(index)}/_settings/{_quote(name)}" @@ -1665,7 +1673,7 @@ async def migrate_to_data_stream( ``_ - :param name: The name of the alias to migrate + :param name: Name of the index alias to convert to a data stream. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -2165,20 +2173,26 @@ async def put_settings( ``_ :param settings: - :param index: A comma-separated list of index names; use `_all` or empty string - to perform the operation on all indices - :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves - into no concrete indices. (This includes `_all` string or when no indices - have been specified) - :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. - :param flat_settings: Return settings in flat format (default: false) - :param ignore_unavailable: Whether specified concrete indices should be ignored - when unavailable (missing or closed) - :param master_timeout: Specify timeout for connection to master - :param preserve_existing: Whether to update existing settings. If set to `true` - existing settings on an index remain unchanged, the default is `false` - :param timeout: Explicit operation timeout + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with `foo` but no index starts with `bar`. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. + :param flat_settings: If `true`, returns settings in flat format. + :param ignore_unavailable: If `true`, returns settings in flat format. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param preserve_existing: If `true`, existing index settings remain unchanged. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. """ if settings is None: raise ValueError("Empty value passed for parameter 'settings'") diff --git a/elasticsearch_serverless/_sync/client/__init__.py b/elasticsearch_serverless/_sync/client/__init__.py index 20b6bd9..c8d067a 100644 --- a/elasticsearch_serverless/_sync/client/__init__.py +++ b/elasticsearch_serverless/_sync/client/__init__.py @@ -3056,129 +3056,186 @@ def search( ``_ - :param index: A comma-separated list of index names to search; use `_all` or - empty string to perform the operation on all indices - :param aggregations: - :param aggs: - :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves - into no concrete indices. (This includes `_all` string or when no indices - have been specified) - :param allow_partial_search_results: Indicate if an error should be returned - if there is a partial search failure or timeout - :param analyze_wildcard: Specify whether wildcard and prefix queries should be - analyzed (default: false) - :param analyzer: The analyzer to use for the query string + :param index: Comma-separated list of data streams, indices, and aliases to search. + Supports wildcards (`*`). To search all data streams and indices, omit this + parameter or use `*` or `_all`. + :param aggregations: Defines the aggregations that are run as part of the search + request. + :param aggs: Defines the aggregations that are run as part of the search request. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with `foo` but no index starts with `bar`. + :param allow_partial_search_results: If true, returns partial results if there + are shard request timeouts or shard failures. If false, returns an error + with no partial results. + :param analyze_wildcard: If true, wildcard and prefix queries are analyzed. This + parameter can only be used when the q query string parameter is specified. + :param analyzer: Analyzer to use for the query string. This parameter can only + be used when the q query string parameter is specified. :param batched_reduce_size: The number of shard results that should be reduced at once on the coordinating node. This value should be used as a protection mechanism to reduce the memory overhead per search request if the potential number of shards in the request can be large. - :param ccs_minimize_roundtrips: Indicates whether network round-trips should - be minimized as part of cross-cluster search requests execution - :param collapse: - :param default_operator: The default operator for query string query (AND or - OR) - :param df: The field to use as default where no field prefix is given in the - query string - :param docvalue_fields: Array of wildcard (*) patterns. The request returns doc - values for field names matching these patterns in the hits.fields property + :param ccs_minimize_roundtrips: If true, network round-trips between the coordinating + node and the remote clusters are minimized when executing cross-cluster search + (CCS) requests. + :param collapse: Collapses search results the values of the specified field. + :param default_operator: The default operator for query string query: AND or + OR. This parameter can only be used when the `q` query string parameter is + specified. + :param df: Field to use as default where no field prefix is given in the query + string. This parameter can only be used when the q query string parameter + is specified. + :param docvalue_fields: Array of wildcard (`*`) patterns. The request returns + doc values for field names matching these patterns in the `hits.fields` property of the response. - :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. :param explain: If true, returns detailed information about score computation as part of a hit. :param ext: Configuration of search extensions defined by Elasticsearch plugins. - :param fields: Array of wildcard (*) patterns. The request returns values for - field names matching these patterns in the hits.fields property of the response. - :param from_: Starting document offset. By default, you cannot page through more - than 10,000 hits using the from and size parameters. To page through more - hits, use the search_after parameter. - :param highlight: - :param ignore_throttled: Whether specified concrete, expanded or aliased indices - should be ignored when throttled - :param ignore_unavailable: Whether specified concrete indices should be ignored - when unavailable (missing or closed) + :param fields: Array of wildcard (`*`) patterns. The request returns values for + field names matching these patterns in the `hits.fields` property of the + response. + :param from_: Starting document offset. Needs to be non-negative. By default, + you cannot page through more than 10,000 hits using the `from` and `size` + parameters. To page through more hits, use the `search_after` parameter. + :param highlight: Specifies the highlighter to use for retrieving highlighted + snippets from one or more fields in your search results. + :param ignore_throttled: If `true`, concrete, expanded or aliased indices will + be ignored when frozen. + :param ignore_unavailable: If `false`, the request returns an error if it targets + a missing or closed index. :param indices_boost: Boosts the _score of documents from specified indices. :param knn: Defines the approximate kNN search to run. - :param lenient: Specify whether format-based query failures (such as providing - text to a numeric field) should be ignored - :param max_concurrent_shard_requests: The number of concurrent shard requests - per node this search executes concurrently. This value should be used to - limit the impact of the search on the cluster in order to limit the number - of concurrent shard requests - :param min_compatible_shard_node: The minimum compatible version that all shards - involved in search should have for this request to be successful - :param min_score: Minimum _score for matching documents. Documents with a lower - _score are not included in the search results. + :param lenient: If `true`, format-based query failures (such as providing text + to a numeric field) in the query string will be ignored. This parameter can + only be used when the `q` query string parameter is specified. + :param max_concurrent_shard_requests: Defines the number of concurrent shard + requests per node this search executes concurrently. This value should be + used to limit the impact of the search on the cluster in order to limit the + number of concurrent shard requests. + :param min_compatible_shard_node: The minimum version of the node that can handle + the request Any handling node with a lower version will fail the request. + :param min_score: Minimum `_score` for matching documents. Documents with a lower + `_score` are not included in the search results. :param pit: Limits the search to a point in time (PIT). If you provide a PIT, - you cannot specify an in the request path. - :param post_filter: - :param pre_filter_shard_size: A threshold that enforces a pre-filter roundtrip - to prefilter search shards based on query rewriting if the number of shards - the search request expands to exceeds the threshold. This filter roundtrip - can limit the number of shards significantly if for instance a shard can - not match any documents based on its rewrite method ie. if date filters are - mandatory to match but the shard bounds and the query are disjoint. - :param preference: Specify the node or shard the operation should be performed - on (default: random) - :param profile: - :param q: Query in the Lucene query string syntax + you cannot specify an `` in the request path. + :param post_filter: Use the `post_filter` parameter to filter search results. + The search hits are filtered after the aggregations are calculated. A post + filter has no impact on the aggregation results. + :param pre_filter_shard_size: Defines a threshold that enforces a pre-filter + roundtrip to prefilter search shards based on query rewriting if the number + of shards the search request expands to exceeds the threshold. This filter + roundtrip can limit the number of shards significantly if for instance a + shard can not match any documents based on its rewrite method (if date filters + are mandatory to match but the shard bounds and the query are disjoint). + When unspecified, the pre-filter phase is executed if any of these conditions + is met: the request targets more than 128 shards; the request targets one + or more read-only index; the primary sort of the query targets an indexed + field. + :param preference: Nodes and shards used for the search. By default, Elasticsearch + selects from eligible nodes and shards using adaptive replica selection, + accounting for allocation awareness. Valid values are: `_only_local` to run + the search only on shards on the local node; `_local` to, if possible, run + the search on shards on the local node, or if not, select shards using the + default method; `_only_nodes:,` to run the search on only + the specified nodes IDs, where, if suitable shards exist on more than one + selected node, use shards on those nodes using the default method, or if + none of the specified nodes are available, select shards from any available + node using the default method; `_prefer_nodes:,` to if + possible, run the search on the specified nodes IDs, or if not, select shards + using the default method; `_shards:,` to run the search only + on the specified shards; `` (any string that does not start + with `_`) to route searches with the same `` to the same shards + in the same order. + :param profile: Set to `true` to return detailed timing information about the + execution of individual components in a search request. NOTE: This is a debugging + tool and adds significant overhead to search execution. + :param q: Query in the Lucene query string syntax using query parameter search. + Query parameter searches do not support the full Elasticsearch Query DSL + but are handy for testing. :param query: Defines the search definition using the Query DSL. - :param rank: Defines the Reciprocal Rank Fusion (RRF) to use - :param request_cache: Specify if request cache should be used for this request - or not, defaults to index level setting - :param rescore: - :param rest_total_hits_as_int: Indicates whether hits.total should be rendered - as an integer or an object in the rest search response - :param routing: A comma-separated list of specific routing values + :param rank: Defines the Reciprocal Rank Fusion (RRF) to use. + :param request_cache: If `true`, the caching of search results is enabled for + requests where `size` is `0`. Defaults to index level settings. + :param rescore: Can be used to improve precision by reordering just the top (for + example 100 - 500) documents returned by the `query` and `post_filter` phases. + :param rest_total_hits_as_int: Indicates whether `hits.total` should be rendered + as an integer or an object in the rest search response. + :param routing: Custom value used to route operations to a specific shard. :param runtime_mappings: Defines one or more runtime fields in the search request. These fields take precedence over mapped fields with the same name. :param script_fields: Retrieve a script evaluation (based on different fields) for each hit. - :param scroll: Specify how long a consistent view of the index should be maintained - for scrolled search - :param search_after: - :param search_type: Search operation type - :param seq_no_primary_term: If true, returns sequence number and primary term - of the last modification of each hit. See Optimistic concurrency control. + :param scroll: Period to retain the search context for scrolling. See Scroll + search results. By default, this value cannot exceed `1d` (24 hours). You + can change this limit using the `search.max_keep_alive` cluster-level setting. + :param search_after: Used to retrieve the next page of hits using a set of sort + values from the previous page. + :param search_type: How distributed term frequencies are calculated for relevance + scoring. + :param seq_no_primary_term: If `true`, returns sequence number and primary term + of the last modification of each hit. :param size: The number of hits to return. By default, you cannot page through - more than 10,000 hits using the from and size parameters. To page through - more hits, use the search_after parameter. - :param slice: - :param sort: + more than 10,000 hits using the `from` and `size` parameters. To page through + more hits, use the `search_after` parameter. + :param slice: Can be used to split a scrolled search into multiple slices that + can be consumed independently. + :param sort: A comma-separated list of : pairs. :param source: Indicates which source fields are returned for matching documents. These fields are returned in the hits._source property of the search response. - :param source_excludes: A list of fields to exclude from the returned _source - field - :param source_includes: A list of fields to extract and return from the _source - field + :param source_excludes: A comma-separated list of source fields to exclude from + the response. You can also use this parameter to exclude fields from the + subset specified in `_source_includes` query parameter. If the `_source` + parameter is `false`, this parameter is ignored. + :param source_includes: A comma-separated list of source fields to include in + the response. If this parameter is specified, only these source fields are + returned. You can exclude fields from this subset using the `_source_excludes` + query parameter. If the `_source` parameter is `false`, this parameter is + ignored. :param stats: Stats groups to associate with the search. Each group maintains a statistics aggregation for its associated searches. You can retrieve these stats using the indices stats API. :param stored_fields: List of stored fields to return as part of a hit. If no fields are specified, no stored fields are included in the response. If this - field is specified, the _source parameter defaults to false. You can pass - _source: true to return both source fields and stored fields in the search - response. - :param suggest: + field is specified, the `_source` parameter defaults to `false`. You can + pass `_source: true` to return both source fields and stored fields in the + search response. + :param suggest: Defines a suggester that provides similar looking terms based + on a provided text. :param suggest_field: Specifies which field to use for suggestions. - :param suggest_mode: Specify suggest mode - :param suggest_size: How many suggestions to return in response + :param suggest_mode: Specifies the suggest mode. This parameter can only be used + when the `suggest_field` and `suggest_text` query string parameters are specified. + :param suggest_size: Number of suggestions to return. This parameter can only + be used when the `suggest_field` and `suggest_text` query string parameters + are specified. :param suggest_text: The source text for which the suggestions should be returned. + This parameter can only be used when the `suggest_field` and `suggest_text` + query string parameters are specified. :param terminate_after: Maximum number of documents to collect for each shard. If a query reaches this limit, Elasticsearch terminates the query early. - Elasticsearch collects documents before sorting. Defaults to 0, which does - not terminate query execution early. + Elasticsearch collects documents before sorting. Use with caution. Elasticsearch + applies this parameter to each shard handling the request. When possible, + let Elasticsearch perform early termination automatically. Avoid specifying + this parameter for requests that target data streams with backing indices + across multiple data tiers. If set to `0` (default), the query does not terminate + early. :param timeout: Specifies the period of time to wait for a response from each shard. If no response is received before the timeout expires, the request fails and returns an error. Defaults to no timeout. :param track_scores: If true, calculate and return document scores, even if the scores are not used for sorting. :param track_total_hits: Number of hits matching the query to count accurately. - If true, the exact number of hits is returned at the cost of some performance. - If false, the response does not include the total number of hits matching - the query. Defaults to 10,000 hits. - :param typed_keys: Specify whether aggregation and suggester names should be - prefixed by their respective types in the response + If `true`, the exact number of hits is returned at the cost of some performance. + If `false`, the response does not include the total number of hits matching + the query. + :param typed_keys: If `true`, aggregation and suggester names are be prefixed + by their respective types in the response. :param version: If true, returns document version as part of a hit. """ if index not in SKIP_IN_PATH: diff --git a/elasticsearch_serverless/_sync/client/indices.py b/elasticsearch_serverless/_sync/client/indices.py index e0bedbe..be2ac41 100644 --- a/elasticsearch_serverless/_sync/client/indices.py +++ b/elasticsearch_serverless/_sync/client/indices.py @@ -263,9 +263,11 @@ def data_streams_stats( ``_ - :param name: A comma-separated list of data stream names; use `_all` or empty - string to perform the operation on all data streams - :param expand_wildcards: + :param name: Comma-separated list of data streams used to limit the request. + Wildcard expressions (`*`) are supported. To target all data streams in a + cluster, omit this parameter or use `*`. + :param expand_wildcards: Type of data stream that wildcard patterns can match. + Supports comma-separated values, such as `open,hidden`. """ if name not in SKIP_IN_PATH: __path = f"/_data_stream/{_quote(name)}/_stats" @@ -1543,22 +1545,28 @@ def get_settings( ``_ - :param index: A comma-separated list of index names; use `_all` or empty string - to perform the operation on all indices - :param name: The name of the settings that should be included - :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves - into no concrete indices. (This includes `_all` string or when no indices - have been specified) - :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. - :param flat_settings: Return settings in flat format (default: false) - :param ignore_unavailable: Whether specified concrete indices should be ignored - when unavailable (missing or closed) - :param include_defaults: Whether to return all default setting for each of the - indices. - :param local: Return local information, do not retrieve the state from master - node (default: false) - :param master_timeout: Specify timeout for connection to master + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param name: Comma-separated list or wildcard expression of settings to retrieve. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with foo but no index starts with `bar`. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. + :param flat_settings: If `true`, returns settings in flat format. + :param ignore_unavailable: If `false`, the request returns an error if it targets + a missing or closed index. + :param include_defaults: If `true`, return all default settings in the response. + :param local: If `true`, the request retrieves information from the local node + only. If `false`, information is retrieved from the master node. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. """ if index not in SKIP_IN_PATH and name not in SKIP_IN_PATH: __path = f"/{_quote(index)}/_settings/{_quote(name)}" @@ -1665,7 +1673,7 @@ def migrate_to_data_stream( ``_ - :param name: The name of the alias to migrate + :param name: Name of the index alias to convert to a data stream. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -2165,20 +2173,26 @@ def put_settings( ``_ :param settings: - :param index: A comma-separated list of index names; use `_all` or empty string - to perform the operation on all indices - :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves - into no concrete indices. (This includes `_all` string or when no indices - have been specified) - :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. - :param flat_settings: Return settings in flat format (default: false) - :param ignore_unavailable: Whether specified concrete indices should be ignored - when unavailable (missing or closed) - :param master_timeout: Specify timeout for connection to master - :param preserve_existing: Whether to update existing settings. If set to `true` - existing settings on an index remain unchanged, the default is `false` - :param timeout: Explicit operation timeout + :param index: Comma-separated list of data streams, indices, and aliases used + to limit the request. Supports wildcards (`*`). To target all data streams + and indices, omit this parameter or use `*` or `_all`. + :param allow_no_indices: If `false`, the request returns an error if any wildcard + expression, index alias, or `_all` value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting `foo*,bar*` returns an error if an index starts + with `foo` but no index starts with `bar`. + :param expand_wildcards: Type of index that wildcard patterns can match. If the + request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as `open,hidden`. + :param flat_settings: If `true`, returns settings in flat format. + :param ignore_unavailable: If `true`, returns settings in flat format. + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + :param preserve_existing: If `true`, existing index settings remain unchanged. + :param timeout: Period to wait for a response. If no response is received before + the timeout expires, the request fails and returns an error. """ if settings is None: raise ValueError("Empty value passed for parameter 'settings'")