├── .circleci ├── config.yml └── maven-release-settings.xml ├── .gitignore ├── Changelog.md ├── LICENSE ├── README.md ├── demo ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── arangodb │ │ └── spring │ │ └── demo │ │ ├── DemoApplication.java │ │ ├── entity │ │ ├── Character.java │ │ ├── ChildOf.java │ │ └── Location.java │ │ ├── repository │ │ ├── CharacterRepository.java │ │ ├── ChildOfRepository.java │ │ └── LocationRepository.java │ │ └── runner │ │ ├── AQLRunner.java │ │ ├── ByExampleRunner.java │ │ ├── CrudRunner.java │ │ ├── DerivedQueryRunner.java │ │ ├── GeospatialRunner.java │ │ └── RelationsRunner.java │ └── resources │ └── application.properties ├── docker ├── find_active_endpoint.sh ├── jwtHeader ├── jwtSecret ├── server.pem └── start_db.sh ├── docs └── Drivers │ └── SpringBootStarter │ ├── GettingStarted │ └── README.md │ └── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── arangodb │ │ └── springframework │ │ └── boot │ │ ├── actuate │ │ ├── ArangoHealthIndicator.java │ │ └── autoconfigure │ │ │ └── ArangoHealthIndicatorAutoConfiguration.java │ │ └── autoconfigure │ │ ├── ArangoAutoConfiguration.java │ │ ├── ArangoProperties.java │ │ └── ArangoRepositoriesAutoConfigureRegistrar.java └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports └── test ├── java └── com │ └── arangodb │ └── springframework │ └── boot │ ├── SpringTest.java │ └── actuate │ └── ArangoHealthIndicatorTest.java └── resources └── application.properties /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | parameters: 4 | docker-img: 5 | type: 'string' 6 | default: "" 7 | 8 | commands: 9 | timeout: 10 | parameters: 11 | duration: 12 | default: '5m' 13 | type: 'string' 14 | steps: 15 | - run: 16 | name: Cancel job after <> 17 | background: true 18 | command: | 19 | sleep <> 20 | echo "Cancelling job as <> has elapsed" 21 | curl --fail -X POST -H "Circle-Token: ${CIRCLE_TOKEN}" "https://circleci.com/api/v1.1/project/github/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}/cancel" 22 | start-db: 23 | parameters: 24 | docker-img: 25 | type: 'string' 26 | default: <> 27 | steps: 28 | - run: 29 | name: Start Database 30 | command: ./docker/start_db.sh 31 | environment: 32 | DOCKER_IMAGE: <> 33 | mvn-info: 34 | steps: 35 | - run: 36 | name: mvn version 37 | command: mvn --version 38 | - run: 39 | name: mvn dependency:tree 40 | command: mvn dependency:tree 41 | mvn-install: 42 | steps: 43 | - run: 44 | name: mvn install 45 | command: mvn install -Dmaven.test.skip=true -Dgpg.skip=true -Dmaven.javadoc.skip=true 46 | test: 47 | steps: 48 | - run: 49 | name: Test 50 | command: mvn test 51 | report: 52 | steps: 53 | - run: 54 | name: Create reports 55 | command: mvn -e surefire-report:report-only 56 | - store_artifacts: 57 | path: target/site 58 | load_cache: 59 | steps: 60 | - run: 61 | name: Generate Cache Checksum 62 | command: find . -name 'pom.xml' | sort | xargs cat > /tmp/maven_cache_seed 63 | - restore_cache: 64 | key: maven-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/maven_cache_seed" }} 65 | store_cache: 66 | steps: 67 | - save_cache: 68 | key: maven-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/maven_cache_seed" }} 69 | paths: 70 | - ~/.m2/repository 71 | config_gpg: 72 | steps: 73 | - run: 74 | name: Configure GPG 75 | command: echo $GPG_PRIVATE_KEY | base64 --decode | gpg --batch --no-tty --import --yes 76 | deploy: 77 | steps: 78 | - run: 79 | name: Deploy to Apache Maven Central 80 | command: mvn -s .circleci/maven-release-settings.xml -Dmaven.test.skip=true deploy 81 | release: 82 | steps: 83 | - run: 84 | name: Release to Apache Maven Central 85 | command: mvn -s .circleci/maven-release-settings.xml -Dmaven.test.skip=true nexus-staging:release 86 | environment: 87 | MAVEN_OPTS: "--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED" 88 | 89 | executors: 90 | j17: 91 | docker: 92 | - image: 'cimg/openjdk:17.0' 93 | j21: 94 | docker: 95 | - image: 'cimg/openjdk:21.0' 96 | 97 | jobs: 98 | test: 99 | parameters: 100 | docker-img: 101 | type: 'string' 102 | default: <> 103 | jdk: 104 | type: 'string' 105 | default: 'j21' 106 | executor: <> 107 | steps: 108 | - timeout 109 | - checkout 110 | - setup_remote_docker 111 | - start-db: 112 | docker-img: <> 113 | - load_cache 114 | - mvn-info 115 | - test 116 | - report 117 | - store_cache 118 | demo: 119 | parameters: 120 | docker-img: 121 | type: 'string' 122 | default: <> 123 | executor: 'j21' 124 | steps: 125 | - timeout 126 | - checkout 127 | - setup_remote_docker 128 | - start-db: 129 | docker-img: <> 130 | - load_cache 131 | - mvn-info 132 | - mvn-install 133 | - run: 134 | name: Run demo 135 | command: mvn spring-boot:run 136 | working_directory: demo 137 | - store_cache 138 | deploy: 139 | executor: 'j17' 140 | steps: 141 | - timeout 142 | - checkout 143 | - load_cache 144 | - mvn-info 145 | - config_gpg 146 | - deploy 147 | - store_cache 148 | release: 149 | executor: 'j17' 150 | steps: 151 | - timeout 152 | - checkout 153 | - load_cache 154 | - mvn-info 155 | - config_gpg 156 | - deploy 157 | - release 158 | - store_cache 159 | 160 | workflows: 161 | test: 162 | jobs: 163 | - test: 164 | name: test-<> 165 | matrix: 166 | parameters: 167 | jdk: 168 | - 'j17' 169 | - 'j21' 170 | demo: 171 | jobs: 172 | - demo: 173 | name: demo-<> 174 | matrix: 175 | parameters: 176 | docker-img: 177 | - 'docker.io/arangodb/arangodb:3.11' 178 | - 'docker.io/arangodb/arangodb:3.12' 179 | deploy: 180 | jobs: 181 | - deploy: 182 | context: java-release 183 | filters: 184 | tags: 185 | only: /^deploy.*/ 186 | branches: 187 | ignore: /.*/ 188 | release: 189 | jobs: 190 | - release: 191 | context: java-release 192 | filters: 193 | tags: 194 | only: /^release.*/ 195 | branches: 196 | ignore: /.*/ 197 | -------------------------------------------------------------------------------- /.circleci/maven-release-settings.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | ossrh 7 | 8 | true 9 | 10 | 11 | ${env.GPG_KEYNAME} 12 | ${env.GPG_PASSPHRASE} 13 | 14 | 15 | 16 | 17 | 18 | 19 | ossrh 20 | ${env.OSSRH_USERNAME} 21 | ${env.OSSRH_PASSWORD} 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | # Eclipse project files 25 | /.classpath 26 | /.project 27 | /.settings 28 | **/target 29 | /bin/ 30 | 31 | # Intellij project files 32 | **/.idea 33 | *.iml 34 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). 6 | 7 | ## [Unreleased] 8 | 9 | ## [3.4-0] - 2024.12.12 10 | 11 | - added support to Spring Boot `3.4` 12 | - updated `arangodb-spring-data` to version `4.5.0` 13 | 14 | ## [3.3-0] - 2024.06.21 15 | 16 | - added support to Spring Boot `3.3.x` (DE-816) 17 | - updated `arangodb-spring-data` to version `4.2.0` 18 | 19 | ## [3.2-0] - 2024.01.24 20 | 21 | - Spring Boot 3.2 support (DE-767) 22 | - updated `arangodb-spring-data` to version `4.1.0` 23 | 24 | ## [3.1-2] - 2024.01.16 25 | 26 | - updated `arangodb-spring-data` to version `4.0.1` 27 | - fixed returning `com.arangodb.internal.InternalArangoDBBuilder` from public API (#25) 28 | 29 | ## [3.1-1] - 2023.09.21 30 | 31 | - re-enabled bean method proxies (#23) 32 | 33 | ## [3.1-0] - 2023.09.19 34 | 35 | - Spring Boot 3.1 support 36 | 37 | ## [3.0-0] - 2023.09.19 38 | 39 | - Spring Boot 3.0 support (#21) 40 | - updated Spring Data ArangoDB to version 4.0 and ArangoDB Java Driver to version 7.1 (#21) 41 | - raised required minimum Java version to JDK 17 (#21) 42 | - added `jwt` and `acquireHostListInterval` configuration properties (#21) 43 | 44 | ## [2.7-0] - 2023.02.08 45 | 46 | - updated `arangodb-spring-data` to `3.8.0` 47 | 48 | ## [2.6-0] - 2022.02.09 49 | 50 | - changed prefix of configuration properties to `arangodb.spring.data.*` 51 | - updated `arangodb-spring-data` to `3.7.0` 52 | - migrated Health Indicator to Spring Boot 2.4 53 | 54 | ## [2.5-0] - 2022.02.09 55 | 56 | - changed prefix of configuration properties to `arangodb.spring.data.*` 57 | - updated `arangodb-spring-data` to `3.7.0` 58 | - migrated Health Indicator to Spring Boot 2.4 59 | 60 | ## [2.4-0] - 2022.02.09 61 | 62 | - changed prefix of configuration properties to `arangodb.spring.data.*` 63 | - updated `arangodb-spring-data` to `3.7.0` 64 | - migrated Health Indicator to Spring Boot 2.4 65 | 66 | ## [2.2.7.RELEASE] - 2020.05.14 67 | 68 | - adopted new versioning scheme matching the Spring Boot versions 69 | 70 | ## [1.0.3] - 2020.05.07 71 | 72 | - dependencies update 73 | 74 | ## [1.0.2] - 2019.09.04 75 | 76 | - updated arangodb-spring-data to 3.2.2 77 | 78 | ## [1.0.1] - 2018-11-12 79 | 80 | ### Added 81 | 82 | - added `spring-boot-configuration-processor` 83 | - added JavaDoc in `ArangoProperties` 84 | 85 | ### Changed 86 | 87 | - made dependency `spring-boot-actuator` optional 88 | 89 | ## [1.0.0] - 2018-11-09 90 | 91 | - initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 ArangoDB GmbH 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ArangoDB-Logo](https://www.arangodb.com/wp-content/uploads/2016/05/ArangoDB_logo_@2.png) 2 | 3 | # Spring Boot Starter ArangoDB 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.arangodb/arangodb-spring-boot-starter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.arangodb/arangodb-spring-boot-starter) 6 | [![CircleCI](https://dl.circleci.com/status-badge/img/gh/arangodb/spring-boot-starter/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/arangodb/spring-boot-starter/tree/main) 7 | 8 | - [Getting Started](docs/Drivers/SpringBootStarter/GettingStarted/README.md) 9 | 10 | ## Learn more 11 | 12 | - [ArangoDB](https://www.arangodb.com/) 13 | - [Spring Data ArangoDB](https://github.com/arangodb/spring-data) 14 | - [Changelog](ChangeLog.md) 15 | - [Demo](./demo) 16 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | ![ArangoDB-Logo](https://www.arangodb.com/wp-content/uploads/2016/05/ArangoDB_logo_@2.png) 2 | 3 | # Spring Data ArangoDB - Demo 4 | 5 | ## Get started 6 | 7 | This is an extensive demo on how to use 8 | [Spring Data ArangoDB](https://github.com/arangodb/spring-data) with an example 9 | dataset of **Game of Thrones** characters and locations. 10 | 11 | ### Build a project with Maven 12 | 13 | First, you have to set up a project and add every needed dependency. 14 | This demo use Maven and Spring Boot and adds `arangodb-spring-boot-starter` to 15 | auto-configure Spring Data ArangoDB. 16 | 17 | Create a Maven `pom.xml`: 18 | 19 | ```xml 20 | 21 | 24 | 4.0.0 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-parent 29 | 3.4.0 30 | 31 | 32 | 33 | com.arangodb 34 | spring-data-arangodb-tutorial 35 | 1.0.0 36 | 37 | demo 38 | Demo project for Spring Boot 39 | 40 | 41 | 17 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter 48 | 49 | 50 | com.arangodb 51 | arangodb-spring-boot-starter 52 | 3.4-0 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | Substitute the versions with the latest available versions that are compatible. 69 | See the [Supported versions](#supported-versions) for details. 70 | 71 | ### Monitor the server health 72 | 73 | ArangoDB health monitoring can be applied to your application by adding 74 | `spring-boot-starter-actuator` to your project and calling the 75 | `GET /actuator/health` endpoint against your application. 76 | 77 | ```xml 78 | 79 | org.springframework.boot 80 | spring-boot-starter-actuator 81 | 82 | ``` 83 | 84 | ### Create an Application class 85 | 86 | After you have ensured that you can fetch all the necessary dependencies, you 87 | can create your first classes. 88 | 89 | The `DemoApplication` class is the main class where you later add certain 90 | `CommandLineRunner` instances to be executed. 91 | 92 | ```java 93 | package com.arangodb.spring.demo; 94 | 95 | import org.springframework.boot.SpringApplication; 96 | import org.springframework.boot.autoconfigure.SpringBootApplication; 97 | 98 | @SpringBootApplication 99 | public class DemoApplication { 100 | public static void main(final String... args) { 101 | Class[] runner = new Class[]{}; 102 | System.exit(SpringApplication.exit(SpringApplication.run(runner, args))); 103 | } 104 | } 105 | ``` 106 | 107 | ### Application configuration 108 | 109 | You need to provide the configuration for the database connection. 110 | You can do this by adding to `src/main/resources/application.properties` 111 | with the properties of 112 | [ArangoProperties](https://github.com/arangodb/spring-boot-starter/blob/main/src/main/java/com/arangodb/springframework/boot/autoconfigure/ArangoProperties.java). 113 | 114 | ``` 115 | arangodb.spring.data.database=spring-demo 116 | arangodb.spring.data.user=root 117 | arangodb.spring.data.password=test 118 | arangodb.spring.data.hosts=localhost:8529 119 | ``` 120 | 121 | ## Data modeling 122 | 123 | Create your first bean representing a collection in your database. With the 124 | `@Document` annotation, you define the collection as a document collection. 125 | In this case, the alternative name characters for the collection are also defined. 126 | By default, the collection name is determined by the class name. `@Document` 127 | also provides additional options for the collection, which is used at the 128 | creation time of the collection. 129 | 130 | Because many operations on documents require a document handle, it's recommended 131 | to add a field of type `String` annotated with `@Id` to every entity. The name 132 | doesn't matter. It's further recommended to **not set or change** the id by hand. 133 | 134 | ```java 135 | package com.arangodb.spring.demo.entity; 136 | 137 | import com.arangodb.springframework.annotation.Document; 138 | import org.springframework.data.annotation.Id; 139 | 140 | @Document("characters") 141 | public class Character { 142 | 143 | @Id // db document field: _key 144 | private String id; 145 | 146 | @ArangoId // db document field: _id 147 | private String arangoId; 148 | 149 | private String name; 150 | private String surname; 151 | private boolean alive; 152 | private Integer age; 153 | 154 | public Character() { 155 | super(); 156 | } 157 | 158 | public Character(final String name, final String surname, final boolean alive) { 159 | super(); 160 | this.name = name; 161 | this.surname = surname; 162 | this.alive = alive; 163 | } 164 | 165 | public Character(final String name, final String surname, final boolean alive, final Integer age) { 166 | super(); 167 | this.name = name; 168 | this.surname = surname; 169 | this.alive = alive; 170 | this.age = age; 171 | } 172 | 173 | // getter & setter 174 | 175 | @Override 176 | public String toString() { 177 | return "Character [id=" + id + ", name=" + name + ", surname=" + surname + ", alive=" + alive + ", age=" + age + "]"; 178 | } 179 | 180 | } 181 | ``` 182 | 183 | ## CRUD operations 184 | 185 | ### Create a repository 186 | 187 | Now that you have your data model, you want to store data. For this, you create 188 | a repository interface which extends `ArangoRepository`. This gives you access 189 | to CRUD operations, paging, and query by example mechanics. 190 | 191 | ```java 192 | package com.arangodb.spring.demo.repository; 193 | 194 | import com.arangodb.spring.demo.entity.Character; 195 | import com.arangodb.springframework.repository.ArangoRepository; 196 | 197 | public interface CharacterRepository extends ArangoRepository { 198 | 199 | } 200 | ``` 201 | 202 | ### Create a CommandLineRunner 203 | 204 | To run your demo with Spring Boot, you have to create a class implementing 205 | `CommandLineRunner`. In this class, you can use the `@Autowired` annotation to 206 | inject your `CharacterRepository` – created one step earlier – and also 207 | `ArangoOperations` which offers a central support for interactions with the 208 | database over a rich feature set. It mostly offers the features from the 209 | [ArangoDB Java driver](https://github.com/arangodb/arangodb-java-driver) 210 | with additional exception translation. 211 | 212 | To get the injection successfully running, you have to add `@ComponentScan` to your 213 | runner to define where Spring can find your configuration class `DemoConfiguration`. 214 | 215 | ```java 216 | package com.arangodb.spring.demo.runner; 217 | 218 | import com.arangodb.spring.demo.repository.CharacterRepository; 219 | import com.arangodb.springframework.core.ArangoOperations; 220 | import org.springframework.beans.factory.annotation.Autowired; 221 | import org.springframework.boot.CommandLineRunner; 222 | import org.springframework.context.annotation.ComponentScan; 223 | 224 | @ComponentScan("com.arangodb.spring.demo") 225 | public class CrudRunner implements CommandLineRunner { 226 | 227 | @Autowired 228 | private ArangoOperations operations; 229 | @Autowired 230 | private CharacterRepository repository; 231 | 232 | @Override 233 | public void run(final String... args) throws Exception { 234 | 235 | } 236 | } 237 | ``` 238 | 239 | ### Save and read an entity 240 | 241 | It's time to save your first entity in the database. Both the database and the 242 | collection don't have to be created manually. This happens automatically as soon 243 | as you execute a database request with the components involved. You don't have 244 | to leave the Java world to manage your database. 245 | 246 | After you saved a character in the database, the id in the original entity is 247 | updated with the one generated from the database. You can then use this id to 248 | find your persisted entity. 249 | 250 | ```java 251 | package com.arangodb.spring.demo.runner; 252 | 253 | import com.arangodb.spring.demo.entity.Character; 254 | import com.arangodb.spring.demo.repository.CharacterRepository; 255 | import com.arangodb.springframework.core.ArangoOperations; 256 | import org.springframework.beans.factory.annotation.Autowired; 257 | import org.springframework.boot.CommandLineRunner; 258 | import org.springframework.context.annotation.ComponentScan; 259 | 260 | import java.util.Optional; 261 | 262 | @ComponentScan("com.arangodb.spring.demo") 263 | public class CrudRunner implements CommandLineRunner { 264 | 265 | @Autowired 266 | private ArangoOperations operations; 267 | @Autowired 268 | private CharacterRepository repository; 269 | 270 | @Override 271 | public void run(String... args) throws Exception { 272 | // first drop the database so that we can run this multiple times with the same dataset 273 | operations.dropDatabase(); 274 | 275 | // save a single entity in the database 276 | // there is no need of creating the collection first. This happen automatically 277 | final Character nedStark = new Character("Ned", "Stark", true, 41); 278 | repository.save(nedStark); 279 | // the generated id from the database is set in the original entity 280 | System.out.println(String.format("Ned Stark saved in the database with id: '%s'", nedStark.getId())); 281 | 282 | // let us take a look whether we can find Ned Stark in the database 283 | final Optional foundNed = repository.findById(nedStark.getId()); 284 | assert foundNed.isPresent(); 285 | System.out.println(String.format("Found %s", foundNed.get())); 286 | } 287 | } 288 | ``` 289 | 290 | ### Run the demo 291 | 292 | The last thing you have to do before you can successfully run your demo 293 | application is to add your command line runner `CrudRunner` to the list of 294 | runners in your main class `DemoApplication`. 295 | 296 | ```java 297 | Class[]runner = new Class[]{CrudRunner.class}; 298 | ``` 299 | 300 | After executing the demo application, you should see the following lines within 301 | your console output. The id will of course deviate. 302 | 303 | ``` 304 | Ned Stark saved in the database with id: '346' 305 | Found Character [id=346, name=Ned, surname=Stark, alive=true, age=41] 306 | ``` 307 | 308 | ### Update an entity 309 | 310 | As everyone probably knows, Ned Stark died in the first season of Game of Thrones. 311 | So, you should to update his 'alive' flag. Thanks to the `id` field in the 312 | `Character` class, you can use the `save()` method of the repository to perform 313 | an upsert with the variable `nedStark` in which `id` is already set. 314 | 315 | Add the following lines of code to the end of our `run()` method in `CrudRunner`: 316 | 317 | ```java 318 | nedStark.setAlive(false); 319 | repository.save(nedStark); 320 | final Optional deadNed = repository.findById(nedStark.getId()); 321 | assert deadNed.isPresent(); 322 | System.out.println(String.format("The 'alive' flag of the persisted Ned Stark is now '%s'",deadNed.get().isAlive())); 323 | ``` 324 | 325 | If you run the demo a second time, the console output should look like this: 326 | 327 | ``` 328 | Ned Stark saved in the database with id: '508' 329 | Found Character [id=508, name=Ned, surname=Stark, alive=true, age=41] 330 | The 'alive' flag of the persisted Ned Stark is now 'false' 331 | ``` 332 | 333 | ### Save and read multiple entities 334 | 335 | What you can do with a single entity, you can also do with multiple entities. 336 | It's not just a single method call for convenience purpose, it also requires 337 | only one database request. 338 | 339 | The following code is for saving a bunch of characters but only the main cast of 340 | Game of Thrones – that's already a lot. After that, you can fetch all of them 341 | from your collection and count them. 342 | 343 | Extend the `run()` method with these lines of code: 344 | 345 | ```java 346 | Collection createCharacters = createCharacters(); 347 | System.out.println(String.format("Save %s additional characters",createCharacters.size())); 348 | repository.saveAll(createCharacters); 349 | 350 | long count = repository.count(); 351 | System.out.println(String.format("A total of %s characters are persisted in the database", count)); 352 | ``` 353 | 354 | You also need the `createCharacters()` method which looks as follow: 355 | 356 | ```java 357 | public static Collection createCharacters(){ 358 | return Arrays.asList( 359 | new Character("Robert","Baratheon",false), 360 | new Character("Jaime","Lannister",true,36), 361 | new Character("Catelyn","Stark",false,40), 362 | new Character("Cersei","Lannister",true,36), 363 | new Character("Daenerys","Targaryen",true,16), 364 | new Character("Jorah","Mormont",false), 365 | new Character("Petyr","Baelish",false), 366 | new Character("Viserys","Targaryen",false), 367 | new Character("Jon","Snow",true,16), 368 | new Character("Sansa","Stark",true,13), 369 | new Character("Arya","Stark",true,11), 370 | new Character("Robb","Stark",false), 371 | new Character("Theon","Greyjoy",true,16), 372 | new Character("Bran","Stark",true,10), 373 | new Character("Joffrey","Baratheon",false,19), 374 | new Character("Sandor","Clegane",true), 375 | new Character("Tyrion","Lannister",true,32), 376 | new Character("Khal","Drogo",false), 377 | new Character("Tywin","Lannister",false), 378 | new Character("Davos","Seaworth",true,49), 379 | new Character("Samwell","Tarly",true,17), 380 | new Character("Stannis","Baratheon",false), 381 | new Character("Melisandre",null,true), 382 | new Character("Margaery","Tyrell",false), 383 | new Character("Jeor","Mormont",false), 384 | new Character("Bronn",null,true), 385 | new Character("Varys",null,true), 386 | new Character("Shae",null,false), 387 | new Character("Talisa","Maegyr",false), 388 | new Character("Gendry",null,false), 389 | new Character("Ygritte",null,false), 390 | new Character("Tormund","Giantsbane",true), 391 | new Character("Gilly",null,true), 392 | new Character("Brienne","Tarth",true,32), 393 | new Character("Ramsay","Bolton",true), 394 | new Character("Ellaria","Sand",true), 395 | new Character("Daario","Naharis",true), 396 | new Character("Missandei",null,true), 397 | new Character("Tommen","Baratheon",true), 398 | new Character("Jaqen","H'ghar",true), 399 | new Character("Roose","Bolton",true), 400 | new Character("The High Sparrow",null,true)); 401 | } 402 | ``` 403 | 404 | After executing the demo again, the console should print the following 405 | additional lines: 406 | 407 | ``` 408 | Save 42 additional characters 409 | A total of 43 characters are persisted in the database 410 | ``` 411 | 412 | ### Read with sorting and paging 413 | 414 | Next to the normal `findAll()` method, `ArangoRepository` also offers the 415 | ability to sort the fetched entities by a given field name. Adding the following 416 | source code at the end of your `run()` method gives you all characters sorted by 417 | field `name`: 418 | 419 | ```java 420 | System.out.println("## Return all characters sorted by name"); 421 | List allSorted = repository.findAll(Sort.by(Sort.Direction.ASC, "name")); 422 | allSorted.forEach(System.out::println); 423 | ``` 424 | 425 | Furthermore, it's possible to use pagination combined with sorting. With the 426 | following code, you get the first 5 characters sorted by name: 427 | 428 | ```java 429 | System.out.println("## Return the first 5 characters sorted by name"); 430 | Page first5Sorted = repository.findAll(PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "name"))); 431 | first5Sorted.forEach(System.out::println); 432 | ``` 433 | 434 | Your console output should include Arya Stark, Bran Stark, Brienne Tarth, Bronn 435 | and Catelyn Stark. 436 | 437 | ``` 438 | ## Return the first 5 characters sorted by name 439 | Character [id=1898, name=Arya, surname=Stark, alive=true, age=11] 440 | Character [id=1901, name=Bran, surname=Stark, alive=true, age=10] 441 | Character [id=1921, name=Brienne, surname=Tarth, alive=true, age=32] 442 | Character [id=1913, name=Bronn, surname=null, alive=true, age=null] 443 | Character [id=1890, name=Catelyn, surname=Stark, alive=false, age=40] 444 | ``` 445 | 446 | ## Query by example 447 | 448 | Since version 1.12, Spring Data provides the `QueryByExampleExecutor` interface 449 | which is also supported by ArangoDB Spring Data. It allows execution of queries 450 | by `Example` instances. 451 | 452 | Create a new `CommandLineRunner` for this: 453 | 454 | ```java 455 | package com.arangodb.spring.demo.runner; 456 | 457 | import org.springframework.beans.factory.annotation.Autowired; 458 | import org.springframework.boot.CommandLineRunner; 459 | import org.springframework.context.annotation.ComponentScan; 460 | import org.springframework.data.domain.Example; 461 | import org.springframework.data.domain.ExampleMatcher; 462 | 463 | import com.arangodb.spring.demo.entity.Character; 464 | import com.arangodb.spring.demo.repository.CharacterRepository; 465 | 466 | @ComponentScan("com.arangodb.spring.demo") 467 | public class ByExampleRunner implements CommandLineRunner { 468 | 469 | @Autowired 470 | private CharacterRepository repository; 471 | 472 | @Override 473 | public void run(final String... args) throws Exception { 474 | System.out.println("# Query by example"); 475 | } 476 | 477 | } 478 | ``` 479 | 480 | Add it to your `DemoApplication`: 481 | 482 | ```java 483 | Class[]runner = new Class[]{ 484 | CrudRunner.class, 485 | ByExampleRunner.class 486 | }; 487 | ``` 488 | 489 | ### Single entity 490 | 491 | First, find Ned Stark again. But this time without knowing the id of the 492 | persisted entity. Start with creating a Character with the same property values 493 | as the searched one. Then create an `Example` instance of it with `Example.of(T) 494 | and search for it with `findOne(Example)` from your `CharacterRepository`: 495 | 496 | ```java 497 | final Character nedStark = new Character("Ned", "Stark", false, 41); 498 | System.out.println(String.format("## Find character which exactly match %s",nedStark)); 499 | Optional foundNedStark = repository.findOne(Example.of(nedStark)); 500 | System.out.println(String.format("Found %s", foundNedStark.get())); 501 | ``` 502 | 503 | If you did everything right, the console output should be as follows: 504 | 505 | ``` 506 | # Query by example 507 | ## Find character which exactly match Character [id=null, name=Ned, surname=Stark, alive=false, age=41] 508 | Found Character [id=1880, name=Ned, surname=Stark, alive=false, age=41] 509 | ``` 510 | 511 | ### Multiple entities 512 | 513 | Now find more than one entity. A lot of Starks die in Game of Thrones, so take a 514 | look who is already dead. For this, you create a new instance of Character with 515 | `surname` `"Stark"` and `alive` `false`. Because you only need these two fields 516 | in your entity, you have to ignore the other fields in your `ExampleMatcher`. 517 | 518 | ```java 519 | System.out.println("## Find all dead Starks"); 520 | Iterable allDeadStarks = repository.findAll(Example.of(new Character(null, "Stark", false))); 521 | allDeadStarks.forEach(System.out::println); 522 | ``` 523 | 524 | After executing the application, the console output should be as follows: 525 | 526 | ``` 527 | ## Find all dead Starks 528 | Character [id=1887, name=Ned, surname=Stark, alive=false, age=41] 529 | Character [id=1890, name=Catelyn, surname=Stark, alive=false, age=40] 530 | Character [id=1899, name=Robb, surname=Stark, alive=false, age=null] 531 | ``` 532 | 533 | In addition to searching for specific values of the example entity, you can 534 | search for dynamically depending values. In the next example, search for a Stark 535 | who is 30 years younger than Ned Stark. Instead of changing the age of Ned Stark 536 | in the previously fetched entity, you use a transformer within the `ExampleMatcher` 537 | and subtract 30 from the age of Ned Stark. 538 | 539 | ```java 540 | System.out.println("## Find all Starks which are 30 years younger than Ned Stark"); 541 | Iterable allYoungerStarks = repository.findAll( 542 | Example.of(foundNedStark.get(), ExampleMatcher.matchingAll() 543 | .withMatcher("surname", GenericPropertyMatcher::exact) 544 | .withIgnorePaths("id", "arangoId", "name", "alive") 545 | .withTransformer("age", age -> age.map(it -> (int) it - 30)))); 546 | allYoungerStarks.forEach(System.out::println); 547 | ``` 548 | 549 | Because you are using the entity `foundNedStark` – fetched from the database – 550 | you have to ignore the `id` field which isn't `null` in this case. 551 | 552 | The console output should only include Arya Stark. 553 | 554 | ``` 555 | ## Find all Starks which are 30 years younger than Ned Stark 556 | Character [id=1898, name=Arya, surname=Stark, alive=true, age=11] 557 | ``` 558 | 559 | Aside from searching for exact and transformed values, you can – in case of type 560 | `String`, also search for other expressions. In this last case, search for every 561 | character whose `surname` ends with `"ark"`. The console output should include 562 | every Stark. 563 | 564 | ```java 565 | System.out.println("## Find all character which surname ends with 'ark' (ignore case)"); 566 | Iterable ark = repository.findAll(Example.of(new Character(null, "ark", false), 567 | ExampleMatcher.matchingAll().withMatcher("surname", GenericPropertyMatcher::endsWith) 568 | .withIgnoreCase() 569 | .withIgnorePaths("name", "alive", "age"))); 570 | ark.forEach(System.out::println); 571 | ``` 572 | 573 | ## Derived queries 574 | 575 | Spring Data ArangoDB supports queries derived from method names by splitting it 576 | into its semantic parts and converting into AQL. The mechanism strips the prefixes 577 | `find..By`, `get..By`, `query..By`, `read..By`, `stream..By`, `count..By`, 578 | `exists..By`, `delete..By`, `remove..By` from the method and parses the rest. 579 | The `By` acts as a separator to indicate the start of the criteria for the query 580 | to be built. You can define conditions on entity properties and concatenate them 581 | with `And` and `Or`. 582 | 583 | You can find the complete list of part types for derived queries in the 584 | [reference documentation](spring-data-arangodb/reference-version-4/repositories/queries/derived-queries.md). 585 | 586 | ### Simple findBy 587 | 588 | Start with an easy example and find characters based on their `surname`. 589 | 590 | The only thing you have to do is to add a method `findBySurname(String)` to your 591 | `CharacterRepository` with a return type which allows the method to return 592 | multiple instances of `Character`. For more information on which return types are 593 | possible, see the [reference documentation](spring-data-arangodb/reference-version-4/repositories/queries/_index.md#return-types). 594 | 595 | ```java 596 | public interface CharacterRepository extends ArangoRepository { 597 | Collection findBySurname(String surname); 598 | } 599 | ``` 600 | 601 | After extending your repository, create a new `CommandLineRunner` and add it to 602 | your `DemoApplication`: 603 | 604 | ```java 605 | Class[]runner=new Class[]{ 606 | CrudRunner.class, 607 | ByExampleRunner.class, 608 | DerivedQueryRunner.class 609 | }; 610 | ``` 611 | 612 | In the `run()` method, call your new `findBySurname(String)` method and try to 613 | find all characters with the `surname` `"Lannister"`. 614 | 615 | ```java 616 | package com.arangodb.spring.demo.runner; 617 | 618 | import org.springframework.beans.factory.annotation.Autowired; 619 | import org.springframework.boot.CommandLineRunner; 620 | import org.springframework.context.annotation.ComponentScan; 621 | 622 | import com.arangodb.spring.demo.entity.Character; 623 | import com.arangodb.spring.demo.repository.CharacterRepository; 624 | 625 | @ComponentScan("com.arangodb.spring.demo") 626 | public class DerivedQueryRunner implements CommandLineRunner { 627 | 628 | @Autowired 629 | private CharacterRepository repository; 630 | 631 | @Override 632 | public void run(final String... args) throws Exception { 633 | System.out.println("# Derived queries"); 634 | 635 | System.out.println("## Find all characters with surname 'Lannister'"); 636 | Iterable lannisters = repository.findBySurname("Lannister"); 637 | lannisters.forEach(System.out::println); 638 | } 639 | } 640 | ``` 641 | 642 | After executing the demo application, you should see the following lines in 643 | your console output: 644 | 645 | ``` 646 | # Derived queries 647 | ## Find all characters with surname 'Lannister' 648 | Character [id=238, name=Jaime, surname=Lannister, alive=true, age=36] 649 | Character [id=240, name=Cersei, surname=Lannister, alive=true, age=36] 650 | Character [id=253, name=Tyrion, surname=Lannister, alive=true, age=32] 651 | Character [id=255, name=Tywin, surname=Lannister, alive=false, age=null] 652 | ``` 653 | 654 | ### Create an index 655 | 656 | Indexes allow fast access to documents, provided the indexed attribute(s) are 657 | used in a query. To make `findBySurname` queries faster, you can create an index 658 | on the `surname` field, adding the `@PersistentIndex` to the `Character` class: 659 | 660 | ```java 661 | 662 | @Document("characters") 663 | @PersistentIndex(fields = {"surname"}) 664 | public class Character { 665 | ``` 666 | 667 | Next time you run the demo, the related queries benefit from the index and 668 | avoid performing a full collection scan. 669 | 670 | ### More complex findBy 671 | 672 | Create some methods with more parts and have a look at how they fit together. 673 | You can use different return types. Again, simply add the methods in your 674 | `CharacterRepository`: 675 | 676 | ```java 677 | List findTop2DistinctBySurnameIgnoreCaseOrderByAgeDesc(String surname); 678 | Collection findBySurnameEndsWithAndAgeBetweenAndNameInAllIgnoreCase( 679 | String suffix, 680 | int lowerBound, 681 | int upperBound, 682 | String[]nameList); 683 | ``` 684 | 685 | Also add the method calls in `DerivedMethodRunner`: 686 | 687 | ```java 688 | System.out.println("## Find top 2 Lannnisters ordered by age"); 689 | List top2 = repository.findTop2DistinctBySurnameIgnoreCaseOrderByAgeDesc("lannister"); 690 | top2.forEach(System.out::println); 691 | 692 | System.out.println("## Find all characters which name is 'Bran' or 'Sansa' and it's surname ends with 'ark' and are between 10 and 16 years old"); 693 | Collection youngStarks = repository.findBySurnameEndsWithAndAgeBetweenAndNameInAllIgnoreCase("ark", 10, 16, new String[]{"Bran", "Sansa"}); 694 | youngStarks.forEach(System.out::println); 695 | ``` 696 | 697 | The new methods produce the following console output: 698 | 699 | ``` 700 | ## Find top 2 Lannnisters ordered by age 701 | Character [id=444, name=Jaime, surname=Lannister, alive=true, age=36] 702 | Character [id=446, name=Cersei, surname=Lannister, alive=true, age=36] 703 | ## Find all characters which name is 'Bran' or 'Sansa' and it's surname ends with 'ark' and are between 10 and 16 years old 704 | Character [id=452, name=Sansa, surname=Stark, alive=true, age=13] 705 | Character [id=456, name=Bran, surname=Stark, alive=true, age=10] 706 | ``` 707 | 708 | ### Single entity result 709 | 710 | With derived queries, you can not only query for multiple entities, but also for 711 | single entities. If you expect only a single entity as the result, you can use 712 | the corresponding return type. 713 | 714 | Because you have a unique persistent index on the fields `name` and `surname`, 715 | you can expect only a single entity when querying for both. 716 | 717 | For this example, add the method `findByNameAndSurname(String, String)` in 718 | `CharacterRepository`, whose return type is `Optional`. 719 | 720 | ```java 721 | Optional findByNameAndSurname(String name, String surname); 722 | ``` 723 | 724 | Call it from `DerivedMethodRunner`: 725 | 726 | ```java 727 | System.out.println("## Find a single character by name & surname"); 728 | Optional tyrion = repository.findByNameAndSurname("Tyrion", "Lannister"); 729 | tyrion.ifPresent(c -> System.out.println(String.format("Found %s", c))); 730 | ``` 731 | 732 | The console output should look like this: 733 | 734 | ``` 735 | ## Find a single character by name & surname 736 | Found Character [id=974, name=Tyrion, surname=Lannister, alive=true, age=32] 737 | ``` 738 | 739 | ### countBy 740 | 741 | Aside from `findBy`, there are other supported prefixes like `countBy`. 742 | In comparison to the previously used `operations.collection(Character.class).count();`, 743 | the `countBy` is able to include filter conditions. 744 | 745 | With the following lines of code, you are able to only count characters that 746 | are still alive. 747 | 748 | `CharacterRepository`: 749 | 750 | ```java 751 | Integer countByAliveTrue(); 752 | ``` 753 | 754 | `DerivedMethodRunner`: 755 | 756 | ```java 757 | System.out.println("## Count how many characters are still alive"); 758 | Integer alive = repository.countByAliveTrue(); 759 | System.out.println(String.format("There are %s characters still alive", alive)); 760 | ``` 761 | 762 | ### removeBy 763 | 764 | The last example for derived queries is `removeBy`. Here, remove all characters 765 | except those whose surname is 'Stark' and who are still alive. 766 | 767 | `CharacterRepository`: 768 | 769 | ```java 770 | void removeBySurnameNotLikeOrAliveFalse(String surname); 771 | ``` 772 | 773 | `DerivedMethodRunner`: 774 | 775 | ```java 776 | System.out.println("## Remove all characters except of which surname is 'Stark' and which are still alive"); 777 | repository.removeBySurnameNotLikeOrAliveFalse("Stark"); 778 | repository.findAll().forEach(System.out::println); 779 | ``` 780 | 781 | Only Arya, Bran, and Sansa are expected to be left. 782 | 783 | ``` 784 | ## Remove all characters except of which surname is 'Stark' and which are still alive 785 | Character [id=1453, name=Sansa, surname=Stark, alive=true, age=13] 786 | Character [id=1454, name=Arya, surname=Stark, alive=true, age=11] 787 | Character [id=1457, name=Bran, surname=Stark, alive=true, age=10] 788 | ``` 789 | 790 | ## Relations 791 | 792 | ArangoDB is a graph database system and Spring Data ArangoDB also supports 793 | features for graphs. 794 | 795 | With the `@Relations` annotation, you can define relationships between the 796 | entities. 797 | 798 | To demonstrate this, use the previously created `Character` entity. 799 | Add a `children` field of type `Collection` annotated with 800 | `@Relations(edges = ChildOf.class, lazy = true)`. 801 | 802 | ```java 803 | @Document("characters") 804 | @PersistentIndex(fields = {"surname"}) 805 | public class Character { 806 | 807 | @Id // db document field: _key 808 | private String id; 809 | 810 | @ArangoId // db document field: _id 811 | private String arangoId; 812 | 813 | private String name; 814 | private String surname; 815 | private boolean alive; 816 | private Integer age; 817 | @Relations(edges = ChildOf.class, lazy = true) 818 | private Collection children; 819 | 820 | // ... 821 | 822 | } 823 | ``` 824 | 825 | Then create an entity for the edge you stated in `@Relations`. Other than a 826 | normal entity annotated with `@Document`, this entity is annotated with 827 | `@Edge`. This allows Spring Data ArangoDB to create an edge collection in 828 | the database. Just like `Character`, `ChildOf` also gets a field for its 829 | `id`. To connect two `Character` entities, it also gets a field of type 830 | `Character` annotated with `@From` and a field of type `Character` annotated 831 | with `@To`. `ChildOf` is persisted in the database with the ids of these 832 | two characters. 833 | 834 | ```java 835 | package com.arangodb.spring.demo.entity; 836 | 837 | import com.arangodb.springframework.annotation.Edge; 838 | import com.arangodb.springframework.annotation.From; 839 | import com.arangodb.springframework.annotation.To; 840 | import org.springframework.data.annotation.Id; 841 | 842 | @Edge 843 | public class ChildOf { 844 | 845 | @Id 846 | private String id; 847 | 848 | @From 849 | private Character child; 850 | 851 | @To 852 | private Character parent; 853 | 854 | public ChildOf(final Character child, final Character parent) { 855 | super(); 856 | this.child = child; 857 | this.parent = parent; 858 | } 859 | 860 | // setter & getter 861 | 862 | @Override 863 | public String toString() { 864 | return "ChildOf [id=" + id + ", child=" + child + ", parent=" + parent + "]"; 865 | } 866 | 867 | } 868 | ``` 869 | 870 | To save instances of `ChildOf` in the database, create a repository for it the 871 | same way you created `CharacterRepository`. 872 | 873 | ```java 874 | package com.arangodb.spring.demo.repository; 875 | 876 | import com.arangodb.spring.demo.entity.ChildOf; 877 | import com.arangodb.springframework.repository.ArangoRepository; 878 | 879 | public interface ChildOfRepository extends ArangoRepository { 880 | 881 | } 882 | ``` 883 | 884 | Implement another `CommandLineRunner` called `RelationsRunner` and add it to 885 | your `DemoApplication` like with all the runners before. 886 | 887 | ```java 888 | Class[] runner = new Class[]{ 889 | CrudRunner.class, 890 | ByExampleRunner.class, 891 | DerivedQueryRunner.class, 892 | RelationsRunner.class 893 | }; 894 | ``` 895 | 896 | In the newly created `RelationsRunner`, inject `CharacterRepository` and 897 | `ChildOfRepository` and built your relations. First, save some characters 898 | because you removed most of them earlier. To do so, use the static 899 | `createCharacter()` method of the `CrudRunner`. After you have successfully 900 | persisted your characters, save some relationships with the edge entity `ChildOf`. 901 | Because `ChildOf` requires instances of `Character` with the `id` field set by 902 | the database, you first have to find them in your `CharacterRepository`. 903 | To ensure you find the correct `Character`, use the derived query method 904 | `findByNameAndSurename(String, String)` that gives you one specific `Character`. 905 | Then create instances of `ChildOf` and save them through `ChildOfRepository`. 906 | 907 | ```java 908 | package com.arangodb.spring.demo.runner; 909 | 910 | import com.arangodb.spring.demo.entity.ChildOf; 911 | import com.arangodb.spring.demo.repository.CharacterRepository; 912 | import com.arangodb.spring.demo.repository.ChildOfRepository; 913 | import org.springframework.beans.factory.annotation.Autowired; 914 | import org.springframework.boot.CommandLineRunner; 915 | import org.springframework.context.annotation.ComponentScan; 916 | 917 | import java.util.Arrays; 918 | 919 | @ComponentScan("com.arangodb.spring.demo") 920 | public class RelationsRunner implements CommandLineRunner { 921 | 922 | @Autowired 923 | private CharacterRepository characterRepo; 924 | @Autowired 925 | private ChildOfRepository childOfRepo; 926 | 927 | @Override 928 | public void run(final String... args) throws Exception { 929 | System.out.println("# Relations"); 930 | characterRepo.saveAll(CrudRunner.createCharacters()); 931 | 932 | // first create some relations for the Starks and Lannisters 933 | Character ned = characterRepo.findByNameAndSurname("Ned", "Stark").get(); 934 | Character catelyn = characterRepo.findByNameAndSurname("Catelyn", "Stark").get(); 935 | Character robb = characterRepo.findByNameAndSurname("Robb", "Stark").get(); 936 | childOfRepo.saveAll(Arrays.asList(new ChildOf(robb, ned), new ChildOf(robb, catelyn))); 937 | Character sansa = characterRepo.findByNameAndSurname("Sansa", "Stark").get(); 938 | childOfRepo.saveAll(Arrays.asList(new ChildOf(sansa, ned), new ChildOf(sansa, catelyn))); 939 | Character arya = characterRepo.findByNameAndSurname("Arya", "Stark").get(); 940 | childOfRepo.saveAll(Arrays.asList(new ChildOf(arya, ned), new ChildOf(arya, catelyn))); 941 | Character bran = characterRepo.findByNameAndSurname("Bran", "Stark").get(); 942 | childOfRepo.saveAll(Arrays.asList(new ChildOf(bran, ned), new ChildOf(bran, catelyn))); 943 | Character jon = characterRepo.findByNameAndSurname("Jon", "Snow").get(); 944 | childOfRepo.save(new ChildOf(jon, ned)); 945 | 946 | Character tywin = characterRepo.findByNameAndSurname("Tywin", "Lannister").get(); 947 | Character jaime = characterRepo.findByNameAndSurname("Jaime", "Lannister").get(); 948 | childOfRepo.save(new ChildOf(jaime, tywin)); 949 | Character cersei = characterRepo.findByNameAndSurname("Cersei", "Lannister").get(); 950 | childOfRepo.save(new ChildOf(cersei, tywin)); 951 | Character joffrey = characterRepo.findByNameAndSurname("Joffrey", "Baratheon").get(); 952 | childOfRepo.saveAll(Arrays.asList(new ChildOf(joffrey, jaime), new ChildOf(joffrey, cersei))); 953 | Character tyrion = characterRepo.findByNameAndSurname("Tyrion", "Lannister").get(); 954 | childOfRepo.save(new ChildOf(tyrion, tywin)); 955 | } 956 | } 957 | ``` 958 | 959 | ### Read relations within an entity 960 | 961 | After you add `@Relations(edges = ChildOf.class, lazy = true) Collection children;` 962 | in `Character` you can load all children of a character when you fetch the 963 | character from the database. Use the `findByNameAndSurname(String, String)` 964 | method again to find one specific character. 965 | 966 | Add the following lines of code to the `run()` method of `RelationsRunner`: 967 | 968 | ```java 969 | Character nedStark = characterRepo.findByNameAndSurname("Ned", "Stark").get(); 970 | System.out.println(String.format("## These are the children of %s:", nedStark)); 971 | nedStark.getChildren().forEach(System.out::println); 972 | ``` 973 | 974 | After executing the demo again, you can see the following console output: 975 | 976 | ``` 977 | ## These are the children of Character [id=2547, name=Ned, surname=Stark, alive=false, age=41]: 978 | Character [id=2488, name=Bran, surname=Stark, alive=true, age=10] 979 | Character [id=2485, name=Arya, surname=Stark, alive=true, age=11] 980 | Character [id=2559, name=Robb, surname=Stark, alive=false, age=null] 981 | Character [id=2556, name=Jon, surname=Snow, alive=true, age=16] 982 | Character [id=2484, name=Sansa, surname=Stark, alive=true, age=13] 983 | ``` 984 | 985 | ### findBy including relations 986 | 987 | The `children` field is not persisted in the character entity itself, it is 988 | represented by the `ChildOf` edge. Nevertheless, you can write a derived method 989 | which includes properties of all connected `Character`. 990 | 991 | With the following two methods – added in `CharacterRepository` – you can query 992 | for a `Character` which has a child with a given name or an age between two 993 | given integers. 994 | 995 | ```java 996 | Collection findByChildrenName(String name); 997 | Collection findByChildrenAgeBetween(int lowerBound, int upperBound); 998 | ``` 999 | 1000 | Call these methods in `RelationsRunner` and search for all parents of 'Sansa' 1001 | and all parents that have a child between 16 and 20 years old. 1002 | 1003 | ``` 1004 | System.out.println("## These are the parents of 'Sansa'"); 1005 | Iterable parentsOfSansa = characterRepo.findByChildrenName("Sansa"); 1006 | parentsOfSansa.forEach(System.out::println); 1007 | 1008 | System.out.println("## These parents have a child which is between 16 and 20 years old"); 1009 | Iterable childrenBetween16a20 = characterRepo.findByChildrenAgeBetween(16, 20); 1010 | childrenBetween16a20.forEach(System.out::println); 1011 | ``` 1012 | 1013 | The console output shows us that Ned and Catelyn are the parents of Sansa and 1014 | that Ned, Jamie, and Cersei have at least one child in the age between 16 and 1015 | 20 years. 1016 | 1017 | ``` 1018 | ## These are the parents of 'Sansa' 1019 | Character [id=2995, name=Ned, surname=Stark, alive=false, age=41] 1020 | Character [id=2998, name=Catelyn, surname=Stark, alive=false, age=40] 1021 | ## These parents have a child which is between 16 and 20 years old 1022 | Character [id=2995, name=Ned, surname=Stark, alive=false, age=41] 1023 | Character [id=2997, name=Jaime, surname=Lannister, alive=true, age=36] 1024 | Character [id=2999, name=Cersei, surname=Lannister, alive=true, age=36] 1025 | ``` 1026 | 1027 | ## Query methods 1028 | 1029 | You can have repository methods with self-written AQL queries. 1030 | 1031 | When it comes to more complex use cases where a derived method would get way too 1032 | long and become unreadable, queries using the [ArangoDB Query Language (AQL)](../../aql/_index.md) 1033 | can be supplied with the `@Query` annotation on methods in your repositories. 1034 | 1035 | AQL supports the usage of [bind parameters](../../aql/fundamentals/bind-parameters.md), 1036 | thus allowing to separate the query text from literal values used in the query. 1037 | There are three ways of passing bind parameters to the query in the `@Query` 1038 | annotation that are described below. 1039 | 1040 | ### Param annotation 1041 | 1042 | To pass bind parameters to your query, you can use the `@Param` annotation. 1043 | With the `@Param` annotation, the argument is placed in the query at the place 1044 | corresponding to the value passed to the `@Param` annotation. 1045 | 1046 | To demonstrate this, add another method to `CharacterRepository`: 1047 | 1048 | ```java 1049 | @Query("FOR c IN characters FILTER c.surname == @surname SORT c.age ASC RETURN c") 1050 | List getWithSurname(@Param("surname") String value); 1051 | ``` 1052 | 1053 | Here, the bind parameter is named `surname` and the method parameter `value` is 1054 | annoated with `@Param("surname")`. Only the value in the `@Param` annotation has 1055 | to match with your bind parameter, the method parameter name does not matter. 1056 | 1057 | Note that this uses the collection name `characters` and not `character` in your 1058 | query. Normally, a collection would be named like the corresponding entity class. 1059 | However, you used `@Document("characters")` in `Character` which set the 1060 | collection name to `characters`. 1061 | 1062 | Create a new `CommandLineRunner` and add it to our `DemoApplication`: 1063 | 1064 | ```java 1065 | package com.arangodb.spring.demo.runner; 1066 | 1067 | import com.arangodb.spring.demo.entity.Character; 1068 | import com.arangodb.spring.demo.repository.CharacterRepository; 1069 | import org.springframework.beans.factory.annotation.Autowired; 1070 | import org.springframework.boot.CommandLineRunner; 1071 | 1072 | public class AQLRunner implements CommandLineRunner { 1073 | 1074 | @Autowired 1075 | private CharacterRepository repository; 1076 | 1077 | @Override 1078 | public void run(final String... args) throws Exception { 1079 | System.out.println("# AQL queries"); 1080 | } 1081 | 1082 | } 1083 | ``` 1084 | 1085 | `DemoApplication`: 1086 | 1087 | ```java 1088 | Class[]runner=new Class[]{ 1089 | CrudRunner.class, 1090 | ByExampleRunner.class, 1091 | DerivedQueryRunner.class, 1092 | RelationsRunner.class, 1093 | AQLRunner.class 1094 | }; 1095 | ``` 1096 | 1097 | Add the following lines to `AQLRunner`: 1098 | 1099 | ```java 1100 | System.out.println("## Find all characters with surname 'Lannister' (sort by age ascending)"); 1101 | List lannisters = repository.getWithSurname("Lannister"); 1102 | lannisters.forEach(System.out::println); 1103 | ``` 1104 | 1105 | The console output should give you all characters with `surname` Lannister. 1106 | 1107 | ``` 1108 | ## Find all characters with surname 'Lannister' (sort by age ascending) 1109 | Character [id=7613, name=Tywin, surname=Lannister, alive=false, age=null] 1110 | Character [id=7611, name=Tyrion, surname=Lannister, alive=true, age=32] 1111 | Character [id=7596, name=Jaime, surname=Lannister, alive=true, age=36] 1112 | Character [id=7598, name=Cersei, surname=Lannister, alive=true, age=36] 1113 | ``` 1114 | 1115 | ### BindVars annotation 1116 | 1117 | In addition to number matching and the `@Param` annotation, you can use a method 1118 | parameter of type `Map` annotated with `@BindVars` as your 1119 | bind parameters. You can then fill the map with any parameter used in the query. 1120 | 1121 | Add the following to `CharacterRepository`: 1122 | 1123 | ```java 1124 | @Query("FOR c IN @@col FILTER c.surname == @surname AND c.age > @age RETURN c") 1125 | ArangoCursor getWithSurnameOlderThan(@Param("age") int value, @BindVars Map bindvars); 1126 | ``` 1127 | 1128 | This query uses three bind parameters `@@col`, `@surname`, and `@age`. 1129 | One of the bind parameter is written with two `@`. This is a special type of 1130 | bind parameter that exists for injecting collection names. This type of 1131 | bind parameter has a name prefixed with an additional `@` symbol. 1132 | 1133 | Furthermore, `@Param` is used for the `@age` bind parameter but not for `@@col` 1134 | and `@surname`. These bind parameters have to be passed through the map annotated 1135 | with `@BindVars`. It is also possible to use both annotations within one query method. 1136 | 1137 | The method call looks as expected. An integer is passed for the `age` 1138 | bind parameter and a map with the keys `surname` and `@col` to your new method. 1139 | 1140 | ```java 1141 | System.out.println("## Find all characters with surname 'Lannister' which are older than 35"); 1142 | Map bindvars = new HashMap<>(); 1143 | bindvars.put("surname", "Lannister"); 1144 | bindvars.put("@col", Character.class); 1145 | ArangoCursor oldLannisters = repository.getWithSurnameOlderThan(35, bindvars); 1146 | oldLannisters.forEach(System.out::println); 1147 | ``` 1148 | 1149 | One additional special handling for collection bind parameter is that you do not 1150 | have to pass the collection name as a String to the method. You can pass the type 1151 | (`Character.class`) to your method. Spring Data ArangoDB determines the 1152 | collection name. This is very convenient if you have used an alternative 1153 | collection name within the `@Document` or `@Edge` annotations. 1154 | 1155 | The console output should be as follows: 1156 | 1157 | ``` 1158 | ## Find all characters with surname 'Lannister' which are older than 35 1159 | Character [id=8294, name=Jaime, surname=Lannister, alive=true, age=36] 1160 | Character [id=8296, name=Cersei, surname=Lannister, alive=true, age=36] 1161 | ``` 1162 | 1163 | ### QueryOptions annotation 1164 | 1165 | Sometimes, you want to be able to configure the query execution on a technical 1166 | level. Spring Data ArangoDB provides the `@QueryOptions` annotation for this. 1167 | With this annotation, you are able to set something like a batch size to control 1168 | the number of results to be transferred from the database server in one roundtrip 1169 | and some other things. 1170 | 1171 | For example, you can return the number of found results. To achieve that you 1172 | have to change the return type in the previously created `getWithSurnameOlderThan(int, Map)` 1173 | method from `Iterable` to `ArangoCursor`. `ArangoCursor` 1174 | provides a `getCount()` method that gives you the number of found results. 1175 | But this value is only returned from the database when you set the `count` flag 1176 | in the query options to `true`, so you also have to add the `@QueryOptions` 1177 | annotation to the method with `count = true`. 1178 | 1179 | ```java 1180 | @Query("FOR c IN @@col FILTER c.surname == @surname AND c.age > @age RETURN c") 1181 | @QueryOptions(count = true) 1182 | Iterable getWithSurnameOlderThan(@Param("age") int value, @BindVars Map bindvars); 1183 | ``` 1184 | 1185 | If you change the type of our `oldLannisters` local variable in `AQLRunner` 1186 | to `ArangoCursor`, you can get the count value from it. 1187 | 1188 | ```java 1189 | ArangoCursor oldLannisters = repository.getWithSurnameOlderThan(35, bindvars); 1190 | System.out.println(String.format("Found %s documents", oldLannisters.getCount())); 1191 | oldLannisters.forEach(System.out::println); 1192 | ``` 1193 | 1194 | The new console output should look like this: 1195 | 1196 | ``` 1197 | ## Find all characters with surname 'Lannister' which are older than 35 1198 | Found 2 documents 1199 | Character [id=9012, name=Jaime, surname=Lannister, alive=true, age=36] 1200 | Character [id=9014, name=Cersei, surname=Lannister, alive=true, age=36] 1201 | ``` 1202 | 1203 | ### Graph traversal 1204 | 1205 | To finish the query method topic, add a [graph traversal](../../aql/graphs/traversals.md) 1206 | written in AQL to this demo where the `ChildOf` edges are involved. 1207 | 1208 | The following query searches for every `Character` connected (through `ChildOf`) 1209 | with the character to whom the passed `id` belongs to. This time, specify the 1210 | edge collection in the query that you pass as a bind parameter with the 1211 | `@Param` annotation. 1212 | 1213 | `CharacterRepository`: 1214 | 1215 | ```java 1216 | @Query("FOR v IN 1..2 INBOUND @arangoId @@edgeCol SORT v.age DESC RETURN DISTINCT v") 1217 | List getAllChildrenAndGrandchildren(@Param("arangoId") String arangoId, @Param("@edgeCol") Class edgeCollection); 1218 | ``` 1219 | 1220 | Like before with `Character.class` in your map, use the type of `ChildOf` as 1221 | the parameter value. To find all children and grandchildren of Tywin Lannister, 1222 | you first have to find him to get his `id` which you can then pass to the 1223 | query method. 1224 | 1225 | `AQLRunner`: 1226 | 1227 | ```java 1228 | System.out.println("## Find all children and grantchildren of 'Tywin Lannister' (sort by age descending)"); 1229 | List children = repository.findByNameAndSurname("Tywin", "Lannister").map(tywin -> 1230 | repository.getAllChildrenAndGrandchildren(tywin.getArangoId(), ChildOf.class)).get(); 1231 | children.forEach(System.out::println); 1232 | ``` 1233 | 1234 | After executing the demo again, you can see the following console output: 1235 | 1236 | ``` 1237 | ## Find all children and grantchildren of 'Tywin Lannister' (sort by age descending) 1238 | Character [id=11255, name=Tyrion, surname=Lannister, alive=true, age=32] 1239 | Character [id=11242, name=Cersei, surname=Lannister, alive=true, age=36] 1240 | Character [id=11253, name=Joffrey, surname=Baratheon, alive=false, age=19] 1241 | Character [id=11240, name=Jaime, surname=Lannister, alive=true, age=36] 1242 | ``` 1243 | 1244 | ## Geospatial queries 1245 | 1246 | Geospatial queries are a subsection of derived queries. To use a geospatial 1247 | query on a collection, a geo index must exist on that collection. A geo index 1248 | can be created on a field which is a two element array, corresponding to latitude 1249 | and longitude coordinates. 1250 | 1251 | As a subsection of derived queries, geospatial queries support the same 1252 | return types, and also these additional three return types: `GeoPage`, 1253 | `GeoResult`, and `GeoResults`. These types must be used in order to get the 1254 | distance of each document as generated by the query. 1255 | 1256 | ### Geo data modeling 1257 | 1258 | To demonstrate geospatial queries, create a new entity class `Location` with a 1259 | `location` field of type `org.springframework.data.geo.Point`. You also have to 1260 | create a geo index on this field. You can do so by annotating the field with 1261 | `@GeoIndexed(geoJson = true)`. As you probably remember, you have already used 1262 | an index in the `Character` class, but you annotated the type and not the 1263 | affected fields. 1264 | 1265 | Spring Data ArangoDB offers two ways of defining an index. With the 1266 | `@Indexed` annotations, indexes for single fields can be defined. 1267 | If the index should include multiple fields, the `@Index` annotations 1268 | can be used on the type instead. See the [reference documentation](spring-data-arangodb/reference-version-4/mapping/indexes.md) 1269 | for more information. 1270 | 1271 | Create a new `Location` class: 1272 | 1273 | ```java 1274 | package com.arangodb.spring.demo.entity; 1275 | 1276 | import com.arangodb.springframework.annotation.Document; 1277 | import com.arangodb.springframework.annotation.GeoIndexed; 1278 | import org.springframework.data.annotation.Id; 1279 | import org.springframework.data.geo.Point; 1280 | 1281 | import java.util.Arrays; 1282 | 1283 | @Document("locations") 1284 | public class Location { 1285 | 1286 | @Id 1287 | private String id; 1288 | private final String name; 1289 | @GeoIndexed(geoJson = true) 1290 | private final Point location; 1291 | 1292 | public Location(final String name, final Point location) { 1293 | super(); 1294 | this.name = name; 1295 | this.location = location; 1296 | } 1297 | 1298 | // getter & setter 1299 | 1300 | @Override 1301 | public String toString() { 1302 | return "Location{" + 1303 | "id='" + id + '\'' + 1304 | ", name='" + name + '\'' + 1305 | ", location=" + location + 1306 | '}'; 1307 | } 1308 | 1309 | } 1310 | ``` 1311 | 1312 | Create the corresponding `LocationRepository` repository: 1313 | 1314 | ```java 1315 | package com.arangodb.spring.demo.repository; 1316 | 1317 | import com.arangodb.spring.demo.entity.Location; 1318 | import com.arangodb.springframework.repository.ArangoRepository; 1319 | 1320 | public interface LocationRepository extends ArangoRepository { 1321 | 1322 | } 1323 | ``` 1324 | 1325 | After that, create a new `CommandLineRunner`, add it to your `DemoApplication`, 1326 | and perform some insert operations with some popular locations from 1327 | Game of Thrones with the coordinates of their real counterparts: 1328 | 1329 | ```java 1330 | package com.arangodb.spring.demo.runner; 1331 | 1332 | import com.arangodb.spring.demo.entity.Location; 1333 | import com.arangodb.spring.demo.repository.LocationRepository; 1334 | import org.springframework.beans.factory.annotation.Autowired; 1335 | import org.springframework.boot.CommandLineRunner; 1336 | 1337 | import java.util.Arrays; 1338 | 1339 | public class GeospatialRunner implements CommandLineRunner { 1340 | 1341 | @Autowired 1342 | private LocationRepository repository; 1343 | 1344 | @Override 1345 | public void run(final String... args) throws Exception { 1346 | System.out.println("# Geospatial"); 1347 | 1348 | repository.saveAll(Arrays.asList( 1349 | new Location("Dragonstone", new Point(-6.815096, 55.167801)), 1350 | new Location("King's Landing", new Point(18.110189, 42.639752)), 1351 | new Location("The Red Keep", new Point(14.446442, 35.896447)), 1352 | new Location("Yunkai", new Point(-7.129532, 31.046642)), 1353 | new Location("Astapor", new Point(-9.774249, 31.50974)), 1354 | new Location("Winterfell", new Point(-5.581312, 54.368321)), 1355 | new Location("Vaes Dothrak", new Point(-6.096125, 54.16776)), 1356 | new Location("Beyond the wall", new Point(-21.094093, 64.265473)) 1357 | )); 1358 | } 1359 | } 1360 | ``` 1361 | 1362 | `DemoApplication`: 1363 | 1364 | ```java 1365 | Class[]runner=new Class[]{ 1366 | CrudRunner.class, 1367 | ByExampleRunner.class, 1368 | DerivedQueryRunner.class, 1369 | RelationsRunner.class, 1370 | AQLRunner.class, 1371 | GeospatialRunner.class 1372 | }; 1373 | ``` 1374 | 1375 | There are two kinds of geospatial query: `Near` and `Within`. 1376 | 1377 | ### Near 1378 | 1379 | `Near` sorts entities by distance from a given point. 1380 | The result can be restricted with paging. 1381 | 1382 | `LocationRepository`: 1383 | 1384 | ```java 1385 | GeoPage findByLocationNear(Point location,Pageable pageable); 1386 | ``` 1387 | 1388 | In this example, search for locations sorted by distance to a given point, 1389 | matching the coordinates of Winterfell. Use pagination to split the results in 1390 | pages of five locations. 1391 | 1392 | ```java 1393 | System.out.println("## Find the first 5 locations near 'Winterfell'"); 1394 | GeoPage first5 = repository.findByLocationNear(new Point(-5.581312, 54.368321), PageRequest.of(0, 5)); 1395 | first5.forEach(System.out::println); 1396 | 1397 | System.out.println("## Find the next 5 locations near 'Winterfell' (only 3 locations left)"); 1398 | GeoPage next5 = repository.findByLocationNear(new Point(-5.581312, 54.368321), PageRequest.of(1, 5)); 1399 | next5.forEach(System.out::println); 1400 | ``` 1401 | 1402 | Because you use the coordinates of Winterfell, the distance in the output to 1403 | Winterfell is `0`. 1404 | 1405 | ``` 1406 | ## Find the first 5 locations near 'Winterfell' 1407 | GeoResult [content: Location [id=14404, name=Yunkai, location=[31.046642, -7.129532]], distance: 3533.2076972451478 KILOMETERS, ] 1408 | GeoResult [content: Location [id=14405, name=Astapor, location=[31.50974, -9.774249]], distance: 3651.785495816579 KILOMETERS, ] 1409 | GeoResult [content: Location [id=14403, name=The Red Keep, location=[35.896447, 14.446442]], distance: 4261.971994059222 KILOMETERS, ] 1410 | GeoResult [content: Location [id=14402, name=King's Landing, location=[42.639752, 18.110189]], distance: 5074.755682897005 KILOMETERS, ] 1411 | GeoResult [content: Location [id=14407, name=Vaes Dothrak, location=[54.16776, -6.096125]], distance: 6049.156388427102 KILOMETERS, ] 1412 | ## Find the next 5 locations near 'Winterfell' (only 3 locations left) 1413 | GeoResult [content: Location [id=14406, name=Winterfell, location=[54.368321, -5.581312]], distance: 6067.104268175527 KILOMETERS, ] 1414 | GeoResult [content: Location [id=14401, name=Dragonstone, location=[55.167801, -6.815096]], distance: 6165.650581599857 KILOMETERS, ] 1415 | GeoResult [content: Location [id=14408, name=Beyond the wall, location=[64.265473, -21.094093]], distance: 7350.229798961836 KILOMETERS, ] 1416 | ``` 1417 | 1418 | ### Within 1419 | 1420 | `Within` both sorts and filters entities, returning those within the given 1421 | distance, range, or shape. 1422 | 1423 | Add some methods to `LocationRepository` that use different filter criteria: 1424 | 1425 | ```java 1426 | GeoResults findByLocationWithin(Point location,Distance distance); 1427 | Iterable findByLocationWithin(Point location,Range distanceRange); 1428 | ``` 1429 | 1430 | With these methods, you can search for locations within a given distance or 1431 | range to our point – Winterfell. 1432 | 1433 | ```java 1434 | System.out.println("## Find all locations within 50 kilometers of 'Winterfell'"); 1435 | GeoResults findWithing50kilometers = repository 1436 | .findByLocationWithin(new Point(-5.581312, 54.368321), new Distance(50, Metrics.KILOMETERS)); 1437 | findWithing50kilometers.forEach(System.out::println); 1438 | 1439 | System.out.println("## Find all locations which are 40 to 50 kilometers away from 'Winterfell'"); 1440 | Iterable findByLocationWithin = repository.findByLocationWithin(new Point(-5.581312, 54.368321), 1441 | Range.of(Range.Bound.inclusive(40000.), Range.Bound.exclusive(50000.))); 1442 | findByLocationWithin.forEach(System.out::println); 1443 | ``` 1444 | 1445 | As you can see in the console output, both _Winterfell_ and _Vaes Dothrak_ are 1446 | located within a 50 kilometers radius around your point. But only _Vaes Dothrak_ 1447 | is obviously more than 40 kilometers away from it. 1448 | 1449 | ``` 1450 | ## Find all locations within 50 kilometers of 'Winterfell' 1451 | GeoResult [content: Location [id=14843, name=Winterfell, location=[54.368321, -5.581312]], distance: 0.0 KILOMETERS, ] 1452 | GeoResult [content: Location [id=14844, name=Vaes Dothrak, location=[54.16776, -6.096125]], distance: 40.186277071065994 KILOMETERS, ] 1453 | ## Find all locations which are 40 to 50 kilometers away from 'Winterfell' 1454 | Location [id=14844, name=Vaes Dothrak, location=[54.16776, -6.096125]] 1455 | ``` 1456 | 1457 | You cannot only implement geo functions going from a single point but it is also 1458 | possible to search for locations within a polygon. 1459 | 1460 | Add a method using `Polygon`: 1461 | 1462 | `LocationRepository`: 1463 | ```java 1464 | Iterable findByLocationWithin(Polygon polygon); 1465 | ``` 1466 | 1467 | `GeospatialRunner`: 1468 | ```java 1469 | System.out.println("## Find all locations within a given polygon"); 1470 | Iterable withinPolygon = repository.findByLocationWithin( 1471 | new Polygon(Arrays.asList(new Point(-25, 40), new Point(-25, 70), new Point(25, 70), new Point(-25, 40)))); 1472 | withinPolygon.forEach(System.out::println); 1473 | ``` 1474 | 1475 | The console output should be as follows: 1476 | 1477 | ``` 1478 | ## Find all locations within a given polygon 1479 | Location [id=16922, name=Beyond the wall, location=[64.265473, -21.094093]] 1480 | ``` 1481 | -------------------------------------------------------------------------------- /demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.arangodb 6 | spring-data-demo 7 | 1.0.0 8 | jar 9 | 10 | spring-data-demo 11 | https://github.com/arangodb/spring-data-demo 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 3.4.0 18 | 19 | 20 | 21 | UTF-8 22 | 17 23 | 24 | 25 | 26 | 27 | central 28 | Maven Central 29 | https://repo1.maven.org/maven2 30 | 31 | false 32 | 33 | 34 | 35 | arangodb-snapshots 36 | https://oss.sonatype.org/content/groups/staging 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter 47 | 48 | 49 | com.arangodb 50 | arangodb-spring-boot-starter 51 | 3.4-0 52 | 53 | 54 | org.assertj 55 | assertj-core 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-enforcer-plugin 68 | 69 | 70 | enforce 71 | 72 | enforce 73 | 74 | 75 | 76 | 77 | 78 | 3.6 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo; 22 | 23 | import com.arangodb.spring.demo.runner.AQLRunner; 24 | import com.arangodb.spring.demo.runner.ByExampleRunner; 25 | import com.arangodb.spring.demo.runner.CrudRunner; 26 | import com.arangodb.spring.demo.runner.DerivedQueryRunner; 27 | import com.arangodb.spring.demo.runner.GeospatialRunner; 28 | import com.arangodb.spring.demo.runner.RelationsRunner; 29 | import org.springframework.boot.SpringApplication; 30 | import org.springframework.boot.autoconfigure.SpringBootApplication; 31 | 32 | /** 33 | * @author Mark Vollmary 34 | */ 35 | @SpringBootApplication 36 | public class DemoApplication { 37 | public static void main(final String... args) { 38 | final Class[] runner = new Class[]{ 39 | CrudRunner.class, 40 | ByExampleRunner.class, 41 | DerivedQueryRunner.class, 42 | RelationsRunner.class, 43 | AQLRunner.class, 44 | GeospatialRunner.class 45 | }; 46 | System.exit(SpringApplication.exit(SpringApplication.run(runner, args))); 47 | } 48 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/entity/Character.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.entity; 22 | 23 | import com.arangodb.springframework.annotation.ArangoId; 24 | import com.arangodb.springframework.annotation.Document; 25 | import com.arangodb.springframework.annotation.PersistentIndex; 26 | import com.arangodb.springframework.annotation.Relations; 27 | import org.springframework.data.annotation.Id; 28 | 29 | import java.util.Collection; 30 | import java.util.Objects; 31 | 32 | /** 33 | * @author Mark Vollmary 34 | */ 35 | @Document("characters") 36 | @PersistentIndex(fields = {"surname"}) 37 | public class Character { 38 | 39 | @Id // db document field: _key 40 | private String id; 41 | 42 | @ArangoId // db document field: _id 43 | private String arangoId; 44 | private String name; 45 | private String surname; 46 | private boolean alive; 47 | private Integer age; 48 | @Relations(edges = ChildOf.class, lazy = true) 49 | private Collection children; 50 | 51 | public Character() { 52 | super(); 53 | } 54 | 55 | public Character(final String name, final String surname, final boolean alive) { 56 | super(); 57 | this.name = name; 58 | this.surname = surname; 59 | this.alive = alive; 60 | } 61 | 62 | public Character(final String name, final String surname, final boolean alive, final Integer age) { 63 | super(); 64 | this.name = name; 65 | this.surname = surname; 66 | this.alive = alive; 67 | this.age = age; 68 | } 69 | 70 | public String getId() { 71 | return id; 72 | } 73 | 74 | public void setId(final String id) { 75 | this.id = id; 76 | } 77 | 78 | public String getArangoId() { 79 | return arangoId; 80 | } 81 | 82 | public void setArangoId(final String arangoId) { 83 | this.arangoId = arangoId; 84 | } 85 | 86 | public String getName() { 87 | return name; 88 | } 89 | 90 | public void setName(final String name) { 91 | this.name = name; 92 | } 93 | 94 | public String getSurname() { 95 | return surname; 96 | } 97 | 98 | public void setSurname(final String surname) { 99 | this.surname = surname; 100 | } 101 | 102 | public boolean isAlive() { 103 | return alive; 104 | } 105 | 106 | public void setAlive(final boolean alive) { 107 | this.alive = alive; 108 | } 109 | 110 | public Integer getAge() { 111 | return age; 112 | } 113 | 114 | public void setAge(final Integer age) { 115 | this.age = age; 116 | } 117 | 118 | public Collection getChildren() { 119 | return children; 120 | } 121 | 122 | public void setChildren(final Collection children) { 123 | this.children = children; 124 | } 125 | 126 | @Override 127 | public String toString() { 128 | return "Character [id=" + id + ", name=" + name + ", surname=" + surname + ", alive=" + alive + ", age=" + age + "]"; 129 | } 130 | 131 | @Override 132 | public boolean equals(Object o) { 133 | if (this == o) return true; 134 | if (o == null || getClass() != o.getClass()) return false; 135 | Character character = (Character) o; 136 | return alive == character.alive && Objects.equals(id, character.id) && Objects.equals(arangoId, character.arangoId) && Objects.equals(name, character.name) && Objects.equals(surname, character.surname) && Objects.equals(age, character.age) && Objects.equals(children, character.children); 137 | } 138 | 139 | @Override 140 | public int hashCode() { 141 | return Objects.hash(id, arangoId, name, surname, alive, age, children); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/entity/ChildOf.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.entity; 22 | 23 | import com.arangodb.springframework.annotation.Edge; 24 | import com.arangodb.springframework.annotation.From; 25 | import com.arangodb.springframework.annotation.To; 26 | import org.springframework.data.annotation.Id; 27 | 28 | import java.util.Objects; 29 | 30 | /** 31 | * @author Mark Vollmary 32 | */ 33 | @Edge 34 | public class ChildOf { 35 | 36 | @Id 37 | private String id; 38 | 39 | @From 40 | private Character child; 41 | 42 | @To 43 | private Character parent; 44 | 45 | public ChildOf(final Character child, final Character parent) { 46 | super(); 47 | this.child = child; 48 | this.parent = parent; 49 | } 50 | 51 | public String getId() { 52 | return id; 53 | } 54 | 55 | public void setId(final String id) { 56 | this.id = id; 57 | } 58 | 59 | public Character getChild() { 60 | return child; 61 | } 62 | 63 | public void setChild(final Character child) { 64 | this.child = child; 65 | } 66 | 67 | public Character getParent() { 68 | return parent; 69 | } 70 | 71 | public void setParent(final Character parent) { 72 | this.parent = parent; 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "ChildOf [id=" + id + ", child=" + child + ", parent=" + parent + "]"; 78 | } 79 | 80 | @Override 81 | public boolean equals(Object o) { 82 | if (this == o) return true; 83 | if (o == null || getClass() != o.getClass()) return false; 84 | ChildOf childOf = (ChildOf) o; 85 | return Objects.equals(id, childOf.id) && Objects.equals(child, childOf.child) && Objects.equals(parent, childOf.parent); 86 | } 87 | 88 | @Override 89 | public int hashCode() { 90 | return Objects.hash(id, child, parent); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/entity/Location.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.entity; 22 | 23 | import com.arangodb.springframework.annotation.Document; 24 | import com.arangodb.springframework.annotation.GeoIndexed; 25 | import org.springframework.data.annotation.Id; 26 | import org.springframework.data.geo.Point; 27 | 28 | import java.util.Objects; 29 | 30 | /** 31 | * @author Mark Vollmary 32 | */ 33 | @Document("locations") 34 | public class Location { 35 | 36 | @Id 37 | private String id; 38 | 39 | private final String name; 40 | 41 | @GeoIndexed(geoJson = true) 42 | private final Point location; 43 | 44 | public Location(final String name, final Point location) { 45 | super(); 46 | this.name = name; 47 | this.location = location; 48 | } 49 | 50 | public String getId() { 51 | return id; 52 | } 53 | 54 | public void setId(final String id) { 55 | this.id = id; 56 | } 57 | 58 | public String getName() { 59 | return name; 60 | } 61 | 62 | public Point getLocation() { 63 | return location; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "Location{" + 69 | "id='" + id + '\'' + 70 | ", name='" + name + '\'' + 71 | ", location=" + location + 72 | '}'; 73 | } 74 | 75 | @Override 76 | public boolean equals(Object o) { 77 | if (this == o) return true; 78 | if (o == null || getClass() != o.getClass()) return false; 79 | Location location1 = (Location) o; 80 | return Objects.equals(id, location1.id) && Objects.equals(name, location1.name) && Objects.equals(location, location1.location); 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | return Objects.hash(id, name, location); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/repository/CharacterRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.repository; 22 | 23 | import com.arangodb.ArangoCursor; 24 | import com.arangodb.spring.demo.entity.Character; 25 | import com.arangodb.springframework.annotation.BindVars; 26 | import com.arangodb.springframework.annotation.Query; 27 | import com.arangodb.springframework.annotation.QueryOptions; 28 | import com.arangodb.springframework.repository.ArangoRepository; 29 | import org.springframework.data.domain.Example; 30 | import org.springframework.data.repository.query.Param; 31 | 32 | import java.util.*; 33 | 34 | /** 35 | * @author Mark Vollmary 36 | */ 37 | public interface CharacterRepository extends ArangoRepository { 38 | 39 | @Override 40 | List findAll(Example example); 41 | 42 | Collection findBySurname(String surname); 43 | 44 | List findTop2DistinctBySurnameIgnoreCaseOrderByAgeDesc(String surname); 45 | 46 | Collection findBySurnameEndsWithAndAgeBetweenAndNameInAllIgnoreCase( 47 | String suffix, 48 | int lowerBound, 49 | int upperBound, 50 | String[] nameList); 51 | 52 | Optional findByNameAndSurname(String name, String surname); 53 | 54 | Integer countByAliveTrue(); 55 | 56 | void removeBySurnameNotLikeOrAliveFalse(String surname); 57 | 58 | Collection findByChildrenName(String name); 59 | 60 | Collection findByChildrenAgeBetween(int lowerBound, int upperBound); 61 | 62 | @Query("FOR c IN characters FILTER c.surname == @surname SORT c.age ASC RETURN c") 63 | List getWithSurname(@Param("surname") String value); 64 | 65 | @Query("FOR c IN @@col FILTER c.surname == @surname AND c.age > @age RETURN c") 66 | @QueryOptions(count = true) 67 | ArangoCursor getWithSurnameOlderThan(@Param("age") int value, @BindVars Map bindvars); 68 | 69 | @Query("FOR v IN 1..2 INBOUND @arangoId @@edgeCol SORT v.age DESC RETURN DISTINCT v") 70 | List getAllChildrenAndGrandchildren(@Param("arangoId") String arangoId, @Param("@edgeCol") Class edgeCollection); 71 | 72 | } 73 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/repository/ChildOfRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.repository; 22 | 23 | import com.arangodb.spring.demo.entity.ChildOf; 24 | import com.arangodb.springframework.repository.ArangoRepository; 25 | 26 | /** 27 | * @author Mark Vollmary 28 | */ 29 | public interface ChildOfRepository extends ArangoRepository { 30 | 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/repository/LocationRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.repository; 22 | 23 | import com.arangodb.spring.demo.entity.Location; 24 | import com.arangodb.springframework.repository.ArangoRepository; 25 | import org.springframework.data.domain.Pageable; 26 | import org.springframework.data.domain.Range; 27 | import org.springframework.data.geo.Distance; 28 | import org.springframework.data.geo.GeoPage; 29 | import org.springframework.data.geo.GeoResults; 30 | import org.springframework.data.geo.Point; 31 | import org.springframework.data.geo.Polygon; 32 | 33 | import java.util.Collection; 34 | 35 | /** 36 | * @author Mark Vollmary 37 | */ 38 | public interface LocationRepository extends ArangoRepository { 39 | 40 | GeoPage findByLocationNear(Point location, Pageable pageable); 41 | 42 | GeoResults findByLocationWithin(Point location, Distance distance); 43 | 44 | Collection findByLocationWithin(Point location, Range distanceRange); 45 | 46 | Collection findByLocationWithin(Polygon polygon); 47 | } 48 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/runner/AQLRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.runner; 22 | 23 | import com.arangodb.ArangoCursor; 24 | import com.arangodb.spring.demo.entity.Character; 25 | import com.arangodb.spring.demo.entity.ChildOf; 26 | import com.arangodb.spring.demo.repository.CharacterRepository; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.CommandLineRunner; 29 | import org.springframework.core.annotation.Order; 30 | 31 | import java.util.Comparator; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | import static org.assertj.core.api.Assertions.assertThat; 37 | 38 | /** 39 | * @author Mark Vollmary 40 | */ 41 | @Order(5) 42 | public class AQLRunner implements CommandLineRunner { 43 | 44 | @Autowired 45 | private CharacterRepository repository; 46 | 47 | @Override 48 | public void run(final String... args) { 49 | System.out.println("# AQL queries"); 50 | 51 | System.out.println("## Find all characters with surname 'Lannister' (sort by age ascending)"); 52 | List lannisters = repository.getWithSurname("Lannister"); 53 | lannisters.forEach(System.out::println); 54 | assertThat(lannisters) 55 | .isNotEmpty() 56 | .allMatch(it -> it.getSurname().equals("Lannister")) 57 | .isSortedAccordingTo(Comparator.comparing(Character::getAge, Comparator.nullsFirst(Comparator.naturalOrder()))); 58 | 59 | System.out.println("## Find all characters with surname 'Lannister' which are older than 35"); 60 | Map bindvars = new HashMap<>(); 61 | bindvars.put("surname", "Lannister"); 62 | bindvars.put("@col", Character.class); 63 | ArangoCursor oldLannisters = repository.getWithSurnameOlderThan(35, bindvars); 64 | System.out.println(String.format("Found %s documents", oldLannisters.getCount())); 65 | oldLannisters.forEach(System.out::println); 66 | 67 | assertThat(oldLannisters.getCount()).isEqualTo(2); 68 | assertThat(repository.getWithSurnameOlderThan(35, bindvars).asListRemaining()) 69 | .isNotEmpty() 70 | .allMatch(it -> it.getSurname().equals("Lannister")) 71 | .allMatch(it -> it.getAge() > 35); 72 | 73 | System.out.println("## Find all children and grantchildren of 'Tywin Lannister' (sort by age descending)"); 74 | List children = repository.findByNameAndSurname("Tywin", "Lannister").map(tywin -> 75 | repository.getAllChildrenAndGrandchildren(tywin.getArangoId(), ChildOf.class)).get(); 76 | children.forEach(System.out::println); 77 | assertThat(children) 78 | .isNotEmpty() 79 | .isSortedAccordingTo(Comparator.comparing(Character::getAge).reversed()); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/runner/ByExampleRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.runner; 22 | 23 | import com.arangodb.spring.demo.entity.Character; 24 | import com.arangodb.spring.demo.repository.CharacterRepository; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.boot.CommandLineRunner; 27 | import org.springframework.context.annotation.ComponentScan; 28 | import org.springframework.core.annotation.Order; 29 | import org.springframework.data.domain.Example; 30 | import org.springframework.data.domain.ExampleMatcher; 31 | import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; 32 | 33 | import java.util.Optional; 34 | 35 | import static org.assertj.core.api.Assertions.assertThat; 36 | 37 | /** 38 | * @author Mark Vollmary 39 | */ 40 | @Order(2) 41 | @ComponentScan("com.arangodb.spring.demo") 42 | public class ByExampleRunner implements CommandLineRunner { 43 | 44 | @Autowired 45 | private CharacterRepository repository; 46 | 47 | @Override 48 | public void run(final String... args) { 49 | System.out.println("# Query by example"); 50 | 51 | final Character nedStark = new Character("Ned", "Stark", false, 41); 52 | 53 | System.out.println(String.format("## Find character which exactly match %s", nedStark)); 54 | Optional foundNedStark = repository.findOne(Example.of(nedStark)); 55 | System.out.println(String.format("Found %s", foundNedStark.get())); 56 | assertThat(foundNedStark).isPresent(); 57 | 58 | System.out.println("## Find all dead Starks"); 59 | Iterable allDeadStarks = repository 60 | .findAll(Example.of(new Character(null, "Stark", false))); 61 | allDeadStarks.forEach(System.out::println); 62 | assertThat(allDeadStarks) 63 | .hasSize(3) 64 | .noneMatch(Character::isAlive) 65 | .allSatisfy(it -> assertThat(it.getSurname()).isEqualTo("Stark")); 66 | 67 | System.out.println("## Find all Starks which are 30 years younger than Ned Stark"); 68 | Iterable allYoungerStarks = repository.findAll( 69 | Example.of(foundNedStark.get(), ExampleMatcher.matchingAll() 70 | .withMatcher("surname", GenericPropertyMatcher::exact) 71 | .withIgnorePaths("id", "arangoId", "name", "alive") 72 | .withTransformer("age", age -> age.map(it -> (int) it - 30)))); 73 | allYoungerStarks.forEach(System.out::println); 74 | assertThat(allYoungerStarks) 75 | .isNotEmpty() 76 | .allSatisfy(it -> { 77 | assertThat(it.getSurname()).isEqualTo("Stark"); 78 | assertThat(it.getAge()).isEqualTo(foundNedStark.get().getAge() - 30); 79 | }); 80 | 81 | System.out.println("## Find all character which surname ends with 'ark' (ignore case)"); 82 | Iterable ark = repository.findAll(Example.of(new Character(null, "ark", false), 83 | ExampleMatcher.matchingAll().withMatcher("surname", GenericPropertyMatcher::endsWith).withIgnoreCase() 84 | .withIgnorePaths("name", "alive", "age"))); 85 | ark.forEach(System.out::println); 86 | assertThat(ark) 87 | .isNotEmpty() 88 | .allMatch(it -> it.getSurname().endsWith("ark")); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/runner/CrudRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.runner; 22 | 23 | import com.arangodb.spring.demo.entity.Character; 24 | import com.arangodb.spring.demo.repository.CharacterRepository; 25 | import com.arangodb.springframework.core.ArangoOperations; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.CommandLineRunner; 28 | import org.springframework.context.annotation.ComponentScan; 29 | import org.springframework.core.annotation.Order; 30 | import org.springframework.data.domain.Page; 31 | import org.springframework.data.domain.PageRequest; 32 | import org.springframework.data.domain.Sort; 33 | 34 | import java.util.*; 35 | import java.util.stream.StreamSupport; 36 | 37 | import static org.assertj.core.api.Assertions.assertThat; 38 | 39 | /** 40 | * @author Mark Vollmary 41 | */ 42 | @Order(1) 43 | @ComponentScan("com.arangodb.spring.demo") 44 | public class CrudRunner implements CommandLineRunner { 45 | 46 | @Autowired 47 | private ArangoOperations operations; 48 | @Autowired 49 | private CharacterRepository repository; 50 | 51 | public static Collection createCharacters() { 52 | return Arrays.asList(new Character("Robert", "Baratheon", false), 53 | new Character("Jaime", "Lannister", true, 36), new Character("Catelyn", "Stark", false, 40), 54 | new Character("Cersei", "Lannister", true, 36), new Character("Daenerys", "Targaryen", true, 16), 55 | new Character("Jorah", "Mormont", false), new Character("Petyr", "Baelish", false), 56 | new Character("Viserys", "Targaryen", false), new Character("Jon", "Snow", true, 16), 57 | new Character("Sansa", "Stark", true, 13), new Character("Arya", "Stark", true, 11), 58 | new Character("Robb", "Stark", false), new Character("Theon", "Greyjoy", true, 16), 59 | new Character("Bran", "Stark", true, 10), new Character("Joffrey", "Baratheon", false, 19), 60 | new Character("Sandor", "Clegane", true), new Character("Tyrion", "Lannister", true, 32), 61 | new Character("Khal", "Drogo", false), new Character("Tywin", "Lannister", false), 62 | new Character("Davos", "Seaworth", true, 49), new Character("Samwell", "Tarly", true, 17), 63 | new Character("Stannis", "Baratheon", false), new Character("Melisandre", null, true), 64 | new Character("Margaery", "Tyrell", false), new Character("Jeor", "Mormont", false), 65 | new Character("Bronn", null, true), new Character("Varys", null, true), new Character("Shae", null, false), 66 | new Character("Talisa", "Maegyr", false), new Character("Gendry", null, false), 67 | new Character("Ygritte", null, false), new Character("Tormund", "Giantsbane", true), 68 | new Character("Gilly", null, true), new Character("Brienne", "Tarth", true, 32), 69 | new Character("Ramsay", "Bolton", true), new Character("Ellaria", "Sand", true), 70 | new Character("Daario", "Naharis", true), new Character("Missandei", null, true), 71 | new Character("Tommen", "Baratheon", true), new Character("Jaqen", "H'ghar", true), 72 | new Character("Roose", "Bolton", true), new Character("The High Sparrow", null, true)); 73 | } 74 | 75 | public static Character createNedStark() { 76 | return new Character("Ned", "Stark", true, 41); 77 | } 78 | 79 | @Override 80 | public void run(String... args) { 81 | // first drop the database so that we can run this multiple times with the same dataset 82 | operations.dropDatabase(); 83 | 84 | System.out.println("# CRUD operations"); 85 | 86 | // save a single entity in the database 87 | // there is no need of creating the collection first. This happen automatically 88 | final Character nedStark = new Character("Ned", "Stark", true, 41); 89 | repository.save(nedStark); 90 | 91 | // the generated id from the database is set in the original entity 92 | System.out.println(String.format("Ned Stark saved in the database with id: '%s'", nedStark.getId())); 93 | assertThat(nedStark.getId()).isNotNull(); 94 | 95 | // lets take a look whether we can find Ned Stark in the database 96 | final Optional foundNed = repository.findById(nedStark.getId()); 97 | System.out.println(String.format("Found %s", foundNed.get())); 98 | assertThat(foundNed).isPresent(); 99 | 100 | nedStark.setAlive(false); 101 | repository.save(nedStark); 102 | final Optional deadNed = repository.findById(nedStark.getId()); 103 | System.out.println(String.format("The 'alive' flag of the persisted Ned Stark is now '%s'", deadNed.get().isAlive())); 104 | assertThat(deadNed.get().isAlive()).isFalse(); 105 | 106 | Collection createCharacters = createCharacters(); 107 | System.out.println(String.format("Save %s additional characters", createCharacters.size())); 108 | Iterable savedCharacters = repository.saveAll(createCharacters); 109 | savedCharacters.forEach(System.out::println); 110 | assertThat(savedCharacters).hasSize(createCharacters.size()); 111 | 112 | long count = repository.count(); 113 | System.out.println(String.format("A total of %s characters are persisted in the database", count)); 114 | assertThat(count).isGreaterThan(createCharacters.size()); 115 | 116 | System.out.println("## Return all characters sorted by name"); 117 | Iterable allSorted = repository.findAll(Sort.by(Sort.Direction.ASC, "name")); 118 | allSorted.forEach(System.out::println); 119 | List allSortedList = StreamSupport.stream(allSorted.spliterator(), false).toList(); 120 | assertThat(allSortedList) 121 | .hasSize((int) count) 122 | .isSortedAccordingTo(Comparator.comparing(Character::getName)); 123 | 124 | System.out.println("## Return the first 5 characters sorted by name"); 125 | Page first5Sorted = repository.findAll(PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "name"))); 126 | first5Sorted.forEach(System.out::println); 127 | assertThat(first5Sorted.get()) 128 | .hasSize(5) 129 | .containsExactlyElementsOf(allSortedList.subList(0, 5)); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/runner/DerivedQueryRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.runner; 22 | 23 | import com.arangodb.spring.demo.entity.Character; 24 | import com.arangodb.spring.demo.repository.CharacterRepository; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.boot.CommandLineRunner; 27 | import org.springframework.context.annotation.ComponentScan; 28 | import org.springframework.core.annotation.Order; 29 | 30 | import java.util.Collection; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | import java.util.Optional; 34 | 35 | import static org.assertj.core.api.Assertions.assertThat; 36 | 37 | /** 38 | * @author Mark Vollmary 39 | */ 40 | @Order(3) 41 | @ComponentScan("com.arangodb.spring.demo") 42 | public class DerivedQueryRunner implements CommandLineRunner { 43 | 44 | @Autowired 45 | private CharacterRepository repository; 46 | 47 | @Override 48 | public void run(final String... args) throws Exception { 49 | System.out.println("# Derived queries"); 50 | 51 | System.out.println("## Find all characters with surname 'Lannister'"); 52 | Iterable lannisters = repository.findBySurname("Lannister"); 53 | lannisters.forEach(System.out::println); 54 | assertThat(lannisters).isNotEmpty(); 55 | 56 | System.out.println("## Find top 2 Lannnisters ordered by age"); 57 | List top2 = repository.findTop2DistinctBySurnameIgnoreCaseOrderByAgeDesc("lannister"); 58 | top2.forEach(System.out::println); 59 | assertThat(top2) 60 | .hasSize(2) 61 | .isSortedAccordingTo(Comparator.comparingInt(Character::getAge).reversed()); 62 | 63 | System.out.println( 64 | "## Find all characters which name is 'Bran' or 'Sansa' and it's surname ends with 'ark' and are between 10 and 16 years old"); 65 | Collection youngStarks = repository.findBySurnameEndsWithAndAgeBetweenAndNameInAllIgnoreCase("ark", 66 | 10, 16, new String[]{"Bran", "Sansa"}); 67 | youngStarks.forEach(System.out::println); 68 | assertThat(youngStarks) 69 | .anyMatch(it -> it.getName().equals("Bran")) 70 | .anyMatch(it -> it.getName().equals("Sansa")) 71 | .allMatch(it -> it.getSurname().endsWith("ark")) 72 | .allSatisfy(it -> assertThat(it.getAge()).isBetween(10, 16)); 73 | 74 | System.out.println("## Find a single character by name & surname"); 75 | Optional tyrion = repository.findByNameAndSurname("Tyrion", "Lannister"); 76 | tyrion.ifPresent(c -> System.out.println(String.format("Found %s", c))); 77 | assertThat(tyrion).isPresent(); 78 | assertThat(tyrion.get().getName()).isEqualTo("Tyrion"); 79 | assertThat(tyrion.get().getSurname()).isEqualTo("Lannister"); 80 | 81 | System.out.println("## Count how many characters are still alive"); 82 | Integer alive = repository.countByAliveTrue(); 83 | System.out.println(String.format("There are %s characters still alive", alive)); 84 | assertThat(alive).isPositive(); 85 | 86 | System.out.println("## Remove all characters except of which surname is 'Stark' and which are still alive"); 87 | repository.removeBySurnameNotLikeOrAliveFalse("Stark"); 88 | Iterable all = repository.findAll(); 89 | all.forEach(System.out::println); 90 | assertThat(all) 91 | .isNotEmpty() 92 | .allSatisfy(it -> { 93 | assertThat(it.getSurname()).isEqualTo("Stark"); 94 | assertThat(it.isAlive()).isTrue(); 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/runner/GeospatialRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.runner; 22 | 23 | import com.arangodb.spring.demo.entity.Location; 24 | import com.arangodb.spring.demo.repository.LocationRepository; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.boot.CommandLineRunner; 27 | import org.springframework.core.annotation.Order; 28 | import org.springframework.data.domain.PageRequest; 29 | import org.springframework.data.domain.Range; 30 | import org.springframework.data.geo.*; 31 | 32 | import java.util.Arrays; 33 | import java.util.Comparator; 34 | 35 | import static org.assertj.core.api.Assertions.assertThat; 36 | 37 | /** 38 | * @author Mark Vollmary 39 | */ 40 | @Order(6) 41 | public class GeospatialRunner implements CommandLineRunner { 42 | 43 | @Autowired 44 | private LocationRepository repository; 45 | 46 | @Override 47 | public void run(final String... args) throws Exception { 48 | System.out.println("# Geospatial"); 49 | 50 | repository.saveAll(Arrays.asList( 51 | new Location("Dragonstone", new Point(-6.815096, 55.167801)), 52 | new Location("King's Landing", new Point(18.110189, 42.639752)), 53 | new Location("The Red Keep", new Point(14.446442, 35.896447)), 54 | new Location("Yunkai", new Point(-7.129532, 31.046642)), 55 | new Location("Astapor", new Point(-9.774249, 31.50974)), 56 | new Location("Winterfell", new Point(-5.581312, 54.368321)), 57 | new Location("Vaes Dothrak", new Point(-6.096125, 54.16776)), 58 | new Location("Beyond the wall", new Point(-21.094093, 64.265473)) 59 | )); 60 | 61 | System.out.println("## Find the first 5 locations near 'Winterfell'"); 62 | GeoPage first5 = repository.findByLocationNear(new Point(-5.581312, 54.368321), 63 | PageRequest.of(0, 5)); 64 | first5.forEach(System.out::println); 65 | assertThat(first5).hasSize(5); 66 | 67 | System.out.println("## Find the next 5 locations near 'Winterfell' (only 3 locations left)"); 68 | GeoPage next5 = repository.findByLocationNear(new Point(-5.581312, 54.368321), 69 | PageRequest.of(1, 5)); 70 | next5.forEach(System.out::println); 71 | assertThat(next5).hasSize(3); 72 | 73 | assertThat(first5.and(next5).get().toList()) 74 | .isSortedAccordingTo(Comparator.comparing(GeoResult::getDistance)); 75 | 76 | System.out.println("## Find all locations within 50 kilometers of 'Winterfell'"); 77 | GeoResults findWithing50kilometers = repository 78 | .findByLocationWithin(new Point(-5.581312, 54.368321), new Distance(50, Metrics.KILOMETERS)); 79 | findWithing50kilometers.forEach(System.out::println); 80 | assertThat(findWithing50kilometers.getContent()) 81 | .isNotEmpty() 82 | .isSortedAccordingTo(Comparator.comparing(GeoResult::getDistance)); 83 | 84 | 85 | System.out.println("## Find all locations which are 40 to 50 kilometers away from 'Winterfell'"); 86 | Iterable findByLocationWithin = repository.findByLocationWithin(new Point(-5.581312, 54.368321), 87 | Range.of(Range.Bound.inclusive(40000.), Range.Bound.exclusive(50000.))); 88 | findByLocationWithin.forEach(System.out::println); 89 | assertThat(findByLocationWithin).isNotEmpty(); 90 | 91 | System.out.println("## Find all locations within a given polygon"); 92 | Iterable withinPolygon = repository.findByLocationWithin( 93 | new Polygon(Arrays.asList(new Point(-25, 40), new Point(-25, 70), new Point(25, 70), new Point(-25, 40)))); 94 | withinPolygon.forEach(System.out::println); 95 | assertThat(withinPolygon).isNotEmpty(); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /demo/src/main/java/com/arangodb/spring/demo/runner/RelationsRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2017 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.spring.demo.runner; 22 | 23 | import com.arangodb.spring.demo.entity.Character; 24 | import com.arangodb.spring.demo.entity.ChildOf; 25 | import com.arangodb.spring.demo.repository.CharacterRepository; 26 | import com.arangodb.spring.demo.repository.ChildOfRepository; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.CommandLineRunner; 29 | import org.springframework.context.annotation.ComponentScan; 30 | import org.springframework.core.annotation.Order; 31 | 32 | import java.util.Arrays; 33 | 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | 36 | /** 37 | * @author Mark Vollmary 38 | */ 39 | @Order(4) 40 | @ComponentScan("com.arangodb.spring.demo") 41 | public class RelationsRunner implements CommandLineRunner { 42 | 43 | @Autowired 44 | private CharacterRepository characterRepo; 45 | @Autowired 46 | private ChildOfRepository childOfRepo; 47 | 48 | @Override 49 | public void run(final String... args) throws Exception { 50 | System.out.println("# Relations"); 51 | characterRepo.saveAll(CrudRunner.createCharacters()); 52 | characterRepo.save(CrudRunner.createNedStark()); 53 | 54 | // first create some relations for the Starks and Lannisters 55 | Character ned = characterRepo.findByNameAndSurname("Ned", "Stark").get(); 56 | Character catelyn = characterRepo.findByNameAndSurname("Catelyn", "Stark").get(); 57 | Character robb = characterRepo.findByNameAndSurname("Robb", "Stark").get(); 58 | childOfRepo.saveAll(Arrays.asList(new ChildOf(robb, ned), new ChildOf(robb, catelyn))); 59 | Character sansa = characterRepo.findByNameAndSurname("Sansa", "Stark").get(); 60 | childOfRepo.saveAll(Arrays.asList(new ChildOf(sansa, ned), new ChildOf(sansa, catelyn))); 61 | Character arya = characterRepo.findByNameAndSurname("Arya", "Stark").get(); 62 | childOfRepo.saveAll(Arrays.asList(new ChildOf(arya, ned), new ChildOf(arya, catelyn))); 63 | Character bran = characterRepo.findByNameAndSurname("Bran", "Stark").get(); 64 | childOfRepo.saveAll(Arrays.asList(new ChildOf(bran, ned), new ChildOf(bran, catelyn))); 65 | Character jon = characterRepo.findByNameAndSurname("Jon", "Snow").get(); 66 | childOfRepo.save(new ChildOf(jon, ned)); 67 | 68 | Character tywin = characterRepo.findByNameAndSurname("Tywin", "Lannister").get(); 69 | Character jaime = characterRepo.findByNameAndSurname("Jaime", "Lannister").get(); 70 | childOfRepo.save(new ChildOf(jaime, tywin)); 71 | Character cersei = characterRepo.findByNameAndSurname("Cersei", "Lannister").get(); 72 | childOfRepo.save(new ChildOf(cersei, tywin)); 73 | Character joffrey = characterRepo.findByNameAndSurname("Joffrey", "Baratheon").get(); 74 | childOfRepo.saveAll(Arrays.asList(new ChildOf(joffrey, jaime), new ChildOf(joffrey, cersei))); 75 | Character tyrion = characterRepo.findByNameAndSurname("Tyrion", "Lannister").get(); 76 | childOfRepo.save(new ChildOf(tyrion, tywin)); 77 | 78 | Character nedStark = characterRepo.findByNameAndSurname("Ned", "Stark").get(); 79 | System.out.println(String.format("## These are the children of %s:", nedStark)); 80 | nedStark.getChildren().forEach(System.out::println); 81 | assertThat(nedStark.getChildren()).isNotEmpty(); 82 | 83 | System.out.println("## These are the parents of 'Sansa'"); 84 | Iterable parentsOfSansa = characterRepo.findByChildrenName("Sansa"); 85 | parentsOfSansa.forEach(System.out::println); 86 | assertThat(parentsOfSansa).isNotEmpty(); 87 | 88 | System.out.println("## These parents have a child which is between 16 and 20 years old"); 89 | Iterable childrenBetween16a20 = characterRepo.findByChildrenAgeBetween(16, 20); 90 | childrenBetween16a20.forEach(System.out::println); 91 | assertThat(childrenBetween16a20).isNotEmpty(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | arangodb.spring.data.database=spring-demo 2 | arangodb.spring.data.user=root 3 | arangodb.spring.data.password=test 4 | arangodb.spring.data.hosts=172.28.0.1:8529 5 | #logging.level.com.arangodb.internal.net.Communication=DEBUG 6 | -------------------------------------------------------------------------------- /docker/find_active_endpoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | COORDINATORS=("172.17.0.1:8529" "172.17.0.1:8539" "172.17.0.1:8549") 4 | 5 | for a in ${COORDINATORS[*]} ; do 6 | if curl -u root:test --silent --fail "http://$a"; then 7 | echo "$a" 8 | exit 0 9 | fi 10 | done 11 | 12 | echo "Could not find any active endpoint!" 13 | exit 1 14 | -------------------------------------------------------------------------------- /docker/jwtHeader: -------------------------------------------------------------------------------- 1 | Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmFuZ29kYiIsInNlcnZlcl9pZCI6ImZvbyJ9.QmuhPHkmRPJuHGxsEqggHGRyVXikV44tb5YU_yWEvEM 2 | -------------------------------------------------------------------------------- /docker/jwtSecret: -------------------------------------------------------------------------------- 1 | Averysecretword 2 | -------------------------------------------------------------------------------- /docker/server.pem: -------------------------------------------------------------------------------- 1 | Bag Attributes 2 | friendlyName: arangotest 3 | localKeyID: 54 69 6D 65 20 31 36 30 34 32 35 36 36 37 39 38 35 34 4 | Key Attributes: 5 | -----BEGIN PRIVATE KEY----- 6 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1WiDnd4+uCmMG 7 | 539ZNZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMAL 8 | X9euSseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7Nzmvdo 9 | gNXoJQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiR 10 | dJVPwUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf 11 | 2MGO64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzb 12 | f8KnRY4PAgMBAAECggEAKi1d/bdW2TldMpvgiFTm15zLjHCpllbKBWFqRj3T9+X7 13 | Duo6Nh9ehopD0YDDe2DNhYr3DsH4sLjUWVDfDpAhutMsU1wlBzmOuC+EuRv/CeDB 14 | 4DFr+0sgCwlti+YAtwWcR05SF7A0Ai0GYW2lUipbtbFSBSjCfM08BlPDsPCRhdM8 15 | DhBn3S45aP7oC8BdhG/etg+DfXW+/nyNwEcMCYG97bzXNjzYpCQjo/bTHdh2UPYM 16 | 4WEAqFzZ5jir8LVS3v7GqpqPmk6FnHJOJpfpOSZoPqnfpIw7SVlNsXHvDaHGcgYZ 17 | Xec7rLQlBuv4RZU7OlGJpK2Ng5kvS9q3nfqqn7YIMQKBgQDqSsYnE+k6CnrSpa2W 18 | B9W/+PChITgkA46XBUUjAueJ7yVZQQEOzl0VI6RoVBp3t66eO8uM9omO8/ogHXku 19 | Ei9UUIIfH4BsSP7G5A06UC/FgReDxwBfbRuS+lupnmc348vPDkFlJZ4hDgWflNev 20 | 7tpUbljSAqUea1VhdBy146V4qwKBgQDGJ6iL1+A9uUM+1UklOAPpPhTQ8ZQDRCj7 21 | 7IMVcbzWYvCMuVNXzOWuiz+VYr3IGCJZIbxbFDOHxGF4XKJnk0vm1qhQQME0PtAF 22 | i1jIfsxpj8KKJl9Uad+XLQCYRV8mIZlhsd/ErRJuz6FyqevKH3nFIb0ggF3x2d06 23 | odTHuj4ILQKBgCUsI/BDSne4/e+59aaeK52/w33tJVkhb1gqr+N0LIRH+ycEF0Tg 24 | HQijlQwwe9qOvBfC6PK+kuipcP/zbSyQGg5Ij7ycZOXJVxL7T9X2rv2pE7AGvNpn 25 | Fz7klfJ9fWbyr310h4+ivkoETYQaO3ZgcSeAMntvi/8djHhf0cZSDgjtAoGBAKvQ 26 | TUNcHjJGxfjgRLkB1dpSmwgEv7sJSaQOkiZw5TTauwq50nsJzYlHcg1cfYPW8Ulp 27 | iAFNBdVNwNn1MFgwjpqMO4rCawObBxIXnhbSYvmQzjStSvFNj7JsMdzWIcdVUMI1 28 | 0fmdu6LbY3ihvzIVkqcMNwnMZCjFKB6jnXTElu7NAoGAS0gNPD/bfzWAhZBBYp9/ 29 | SLGOvjHKrSVWGwDiqdAGuh6xg+1C3F+XpiITP6d3Wv3PCJ/Gia5isQPSMaXG+xTt 30 | 6huBgFlksHqr0tsQA9dcgGW7BDr5VhRq5/WinaLhGGy1R+i2zbDmQXgHbCO+RH/s 31 | bD9F4LZ3RoXmGHLW0IUggPw= 32 | -----END PRIVATE KEY----- 33 | Bag Attributes 34 | friendlyName: arangotest 35 | localKeyID: 54 69 6D 65 20 31 36 30 34 32 35 36 36 37 39 38 35 34 36 | subject=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost 37 | 38 | issuer=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost 39 | 40 | -----BEGIN CERTIFICATE----- 41 | MIIDezCCAmOgAwIBAgIEeDCzXzANBgkqhkiG9w0BAQsFADBuMRAwDgYDVQQGEwdV 42 | bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD 43 | VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhv 44 | c3QwHhcNMjAxMTAxMTg1MTE5WhcNMzAxMDMwMTg1MTE5WjBuMRAwDgYDVQQGEwdV 45 | bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD 46 | VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhv 47 | c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1WiDnd4+uCmMG539Z 48 | NZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMALX9eu 49 | SseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7NzmvdogNXo 50 | JQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiRdJVP 51 | wUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf2MGO 52 | 64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzbf8Kn 53 | RY4PAgMBAAGjITAfMB0GA1UdDgQWBBTBrv9Awynt3C5IbaCNyOW5v4DNkTANBgkq 54 | hkiG9w0BAQsFAAOCAQEAIm9rPvDkYpmzpSIhR3VXG9Y71gxRDrqkEeLsMoEyqGnw 55 | /zx1bDCNeGg2PncLlW6zTIipEBooixIE9U7KxHgZxBy0Et6EEWvIUmnr6F4F+dbT 56 | D050GHlcZ7eOeqYTPYeQC502G1Fo4tdNi4lDP9L9XZpf7Q1QimRH2qaLS03ZFZa2 57 | tY7ah/RQqZL8Dkxx8/zc25sgTHVpxoK853glBVBs/ENMiyGJWmAXQayewY3EPt/9 58 | wGwV4KmU3dPDleQeXSUGPUISeQxFjy+jCw21pYviWVJTNBA9l5ny3GhEmcnOT/gQ 59 | HCvVRLyGLMbaMZ4JrPwb+aAtBgrgeiK4xeSMMvrbhw== 60 | -----END CERTIFICATE----- 61 | -------------------------------------------------------------------------------- /docker/start_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Configuration environment variables: 4 | # STARTER_MODE: (single|cluster|activefailover), default single 5 | # DOCKER_IMAGE: ArangoDB docker image, default docker.io/arangodb/arangodb:latest 6 | # SSL: (true|false), default false 7 | # EXTENDED_NAMES: (true|false), default false 8 | # ARANGO_LICENSE_KEY: only required for ArangoDB Enterprise 9 | 10 | # EXAMPLE: 11 | # STARTER_MODE=cluster SSL=true ./start_db.sh 12 | 13 | STARTER_MODE=${STARTER_MODE:=single} 14 | DOCKER_IMAGE=${DOCKER_IMAGE:=docker.io/arangodb/arangodb:latest} 15 | SSL=${SSL:=false} 16 | EXTENDED_NAMES=${EXTENDED_NAMES:=false} 17 | 18 | STARTER_DOCKER_IMAGE=docker.io/arangodb/arangodb-starter:latest 19 | GW=172.28.0.1 20 | docker network create arangodb --subnet 172.28.0.0/16 21 | 22 | # exit when any command fails 23 | set -e 24 | 25 | docker pull $STARTER_DOCKER_IMAGE 26 | docker pull $DOCKER_IMAGE 27 | 28 | LOCATION=$(pwd)/$(dirname "$0") 29 | AUTHORIZATION_HEADER=$(cat "$LOCATION"/jwtHeader) 30 | 31 | STARTER_ARGS= 32 | SCHEME=http 33 | ARANGOSH_SCHEME=http+tcp 34 | COORDINATORS=("$GW:8529" "$GW:8539" "$GW:8549") 35 | 36 | if [ "$STARTER_MODE" == "single" ]; then 37 | COORDINATORS=("$GW:8529") 38 | fi 39 | 40 | if [ "$SSL" == "true" ]; then 41 | STARTER_ARGS="$STARTER_ARGS --ssl.keyfile=/data/server.pem" 42 | SCHEME=https 43 | ARANGOSH_SCHEME=http+ssl 44 | fi 45 | 46 | if [ "$EXTENDED_NAMES" == "true" ]; then 47 | STARTER_ARGS="${STARTER_ARGS} --all.database.extended-names=true" 48 | fi 49 | 50 | # data volume 51 | docker create -v /data --name arangodb-data alpine:3 /bin/true 52 | docker cp "$LOCATION"/jwtSecret arangodb-data:/data 53 | docker cp "$LOCATION"/server.pem arangodb-data:/data 54 | 55 | docker run -d \ 56 | --name=adb \ 57 | -p 8528:8528 \ 58 | --volumes-from arangodb-data \ 59 | -v /var/run/docker.sock:/var/run/docker.sock \ 60 | -e ARANGO_LICENSE_KEY="$ARANGO_LICENSE_KEY" \ 61 | $STARTER_DOCKER_IMAGE \ 62 | $STARTER_ARGS \ 63 | --docker.net-mode=default \ 64 | --docker.container=adb \ 65 | --auth.jwt-secret=/data/jwtSecret \ 66 | --starter.address="${GW}" \ 67 | --docker.image="${DOCKER_IMAGE}" \ 68 | --starter.local --starter.mode=${STARTER_MODE} --all.log.level=debug --all.log.output=+ --log.verbose --all.server.descriptors-minimum=1024 69 | 70 | 71 | wait_server() { 72 | # shellcheck disable=SC2091 73 | until $(curl --output /dev/null --insecure --fail --silent --head -i -H "$AUTHORIZATION_HEADER" "$SCHEME://$1/_api/version"); do 74 | printf '.' 75 | sleep 1 76 | done 77 | } 78 | 79 | echo "Waiting..." 80 | 81 | for a in ${COORDINATORS[*]} ; do 82 | wait_server "$a" 83 | done 84 | 85 | set +e 86 | for a in ${COORDINATORS[*]} ; do 87 | echo "" 88 | echo "Setting username and password..." 89 | docker run --rm ${DOCKER_IMAGE} arangosh --server.endpoint="$ARANGOSH_SCHEME://$a" --server.authentication=false --javascript.execute-string='require("org/arangodb/users").update("root", "test")' 90 | done 91 | set -e 92 | 93 | for a in ${COORDINATORS[*]} ; do 94 | echo "" 95 | echo "Requesting endpoint version..." 96 | curl -u root:test --insecure --fail "$SCHEME://$a/_api/version" 97 | done 98 | 99 | echo "" 100 | echo "" 101 | echo "Done, your deployment is reachable at: " 102 | for a in ${COORDINATORS[*]} ; do 103 | echo "$SCHEME://$a" 104 | echo "" 105 | done 106 | 107 | if [ "$STARTER_MODE" == "activefailover" ]; then 108 | LEADER=$("$LOCATION"/find_active_endpoint.sh) 109 | echo "Leader: $SCHEME://$LEADER" 110 | echo "" 111 | fi 112 | -------------------------------------------------------------------------------- /docs/Drivers/SpringBootStarter/GettingStarted/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Starter ArangoDB - Getting Started 2 | 3 | ## Supported versions 4 | 5 | Spring Boot Starter ArangoDB is compatible with all supported stable versions of ArangoDB server, see 6 | [Product Support End-of-life Announcements](https://www.arangodb.com/eol-notice){:target="_blank"}. 7 | 8 | 9 | It is released in the following versions, each one compatible with the corresponding versions of Spring Boot, 10 | Spring Framework, Spring Data ArangoDB, ArangoDB Java Driver: 11 | 12 | | Spring Boot Starter ArangoDB | Spring Boot | Spring Framework | Spring Data ArangoDB | ArangoDB Java Driver | 13 | |------------------------------|-------------|------------------|----------------------|----------------------| 14 | | 3.1-x | 3.1 | 6.0 | 4.0 | 7.1 | 15 | | 3.0-x | 3.0 | 6.0 | 4.0 | 7.1 | 16 | | 2.7-x | 2.7 | 5.3 | 3.10 | 6.25 | 17 | 18 | 19 | Note that the adopted versioning scheme does not honour the semantic versioning rules, indeed minor or patch 20 | releases could introduce new features or breaking changes. Please refer to 21 | [releases](https://github.com/arangodb/spring-boot-starter/releases) for details. 22 | 23 | 24 | ## Maven 25 | 26 | Add `arangodb-spring-boot-starter` to your project to auto configure Spring Data ArangoDB. 27 | 28 | ```xml 29 | 30 | com.arangodb 31 | arangodb-spring-boot-starter 32 | 3.x-y 33 | 34 | ``` 35 | 36 | ## Configuration 37 | 38 | Configure the properties files of your application with the properties of [ArangoProperties](https://github.com/mpv1989/spring-boot-starter/blob/master/src/main/java/com/arangodb/springframework/boot/autoconfigure/ArangoProperties.java). 39 | 40 | ``` 41 | arangodb.spring.data.database=mydb 42 | arangodb.spring.data.user=root 43 | arangodb.spring.data.password=1234 44 | ``` 45 | 46 | ## Monitor 47 | 48 | ArangoDB health monitoring can be applied to your application by adding `spring-boot-starter-actuator` to your project and call HTTP `GET /actuator/health` against your application. 49 | 50 | ```xml 51 | 52 | org.springframework.boot 53 | spring-boot-starter-actuator 54 | 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/Drivers/SpringBootStarter/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Starter ArangoDB 2 | 3 | - [Getting Started](GettingStarted/README.md) 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.arangodb 7 | arangodb-spring-boot-starter 8 | 3.4-0 9 | 2017 10 | jar 11 | 12 | arangodb-spring-boot-starter 13 | ArangoDB Spring Boot starter 14 | http://maven.apache.org 15 | 16 | 17 | 18 | Apache License 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0 20 | repo 21 | 22 | 23 | 24 | 25 | UTF-8 26 | 27 | 28 | 29 | 30 | mpv1989 31 | Mark Vollmary 32 | https://github.com/mpv1989 33 | 34 | 35 | 36 | 37 | 38 | ossrh 39 | https://oss.sonatype.org/content/repositories/snapshots 40 | 41 | 42 | ossrh 43 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 44 | 45 | 46 | 47 | 48 | 49 | arangodb-snapshots 50 | https://oss.sonatype.org/content/groups/staging 51 | 52 | true 53 | 54 | 55 | false 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-enforcer-plugin 65 | 3.4.0 66 | 67 | 68 | enforce 69 | 70 | enforce 71 | 72 | 73 | 74 | 75 | 76 | 77 | 3.6 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.sonatype.plugins 87 | nexus-staging-maven-plugin 88 | 1.6.13 89 | true 90 | 91 | ossrh 92 | https://oss.sonatype.org/ 93 | 84aff6e87e214c 94 | false 95 | 96 | 97 | 98 | 99 | org.apache.maven.plugins 100 | maven-assembly-plugin 101 | 3.6.0 102 | 103 | 104 | assembly 105 | package 106 | 107 | single 108 | 109 | 110 | 111 | 112 | 113 | ${project.artifactId}-${project.version}-standalone 114 | 115 | false 116 | false 117 | 118 | jar-with-dependencies 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-compiler-plugin 126 | 3.11.0 127 | 128 | 17 129 | 17 130 | 131 | 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-resources-plugin 136 | 3.3.1 137 | 138 | UTF-8 139 | 140 | 141 | 142 | 143 | org.apache.maven.plugins 144 | maven-source-plugin 145 | 3.3.0 146 | 147 | 148 | 149 | jar 150 | 151 | 152 | 153 | 154 | 155 | 156 | org.apache.maven.plugins 157 | maven-javadoc-plugin 158 | 3.5.0 159 | 160 | 161 | attach-javadocs 162 | 163 | jar 164 | 165 | 166 | 167 | 168 | 169 | 170 | maven-surefire-plugin 171 | 3.1.2 172 | 173 | 174 | **/*Test.java 175 | **/*Example.java 176 | 177 | 178 | 179 | 180 | 181 | org.apache.maven.plugins 182 | maven-deploy-plugin 183 | 3.1.1 184 | 185 | 10 186 | 187 | 188 | 189 | 190 | org.apache.maven.plugins 191 | maven-gpg-plugin 192 | 3.1.0 193 | 194 | 195 | --pinentry-mode 196 | loopback 197 | 198 | 199 | 200 | 201 | sign-artifacts 202 | verify 203 | 204 | sign 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | org.springframework.boot 217 | spring-boot-starter 218 | compile 219 | 220 | 221 | com.arangodb 222 | arangodb-spring-data 223 | compile 224 | 225 | 226 | org.springframework.boot 227 | spring-boot-autoconfigure 228 | compile 229 | 230 | 231 | org.springframework.boot 232 | spring-boot-configuration-processor 233 | compile 234 | true 235 | 236 | 237 | org.springframework.boot 238 | spring-boot-actuator 239 | compile 240 | true 241 | 242 | 243 | org.springframework.boot 244 | spring-boot-actuator-autoconfigure 245 | compile 246 | true 247 | 248 | 249 | 250 | 251 | org.springframework.boot 252 | spring-boot-starter-test 253 | test 254 | 255 | 256 | org.mockito 257 | mockito-inline 258 | test 259 | 260 | 261 | 262 | 263 | 264 | 265 | org.springframework.boot 266 | spring-boot-dependencies 267 | 3.4.0 268 | pom 269 | import 270 | 271 | 272 | com.arangodb 273 | arangodb-spring-data 274 | 4.5.0 275 | 276 | 277 | org.mockito 278 | mockito-core 279 | 5.2.0 280 | test 281 | 282 | 283 | org.mockito 284 | mockito-junit-jupiter 285 | 5.2.0 286 | test 287 | 288 | 289 | org.mockito 290 | mockito-inline 291 | 5.2.0 292 | test 293 | 294 | 295 | 296 | 297 | 298 | https://github.com/arangodb/spring-data-boot-starter 299 | scm:git:git://github.com/arangodb/spring-data-boot-starter.git 300 | scm:git:git://github.com/arangodb/spring-data-boot-starter.git 301 | 302 | 303 | 304 | ArangoDB GmbH 305 | https://www.arangodb.com 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/springframework/boot/actuate/ArangoHealthIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2018 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | package com.arangodb.springframework.boot.actuate; 21 | 22 | import org.springframework.boot.actuate.health.AbstractHealthIndicator; 23 | import org.springframework.boot.actuate.health.Health; 24 | import org.springframework.boot.actuate.health.HealthIndicator; 25 | import org.springframework.util.Assert; 26 | 27 | import com.arangodb.entity.ArangoDBVersion; 28 | import com.arangodb.springframework.core.ArangoOperations; 29 | 30 | /** 31 | * Simple {@link HealthIndicator} returning status information of ArangoDB. 32 | * 33 | * @author Mark Vollmary 34 | * 35 | */ 36 | public class ArangoHealthIndicator extends AbstractHealthIndicator { 37 | 38 | private final ArangoOperations operations; 39 | 40 | public ArangoHealthIndicator(final ArangoOperations operations) { 41 | super("ArangoDB health check failed"); 42 | Assert.notNull(operations, "ArangoOperations must not be null"); 43 | this.operations = operations; 44 | } 45 | 46 | @Override 47 | protected void doHealthCheck(final Health.Builder builder) { 48 | final ArangoDBVersion version = operations.driver().getVersion(); 49 | builder.up() 50 | .withDetail("server", version.getServer()) 51 | .withDetail("version", version.getVersion()) 52 | .withDetail("license", version.getLicense()); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/springframework/boot/actuate/autoconfigure/ArangoHealthIndicatorAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.springframework.boot.actuate.autoconfigure; 2 | 3 | import com.arangodb.springframework.boot.actuate.ArangoHealthIndicator; 4 | import com.arangodb.springframework.boot.autoconfigure.ArangoAutoConfiguration; 5 | import com.arangodb.springframework.core.ArangoOperations; 6 | import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration; 7 | import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; 8 | import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration; 9 | import org.springframework.boot.actuate.health.HealthContributor; 10 | import org.springframework.boot.actuate.health.HealthIndicator; 11 | import org.springframework.boot.autoconfigure.AutoConfiguration; 12 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 15 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 16 | import org.springframework.context.annotation.Bean; 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * @author Mark Vollmary 22 | * @author Michele Rastelli 23 | */ 24 | @AutoConfiguration 25 | @ConditionalOnClass({ ArangoOperations.class, HealthIndicator.class, HealthContributorAutoConfiguration.class }) 26 | @ConditionalOnBean(ArangoOperations.class) 27 | @ConditionalOnEnabledHealthIndicator("arango") 28 | @AutoConfigureAfter(ArangoAutoConfiguration.class) 29 | public class ArangoHealthIndicatorAutoConfiguration 30 | extends CompositeHealthContributorConfiguration { 31 | 32 | public ArangoHealthIndicatorAutoConfiguration() { 33 | super(ArangoHealthIndicator::new); 34 | } 35 | 36 | @Bean 37 | @ConditionalOnMissingBean(name = "arangoHealthIndicator") 38 | public HealthContributor arangoHealthIndicator(Map operations) { 39 | return createContributor(operations); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/springframework/boot/autoconfigure/ArangoAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2018 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | package com.arangodb.springframework.boot.autoconfigure; 21 | 22 | import com.arangodb.ArangoDB; 23 | import com.arangodb.ContentType; 24 | import com.arangodb.config.HostDescription; 25 | import com.arangodb.internal.serde.ContentTypeFactory; 26 | import com.arangodb.springframework.config.ArangoConfiguration; 27 | import com.arangodb.springframework.core.ArangoOperations; 28 | import org.springframework.boot.autoconfigure.AutoConfiguration; 29 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 30 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 31 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 32 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 33 | import org.springframework.context.annotation.Configuration; 34 | import org.springframework.context.annotation.Import; 35 | 36 | /** 37 | * {@link EnableAutoConfiguration} class for ArangoDB 38 | * 39 | * @author Mark Vollmary 40 | * @author Arne Burmeister 41 | */ 42 | @AutoConfiguration 43 | @ConditionalOnClass(ArangoDB.class) 44 | @ConditionalOnMissingBean(ArangoOperations.class) 45 | @EnableConfigurationProperties(ArangoProperties.class) 46 | @Import({ArangoRepositoriesAutoConfigureRegistrar.class, ArangoAutoConfiguration.ArangoBootConfiguration.class}) 47 | public class ArangoAutoConfiguration { 48 | 49 | @Configuration 50 | static class ArangoBootConfiguration implements ArangoConfiguration { 51 | 52 | private final ArangoProperties properties; 53 | 54 | ArangoBootConfiguration(final ArangoProperties properties) { 55 | super(); 56 | this.properties = properties; 57 | } 58 | 59 | @Override 60 | public ArangoDB.Builder arango() { 61 | final ArangoDB.Builder builder = new ArangoDB.Builder() 62 | .user(properties.getUser()) 63 | .password(properties.getPassword()) 64 | .jwt(properties.getJwt()) 65 | .timeout(properties.getTimeout()) 66 | .useSsl(properties.getUseSsl()) 67 | .maxConnections(properties.getMaxConnections()) 68 | .connectionTtl(properties.getConnectionTtl()) 69 | .acquireHostList(properties.getAcquireHostList()) 70 | .acquireHostListInterval(properties.getAcquireHostListInterval()) 71 | .loadBalancingStrategy(properties.getLoadBalancingStrategy()) 72 | .protocol(properties.getProtocol()); 73 | properties.getHosts().stream().map(HostDescription::parse) 74 | .forEach(host -> builder.host(host.getHost(), host.getPort())); 75 | return builder; 76 | } 77 | 78 | @Override 79 | public ContentType contentType() { 80 | return ContentTypeFactory.of(properties.getProtocol()); 81 | } 82 | 83 | @Override 84 | public String database() { 85 | return properties.getDatabase(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/springframework/boot/autoconfigure/ArangoProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2018 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | package com.arangodb.springframework.boot.autoconfigure; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | 25 | import org.springframework.boot.context.properties.ConfigurationProperties; 26 | 27 | import com.arangodb.Protocol; 28 | import com.arangodb.entity.LoadBalancingStrategy; 29 | import com.arangodb.internal.ArangoDefaults; 30 | 31 | /** 32 | * @author Mark Vollmary 33 | * 34 | */ 35 | @ConfigurationProperties(prefix = "arangodb.spring.data") 36 | public class ArangoProperties { 37 | 38 | /** 39 | * Database name. 40 | */ 41 | private String database = "_system"; 42 | 43 | /** 44 | * Hosts to connect to. Multiple hosts can be added to provide fallbacks in a 45 | * single server with active failover or load balancing in a cluster setup. 46 | */ 47 | private Collection hosts = new ArrayList<>(); 48 | 49 | /** 50 | * Username to use for authentication. 51 | */ 52 | private String user = ArangoDefaults.DEFAULT_USER; 53 | 54 | /** 55 | * Password for the user for authentication. 56 | */ 57 | private String password; 58 | 59 | /** 60 | * JWT for the user authentication. 61 | */ 62 | private String jwt; 63 | 64 | /** 65 | * Connection and request timeout in milliseconds. 66 | */ 67 | private Integer timeout = ArangoDefaults.DEFAULT_TIMEOUT; 68 | 69 | /** 70 | * If set to {@code true} SSL will be used when connecting to an ArangoDB 71 | * server. 72 | */ 73 | private Boolean useSsl = ArangoDefaults.DEFAULT_USE_SSL; 74 | 75 | /** 76 | * Maximum number of connections the built-in connection pool will open per host. 77 | */ 78 | private Integer maxConnections; 79 | 80 | /** 81 | * Maximum time to life of a connection. 82 | */ 83 | private Long connectionTtl; 84 | 85 | /** 86 | * Whether the driver should acquire a list of available coordinators in 87 | * an ArangoDB cluster or a single server with active failover. 88 | */ 89 | private Boolean acquireHostList = ArangoDefaults.DEFAULT_ACQUIRE_HOST_LIST; 90 | 91 | /** 92 | * Interval for acquireHostList. 93 | */ 94 | private Integer acquireHostListInterval = ArangoDefaults.DEFAULT_ACQUIRE_HOST_LIST_INTERVAL; 95 | 96 | /** 97 | * Load balancing strategy to be used in an ArangoDB cluster setup. 98 | */ 99 | private LoadBalancingStrategy loadBalancingStrategy = ArangoDefaults.DEFAULT_LOAD_BALANCING_STRATEGY; 100 | 101 | /** 102 | * Network protocol to be used to connect to ArangoDB. 103 | */ 104 | private Protocol protocol = ArangoDefaults.DEFAULT_PROTOCOL; 105 | 106 | public ArangoProperties() { 107 | super(); 108 | } 109 | 110 | public final String getDatabase() { 111 | return database; 112 | } 113 | 114 | public final void setDatabase(final String database) { 115 | this.database = database; 116 | } 117 | 118 | public final String getUser() { 119 | return user; 120 | } 121 | 122 | public final void setUser(final String user) { 123 | this.user = user; 124 | } 125 | 126 | public final String getPassword() { 127 | return password; 128 | } 129 | 130 | public final void setPassword(final String password) { 131 | this.password = password; 132 | } 133 | 134 | public final String getJwt() { 135 | return jwt; 136 | } 137 | 138 | public final void setJwt(String jwt) { 139 | this.jwt = jwt; 140 | } 141 | 142 | public final Collection getHosts() { 143 | return hosts; 144 | } 145 | 146 | public final void setHosts(final Collection hosts) { 147 | this.hosts = hosts; 148 | } 149 | 150 | public final Integer getTimeout() { 151 | return timeout; 152 | } 153 | 154 | public final void setTimeout(final Integer timeout) { 155 | this.timeout = timeout; 156 | } 157 | 158 | public final Boolean getUseSsl() { 159 | return useSsl; 160 | } 161 | 162 | public final void setUseSsl(final Boolean useSsl) { 163 | this.useSsl = useSsl; 164 | } 165 | 166 | public final Integer getMaxConnections() { 167 | return maxConnections; 168 | } 169 | 170 | public final void setMaxConnections(final Integer maxConnections) { 171 | this.maxConnections = maxConnections; 172 | } 173 | 174 | public final Long getConnectionTtl() { 175 | return connectionTtl; 176 | } 177 | 178 | public final void setConnectionTtl(final Long connectionTtl) { 179 | this.connectionTtl = connectionTtl; 180 | } 181 | 182 | public final Boolean getAcquireHostList() { 183 | return acquireHostList; 184 | } 185 | 186 | public final void setAcquireHostList(final Boolean acquireHostList) { 187 | this.acquireHostList = acquireHostList; 188 | } 189 | 190 | public final Integer getAcquireHostListInterval() { 191 | return acquireHostListInterval; 192 | } 193 | 194 | public final void setAcquireHostListInterval(Integer acquireHostListInterval) { 195 | this.acquireHostListInterval = acquireHostListInterval; 196 | } 197 | 198 | public final LoadBalancingStrategy getLoadBalancingStrategy() { 199 | return loadBalancingStrategy; 200 | } 201 | 202 | public final void setLoadBalancingStrategy(final LoadBalancingStrategy loadBalancingStrategy) { 203 | this.loadBalancingStrategy = loadBalancingStrategy; 204 | } 205 | 206 | public final Protocol getProtocol() { 207 | return protocol; 208 | } 209 | 210 | public final void setProtocol(final Protocol protocol) { 211 | this.protocol = protocol; 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/springframework/boot/autoconfigure/ArangoRepositoriesAutoConfigureRegistrar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2018 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | package com.arangodb.springframework.boot.autoconfigure; 21 | 22 | import java.lang.annotation.Annotation; 23 | 24 | import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; 25 | import org.springframework.data.repository.config.RepositoryConfigurationExtension; 26 | 27 | import com.arangodb.springframework.annotation.EnableArangoRepositories; 28 | import com.arangodb.springframework.repository.ArangoRepositoryConfigurationExtension; 29 | 30 | /** 31 | * @author Mark Vollmary 32 | * 33 | */ 34 | public class ArangoRepositoriesAutoConfigureRegistrar extends AbstractRepositoryConfigurationSourceSupport { 35 | 36 | @Override 37 | protected Class getAnnotation() { 38 | return EnableArangoRepositories.class; 39 | } 40 | 41 | @Override 42 | protected Class getConfiguration() { 43 | return EnableArangoRepositoriesConfiguration.class; 44 | } 45 | 46 | @Override 47 | protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() { 48 | return new ArangoRepositoryConfigurationExtension(); 49 | } 50 | 51 | @EnableArangoRepositories 52 | private static class EnableArangoRepositoriesConfiguration { 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.arangodb.springframework.boot.autoconfigure.ArangoAutoConfiguration 2 | com.arangodb.springframework.boot.actuate.autoconfigure.ArangoHealthIndicatorAutoConfiguration 3 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/springframework/boot/SpringTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2016 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | 21 | package com.arangodb.springframework.boot; 22 | 23 | import com.arangodb.springframework.annotation.Document; 24 | import com.arangodb.springframework.annotation.Ref; 25 | import com.arangodb.springframework.boot.actuate.ArangoHealthIndicator; 26 | import com.arangodb.springframework.core.ArangoOperations; 27 | import com.arangodb.springframework.core.mapping.event.AfterLoadEvent; 28 | import com.arangodb.springframework.core.mapping.event.AfterSaveEvent; 29 | import com.arangodb.springframework.core.mapping.event.ArangoMappingEvent; 30 | import org.junit.jupiter.api.BeforeEach; 31 | import org.junit.jupiter.api.Test; 32 | import org.springframework.beans.factory.annotation.Autowired; 33 | import org.springframework.boot.SpringBootConfiguration; 34 | import org.springframework.boot.actuate.health.Status; 35 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 36 | import org.springframework.boot.test.context.SpringBootTest; 37 | import org.springframework.boot.test.context.TestConfiguration; 38 | import org.springframework.context.ApplicationListener; 39 | import org.springframework.context.annotation.Bean; 40 | import org.springframework.data.annotation.Id; 41 | import org.springframework.data.repository.CrudRepository; 42 | import org.springframework.lang.NonNull; 43 | 44 | import java.util.ArrayList; 45 | import java.util.List; 46 | import java.util.Objects; 47 | 48 | import static org.assertj.core.api.Assertions.assertThat; 49 | 50 | /** 51 | * @author Michele Rastelli 52 | * @author Arne Burmeister 53 | */ 54 | @SpringBootTest 55 | @EnableAutoConfiguration 56 | @SpringBootConfiguration 57 | public class SpringTest { 58 | 59 | @Autowired 60 | private ArangoOperations operations; 61 | 62 | @Autowired 63 | private ArangoHealthIndicator arangoHealthIndicator; 64 | 65 | @Autowired 66 | private CatRepo repo; 67 | 68 | @Autowired 69 | private MouseRepo referencingRepo; 70 | 71 | @Autowired 72 | private Listener listener; 73 | 74 | @BeforeEach 75 | void clearEvents() { 76 | listener.events.clear(); 77 | } 78 | 79 | @Test 80 | public void operationsShouldBeNotNull() { 81 | assertThat(operations).isNotNull(); 82 | } 83 | 84 | @Test 85 | public void getVersion() { 86 | assertThat(operations.getVersion()).isNotNull(); 87 | } 88 | 89 | @Test 90 | public void health() { 91 | assertThat(arangoHealthIndicator.health().getStatus()).isEqualTo(Status.UP); 92 | } 93 | 94 | @Test 95 | void repo() { 96 | Cat cat = new Cat(); 97 | cat.name = "Silvestro"; 98 | Cat saved = repo.save(cat); 99 | Cat read = repo.findById(saved.id).orElseThrow(); 100 | assertThat(read.id).isNotNull(); 101 | assertThat(read.name).isEqualTo(cat.name); 102 | } 103 | 104 | @Test 105 | void event() { 106 | Cat cat = new Cat(); 107 | cat.name = "Tom"; 108 | Cat saved = repo.save(cat); 109 | assertThat(listener.events).hasSize(3) 110 | .describedAs("event after save of cat") 111 | .anyMatch(event -> matches(event, AfterSaveEvent.class, saved)); 112 | Mouse mouse = new Mouse(); 113 | mouse.huntBy = cat; 114 | referencingRepo.save(mouse); 115 | listener.events.clear(); 116 | 117 | Cat hunter = referencingRepo.findById(mouse.id).orElseThrow().huntBy; 118 | assertThat(hunter).isEqualTo(saved); 119 | assertThat(listener.events).describedAs("event after resolving cat") 120 | .anyMatch(event -> matches(event, AfterLoadEvent.class, hunter)); 121 | } 122 | 123 | @TestConfiguration 124 | static class EventConfig { 125 | @Bean 126 | ApplicationListener> listener() { 127 | return new Listener(); 128 | } 129 | } 130 | 131 | @SuppressWarnings("rawtypes") 132 | private static boolean matches(ArangoMappingEvent event, Class type, Object source) { 133 | return type.isInstance(event) && event.getSource().equals(source); 134 | } 135 | } 136 | 137 | interface CatRepo extends CrudRepository { 138 | } 139 | 140 | interface MouseRepo extends CrudRepository { 141 | } 142 | 143 | @Document 144 | class Cat { 145 | @Id 146 | String id; 147 | String name; 148 | 149 | @Override 150 | public boolean equals(Object o) { 151 | if (this == o) return true; 152 | if (o == null || getClass() != o.getClass()) return false; 153 | Cat cat = (Cat) o; 154 | return Objects.equals(id, cat.id) && Objects.equals(name, cat.name); 155 | } 156 | 157 | @Override 158 | public int hashCode() { 159 | return Objects.hash(id); 160 | } 161 | } 162 | 163 | @Document 164 | class Mouse { 165 | @Id 166 | String id; 167 | 168 | @Ref 169 | Cat huntBy; 170 | 171 | @Override 172 | public boolean equals(Object o) { 173 | if (this == o) return true; 174 | if (o == null || getClass() != o.getClass()) return false; 175 | Mouse mouse = (Mouse) o; 176 | return Objects.equals(id, mouse.id); 177 | } 178 | 179 | @Override 180 | public int hashCode() { 181 | return Objects.hash(id); 182 | } 183 | } 184 | 185 | class Listener implements ApplicationListener> { 186 | 187 | final List> events = new ArrayList<>(); 188 | 189 | @Override 190 | public void onApplicationEvent(@NonNull ArangoMappingEvent event) { 191 | events.add(event); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/springframework/boot/actuate/ArangoHealthIndicatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DISCLAIMER 3 | * 4 | * Copyright 2018 ArangoDB GmbH, Cologne, Germany 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Copyright holder is ArangoDB GmbH, Cologne, Germany 19 | */ 20 | package com.arangodb.springframework.boot.actuate; 21 | 22 | import com.arangodb.ArangoDB; 23 | import com.arangodb.entity.ArangoDBVersion; 24 | import com.arangodb.entity.License; 25 | import com.arangodb.springframework.core.ArangoOperations; 26 | import org.junit.jupiter.api.Test; 27 | import org.springframework.boot.actuate.health.Status; 28 | 29 | import static org.assertj.core.api.Assertions.assertThat; 30 | import static org.mockito.Mockito.doReturn; 31 | import static org.mockito.Mockito.mock; 32 | 33 | /** 34 | * @author Mark Vollmary 35 | * 36 | */ 37 | public class ArangoHealthIndicatorTest { 38 | 39 | @Test 40 | public void doHealthCheck() { 41 | final ArangoOperations operations = mock(ArangoOperations.class); 42 | { 43 | final ArangoDB arango = mock(ArangoDB.class); 44 | { 45 | final ArangoDBVersion version = mock(ArangoDBVersion.class); 46 | doReturn("ArangoDB").when(version).getServer(); 47 | doReturn("3.4.0").when(version).getVersion(); 48 | doReturn(License.COMMUNITY).when(version).getLicense(); 49 | doReturn(version).when(arango).getVersion(); 50 | } 51 | doReturn(arango).when(operations).driver(); 52 | } 53 | final ArangoHealthIndicator indicator = new ArangoHealthIndicator(operations); 54 | assertThat(indicator.health().getStatus()).isEqualTo(Status.UP); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | arangodb.spring.data.hosts=172.28.0.1:8529 2 | arangodb.spring.data.password=test 3 | --------------------------------------------------------------------------------