🦚 Fix #8: Provide an endpoint that uses an aggregation pipeline (#11)

Co-authored-by: alexis.drai@etu.uca.fr <alexis.drai@etu.uca.fr>
Reviewed-on: #11
main
Alexis Drai 2 years ago
parent 4147d840ea
commit c817ccd5a7

@ -10,6 +10,7 @@
- [🌺Special requests](#special-requests)
- [`Pokemong` by nickname](#pokemong-by-nickname)
- [`Pokemong` in date interval](#pokemong-in-date-interval)
- [🦚Aggregation pipeline](#aggregation-pipeline)
- [Prep steps](#prep-steps)
- [Java version](#java-version)
- [🔐Database connection](#database-connection)
@ -309,9 +310,33 @@ where `{nickname}` is a partial, case-insensitive search term.
#### `Pokemong` in date interval
Users can also use the route `pokemong/dob/{startDate}/{endDate}` to search for
Users can also use the route `pokemong/dob/{start-date}/{end-date}` to search for
`pokemongs` who where born within that interval (bounds included).
### 🦚Aggregation pipeline
Finally, the endpoint `pokemong/count-by-evo-stage` is provided, to get a mapping of evolution stages with
the number of `pokemongs`who achieved that evolution stage.
As an example of a potential output:
```json
[
{
"count": 15,
"evoStage": 0
},
{
"count": 4,
"evoStage": 1
},
{
"count": 5,
"evoStage": 2
}
]
```
## Prep steps
### ♨Java version
@ -376,11 +401,13 @@ You can run the application in dev mode using:
<details><summary>🏫 If you are the corrector</summary>
Please navigate to the root of this project in a terminal and run the provided `load_data.sh` script.
The database should already be populated with the sample dataset.
However, if you want to reload that dataset, please navigate to the root of this project in a terminal and run
the provided `load_data.sh` script.
If the script wasn't provided, that was a mistake. Sorry. Please request them to the owner of this repo, or follow the
alternate
procedure below.
alternate procedure below.
</details>

@ -38,3 +38,59 @@
"effectiveAgainst": ["GRASS"]
}
}
{
"_id": { "$oid":"60a64f7eae945a6e60b0e919" },
"schemaVersion": 2,
"name": "Twineedle",
"power": 25,
"pp": 20,
"category": "PHYSICAL",
"accuracy": 100,
"type": {
"name": "BUG",
"weakAgainst": ["FIRE", "FLYING"],
"effectiveAgainst": ["GRASS", "PSYCHIC"]
}
}
{
"_id": { "$oid":"60a64f7eae945a6e60b0e91a" },
"schemaVersion": 2,
"name": "Rage",
"power": 20,
"pp": 20,
"category": "PHYSICAL",
"accuracy": 100,
"type": {
"name": "NORMAL",
"weakAgainst": ["ROCK"],
"effectiveAgainst": []
}
}
{
"_id": { "$oid":"60a64f7eae945a6e60b0e91c" },
"schemaVersion": 2,
"name": "Gust",
"power": 40,
"pp": 35,
"category": "SPECIAL",
"accuracy": 100,
"type": {
"name": "FLYING",
"weakAgainst": ["ROCK", "ELECTRIC", "ICE"],
"effectiveAgainst": ["GRASS", "FIGHTING", "BUG"]
}
}
{
"_id": { "$oid":"60a64f7eae945a6e60b0e91d" },
"schemaVersion": 2,
"name": "Confusion",
"power": 50,
"pp": 25,
"category": "SPECIAL",
"accuracy": 100,
"type": {
"name": "PSYCHIC",
"weakAgainst": ["BUG", "GHOST", "DARK"],
"effectiveAgainst": ["FIGHTING", "POISON"]
}
}

@ -96,3 +96,97 @@
}
]
}
{
"_id": {
"$oid": "60a64f7eae945a6e60b0e918"
},
"schemaVersion": 1,
"nickname": "Buzzy",
"dob": {
"$date": {
"$numberLong": "761597651000"
}
},
"level": 8,
"pokedexId": 15,
"evoStage": 2,
"evoTrack": [
"WEEDLE",
"KAKUNA",
"BEEDRILL"
],
"types": [
{
"name": "BUG",
"weakAgainst": [
"FIRE",
"FLYING"
],
"effectiveAgainst": [
"GRASS",
"PSYCHIC"
]
}
],
"moveSet": [
{
"_id": {
"$oid": "60a64f7eae945a6e60b0e919"
},
"name": "Twineedle"
},
{
"_id": {
"$oid": "60a64f7eae945a6e60b0e91a"
},
"name": "Rage"
}
]
}
{
"_id": {
"$oid": "60a64f7eae945a6e60b0e91b"
},
"schemaVersion": 1,
"nickname": "Flutter",
"dob": {
"$date": {
"$numberLong": "861597551000"
}
},
"level": 12,
"pokedexId": 12,
"evoStage": 2,
"evoTrack": [
"CATERPIE",
"METAPOD",
"BUTTERFREE"
],
"types": [
{
"name": "BUG",
"weakAgainst": [
"FIRE",
"FLYING"
],
"effectiveAgainst": [
"GRASS",
"PSYCHIC"
]
}
],
"moveSet": [
{
"_id": {
"$oid": "60a64f7eae945a6e60b0e91c"
},
"name": "Gust"
},
{
"_id": {
"$oid": "60a64f7eae945a6e60b0e91d"
},
"name": "Confusion"
}
]
}

@ -39,10 +39,10 @@ public class PokemongController extends GenericController<Pokemong> {
}
@GET
@Path("/dob/{startDate}/{endDate}")
@Path("/dob/{start-date}/{end-date}")
public Response findByDateOfBirthInterval(
@PathParam("startDate") String startDate,
@PathParam("endDate") String endDate
@PathParam("start-date") String startDate,
@PathParam("end-date") String endDate
) {
if (StringUtils.isBlankStringOrNull(startDate) || StringUtils.isBlankStringOrNull(endDate)) {
return Response.ok(new ArrayList<>())
@ -59,4 +59,10 @@ public class PokemongController extends GenericController<Pokemong> {
}
}
@GET
@Path("/count-by-evo-stage")
public Response countPokemongsByEvoStage() {
return Response.ok(pokemongService.countPokemongsByEvoStage())
.build();
}
}

@ -3,17 +3,23 @@ package fr.uca.iut.repositories;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Accumulators;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import fr.uca.iut.entities.Pokemong;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.jetbrains.annotations.NotNull;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@ -40,10 +46,15 @@ public class PokemongRepository extends GenericRepository<Pokemong> {
@Override
protected MongoCollection<Pokemong> getCollection() {
MongoDatabase db = mongoClient.getDatabase(DB_NAME);
MongoDatabase db = getMongoDatabase();
return db.getCollection(Pokemong.COLLECTION_NAME, Pokemong.class);
}
@NotNull
private MongoDatabase getMongoDatabase() {
return mongoClient.getDatabase(DB_NAME);
}
/**
* Fetches the list of Pokemong entities that have a nickname matching the provided nickname.
* The match is case-insensitive and ignores leading and trailing spaces.
@ -77,4 +88,28 @@ public class PokemongRepository extends GenericRepository<Pokemong> {
.into(new ArrayList<>());
}
/**
* Counts the number of Pokemongs grouped by their evolution stage.
*
* @return A list of MongoDB Documents, each containing the evolution stage
* ('evoStage') and the count of Pokemongs at that stage.
*/
public List<Document> countPokemongsByEvoStage() {
List<Bson> pipeline = Arrays.asList(
Aggregates.group("$evoStage", Accumulators.sum("count", 1)),
// "evoStage" becomes "_id" here
Aggregates.project(Projections.fields(
Projections.excludeId(),
// we discard that "_id" field
Projections.computed("evoStage", "$_id"),
// but we add it back in after renaming it "evoStage"
Projections.include("count")
// and of course we still want to keep the count
))
);
MongoCollection<Document> collection = getMongoDatabase().getCollection(getCollection().getNamespace().getCollectionName());
return collection.aggregate(pipeline, Document.class).into(new ArrayList<>());
}
}

@ -13,6 +13,7 @@ import fr.uca.iut.utils.exceptions.NonValidEntityException;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.bson.Document;
import org.jetbrains.annotations.NotNull;
import java.time.LocalDate;
@ -208,10 +209,6 @@ public class PokemongService extends GenericService<Pokemong> {
return repository.existsById(pokemongId);
}
public List<Pokemong> findByNickname(String nickname) {
return pokemongRepository.findByNickname(nickname);
}
public void batchUpdatePokemongTrainers(@NotNull Set<TrainerPokemong> trainerPokemongs,
@Nullable String trainerId) {
List<Pokemong> pokemongsToUpdate = new ArrayList<>();
@ -225,8 +222,17 @@ public class PokemongService extends GenericService<Pokemong> {
updateAll(pokemongsToUpdate);
}
public List<Pokemong> findByNickname(String nickname) {
return pokemongRepository.findByNickname(nickname);
}
public List<Pokemong> findByDateOfBirthInterval(LocalDate startDate, LocalDate endDate) {
return pokemongRepository.findByDateOfBirthInterval(startDate, endDate);
}
public List<Document> countPokemongsByEvoStage() {
return pokemongRepository.countPokemongsByEvoStage();
}
}

@ -94,19 +94,19 @@ paths:
items:
$ref: '#/components/schemas/Pokemong'
/pokemong/dob/{startDate}/{endDate}:
/pokemong/dob/{start-date}/{end-date}:
get:
summary: Get Pokemongs born within a certain date interval
parameters:
- name: startDate
- name: start-date
in: path
required: true
description: The start of the date interval (inclusive)
schema:
type: string
format: date
- name: endDate
- name: end-date
in: path
required: true
description: The end of the date interval (inclusive)
@ -125,6 +125,20 @@ paths:
'400':
description: Invalid date format
/pokemong/count-by-evo-stage:
get:
summary: Get a mapping of each distinct evolution stage to the count of Pokemongs at that stage
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
additionalProperties:
type: integer
/pokemong:
get:

Loading…
Cancel
Save