├── .gitignore
├── README.md
├── build.gradle.kts
├── docker-compose.yml
├── docs
├── .swagger-codegen-ignore
├── .swagger-codegen
│ └── VERSION
└── index.html
├── exercises.json
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── 01-architecture-atlas_application.png
├── 02-architecture-kotlin_application.png
├── 03-creating_exercises_collection.png
├── 04-creating_trigger_details.png
├── 05-creating_trigger_details.png
├── 06-creating_function.png
├── 07-app_services.png
├── 08-rename_function.png
├── 09-rename_function.png
├── 10-huggingFace_token.png
├── 11-creating_application_new_value.png
├── 12-creating_application_new_value_secret.png
├── 13-creating_application_new_value_Value.png
├── 14-values_list.png
├── 15-exercises_document_sample.png
├── 16-mongo_import.png
├── 17-mongo_import_success.png
├── 18-app_service_logs.png
├── 19-collection_exercises_with_embedded_imported.png
├── 20-postman_final_demonstration.png
├── 21-creating_atlas_vector_index.png
├── 22-creating_atlas_vector_index.png
├── 23-creating_atlas_vector_index.png
├── 24-creating_atlas_vector_index.png
├── 25-running_application.png
└── 26-postman_request.png
├── settings.gradle.kts
└── src
├── main
├── kotlin
│ └── com
│ │ └── mongodb
│ │ ├── Application.kt
│ │ ├── application
│ │ ├── request
│ │ │ ├── FitnessRequest.kt
│ │ │ └── SentenceRequest.kt
│ │ ├── response
│ │ │ ├── ExercisesResponse.kt
│ │ │ └── FitnessResponse.kt
│ │ └── routes
│ │ │ ├── ExercisesRoutes.kt
│ │ │ └── FitnessRoute.kt
│ │ ├── domain
│ │ ├── entity
│ │ │ ├── Exercises.kt
│ │ │ └── Fitness.kt
│ │ └── ports
│ │ │ ├── ExerciseRepository.kt
│ │ │ └── FitnessRepository.kt
│ │ └── infrastructure
│ │ ├── ExerciseRepositoryImpl.kt
│ │ └── FitnessRepositoryImpl.kt
└── resources
│ ├── application.conf
│ ├── logback.xml
│ └── openapi
│ └── documentation.yaml
└── test
└── kotlin
└── com
└── mongodb
└── ApplicationTest.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 | bin/
16 | !**/src/main/**/bin/
17 | !**/src/test/**/bin/
18 |
19 | ### IntelliJ IDEA ###
20 | .idea
21 | *.iws
22 | *.iml
23 | *.ipr
24 | out/
25 | !**/src/main/**/out/
26 | !**/src/test/**/out/
27 |
28 | ### NetBeans ###
29 | /nbproject/private/
30 | /nbbuild/
31 | /dist/
32 | /nbdist/
33 | /.nb-gradle/
34 |
35 | ### VS Code ###
36 | .vscode/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kotlin-ktor-mongodb-vector-search
2 |
3 | This is the source code about my MongoDB articles.
4 | - [`Mastering Kotlin: Creating an API With Ktor and MongoDB Atlas`](https://www.mongodb.com/developer/languages/kotlin/mastering-kotlin-creating-api-ktor-mongodb-atlas/)
5 | - [`Beyond Basics: Enhancing Kotlin Ktor API With Vector Search`](https://www.mongodb.com/developer/products/atlas/beyond-basics-enhancing-kotlin-ktor-api-vector-search/)
6 |
7 |
8 |
9 | Kotlin's simplicity, Java interoperability, and Ktor's user-friendly framework combined with MongoDB Atlas' flexible cloud database provide a robust stack for modern software development.
10 | Together, we'll demonstrate and set up the Ktor project, implement CRUD operations, define API route endpoints, and run the application. By the end, you'll have a solid understanding of Kotlin's capabilities in API development and the tools needed to succeed in modern software development.
11 |
12 | ## MongoDB Atlas Flow
13 | 
14 |
15 | ## Kotlin Ktor Flow
16 | 
17 |
18 | ## Built with
19 |
20 | - [Kotlin - Programming Language](https://kotlinlang.org/docs/coroutines-overview.html)
21 | - [Ktor - Asynchronous framework](https://ktor.io/)
22 | - [Koin - Dependency Injection framework](https://insert-koin.io/)
23 | - [MongoDB Kotlin Driver — Kotlin Coroutine](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/)
24 |
25 | ## Running
26 |
27 | Follow the steps below to get the Fitness Tracker App up and running local / MongoDB Atlas.
28 |
29 | ### Local
30 |
31 | 1. Clone the repository to your local machine:
32 |
33 | ```bash
34 | git clone https://github.com/your-username/fitness-tracker-app.git
35 | cd fitness-tracker
36 | ```
37 |
38 | 2. Start the application using Docker Compose:
39 |
40 | ```bash
41 | docker-compose up -d
42 | ```
43 |
44 | 3. Compile the application jar using Gradle:
45 |
46 | ```bash
47 | ./gradlew shadowJar
48 | ```
49 |
50 | 4. Run the application
51 |
52 | ```bash
53 | java -jar -DMONGO_URI="mongodb://localhost:27017/fitness/" -DMONGO_DATABASE="my_database" build/libs
54 | ```
55 |
56 | ### MongoDB Atlas
57 |
58 | 1. Clone the repository to your local machine:
59 |
60 | ```bash
61 | git clone https://github.com/your-username/fitness-tracker-app.git
62 | cd fitness-tracker
63 | ```
64 |
65 | 2. Compile the application jar using Gradle:
66 |
67 | ```bash
68 | ./gradlew shadowJar
69 | ```
70 |
71 | 4. Run the application
72 |
73 | ```bash
74 | java -jar -DMONGO_URI="mongodb+srv://:@/?retryWrites=true&w=majority" -DMONGO_DATABASE="my_database" build/libs
75 |
76 | ```
77 | ### Swagger UI
78 |
79 | To explore the API documentation and interact with the Fitness Tracker App, you can use Swagger. Open your web browser and navigate to:
80 |
81 | http://localhost:8080/swagger-ui/
82 |
83 |
84 | 
85 |
86 |
87 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | val ktor_version: String by project
2 | val kotlin_version: String by project
3 | val logback_version: String by project
4 |
5 | plugins {
6 | kotlin("jvm") version "1.9.22"
7 | id("io.ktor.plugin") version "2.3.7"
8 | id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22"
9 | }
10 |
11 | group = "com.mongodb"
12 | version = "0.0.1"
13 |
14 | application {
15 | mainClass.set("com.mongodb.ApplicationKt")
16 |
17 | val isDevelopment: Boolean = project.ext.has("development")
18 | applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
19 | }
20 |
21 | repositories {
22 | mavenCentral()
23 | }
24 |
25 | dependencies {
26 | implementation("io.ktor:ktor-server-core-jvm")
27 | implementation("io.ktor:ktor-server-swagger-jvm")
28 | implementation("io.ktor:ktor-server-content-negotiation-jvm")
29 | implementation("io.ktor:ktor-serialization-gson-jvm")
30 | implementation("io.ktor:ktor-server-tomcat-jvm")
31 | implementation("ch.qos.logback:logback-classic:$logback_version")
32 | implementation("io.ktor:ktor-server-config-yaml:2.3.8")
33 |
34 | //MongoDB
35 | implementation("org.mongodb:mongodb-driver-kotlin-coroutine:4.10.1")
36 |
37 | //Koin
38 | implementation("io.insert-koin:koin-ktor:3.5.3")
39 | implementation("io.insert-koin:koin-logger-slf4j:3.5.3")
40 |
41 | //Client
42 | implementation("io.ktor:ktor-client-core:$ktor_version")
43 | implementation("io.ktor:ktor-client-cio:$ktor_version")
44 |
45 | // tests
46 | testImplementation("io.ktor:ktor-server-tests-jvm")
47 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
48 | }
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 |
3 | services:
4 | mongodb:
5 | image: mongo
6 | container_name: mongodb
7 | restart: always
8 | ports:
9 | - "27017:27017"
10 | volumes:
11 | - mongodb_data:/data/db
12 |
13 | volumes:
14 | mongodb_data:
15 |
--------------------------------------------------------------------------------
/docs/.swagger-codegen-ignore:
--------------------------------------------------------------------------------
1 | # Swagger Codegen Ignore
2 | # Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
3 |
4 | # Use this file to prevent files from being overwritten by the generator.
5 | # The patterns follow closely to .gitignore or .dockerignore.
6 |
7 | # As an example, the C# client generator defines ApiClient.cs.
8 | # You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line:
9 | #ApiClient.cs
10 |
11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*):
12 | #foo/*/qux
13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14 |
15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16 | #foo/**/qux
17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18 |
19 | # You can also negate patterns with an exclamation (!).
20 | # For example, you can ignore all files in a docs folder with the file extension .md:
21 | #docs/*.md
22 | # Then explicitly reverse the ignore rule for a single file:
23 | #!docs/README.md
24 |
--------------------------------------------------------------------------------
/docs/.swagger-codegen/VERSION:
--------------------------------------------------------------------------------
1 | 3.0.52
--------------------------------------------------------------------------------
/exercises.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "exerciseNumber": 1,
4 | "title": "Banded crunch isometric hold",
5 | "description": "The banded crunch isometric hold is an exercise targeting the abdominal muscles, particularly the rectus abdominis or \"six-pack\" muscles. The band adds resistance and continuous tension to this popular exercise.",
6 | "type": "Strength",
7 | "bodyPart": "Abdominals",
8 | "equipment": "Bands",
9 | "level": "Intermediate",
10 | "rating": 5.4,
11 | "ratingDesc": ""
12 | },
13 | {
14 | "exerciseNumber": 2,
15 | "title": "FYR Banded Plank Jack",
16 | "description": "The banded plank jack is a variation on the plank that involves moving the legs in and out for repetitions. Having a band around the thighs forces the lower body to work harder, particularly the hips and glutes. The plank jack is commonly performed as part of a bodyweight circuit, or as part of a dynamic warm-up.",
17 | "type": "Strength",
18 | "bodyPart": "Abdominals",
19 | "equipment": "Bands",
20 | "level": "Intermediate",
21 | "rating": 5.5,
22 | "ratingDesc": ""
23 | },
24 |
25 |
26 | {
27 | "exerciseNumber": 3,
28 | "title": "Banded crunch",
29 | "description": "The banded crunch is an exercise targeting the abdominal muscles, particularly the rectus abdominis or \"six-pack\" muscles. The band adds resistance and continuous tension to this popular exercise.",
30 | "type": "Strength",
31 | "bodyPart": "Abdominals",
32 | "equipment": "Bands",
33 | "level": "Intermediate",
34 | "rating": 5.2,
35 | "ratingDesc": ""
36 | },
37 | {
38 | "exerciseNumber": 4,
39 | "title": "Crunch",
40 | "description": "The crunch is a popular core exercise targeting the rectus abdominis, or \"six-pack\" muscles, as well as the obliques. It has been the centerpiece of many ab-focused workouts, due to both its simplicity and the intense burn and mind-muscle connection it produces. It can be performed for time or reps as part of the ab-focused portion of any workout.",
41 | "type": "Strength",
42 | "bodyPart": "Abdominals",
43 | "equipment": "Bands",
44 | "level": "Intermediate",
45 | "rating": 5.8,
46 | "ratingDesc": ""
47 | },
48 | {
49 | "exerciseNumber": 5,
50 | "title": "Iliotibial band SMR",
51 | "description": "Iliotibial band self-myofascial release (SMR) is a self-administered soft-tissue treatment for the lower body, usually using a foam roller or other \"trigger point\" massage tool. By applying pressure strategically to the IT band along the side of the upper leg, some people find they can cause this often painful and tight area to relax or \"release.\" IT band SMR is often accompanied by SMR on the glute or quad muscles, or by stretches that target the glutes or quads.",
52 | "type": "Stretching",
53 | "bodyPart": "Abductors",
54 | "equipment": "Foam Roll",
55 | "level": "Intermediate",
56 | "rating": 8.2,
57 | "ratingDesc": "Average"
58 | },
59 | {
60 | "exerciseNumber": 6,
61 | "title": "Adductor SMR",
62 | "description": "Adductor self-myofascial release (SMR) is a self-administered soft-tissue treatment for the lower body, usually using a foam roller, lacrosse ball, or other \"trigger point\" massage tool. By applying pressure strategically to the adductor muscles in the inner thigh region, some people find they can cause this often painful and tight muscle to relax or \"release.\" Adductor SMR is often accompanied by SMR on the iliotibial (IT) band or glutes (TFL muscles), or by stretches that target the adductors.",
63 | "type": "Stretching",
64 | "bodyPart": "Adductors",
65 | "equipment": "Foam Roll",
66 | "level": "Beginner",
67 | "rating": 4,
68 | "ratingDesc": "Average"
69 | },
70 |
71 | {
72 | "exerciseNumber": 7,
73 | "title": "Incline dumbbell biceps curl",
74 | "description": "The incline dumbbell biceps curl is an exercise targeting the biceps and performed face up on an incline bench. This creates a vertical arm angle, which helps to isolate the biceps and limit involvement of the shoulder muscles. This curl variation is usually performed for moderate to high reps, such as 8-12 reps per set or more, as part of upper-body or arm-focused training.",
75 | "type": "Strength",
76 | "bodyPart": "Biceps",
77 | "equipment": "Bands",
78 | "level": "Intermediate",
79 | "rating": 7.0,
80 | "ratingDesc": ""
81 | },
82 | {
83 | "exerciseNumber": 8,
84 | "title": "Banded biceps curl in squat position",
85 | "description": "The banded biceps curl in squat position is an exercise targeting the biceps, although it also works the core and lower body.",
86 | "type": "Strength",
87 | "bodyPart": "Biceps",
88 | "equipment": "Bands",
89 | "level": "Intermediate",
90 | "rating": 7.0,
91 | "ratingDesc": ""
92 | },
93 | {
94 | "exerciseNumber": 9,
95 | "title": "Banded Hammer Press - Gethin Variation",
96 | "description": "The machine chest press is an upper body exercise targeting the pectoral muscles.",
97 | "type": "Strength",
98 | "bodyPart": "Biceps",
99 | "equipment": "Bands",
100 | "level": "Intermediate",
101 | "rating": 7.0,
102 | "ratingDesc": ""
103 | },
104 | {
105 | "exerciseNumber": 10,
106 | "title": "Band standing concentration curl",
107 | "description": "The band standing concentration curl is a variation of a popular biceps-focused exercise utilizing a band instead of a dumbbell. It allows you to focus on one biceps at a time and really squeeze out intense peak contractions. Band concentration curls are usually performed for moderate to high reps, such as 8-12 reps per set or more, as part of an upper-body or arm-focused workout.",
108 | "type": "Strength",
109 | "bodyPart": "Biceps",
110 | "equipment": "Bands",
111 | "level": "Intermediate",
112 | "rating": 7.0,
113 | "ratingDesc": ""
114 | },
115 | {
116 | "exerciseNumber": 11,
117 | "title": "Brachialis SMR",
118 | "description": "Brachialis self-myofascial release (SMR) is a self-administered soft-tissue treatment for the upper body, usually using a foam roller, lacrosse ball, or other \"trigger point\" massage tool. By applying pressure strategically to the biceps or brachialis muscles of the upper arm, some people find they can cause this often painful and tight muscle to relax or \"release.\" Brachialis SMR is often accompanied by SMR on the triceps muscles or pectorals, or by stretches that target the biceps or pecs.",
119 | "type": "Stretching",
120 | "bodyPart": "Biceps",
121 | "equipment": "Foam Roll",
122 | "level": "Beginner",
123 | "rating": 3.2,
124 | "ratingDesc": "Average"
125 | },
126 |
127 |
128 | {
129 | "exerciseNumber": 12,
130 | "title": "Calf SMR",
131 | "description": "Calf self-myofascial release (SMR) is a self-administered soft-tissue treatment for the lower body, usually using a foam roller, lacrosse ball, or other \"trigger point\" massage tool. By applying pressure strategically to the calf muscles of the lower leg, some people find they can cause this often painful and tight muscle to relax or \"release.\" Calf SMR is often accompanied by SMR on the quadriceps muscles, or by stretches that target the calves.",
132 | "type": "Stretching",
133 | "bodyPart": "Calves",
134 | "equipment": "Foam Roll",
135 | "level": "Beginner",
136 | "rating": 6,
137 | "ratingDesc": "Average"
138 | },
139 |
140 | {
141 | "exerciseNumber": 13,
142 | "title": "Standing barbell calf raise",
143 | "description": "The standing barbell calf raise is a popular exercise to target the calf muscles of the lower leg, particularly the gastrocnemius muscle. It can be performed for time or for reps, either using a barbell or Smith machine bar if balance is a problem.",
144 | "type": "Strength",
145 | "bodyPart": "Calves",
146 | "equipment": "Barbell",
147 | "level": "Beginner",
148 | "rating": 8.2,
149 | "ratingDesc": "Average"
150 | },
151 |
152 |
153 | {
154 | "exerciseNumber": 14,
155 | "title": "Band-suspended kettlebell bench press",
156 | "description": "The band-suspended kettlebell bench press is a bench press variation where kettlebells or weight plates are hung from the ends of a barbell by looped bands. The suspended weights can be in addition to normally loaded weight plates, or in the place of them. Because the instability of the weights makes the lift difficult, this bench press variety is a popular accessory movement for building the traditional bench press in powerlifting training.",
157 | "type": "Strength",
158 | "bodyPart": "Chest",
159 | "equipment": "Bands",
160 | "level": "Intermediate",
161 | "rating": 7.0,
162 | "ratingDesc": ""
163 | },
164 |
165 | {
166 | "exerciseNumber": 15,
167 | "title": "Incline band bench press",
168 | "description": "The incline band bench press is variation of the incline press, a popular upper-body exercise targeting the upper pectoral muscles. While it can be a strength-focused movement, it is usually performed for moderate to high reps, such as 8-12 reps per set or more, as part of upper-body or chest-focused training.",
169 | "type": "Strength",
170 | "bodyPart": "Chest",
171 | "equipment": "Bands",
172 | "level": "Intermediate",
173 | "rating": 7.0,
174 | "ratingDesc": ""
175 | },
176 | {
177 | "exerciseNumber": 16,
178 | "title": "Band push-up",
179 | "description": "The band push-up is a progression of the popular bodyweight version of the exercise commonly used in military and tactical physical fitness tests. Adding the dynamic resistance of the band matches the strength curve of the push-up and helps build upper-body muscle and strength, emphasizing the chest, triceps, and shoulders, but also working the upper back and core.",
180 | "type": "Strength",
181 | "bodyPart": "Chest",
182 | "equipment": "Bands",
183 | "level": "Intermediate",
184 | "rating": 7.0,
185 | "ratingDesc": ""
186 | },
187 | {
188 | "exerciseNumber": 17,
189 | "title": "Barbell Bench Press - Medium Grip",
190 | "description": "The bench press is a compound exercise that builds strength and muscle in the chest and triceps. When many people think of listing, the bench press is often the first exercise that comes to mind",
191 | "type": "Strength",
192 | "bodyPart": "Chest",
193 | "equipment": "Barbell",
194 | "level": "Intermediate",
195 | "rating": 9,
196 | "ratingDesc": "Average"
197 | },
198 |
199 | {
200 | "exerciseNumber": 18,
201 | "title": "Standing alternating band speed row",
202 | "description": "The standing alternating band speed row is an explosive back exercise using exercise bands. It targets the muscles of the middle and upper back, including the lats (latissimus dorsi), rhomboids, and trapezius, as well as the biceps. It can be performed for time or reps either as part of a power or speed-focused portion of a workout or as a burnout at the end of a workout.",
203 | "type": "Strength",
204 | "bodyPart": "Forearms",
205 | "equipment": "Bands",
206 | "level": "Intermediate",
207 | "rating": 7.0,
208 | "ratingDesc": ""
209 | },
210 | {
211 | "exerciseNumber": 19,
212 | "title": "Band roundhouse elbow",
213 | "description": "The band roundhouse elbow is an explosive rotational power exercise using exercise bands. It targets the muscles of the obliques and deep core, but also engrains full-body coordination and striking ability. It can work in an ab workout, in martial arts training, or in general strength and muscle training.",
214 | "type": "Strength",
215 | "bodyPart": "Forearms",
216 | "equipment": "Bands",
217 | "level": "Intermediate",
218 | "rating": 7.0,
219 | "ratingDesc": ""
220 | },
221 | {
222 | "exerciseNumber": 20,
223 | "title": "Palms-down wrist curl over bench",
224 | "description": "The palms-down wrist curl over bench is an exercise targeting the forearms. It is usually performed for high reps, such as 10-15 reps per set or more, as part of a grip or arm-focused workout.",
225 | "type": "Strength",
226 | "bodyPart": "Forearms",
227 | "equipment": "Barbell",
228 | "level": "Intermediate",
229 | "rating": 9.5,
230 | "ratingDesc": "Average"
231 | },
232 | {
233 | "exerciseNumber": 21,
234 | "title": "Palms-up wrist curl over bench",
235 | "description": "The palms-up wrist curl over bench is an exercise targeting the forearms. It is usually performed for high reps, such as 10-15 reps per set or more, as part of a grip or arm-focused workout.",
236 | "type": "Strength",
237 | "bodyPart": "Forearms",
238 | "equipment": "Barbell",
239 | "level": "Intermediate",
240 | "rating": 9.4,
241 | "ratingDesc": "Average"
242 | },
243 | {
244 | "exerciseNumber": 22,
245 | "title": "Standing behind-the-back wrist curl",
246 | "description": "The standing behind-the-back wrist curl is a popular exercise to target the muscles in the forearms, wrists, and fingers. It is generally performed for moderate to high reps, such as 8-15 reps per set, and can work as isolated forearm training or as part of an arms-focused workout.",
247 | "type": "Strength",
248 | "bodyPart": "Forearms",
249 | "equipment": "Barbell",
250 | "level": "Beginner",
251 | "rating": 9.3,
252 | "ratingDesc": "Average"
253 | },
254 |
255 |
256 | {
257 | "exerciseNumber": 23,
258 | "title": "Piriformis SMR",
259 | "description": "Piriformis self-myofascial release (SMR) is a self-administered soft-tissue treatment for the lower body, usually using a foam roller, lacrosse ball, or other \"trigger point\" massage tool. By applying pressure strategically to the piriformis muscles in the upper gluteal region, some people find they can cause this often painful and tight muscle to relax or \"release.\" Piriformis SMR is often accompanied by SMR on the iliotibial (IT) band or tensor fascia lata (TFL muscles), or by stretches that target the glutes and hips.",
260 | "type": "Stretching",
261 | "bodyPart": "Glutes",
262 | "equipment": "Foam Roll",
263 | "level": "Intermediate",
264 | "rating": 6.6,
265 | "ratingDesc": "Average"
266 | },
267 | {
268 | "exerciseNumber": 24,
269 | "title": "Barbell glute bridge",
270 | "description": "The barbell glute bridge is a popular exercise targeting the muscles of the glutes and hamstrings. It can be done as a strength movement on its own, as an activation drill or warm-up for lower-body training, or as a burnout at the end of a lower-body workout.",
271 | "type": "Powerlifting",
272 | "bodyPart": "Glutes",
273 | "equipment": "Barbell",
274 | "level": "Intermediate",
275 | "rating": 9.4,
276 | "ratingDesc": "Average"
277 | },
278 | {
279 | "exerciseNumber": 25,
280 | "title": "Barbell Hip Thrust",
281 | "description": "The barbell hip thrust is an exercise that engages the posterior chain, particular the glutes. It involves fully extending your hips while holding a weighted barbell across the midsection.",
282 | "type": "Powerlifting",
283 | "bodyPart": "Glutes",
284 | "equipment": "Barbell",
285 | "level": "Intermediate",
286 | "rating": 9.1,
287 | "ratingDesc": "Average"
288 | },
289 |
290 | {
291 | "exerciseNumber": 26,
292 | "title": "Kettlebell thruster",
293 | "description": "The kettlebell thruster is a popular kettlebell movement that combines a kettlebell front squat with a kettlebell press, while holding the bell in a goblet or bottoms-up grip. It is preceded by a clean at the start of each set, and you can perform a clean between each rep if you choose. It can be trained in traditional strength or muscle-building rep ranges, in circuit or fat-loss training, or as part of a larger kettlebell combination or complex.",
294 | "type": "Strength",
295 | "bodyPart": "Glutes",
296 | "equipment": "Kettlebells",
297 | "level": "Intermediate",
298 | "rating": 8.6,
299 | "ratingDesc": "Average"
300 | },
301 |
302 | {
303 | "exerciseNumber": 27,
304 | "title": "Single-arm triceps kick-back",
305 | "description": "The single-arm triceps kick-back is a popular movement to increase size and strength of the triceps. It is usually performed for moderate to high reps, at least 8-12 reps, as part of an upper-body or arm-focused workout.",
306 | "type": "Strength",
307 | "bodyPart": "Glutes",
308 | "equipment": "Dumbbell",
309 | "level": "Intermediate",
310 | "rating": 7.0,
311 | "ratingDesc": ""
312 | },
313 |
314 | {
315 | "exerciseNumber": 28,
316 | "title": "World's greatest stretch",
317 | "description": "The world's greatest stretch is a popular movement that earned its name because it targets multiple parts of the body, including the hips, shoulders, and thoracic spine, in a single sequence. This makes it a great dynamic warm-up, post-workout stretch, or mobility work you can do anytime.",
318 | "type": "Stretching",
319 | "bodyPart": "Hamstrings",
320 | "equipment": "Body Only",
321 | "level": "Intermediate",
322 | "rating": 4.4,
323 | "ratingDesc": "Average"
324 | },
325 |
326 | {
327 | "exerciseNumber": 29,
328 | "title": "Single-leg balance and reach",
329 | "description": "The single-leg balance and reach is an exercise that focuses on lower-body strength and stability.",
330 | "type": "Stretching",
331 | "bodyPart": "Hamstrings",
332 | "equipment": "Body Only",
333 | "level": "Beginner",
334 | "rating": 0,
335 | "ratingDesc": ""
336 | },
337 |
338 | {
339 | "exerciseNumber": 30,
340 | "title": "Band-assisted pull-up",
341 | "description": "The band-assisted pull-up is a variation of the pull-up exercise in which the reps are performed with an elastic band looped around the feet or knees. This reduces weight and resistance at the bottom of the rep, while increasing it toward the top. Like other pull-up variations, it builds strength and muscle in the upper back, biceps, and core, and is often used as a way to help people boost their pull-up numbers or perform their first rep.",
342 | "type": "Strength",
343 | "bodyPart": "Lats",
344 | "equipment": "Bands",
345 | "level": "Intermediate",
346 | "rating": 7.6,
347 | "ratingDesc": "Average"
348 | },
349 | {
350 | "exerciseNumber": 31,
351 | "title": "Assisted Chin-Up",
352 | "description": "The reverse-grip chin-up is a variation of the pull-up exercise in which the reps are performed with the palms facing toward the body, in an underhand position, with a band looped around the feet or knees. This reduces weight and resistance at the bottom of the rep, while increasing it toward the top. Like other pull-up variations, it builds strength and muscle in the upper back, biceps, and core, but it utilizes the biceps slightly more than overhand band-assisted pull-ups.",
353 | "type": "Strength",
354 | "bodyPart": "Lats",
355 | "equipment": "Bands",
356 | "level": "Beginner",
357 | "rating": 0,
358 | "ratingDesc": "Average"
359 | },
360 |
361 | {
362 | "exerciseNumber": 32,
363 | "title": "Band good morning-",
364 | "description": "The band good morning is an exercise targeting the hamstrings and glutes. It is often performed in powerlifting-style training for high reps, or in the place of barbell good mornings.",
365 | "type": "Strength",
366 | "bodyPart": "Lower Back",
367 | "equipment": "Bands",
368 | "level": "Intermediate",
369 | "rating": 7.0,
370 | "ratingDesc": ""
371 | },
372 |
373 | {
374 | "exerciseNumber": 33,
375 | "title": "Band deadlift",
376 | "description": "The band deadlift is a lower-body exercise that mainly targets the hamstrings while also benefiting the glutes and lats using a proper hip-hinging motion.",
377 | "type": "Strength",
378 | "bodyPart": "Lower Back",
379 | "equipment": "Bands",
380 | "level": "Intermediate",
381 | "rating": 7.0,
382 | "ratingDesc": ""
383 | },
384 | {
385 | "exerciseNumber": 34,
386 | "title": "Lower back SMR",
387 | "description": "Lower back self-myofascial release (SMR) is a self-administered soft-tissue treatment, usually using a foam roller, lacrosse ball, or other \"trigger point\" massage tool. By applying pressure strategically to the quadratus lumborum or spinal erector (erector spinae) muscles of the lower back, some people find they can cause this often painful and tight area to relax or \"release.\" Lower back SMR is often accompanied by SMR on the glutes or piriformis muscles, or by stretches that target the piriformis or hips. However, some people find that SMR on the lower back can aggravate existing pain or injury, so be cautious when performing it.",
388 | "type": "Stretching",
389 | "bodyPart": "Lower Back",
390 | "equipment": "Foam Roll",
391 | "level": "Intermediate",
392 | "rating": 4.5,
393 | "ratingDesc": "Average"
394 | },
395 |
396 |
397 | {
398 | "exerciseNumber": 35,
399 | "title": "Partner side plank band row",
400 | "description": "The partner side plank band row is an abdominal exercise where two partners perform side planks while pulling on the opposite ends of an exercise band. This technique can be done for time or reps in any ab-focused workout.",
401 | "type": "Strength",
402 | "bodyPart": "Middle Back",
403 | "equipment": "Bands",
404 | "level": "Intermediate",
405 | "rating": 0,
406 | "ratingDesc": ""
407 | },
408 | {
409 | "exerciseNumber": 36,
410 | "title": "Band-assisted chin-up",
411 | "description": "The band-assisted chin-up is a variation of the pull-up exercise in which the reps are performed with the palms facing toward the body in an underhand position, with a band looped around the feet or knees. This reduces weight and resistance at the bottom of the rep, while increasing it toward the top. Like other pull-up variations, it builds strength and muscle in the upper back, biceps, and core, but it utilizes the biceps slightly more than overhand band-assisted pull-ups.",
412 | "type": "Strength",
413 | "bodyPart": "Middle Back",
414 | "equipment": "Bands",
415 | "level": "Intermediate",
416 | "rating": 7.0,
417 | "ratingDesc": ""
418 | },
419 | {
420 | "exerciseNumber": 37,
421 | "title": "Band seated row",
422 | "description": "The band seated row is a band alternative to the popular cable exercise and trains the muscles of the upper back, including the lats (latissimus dorsi), traps, rhomboids, and rear deltoids. It also targets the biceps to a lesser degree. The band row can work well in a variety of rep ranges, requiring a heavier band for muscle-building and strength workouts and a lighter band for weight-loss work and as an accessory movement.",
423 | "type": "Strength",
424 | "bodyPart": "Traps",
425 | "equipment": "Bands",
426 | "level": "Intermediate",
427 | "rating": 7.0,
428 | "ratingDesc": ""
429 | },
430 | {
431 | "exerciseNumber": 38,
432 | "title": "Barbell shrug",
433 | "description": "The barbell shrug is an exercise targeting the traps (trapezius muscles). It is popular in strength and muscle-focused upper-body training, and is often trained on a shoulder day. With the assistance of straps, it can be loaded heavily, but it is still usually performed for moderate to high reps, such as 8-10 reps per set.",
434 | "type": "Strength",
435 | "bodyPart": "Traps",
436 | "equipment": "Barbell",
437 | "level": "Intermediate",
438 | "rating": 8.4,
439 | "ratingDesc": "Average"
440 | },
441 | {
442 | "exerciseNumber": 39,
443 | "title": "Barbell behind-the-back shrug",
444 | "description": "The barbell behind-the-back shrug is an exercise targeting the traps. Having the bar behind you helps keep your shoulders pulled up and back, rather than rounded forward. It is popular in strength and muscle-focused upper-body training, and is often trained on a shoulder day. With the assistance of straps, it can be loaded heavily, but it is still usually performed for moderate to high reps, such as 8-10 reps per set.",
445 | "type": "Strength",
446 | "bodyPart": "Traps",
447 | "equipment": "Barbell",
448 | "level": "Intermediate",
449 | "rating": 8.3,
450 | "ratingDesc": "Average"
451 | },
452 |
453 | {
454 | "exerciseNumber": 40,
455 | "title": "Single-arm inverted row",
456 | "description": "The single-arm inverted row is a bodyweight exercise targeting the muscles of the lats (latissimus dorsi) and upper back, using the bar of the Smith machine to hang and pull from. You can change the bar height to customize the difficulty level and desired angle of the row.",
457 | "type": "Strength",
458 | "bodyPart": "Traps",
459 | "equipment": "Barbell",
460 | "level": "Intermediate",
461 | "rating": 7.0,
462 | "ratingDesc": ""
463 | },
464 |
465 |
466 | {
467 | "exerciseNumber": 41,
468 | "title": "Single-leg depth squat",
469 | "description": "The single-leg depth squat is an exercise targeting the muscles of the lower body, including the quads, glutes, and hamstrings. It is sometimes used as a substitute for the pistol squat, since it doesn't demand the same level of flexibility to perform. It is usually performed for low to moderate reps with a focus on proper form and control.",
470 | "type": "Strength",
471 | "bodyPart": "Quadriceps",
472 | "equipment": "Bands",
473 | "level": "Intermediate",
474 | "rating": 9,
475 | "ratingDesc": "Average"
476 | },
477 |
478 | {
479 | "exerciseNumber": 42,
480 | "title": "Banded jump squat",
481 | "description": "The banded jump squat is an explosive bodyweight squat variation performed with a band around the thighs just above the knees. This increases muscle activation in the hips and glutes, while the squat targets the quads, hamstrings, and glutes. The banded jump squat can be performed for low reps as a power-focused exercise, or for higher reps to build muscle in the lower body, challenge conditioning, and burn fat.",
482 | "type": "Strength",
483 | "bodyPart": "Quadriceps",
484 | "equipment": "Bands",
485 | "level": "Intermediate",
486 | "rating": 7.0,
487 | "ratingDesc": ""
488 | },
489 | {
490 | "exerciseNumber": 43,
491 | "title": "Speed Box Squat",
492 | "description": "The banded barbell box squat is a strength and power exercise targeting the lower body. It is popular in powerlifting training, either performed with light weights as a power and speed exercise, or heavier for strength. It is usually performed to a box of a height that demands the squatter to decent to around parallel.",
493 | "type": "Powerlifting",
494 | "bodyPart": "Quadriceps",
495 | "equipment": "Bands",
496 | "level": "Intermediate",
497 | "rating": 7.0,
498 | "ratingDesc": ""
499 | },
500 | {
501 | "exerciseNumber": 44,
502 | "title": "Band Pull Apart",
503 | "description": "The band pull-apart is an exercise targeting the upper back muscles, including the trapezius, rear delts, and rhomboids. The band creates dynamic tension, allowing you to really focus on contracting these muscles, making it a great activation drill prior to upper-body lifting.",
504 | "type": "Strength",
505 | "bodyPart": "Shoulders",
506 | "equipment": "Bands",
507 | "level": "Intermediate",
508 | "rating": 8.2,
509 | "ratingDesc": "Average"
510 | },
511 | {
512 | "exerciseNumber": 45,
513 | "title": "Band shoulder press",
514 | "description": "The band shoulder press is a deltoid exercise and an alternative to the classic dumbbell shoulder press. The dynamic tension of the band forces the core to stabilize the body and more closely matches the strength curve of the shoulder press. It can be performed in low reps, such as 5-8 reps per set, to build shoulder strength, or for higher reps to build muscle and for conditioning. It can work as the main focus of a shoulder day but is also popular as an accessory movement to the bench press or barbell military press.",
515 | "type": "Strength",
516 | "bodyPart": "Shoulders",
517 | "equipment": "Bands",
518 | "level": "Intermediate",
519 | "rating": 7.0,
520 | "ratingDesc": ""
521 | },
522 | {
523 | "exerciseNumber": 46,
524 | "title": "Band lateral raise",
525 | "description": "The band lateral raise is a shoulder exercise that targets the medial or middle head of the deltoid muscle. It's a variation of the dumbbell lateral raise, a staple strength-training move. The band lateral raise is a great option for warm-up or accessory work on upper-body training days. The dynamic tension of the band provides constant tension and matches the strength curve of the movement. It is usually performed for moderate to high reps, at least 8-12 reps, as part of the upper-body or shoulder-focused portion of a workout.",
526 | "type": "Strength",
527 | "bodyPart": "Shoulders",
528 | "equipment": "Bands",
529 | "level": "Intermediate",
530 | "rating": 7.0,
531 | "ratingDesc": ""
532 | },
533 | {
534 | "exerciseNumber": 47,
535 | "title": "Band upright row",
536 | "description": "The band upright row is a popular movement for building stronger and bigger traps and shoulders and similar to the dumbbell upright row. Many lifters combine this move with either their back or shoulder workout, since it involves both body parts. Using the band provides dynamic resistance and is easier on the shoulder than using a barbell. It is generally performed for moderate to high reps, such as 8-12 reps per set or more.",
537 | "type": "Strength",
538 | "bodyPart": "Shoulders",
539 | "equipment": "Bands",
540 | "level": "Intermediate",
541 | "rating": 7.0,
542 | "ratingDesc": ""
543 | },
544 |
545 | {
546 | "exerciseNumber": 48,
547 | "title": "Cable V-bar push-down",
548 | "description": "The cable V-bar push-down is a popular gym exercise for targeting the triceps. It utilizes an angled bar, which can allow you to move heavier weights more comfortably than a straight bar or rope. It is usually performed for moderate to high reps, such as 8-12 reps or more per set, as part of an upper-body or arm-focused workout.",
549 | "type": "Strength",
550 | "bodyPart": "Triceps",
551 | "equipment": "Cable",
552 | "level": "Intermediate",
553 | "rating": 9.1,
554 | "ratingDesc": "Average"
555 | },
556 | {
557 | "exerciseNumber": 49,
558 | "title": "Reverse Grip Triceps Pushdown",
559 | "description": "The reverse-grip cable straight-bar push-down is a twist on the popular cable straight-bar push-down. The difference has to do with how the hands are positioned holding the bar: the palms facing up rather than down. Because grip will be a limiting factor, this movement is usually performed for moderate to high reps, such as 8-12 reps per set or higher.",
560 | "type": "Strength",
561 | "bodyPart": "Triceps",
562 | "equipment": "Cable",
563 | "level": "Intermediate",
564 | "rating": 9,
565 | "ratingDesc": "Average"
566 | },
567 | {
568 | "exerciseNumber": 50,
569 | "title": "Kneeling cable triceps extension",
570 | "description": "The kneeling cable triceps extension is a single-joint exercise meant to isolate the triceps muscles using a high pully on a cable stack and a bench. It can also be performed in a tall-kneeling stance from a low pully. It is most commonly used in muscle-building triceps or arm workouts.",
571 | "type": "Strength",
572 | "bodyPart": "Triceps",
573 | "equipment": "Cable",
574 | "level": "Intermediate",
575 | "rating": 9,
576 | "ratingDesc": "Average"
577 | }
578 | ]
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ktor_version=2.3.7
2 | kotlin_version=1.9.22
3 | logback_version=1.4.14
4 | kotlin.code.style=official
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
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 | # https://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 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/images/01-architecture-atlas_application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/01-architecture-atlas_application.png
--------------------------------------------------------------------------------
/images/02-architecture-kotlin_application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/02-architecture-kotlin_application.png
--------------------------------------------------------------------------------
/images/03-creating_exercises_collection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/03-creating_exercises_collection.png
--------------------------------------------------------------------------------
/images/04-creating_trigger_details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/04-creating_trigger_details.png
--------------------------------------------------------------------------------
/images/05-creating_trigger_details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/05-creating_trigger_details.png
--------------------------------------------------------------------------------
/images/06-creating_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/06-creating_function.png
--------------------------------------------------------------------------------
/images/07-app_services.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/07-app_services.png
--------------------------------------------------------------------------------
/images/08-rename_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/08-rename_function.png
--------------------------------------------------------------------------------
/images/09-rename_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/09-rename_function.png
--------------------------------------------------------------------------------
/images/10-huggingFace_token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/10-huggingFace_token.png
--------------------------------------------------------------------------------
/images/11-creating_application_new_value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/11-creating_application_new_value.png
--------------------------------------------------------------------------------
/images/12-creating_application_new_value_secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/12-creating_application_new_value_secret.png
--------------------------------------------------------------------------------
/images/13-creating_application_new_value_Value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/13-creating_application_new_value_Value.png
--------------------------------------------------------------------------------
/images/14-values_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/14-values_list.png
--------------------------------------------------------------------------------
/images/15-exercises_document_sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/15-exercises_document_sample.png
--------------------------------------------------------------------------------
/images/16-mongo_import.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/16-mongo_import.png
--------------------------------------------------------------------------------
/images/17-mongo_import_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/17-mongo_import_success.png
--------------------------------------------------------------------------------
/images/18-app_service_logs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/18-app_service_logs.png
--------------------------------------------------------------------------------
/images/19-collection_exercises_with_embedded_imported.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/19-collection_exercises_with_embedded_imported.png
--------------------------------------------------------------------------------
/images/20-postman_final_demonstration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/20-postman_final_demonstration.png
--------------------------------------------------------------------------------
/images/21-creating_atlas_vector_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/21-creating_atlas_vector_index.png
--------------------------------------------------------------------------------
/images/22-creating_atlas_vector_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/22-creating_atlas_vector_index.png
--------------------------------------------------------------------------------
/images/23-creating_atlas_vector_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/23-creating_atlas_vector_index.png
--------------------------------------------------------------------------------
/images/24-creating_atlas_vector_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/24-creating_atlas_vector_index.png
--------------------------------------------------------------------------------
/images/25-running_application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/25-running_application.png
--------------------------------------------------------------------------------
/images/26-postman_request.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ricardohsmello/kotlin-ktor-mongodb-vector-search/99367f7248224b90e98a010980dd4909fc09c900/images/26-postman_request.png
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "com.mongodb.fitnesstracker"
2 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/Application.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb
2 |
3 | import com.mongodb.application.routes.exercisesRoutes
4 | import com.mongodb.application.routes.fitnessRoutes
5 | import com.mongodb.domain.ports.ExercisesRepository
6 | import com.mongodb.domain.ports.FitnessRepository
7 | import com.mongodb.infrastructure.ExercisesRepositoryImpl
8 | import com.mongodb.infrastructure.FitnessRepositoryImpl
9 | import com.mongodb.kotlin.client.coroutine.MongoClient
10 | import io.ktor.serialization.gson.*
11 | import io.ktor.server.application.*
12 | import io.ktor.server.plugins.contentnegotiation.*
13 | import io.ktor.server.plugins.swagger.*
14 | import io.ktor.server.routing.*
15 | import io.ktor.server.tomcat.*
16 | import org.koin.dsl.module
17 | import org.koin.ktor.plugin.Koin
18 | import org.koin.logger.slf4jLogger
19 |
20 | fun main(args: Array): Unit = EngineMain.main(args)
21 | fun Application.module() {
22 |
23 | install(ContentNegotiation) {
24 | gson {
25 | }
26 | }
27 |
28 | install(Koin) {
29 | slf4jLogger()
30 | modules(module {
31 | single { MongoClient.create(environment.config.propertyOrNull("ktor.mongo.uri")?.getString() ?: throw RuntimeException("Failed to access MongoDB URI.")) }
32 | single { get().getDatabase(environment.config.property("ktor.mongo.database").getString()) }
33 | }, module {
34 | single { FitnessRepositoryImpl(get()) }
35 | single { ExercisesRepositoryImpl(get()) }
36 | })
37 | }
38 |
39 | routing {
40 | swaggerUI(path = "swagger-ui", swaggerFile = "openapi/documentation.yaml") {
41 | version = "4.15.5"
42 | }
43 | fitnessRoutes()
44 | exercisesRoutes()
45 | }
46 | }
47 |
48 | fun ApplicationCall.huggingFaceApiUrl(): String {
49 | return application.environment.config.propertyOrNull("ktor.huggingface.api.url")?.getString()
50 | ?: throw RuntimeException("Failed to access Hugging Face API base URL.")
51 |
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/application/request/FitnessRequest.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.application.request
2 |
3 | import com.mongodb.domain.entity.Fitness
4 | import com.mongodb.domain.entity.FitnessDetails
5 | import org.bson.types.ObjectId
6 |
7 | data class FitnessRequest(
8 | val exerciseType: String,
9 | val notes: String,
10 | val details: FitnessDetails
11 | )
12 | fun FitnessRequest.toDomain(): Fitness {
13 | return Fitness(
14 | id = ObjectId(),
15 | exerciseType = exerciseType,
16 | notes = notes,
17 | details = details
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/application/request/SentenceRequest.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.application.request
2 |
3 | data class SentenceRequest(
4 | val input: String
5 | )
6 | {
7 | fun convertResponse(body: String) =
8 | body
9 | .replace("[", "")
10 | .replace("]", "")
11 | .split(",")
12 | .map { it.trim().toDouble() }
13 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/application/response/ExercisesResponse.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.application.response
2 | data class ExercisesResponse(
3 | val exerciseNumber: Int,
4 | val bodyPart: String,
5 | val type: String,
6 | val description: String,
7 | val title: String
8 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/application/response/FitnessResponse.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.application.response
2 |
3 | import com.mongodb.domain.entity.FitnessDetails
4 |
5 | data class FitnessResponse(
6 | val id: String,
7 | val exerciseType: String,
8 | val notes: String,
9 | val details: FitnessDetails
10 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/application/routes/ExercisesRoutes.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.application.routes
2 |
3 | import com.mongodb.application.request.SentenceRequest
4 | import com.mongodb.domain.ports.ExercisesRepository
5 | import com.mongodb.huggingFaceApiUrl
6 | import io.ktor.client.*
7 | import io.ktor.client.call.*
8 | import io.ktor.client.engine.cio.*
9 | import io.ktor.client.request.*
10 | import io.ktor.client.statement.*
11 | import io.ktor.http.*
12 | import io.ktor.http.content.*
13 | import io.ktor.server.application.*
14 | import io.ktor.server.request.*
15 | import io.ktor.server.response.*
16 | import io.ktor.server.routing.*
17 | import org.koin.ktor.ext.inject
18 |
19 | fun Route.exercisesRoutes() {
20 | val repository by inject()
21 |
22 | route("/exercises/processRequest") {
23 | post {
24 | val sentence = call.receive()
25 |
26 | val response = requestSentenceTransform(sentence.input, call.huggingFaceApiUrl())
27 |
28 | if (response.status.isSuccess()) {
29 | val embedding = sentence.convertResponse(response.body())
30 | val similarDocuments = repository.findSimilarExercises(embedding)
31 |
32 | call.respond(HttpStatusCode.Accepted, similarDocuments.map { it.toResponse() })
33 | }
34 | }
35 | }
36 | }
37 | suspend fun requestSentenceTransform(input: String, huggingFaceURL: String): HttpResponse {
38 | return HttpClient(CIO).use { client ->
39 |
40 | val response = client.post(huggingFaceURL) {
41 | val content = TextContent(input, ContentType.Text.Plain)
42 | setBody(content)
43 | }
44 |
45 | response
46 | }
47 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/application/routes/FitnessRoute.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.application.routes
2 |
3 | import com.mongodb.domain.ports.FitnessRepository
4 | import io.ktor.http.*
5 | import io.ktor.server.application.*
6 | import io.ktor.server.request.*
7 | import io.ktor.server.response.*
8 | import io.ktor.server.routing.*
9 | import org.bson.types.ObjectId
10 | import org.koin.ktor.ext.inject
11 |
12 | fun Route.fitnessRoutes() {
13 |
14 | val repository by inject()
15 |
16 | route("/fitness") {
17 | get {
18 | repository.findAll()?.let { list ->
19 | call.respond(list.map { it.toResponse() })
20 | } ?: call.respondText("No records found")
21 | }
22 |
23 | get("/{id?}") {
24 | val id = call.parameters["id"]
25 | if (id.isNullOrEmpty()) {
26 | return@get call.respondText(
27 | text = "Missing id",
28 | status = HttpStatusCode.BadRequest
29 | )
30 | }
31 |
32 | repository.findById(ObjectId(id))?.let {
33 | call.respond(it.toResponse())
34 | } ?: call.respondText("No records found for id $id")
35 | }
36 |
37 | get("/exerciseType/{type?}") {
38 | val type = call.parameters["type"]
39 | type?.takeIf { it.isNotEmpty() }?.let {
40 | call.respond(repository.findByExerciseType(it).ifEmpty { "No records found for exerciseType $it" })
41 | } ?: call.respond(HttpStatusCode.BadRequest, "Missing exercise type param")
42 | }
43 |
44 | post {
45 | val fitness = call.receive()
46 | println(fitness)
47 | }
48 | // post {
49 | // val fitness = call.receive()
50 | // val insertedId = repository.insertOne(fitness.toDomain())
51 | // call.respond(HttpStatusCode.Created, "Created fitness with id $insertedId")
52 | //
53 | // }
54 |
55 | delete("/{id?}") {
56 | val id = call.parameters["id"] ?: return@delete call.respondText(
57 | text = "Missing fitness id",
58 | status = HttpStatusCode.BadRequest
59 | )
60 |
61 | val delete: Long = repository.deleteById(ObjectId(id))
62 |
63 | if (delete == 1L) {
64 | return@delete call.respondText("Fitness Deleted successfully", status = HttpStatusCode.OK)
65 | }
66 | return@delete call.respondText("Fitness not found", status = HttpStatusCode.NotFound)
67 |
68 | }
69 |
70 | patch("/{id?}") {
71 | val id = call.parameters["id"] ?: return@patch call.respondText(
72 | text = "Missing fitness id",
73 | status = HttpStatusCode.BadRequest
74 | )
75 |
76 | val updated = repository.updateOne(ObjectId(id), call.receive())
77 |
78 | call.respondText(
79 | text = if (updated == 1L) "Fitness updated successfully" else "Fitness not found",
80 | status = if (updated == 1L) HttpStatusCode.OK else HttpStatusCode.NotFound
81 | )
82 | }
83 | }
84 | }
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/domain/entity/Exercises.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.domain.entity
2 |
3 | import com.mongodb.application.response.ExercisesResponse
4 | import org.bson.codecs.pojo.annotations.BsonId
5 | import org.bson.types.ObjectId
6 |
7 | data class Exercises(
8 | @BsonId
9 | val id: ObjectId,
10 | val exerciseNumber: Int,
11 | val title: String,
12 | val description: String,
13 | val type: String,
14 | val bodyPart: String,
15 | val equipment: String,
16 | val level: String,
17 | val rating: Double,
18 | val ratingDesc: String,
19 | val descEmbedding: List
20 | ){
21 | fun toResponse() = ExercisesResponse(
22 | exerciseNumber = exerciseNumber,
23 | title = title,
24 | description = description,
25 | bodyPart = bodyPart,
26 | type = type
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/domain/entity/Fitness.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.domain.entity
2 |
3 | import com.mongodb.application.response.FitnessResponse
4 | import org.bson.codecs.pojo.annotations.BsonId
5 | import org.bson.types.ObjectId
6 |
7 | data class Fitness(
8 | @BsonId
9 | val id: ObjectId,
10 | val exerciseType: String,
11 | val notes: String,
12 | val details: FitnessDetails
13 | ){
14 | fun toResponse() = FitnessResponse(
15 | id = id.toString(),
16 | exerciseType = exerciseType,
17 | notes = notes,
18 | details = details
19 | )
20 | }
21 |
22 | data class FitnessDetails(
23 | val durationMinutes: Int,
24 | val distance: Double,
25 | val caloriesBurned: Int
26 | )
27 |
28 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/domain/ports/ExerciseRepository.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.domain.ports
2 |
3 | import com.mongodb.domain.entity.Exercises
4 | interface ExercisesRepository {
5 | suspend fun findSimilarExercises(embedding: List): List
6 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/domain/ports/FitnessRepository.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.domain.ports
2 |
3 | import com.mongodb.domain.entity.Fitness
4 | import org.bson.BsonValue
5 | import org.bson.types.ObjectId
6 |
7 | interface FitnessRepository {
8 |
9 | suspend fun findAll(): List?
10 |
11 | suspend fun findById(objectId: ObjectId): Fitness?
12 |
13 | suspend fun findByExerciseType(type: String): List
14 |
15 | suspend fun insertOne(fitness: Fitness): BsonValue?
16 |
17 | suspend fun deleteById(objectId: ObjectId): Long
18 |
19 | suspend fun updateOne(objectId: ObjectId, fitness: Fitness): Long
20 |
21 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/infrastructure/ExerciseRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.infrastructure
2 |
3 | import com.mongodb.domain.entity.Exercises
4 | import com.mongodb.domain.ports.ExercisesRepository
5 | import com.mongodb.kotlin.client.coroutine.MongoDatabase
6 | import kotlinx.coroutines.flow.toList
7 | import org.bson.Document
8 |
9 | class ExercisesRepositoryImpl(
10 | private val mongoDatabase: MongoDatabase
11 | ) : ExercisesRepository {
12 |
13 | companion object {
14 | const val EXERCISES_COLLECTION = "exercises"
15 | }
16 |
17 | override suspend fun findSimilarExercises(embedding: List): List {
18 | val result =
19 | mongoDatabase.getCollection(EXERCISES_COLLECTION).withDocumentClass().aggregate(
20 | listOf(
21 | Document(
22 | "\$vectorSearch",
23 | Document("queryVector", embedding)
24 | .append("path", "descEmbedding")
25 | .append("numCandidates", 3L)
26 | .append("index", "vector_index")
27 | .append("limit", 3L)
28 | )
29 | )
30 | )
31 |
32 | return result.toList()
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/mongodb/infrastructure/FitnessRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb.infrastructure
2 |
3 | import com.mongodb.MongoException
4 | import com.mongodb.client.model.Filters
5 | import com.mongodb.client.model.UpdateOptions
6 | import com.mongodb.client.model.Updates
7 | import com.mongodb.domain.entity.Fitness
8 | import com.mongodb.domain.ports.FitnessRepository
9 | import com.mongodb.kotlin.client.coroutine.MongoDatabase
10 | import kotlinx.coroutines.flow.firstOrNull
11 | import kotlinx.coroutines.flow.toList
12 | import org.bson.BsonValue
13 | import org.bson.types.ObjectId
14 |
15 | class FitnessRepositoryImpl(
16 | private val mongoDatabase: MongoDatabase
17 | ) : FitnessRepository {
18 |
19 | companion object {
20 | const val FITNESS_COLLECTION = "fitness"
21 | }
22 |
23 | override suspend fun findAll(): List {
24 | return mongoDatabase.getCollection(FITNESS_COLLECTION).find().toList()
25 | }
26 |
27 | override suspend fun findById(objectId: ObjectId): Fitness? =
28 | mongoDatabase.getCollection(FITNESS_COLLECTION).withDocumentClass()
29 | .find(Filters.eq("_id", objectId))
30 | .firstOrNull()
31 |
32 | override suspend fun findByExerciseType(type: String): List =
33 | mongoDatabase.getCollection(FITNESS_COLLECTION).withDocumentClass()
34 | .find(Filters.eq(Fitness::exerciseType.name, type))
35 | .toList()
36 |
37 | override suspend fun insertOne(fitness: Fitness): BsonValue? {
38 |
39 | try {
40 | val result = mongoDatabase.getCollection(FITNESS_COLLECTION).insertOne(
41 | fitness
42 | )
43 |
44 | return result.insertedId
45 | } catch (e: MongoException) {
46 | System.err.println("Unable to insert due to an error: $e")
47 | }
48 |
49 | return null
50 | }
51 |
52 | override suspend fun deleteById(objectId: ObjectId): Long {
53 |
54 | try {
55 | val result = mongoDatabase.getCollection(FITNESS_COLLECTION).deleteOne(Filters.eq("_id", objectId))
56 | return result.deletedCount
57 | } catch (e: MongoException) {
58 | System.err.println("Unable to delete due to an error: $e")
59 | }
60 |
61 | return 0
62 | }
63 |
64 | override suspend fun updateOne(objectId: ObjectId, fitness: Fitness): Long {
65 | try {
66 | val query = Filters.eq("_id", objectId)
67 | val updates = Updates.combine(
68 | Updates.set(Fitness::exerciseType.name, fitness.exerciseType),
69 | Updates.set(Fitness::notes.name, fitness.notes),
70 | Updates.set(Fitness::details.name, fitness.details)
71 | )
72 |
73 | val options = UpdateOptions().upsert(true)
74 |
75 | val result =
76 | mongoDatabase.getCollection(FITNESS_COLLECTION)
77 | .updateOne(query, updates, options)
78 |
79 | return result.modifiedCount
80 | } catch (e: MongoException) {
81 | System.err.println("Unable to update due to an error: $e")
82 | }
83 |
84 | return 0
85 | }
86 | }
--------------------------------------------------------------------------------
/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 | ktor {
2 | deployment {
3 | port = 8081
4 | }
5 | application {
6 | modules = [ com.mongodb.ApplicationKt.module ]
7 | }
8 | mongo {
9 | uri = ${?MONGO_URI}
10 | database = ${?MONGO_DATABASE}
11 | }
12 | huggingface {
13 | api {
14 | url = "https://api-inference.huggingface.co/pipeline/feature-extraction/sentence-transformers/all-MiniLM-L6-v2"
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/openapi/documentation.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.0
2 | info:
3 | title: Fitness API
4 | version: 1.0.0
5 | description: |
6 | This Swagger documentation file outlines the API specifications for a Fitness Tracker application built with Ktor and MongoDB. The API allows users to manage fitness records, including retrieving all records, creating new records, updating and deleting records by ID, and retrieving records by exercise type. The API uses the Fitness and FitnessDetails data classes to structure the fitness-related information.
7 |
8 | - [The Fitness Tracker GitHub repository](https://github.com/ricardohsmello/fitness-tracker)
9 | paths:
10 | /fitness:
11 | get:
12 | summary: Retrieve all fitness records
13 | responses:
14 | '200':
15 | description: Successful response
16 | content:
17 | application/json:
18 | example: []
19 | post:
20 | summary: Create a new fitness record
21 | requestBody:
22 | required: true
23 | content:
24 | application/json:
25 | schema:
26 | $ref: '#/components/schemas/FitnessRequest'
27 | responses:
28 | '201':
29 | description: Fitness created successfully
30 | '400':
31 | description: Bad request
32 | /fitness/{id}:
33 | get:
34 | summary: Retrieve fitness record by ID
35 | parameters:
36 | - name: id
37 | in: path
38 | required: true
39 | schema:
40 | type: string
41 | responses:
42 | '200':
43 | description: Successful response
44 | content:
45 | application/json:
46 | example: {}
47 | '404':
48 | description: Fitness not found
49 | delete:
50 | summary: Delete fitness record by ID
51 | parameters:
52 | - name: id
53 | in: path
54 | required: true
55 | schema:
56 | type: string
57 | responses:
58 | '200':
59 | description: Fitness deleted successfully
60 | '400':
61 | description: Bad request
62 | '404':
63 | description: Fitness not found
64 | patch:
65 | summary: Update fitness record by ID
66 | parameters:
67 | - name: id
68 | in: path
69 | required: true
70 | schema:
71 | type: string
72 | requestBody:
73 | required: true
74 | content:
75 | application/json:
76 | schema:
77 | $ref: '#/components/schemas/FitnessRequest'
78 | responses:
79 | '200':
80 | description: Fitness updated successfully
81 | '400':
82 | description: Bad request
83 | '404':
84 | description: Fitness not found
85 | /fitness/exerciseType/{type}:
86 | get:
87 | summary: Retrieve fitness records by exercise type
88 | parameters:
89 | - name: type
90 | in: path
91 | required: true
92 | schema:
93 | type: string
94 | responses:
95 | '200':
96 | description: Successful response
97 | content:
98 | application/json:
99 | example: []
100 | '400':
101 | description: Bad request
102 | components:
103 | schemas:
104 | Fitness:
105 | type: object
106 | properties:
107 | id:
108 | type: string
109 | format: uuid
110 | exerciseType:
111 | type: string
112 | startTime:
113 | type: string
114 | format: date-time
115 | endTime:
116 | type: string
117 | format: date-time
118 | notes:
119 | type: string
120 | details:
121 | $ref: '#/components/schemas/FitnessDetails'
122 | required:
123 | - id
124 | - exerciseType
125 | - startTime
126 | - endTime
127 | - notes
128 | - details
129 |
130 | FitnessDetails:
131 | type: object
132 | properties:
133 | durationMinutes:
134 | type: integer
135 | format: int32
136 | distance:
137 | type: number
138 | format: double
139 | caloriesBurned:
140 | type: integer
141 | format: int32
142 | required:
143 | - durationMinutes
144 | - distance
145 | - caloriesBurned
146 |
147 | FitnessRequest:
148 | type: object
149 | properties:
150 | exerciseType:
151 | type: string
152 | startTime:
153 | type: string
154 | format: date-time
155 | endTime:
156 | type: string
157 | format: date-time
158 | notes:
159 | type: string
160 | details:
161 | $ref: '#/components/schemas/FitnessDetails'
162 | required:
163 | - exerciseType
164 | - startTime
165 | - endTime
166 | - notes
167 | - details
168 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/mongodb/ApplicationTest.kt:
--------------------------------------------------------------------------------
1 | package com.mongodb
2 |
3 | class ApplicationTest {
4 | // @Test
5 | // fun testRoot() = testApplication {
6 | // application {
7 | // configureRouting()
8 | // }
9 | // client.get("/").apply {
10 | // assertEquals(HttpStatusCode.OK, status)
11 | // assertEquals("Hello World!", bodyAsText())
12 | // }
13 | // }
14 | }
15 |
--------------------------------------------------------------------------------