Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Produtoschevron-right
MongoDBchevron-right

Lidar com dados de série temporal com o MongoDB

Tim Kelly13 min read • Published Nov 19, 2024 • Updated Nov 19, 2024
MongoDBTime SeriesJava
APLICATIVO COMPLETO
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Embora os bancos de dados relacionais sejam o padrão comum para muitas tarefas, o MongoDB está exclusivamente preparado para lidar com aplicativos de séries temporais, desde como os dados são armazenados e acessados até como se espera que seu banco de dados de dados seja dimensionado. Neste tutorial, criaremos um aplicação de rastreamento de Navios para gerenciar uma Frota de botes que atravessam o Atlântica. Visualize Amazon o c Todos esses cenários geram quantidades enormes de dados de séries temporais à medida que a localização de cada veículo é continuamente rastreada e enviada para bancos de dados. O aplicação completo está disponível em Github Github. Então, vamos trabalhar e ver por que o MongoDB é o melhor banco de dados de dados para lidar com dados de séries temporais.
Como seleciono um banco de banco de dados de séries temporais?
Alta taxa de transferência e desempenho de gravação
Grandes clientes, como os do setor financeiro, IoT ou comunicações, gerenciam volumes massivos de dados de séries temporais. O alto desempenho de gravação é essencial para lidar com o influxo contínuo de dados de forma eficiente, sem gargalos ou perda de dados.
As esferas típicas nas quais utilizamos dados de séries temporais são de financiamento, IoT e comunicações. Todos esses são domínios que criam um influxo massivo e contínuo de dados que precisam ser manipulados sem gargalos ou perda de dados. O MongoDB tem uma coleção de séries temporais nativa especificamente otimizada para alta taxa de transferência de gravação.
As coleções de séries temporais utilizam um formato de armazenamento em colunas subjacente e armazenam dados em ordem de tempo. Esse formato fornece maior eficiência de query, uso de disco reduzido, E/S reduzida para operações de leitura e aumento do uso de cache do WiredTiger.
Agora vamos colocá-los uns contra os outros: MongoDB versus bancos de dados relacionais! Fazer tudo isso com bancos de dados relacionais é possível, mas geralmente precisa de ajustes, como particionamento de tabelas e uso de extensões, ou um banco de banco de dados de séries temporais dedicado, para corresponder ao desempenho de escrita do MongoDB. Isso requer mais configuração e manutenção contínua.
Dimensionamento e fragmentação
Setores como financeiros, de logstica e de serviços em nuvem enfrentam demanda cada vez maior por escalabilidade do banco de dados de dados. À medida que os volumes de dados se expandem, esses clientes precisam escalar seus bancos de dados horizontalmente para manter o desempenho e atender às solicitações dos usuários com eficiência.
No MongoDB, o dimensionamento horizontal é simplificado por meio do compartilhamento nativo . A fragmentação permite a distribuição de dados em vários servidores ou nós, de modo que nenhum servidor se torne um gargalo. Isso oferece alta disponibilidade, maior taxa de transferência de leitura e escrita e capacidade de armazenamento praticamente ilimitada. Sempre podemos adicionar outro computador!
A fragmentação é essencial para manter o alto desempenho à medida que os dados são dimensionados. Por outro lado, um banco de banco de dados popular como o PostgreSQL depende do dimensionamento vertical por padrão e pode exigir soluções externas complexas como o Citus para dimensionamento horizontal. Essas soluções aumentam o tempo de configuração e a complexidade operacional, tornando a abordagem integrada do MongoDB mais direta e tentadora para clientes com requisitos de grande escala.
Pré-requisitos
Para acompanhar este tutorial, você deve ter o seguinte:
Crie um aplicação Maven Go e acesse o POM. Aqui adicionaremos nossas dependências.
1 <dependencies>
2 <dependency>
3 <groupId>org.mongodb</groupId>
4 <artifactId>mongodb-driver-sync</artifactId>
5 <version>5.2.0</version>
6 </dependency>
7 <dependency>
8 <groupId>io.javalin</groupId>
9 <artifactId>javalin</artifactId>
10 <version>6.3.0</version>
11 </dependency>
12 <dependency>
13 <groupId>com.fasterxml.jackson.core</groupId>
14 <artifactId>jackson-databind</artifactId>
15 <version>2.17.2</version>
16 </dependency>
17 </dependencies>
Estamos usando o MongoDB Java Driver para acessar nossa interação com nosso banco de dados de dados . Em seguida, temos o Javalin para criar nossa API. Por último, estamos usando o jackson databind para, sim, vincular nossos dados.
Conectando ao MongoDB
Crie uma classe MongodbConfig.java em um pacote de configuração para se conectar ao MongoDB e garantir que o banco de dados de dados e a coleção existam. Precisamos inicializar nosso banco de dados de dados e criar nossa coleção de séries temporais.
1package com.mongodb.shiptracker.config;
2
3import com.mongodb.ConnectionString;
4import com.mongodb.MongoClientSettings;
5import com.mongodb.client.MongoClient;
6import com.mongodb.client.MongoClients;
7import com.mongodb.client.MongoDatabase;
8import com.mongodb.ServerApi;
9import com.mongodb.ServerApiVersion;
10import com.mongodb.client.MongoIterable;
11import com.mongodb.client.model.CreateCollectionOptions;
12import com.mongodb.client.model.TimeSeriesGranularity;
13import com.mongodb.client.model.TimeSeriesOptions;
14
15public class MongodbConfig {
16 private static final String CONNECTION_STRING = "YOUR-CONNECTION-STRING";
17 private static MongoDatabase database;
18
19 public static MongoDatabase getDatabase() {
20 if (database == null) {
21 database = initializeDatabase();
22 }
23 createTimeSeriesCollectionIfNotExists(database, "shipLocations");
24
25 return database;
26 }
27
28 private static MongoDatabase initializeDatabase() {
29 ServerApi serverApi = ServerApi.builder()
30 .version(ServerApiVersion.V1)
31 .build();
32 MongoClientSettings settings = MongoClientSettings.builder()
33 .applyConnectionString(new ConnectionString(CONNECTION_STRING))
34 .serverApi(serverApi)
35 .build();
36
37 MongoClient mongoClient = MongoClients.create(settings);
38 MongoDatabase db = mongoClient.getDatabase("ShipTracker");
39
40 return db;
41 }
42
43 private static void createTimeSeriesCollectionIfNotExists(MongoDatabase db, String collectionName) {
44
45 MongoIterable<String> collections = db.listCollectionNames();
46 for (String name : collections) {
47 if (name.equals(collectionName)) {
48 System.out.println("Time series collection '" + collectionName + "' already exists.");
49 return;
50 }
51 }
52
53 TimeSeriesOptions timeSeriesOptions = new TimeSeriesOptions("timestamp")
54 .metaField("boatId")
55 .granularity(TimeSeriesGranularity.SECONDS);
56
57 CreateCollectionOptions collOptions = new CreateCollectionOptions().timeSeriesOptions(timeSeriesOptions);
58
59 db.createCollection(collectionName, collOptions);
60 System.out.println("Time series collection '" + collectionName + "' created.");
61 }
62}
Adicione sua própria string de conexão para seu banco de dados de dados , que você pode obter na UI do Atlas.
Para nossa coleção de séries temporais, especificamos um timeField como um campo para conter nossos dados de tempo — timestamp, em nosso caso. Este deve ser um tipo de data.
Em seguida, adicionamos nosso metaField. Documentos de séries temporais podem conter metadados sobre cada documento. O MongoDB usa o metaField para agrupar conjuntos de documentos, tanto para otimização de armazenamento interno quanto para eficiência de query. Confira nossos documentos Docs para saber mais sobre o metaField em metaField Considerations.
Por último, temos nossa granularidade. Quando criamos uma coleção de séries temporais, o MongoDB cria automaticamente uma collection system.buckets e agrupa dados de séries temporais de entrada em buckets. Ao definir a granularidade, controlamos a frequência com que os dados são agrupados com base na taxa de ingestão de seus dados.
Você pode usar os parâmetros de bucketing personalizados bucketMaxSpanSeconds e bucketRoundingSeconds para especificar os limites do bucket e controlar com mais precisão como os dados de séries temporais são buckets.
Vamos usar segundos, pois essa é apenas uma simulação de nossos botes navegando sobre aquele grande mar azul, e seria mais do que entediante esperar por nossas atualizações de hora em hora.
Modelos para os nossos dados
Um modelo simples de porta (e localização)
Agora, precisamos de alguns modelos para interagir com nosso aplicação. Crie um modelo básico para as portas para representar suas localizações geográficas. Criar uma classe Port e Location em um pacote de modelo.
1package com.mongodb.shiptracker.model;
2
3import org.bson.Document;
4
5import java.util.Arrays;
6
7public class Port {
8 private String name;
9 private double latitude;
10 private double longitude;
11
12 public Port(String name, double latitude, double longitude) {
13 this.name = name;
14 this.latitude = latitude;
15 this.longitude = longitude;
16 }
17
18 public Document toDocument() {
19 return new Document("name", name)
20 .append("location", new Document("type", "Point")
21 .append("coordinates", Arrays.asList(longitude, latitude))); // GeoJSON format: [longitude, latitude]
22 }
23
24 // add your getters and setters
25}
1package com.mongodb.shiptracker.model;
2
3public class Location {
4 private double latitude;
5 private double longitude;
6
7 public Location(double latitude, double longitude) {
8 this.latitude = latitude;
9 this.longitude = longitude;
10 }
11
12 // add your getters and setters
13}
Estamos adicionando um método toDocument na parte inferior para que possamos armazenar nossas portas como GeoJSON dados GeoJSON . Isso nos permitirá aproveitar as queries geoespaciais do MongoDB.
Vamos precisar de um bote (qualquer tamanho serve)
Para este exemplo, rastrearemos botes ao longo do mar aberto. Para fazer isso, primeiro precisaremos de um Boat modelo.
1package com.mongodb.shiptracker.model;
2
3import org.bson.Document;
4import org.bson.types.ObjectId;
5
6public class Boat {
7 private ObjectId _id;
8 private String boatId;
9 private Location location;
10 private Location destination;
11 private String startPort;
12 private String endPort;
13
14 public Boat () {}
15
16 public Boat(String boatId, Location location, Location destination) {
17 this._id = new ObjectId();
18 this.boatId = boatId;
19 this.location = location;
20 this.destination = destination;
21 }
22
23 public String getStartPort() {
24 return startPort;
25 }
26
27 public void setStartPort(String startPort) {
28 this.startPort = startPort;
29 }
30
31 public String getEndPort() {
32 return endPort;
33 }
34
35 public void setEndPort(String endPort) {
36 this.endPort = endPort;
37 }
38
39 public Boat(String boatId, String startPort, String endPort) {
40 this._id = new ObjectId();
41 this.boatId = boatId;
42 this.startPort = startPort;
43 this.endPort = endPort;
44 }
45
46 // add your getters and setters
47
48 public Document toDocument() {
49 return new Document("_id", _id)
50 .append("boatId", boatId)
51 .append("startLocation", new Document("latitude", location.getLatitude())
52 .append("longitude", location.getLongitude()))
53 .append("destination", new Document("latitude", destination.getLatitude())
54 .append("longitude", destination.getLongitude()));
55 }
56}
Assim como antes, estamos adicionando um toDocument método, mas isso não é tudo. Precisamos dos nossos botes para atravessar esse profundo mar azul. Adicione uma mudança de método para a parte inferior do seu modelo de bote.
1 // Simulate movement by moving the boat incrementally towards its destination
2 public void move() {
3 double currentLatitude = location.getLatitude();
4 double currentLongitude = location.getLongitude();
5 double destLatitude = destination.getLatitude();
6 double destLongitude = destination.getLongitude();
7
8 double nauticalMilesPerHour = 20;
9 double stepSizeInDegrees = nauticalMilesPerHour / 60.0; // Convert nautical miles to degrees
10
11 // Calculate direction double latDirection = destLatitude - currentLatitude;
12 double lonDirection = destLongitude - currentLongitude;
13 double distance = Math.sqrt(latDirection * latDirection + lonDirection * lonDirection);
14
15 if (distance > stepSizeInDegrees) {
16 // Normalize direction
17 latDirection /= distance;
18 lonDirection /= distance;
19
20 // Update current location based on step size
21 double newLatitude = currentLatitude + latDirection * stepSizeInDegrees;
22 double newLongitude = currentLongitude + lonDirection * stepSizeInDegrees;
23
24 location.setLatitude(newLatitude);
25 location.setLongitude(newLongitude);
26 } else {
27 // If the boat is close enough, set it directly to the destination
28 location.setLatitude(destLatitude);
29 location.setLongitude(destLongitude);
30 }
31 }
Agora, como solicitamos um bote? Para facilitar a interface com nossa API e o mapeamento de nossas entradas JSON, teremos uma solicitação de bote.
1package com.mongodb.shiptracker.model;
2
3public class BoatRequest {
4 private String boatId;
5 private String startPort;
6 private String endPort;
7
8 public BoatRequest() {
9 }
10
11 // getters and setters
12}
Isso nos permitirá fornecer um ID de bote e um porta de origem e destino, a partir da lista de portas no banco de banco de dados.
Repositórios e implementações
Ops , a tarefa de interface com um banco de dados de dados . Ele residirá em nosso pacote de repositório , em vários repositórios e implementações. Vamos começar com o repositório de portas.
Repositório de portas
Crie um PortRepository.
1package com.mongodb.shiptracker.repository;
2
3import com.mongodb.shiptracker.model.Port;
4
5public interface PortRepository {
6 void addPort(Port port);
7 Port getPortByName(String name);
8}
Aqui só precisamos adicionar uma porta e obter as coordenadas de uma porta, dado seu nome. Agora, para PortRepositoryImpl:
1package com.mongodb.shiptracker.repository;
2
3import com.mongodb.client.MongoCollection;
4import com.mongodb.client.MongoDatabase;
5import com.mongodb.shiptracker.config.MongodbConfig;
6import com.mongodb.shiptracker.model.Port;
7import org.bson.Document;
8
9import java.util.List;
10
11public class PortRepositoryImpl implements PortRepository {
12 private final MongoDatabase database;
13
14 public PortRepositoryImpl() {
15 this.database = MongodbConfig.getDatabase();
16 }
17
18 @Override
19 public void addPort(Port port) {
20 MongoCollection<Document> collection = database.getCollection("ports");
21 collection.insertOne(port.toDocument());
22 }
23
24 @Override
25 public Port getPortByName(String name) {
26 MongoCollection<Document> collection = database.getCollection("ports");
27 Document query = new Document("name", name);
28 Document result = collection.find(query).first();
29
30 if (result != null) {
31 List<Double> coordinates = result.get("location", Document.class).getList("coordinates", Double.class);
32 return new Port(
33 result.getString("name"),
34 coordinates.get(1), // Latitude
35 coordinates.get(0) // Longitude
36 );
37 }
38 return null;
39 }
40}
Um tratamento de erros muito mínima, mas suficiente para nos colocar em funcionamento.
Repositório de botes
Agora, criaremos nosso ainda mais simples BoatRepository:
1package com.mongodb.shiptracker.repository;
2
3import com.mongodb.shiptracker.model.Boat;
4
5public interface BoatRepository {
6 void addBoat(Boat boat);
7}
E implementaremos nosso método isolado:
1package com.mongodb.shiptracker.repository;
2
3import com.mongodb.client.MongoCollection;
4import com.mongodb.client.MongoDatabase;
5import com.mongodb.shiptracker.config.MongodbConfig;
6import com.mongodb.shiptracker.model.Boat;
7import org.bson.Document;
8
9
10public class BoatRepositoryImpl implements BoatRepository {
11 private final MongoDatabase database;
12
13 public BoatRepositoryImpl() {
14 this.database = MongodbConfig.getDatabase();
15 }
16
17 @Override
18 public void addBoat(Boat boat) {
19 MongoCollection<Document> collection = database.getCollection("boats");
20 collection.insertOne(boat.toDocument());
21 }
22}
tão simples. Tudo o que estamos fazendo aqui é adicionar um método para adicionar um bote ao nosso banco de dados de dados.
Repositório do TimeSeries
Agora, vamos nos entreter mais um pouco aqui. Vamos criar um TimeSeriesRepository e adicionar um método para aproveitar a capacidade do MongoDB de lidar com queries geoespaciais em uma coleção de séries temporais.
1package com.mongodb.shiptracker.repository;
2
3import com.mongodb.shiptracker.model.Location;
4import org.bson.Document;
5
6import java.util.List;
7
8public interface TimeSeriesRepository {
9 void logBoatLocation(String boatId, Location location);
10 List<Document> calculateTotalDistanceTraveled();
11}
Vamos ter um método para calcular a distância total percorrida por todos os botes que navegam atualmente em nosso aplicação.
1package com.mongodb.shiptracker.repository;
2
3import com.mongodb.client.AggregateIterable;
4import com.mongodb.client.MongoCollection;
5import com.mongodb.client.MongoDatabase;
6import com.mongodb.client.model.*;
7import com.mongodb.shiptracker.config.MongodbConfig;
8import com.mongodb.shiptracker.model.Location;
9import org.bson.Document;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Date;
14import java.util.List;
15
16
17public class TimeSeriesRepositoryImpl implements TimeSeriesRepository {
18
19 private final MongoDatabase database;
20
21 public TimeSeriesRepositoryImpl() {
22 this.database = MongodbConfig.getDatabase();
23 }
24
25 @Override
26 public void logBoatLocation(String boatId, Location location) {
27 MongoCollection<Document> collection = database.getCollection("shipLocations");
28 Document geoJsonLocation = new Document("type", "Point")
29 .append("coordinates", Arrays.asList(location.getLongitude(), location.getLatitude()));
30
31 Document logEntry = new Document("boatId", boatId)
32 .append("timestamp", new Date())
33 .append("location", geoJsonLocation);
34
35 collection.insertOne(logEntry);
36 }
37
38 @Override
39 public List<Document> calculateTotalDistanceTraveled() {
40 MongoCollection<Document> collection = database.getCollection("shipLocations");
41
42 // Step 1: Set window fields to shift coordinates for calculating the previous position
43 Document setWindowFieldsStage = new Document("$setWindowFields",
44 new Document("partitionBy", "$boatId")
45 .append("sortBy", new Document("timestamp", 1L))
46 .append("output",
47 new Document("previousCoordinates",
48 new Document("$shift",
49 new Document("output", "$location.coordinates")
50 .append("by", -1L)
51 )
52 )
53 )
54 );
55
56 // Step 2: Calculate the distance between current and previous coordinates
57 Document setDistanceStage = new Document("$set",
58 new Document("distance",
59 new Document("$sqrt",
60 new Document("$add", Arrays.asList(
61 new Document("$pow", Arrays.asList(
62 new Document("$subtract", Arrays.asList(
63 new Document("$arrayElemAt", Arrays.asList("$location.coordinates", 1L)),
64 new Document("$arrayElemAt", Arrays.asList("$previousCoordinates", 1L))
65 )),
66 2L
67 )),
68 new Document("$pow", Arrays.asList(
69 new Document("$subtract", Arrays.asList(
70 new Document("$arrayElemAt", Arrays.asList("$location.coordinates", 0L)),
71 new Document("$arrayElemAt", Arrays.asList("$previousCoordinates", 0L))
72 )),
73 2L
74 ))
75 ))
76 )
77 )
78 );
79
80 // Step 3: Group by boatId and calculate the total distance
81 Document groupTotalDistanceStage = new Document("$group",
82 new Document("_id", "$boatId")
83 .append("totalDistance", new Document("$sum", "$distance"))
84 );
85
86 // Perform the aggregation and collect results into a list
87 AggregateIterable<Document> result = collection.aggregate(Arrays.asList(setWindowFieldsStage, setDistanceStage, groupTotalDistanceStage));
88 List<Document> resultList = new ArrayList<>();
89 result.forEach(resultList::add);
90
91 return resultList;
92 }
93
94}
Embora essa agregação possa não estar perfeitamente otimizada, ela mostra alguns recursos interessantes. O MongoDB é um banco de banco de dados de séries temporais , quando implementado com uma coleção de séries temporais. Ele fornece agregação pipeline stages especificamente para analisar dados de séries temporais, como $setWindowFields, que podemos usar para aplicar um ou mais operadores e um período de tempo específico em nossos dados.
A primeira etapa descobre onde cada bote estava pouco antes do registro atual:
ele agrupa os dados por ID do bote e, em seguida, classifica os dados por tempo, para que as posições mais recentes estejam em ordem. Para cada registro, é adicionado um novo campo chamado previousCoordinates, que contém a posição gps do registro de data/hora anterior.
É como dizer: "Para cada posição, qual foi a última posição do bote?"
Agora que cada registro conhece as posições atual e anterior, ele calcula a distância que o bote percorreu entre esses dois pontos:
A fórmula que ele usa é o teorema de Pitágoras. É mais ou menos assim:
  1. Encontre a diferença entre a latitude atual e anterior.
  2. Encontre a diferença entre a longitude atual e a anterior.
  3. Eleve as duas diferenças e adicione-as.
  4. Pegue a raiz quadrada da soma para obter a distância.
Isso adiciona um novo campo a cada registro chamado distance, que é a distância que o bote se moveu entre as duas posições.
Por fim, calcula a distância total que cada bote percorreu: agrupa todos os registros de cada bote. Por último, soma todos os valores de distância de cada bote para obter a distância total percorrida.
Leia mais sobre o que está disponível com dados de séries temporais e veja o que mais é possível com os estágios de agregação .
Serviços (a lógica de negócios)
Agora que temos os repositórios criados e implementados, é hora de começar a trabalhar ( lógica ).
Serviço de bote
Vamos começar com o serviço de bote. Crie um pacote de serviço e adicionaremos nosso BoatService lá.
1package com.mongodb.shiptracker.service;
2
3import com.mongodb.shiptracker.model.Boat;
4import com.mongodb.shiptracker.model.Location;
5import com.mongodb.shiptracker.model.Port;
6import com.mongodb.shiptracker.repository.BoatRepositoryImpl;
7import com.mongodb.shiptracker.repository.PortRepositoryImpl;
8
9public class BoatService {
10 private final PortRepositoryImpl portRepository;
11 private final BoatRepositoryImpl boatRepository;
12
13 public BoatService() {
14 this.portRepository = new PortRepositoryImpl();
15 this.boatRepository = new BoatRepositoryImpl();
16 }
17
18 public Boat createBoat(String boatId, String startPortName, String endPortName) {
19 Port startPort = portRepository.getPortByName(startPortName);
20 Port endPort = portRepository.getPortByName(endPortName);
21
22 if (startPort != null && endPort != null) {
23 Boat boat = new Boat(
24 boatId,
25 new Location(startPort.getLatitude(), startPort.getLongitude()),
26 new Location(endPort.getLatitude(), endPort.getLongitude())
27 );
28 boatRepository.addBoat(boat);
29 return boat;
30 } else {
31 return null;
32 }
33 }
34}
Aqui, criaremos nossos botes. Aceitamos os nomes dos portas de início e fim e obtemos as coordenadas necessárias para inicializar a localização do bote e o destino pretendido.
Serviço de Porta
Em seguida, temos nosso PortService.
1package com.mongodb.shiptracker.service;
2
3import com.mongodb.shiptracker.model.Port;
4import com.mongodb.shiptracker.repository.PortRepositoryImpl;
5
6import java.util.Arrays;
7import java.util.List;
8
9public class PortService {
10 private final PortRepositoryImpl portRepository;
11
12 public PortService() {
13 this.portRepository = new PortRepositoryImpl();
14 }
15
16 public void insertInitialPorts() {
17 List<Port> ports = Arrays.asList(
18 new Port("New York", 40.7128, -74.0060),
19 new Port("Rotterdam", 51.9244, 4.4777),
20 new Port("Savannah", 32.0835, -81.0998),
21 new Port("Antwerp", 51.2194, 4.4025),
22 new Port("Miami", 25.7617, -80.1918),
23 new Port("Lisbon", 38.7223, -9.1393),
24 new Port("Halifax", 44.6488, -63.5752),
25 new Port("Le Havre", 49.4944, 0.1079),
26 new Port("Charleston", 32.7765, -79.9311),
27 new Port("Hamburg", 53.5511, 9.9937)
28 );
29 ports.forEach(portRepository::addPort);
30 }
31}
Isso inicializará nosso aplicação com uma lista de portas ao redor do mundo quando for chamado. Agora, em uma inspeção cuidadosa, você pode notar que esta não é a localização exata dessas portas, mas, em minha resposta, essas informações são mais difíceis de encontrar do que você poderia esperar. Ele servirá para nossa demonstração (ignore o início e o fim de cada viagem em que o bote atravessa milagrosamente a terra).
Serviço à distância
Agora, temos um muito DistanceService simples.
1package com.mongodb.shiptracker.service;
2
3import com.mongodb.shiptracker.repository.TimeSeriesRepositoryImpl;
4import org.bson.Document;
5
6import java.util.List;
7
8public class DistanceService {
9 private final TimeSeriesRepositoryImpl timeSeriesRepository;
10
11 public DistanceService() {
12 this.timeSeriesRepository = new TimeSeriesRepositoryImpl();
13 }
14
15 public List<Document> calculateTotalDistanceTraveled() {
16 return timeSeriesRepository.calculateTotalDistanceTraveled();
17 }
18}
Simulação
Para fazer as coisas funcionarem sem georreferenciar um grupo de Navios de verdade navegando em alto-mar (meu gerente negado o pedido de orçamento), vamos criar uma rápida classe de simulação.
1package com.mongodb.shiptracker.service;
2
3import com.mongodb.shiptracker.model.Boat;
4import com.mongodb.shiptracker.repository.TimeSeriesRepositoryImpl;
5
6import java.util.ArrayList;
7import java.util.List;
8
9public class Simulator {
10 private final List<Boat> boats;
11 private final TimeSeriesRepositoryImpl timeSeriesRepositoryImpl;
12
13 public Simulator() {
14 this.boats = new ArrayList<>();
15 this.timeSeriesRepositoryImpl = new TimeSeriesRepositoryImpl(); // Initialize the time series repository
16 }
17
18 public void addBoat(Boat boat) {
19 boats.add(boat);
20 }
21
22 // Run the simulation until each boat reaches its destination
23 public void runSimulation() {
24 boolean hasActiveBoats = true;
25
26 while (hasActiveBoats) {
27 System.out.println("Simulation step");
28 hasActiveBoats = false;
29
30 // Move all boats in one step and log their locations
31 for (Boat boat : new ArrayList<>(boats)) {
32 if (!(boat.getLocation().getLatitude() == boat.getDestination().getLatitude() &&
33 boat.getLocation().getLongitude() == boat.getDestination().getLongitude())) {
34 boat.move();
35 timeSeriesRepositoryImpl.logBoatLocation(boat.getBoatId(), boat.getLocation()); // Log the new location
36
37 System.out.println("Boat " + boat.getBoatId() + " moved to new position: (" +
38 boat.getLocation().getLatitude() + ", " + boat.getLocation().getLongitude() + ")");
39
40 hasActiveBoats = true;
41 } else {
42 System.out.println("Boat " + boat.getBoatId() + " has reached its destination.");
43 }
44 }
45
46 // Remove boats that have reached their destinations after all have been processed
47 boats.removeIf(boat -> boat.getLocation().getLatitude() == boat.getDestination().getLatitude() &&
48 boat.getLocation().getLongitude() == boat.getDestination().getLongitude());
49
50 try {
51 Thread.sleep(1000); // Pause for 1 second between steps to simulate real-time updates
52 } catch (InterruptedException e) {
53 e.printStackTrace();
54 }
55 }
56 System.out.println("All boats have reached their destinations. Simulation complete.");
57 }
58}
Isso nos permitirá simular os botes em nossos bancos de dados se mudando para seu destino. Nada de especial — estamos esperando que os botes se movam em linha reta e a uma velocidade constante, atualizando a localização a cada segundo.
Controlando nossa API
Para interagir com nosso aplicativo, usaremos o Javalin para criar uma API simples. Primeiro, precisamos de um pacote de controlador, onde podemos adicionar ShipTrackerController nosso.
1package com.mongodb.shiptracker.controller;
2
3import com.mongodb.shiptracker.model.Boat;
4import com.mongodb.shiptracker.model.BoatRequest;
5import com.mongodb.shiptracker.service.BoatService;
6import com.mongodb.shiptracker.service.DistanceService;
7import com.mongodb.shiptracker.service.PortService;
8import com.mongodb.shiptracker.service.Simulator;
9import io.javalin.Javalin;
10import io.javalin.http.Context;
11
12public class ShipTrackerController {
13 private final PortService portService;
14 private final BoatService boatService;
15 private final Simulator simulator;
16 private final DistanceService distanceService;
17
18 public ShipTrackerController() {
19 this.portService = new PortService();
20 this.boatService = new BoatService();
21 this.simulator = new Simulator();
22 this.distanceService = new DistanceService();
23 }
24
25 public void registerRoutes(Javalin app) {
26 app.post("/ports/init", this::insertInitialPorts); // Initializes ports
27 app.post("/boats", this::createBoat); // Creates a boat
28 app.post("/simulate", this::runSimulation); // Runs the simulation
29 app.get("/boats/totalDistance", this::getTotalDistances); // Gets total distance traveled for all boats
30
31 app.exception(Exception.class, (e, ctx) -> {
32 e.printStackTrace(); // Logs the full stack trace for debugging
33 ctx.status(500).result("Internal Server Error: " + e.getMessage());
34 });
35 }
36
37 private void insertInitialPorts(Context ctx) {
38 portService.insertInitialPorts();
39 ctx.status(201).result("Ports initialized.");
40 }
41
42 private void createBoat(Context ctx) {
43 System.out.println("Received JSON: " + ctx.body()); // Log raw JSON for verification
44
45 BoatRequest boatRequest = ctx.bodyAsClass(BoatRequest.class);
46
47 System.out.println(boatRequest.getStartPort());
48
49 if (boatRequest.getBoatId() == null) {
50 ctx.status(400).result("Missing 'boatId' parameter.");
51 return;
52 }
53 if (boatRequest.getStartPort() == null) {
54 ctx.status(400).result("Missing 'startPort' parameter.");
55 return;
56 }
57 if (boatRequest.getEndPort() == null) {
58 ctx.status(400).result("Missing 'endPort' parameter.");
59 return;
60 }
61
62 Boat boat = boatService.createBoat(boatRequest.getBoatId(), boatRequest.getStartPort(), boatRequest.getEndPort());
63 if (boat != null) {
64 simulator.addBoat(boat);
65 ctx.status(201).json(boat);
66 } else {
67 ctx.status(404).result("One or both ports not found.");
68 }
69 }
70
71 private void runSimulation(Context ctx) {
72 simulator.runSimulation();
73 ctx.status(200).result("Simulation complete.");
74 }
75
76 private void getTotalDistances(Context ctx) {
77 ctx.json(distanceService.calculateTotalDistanceTraveled());
78 }
79}
Isso nos permitirá acessar toda essa lógica em que estamos trabalhando até agora. Podemos inicializar nossas portas, adicionar nosso(s) bote(s), simular o movimento de nossos botes e obter a distância total percorrida por todos os botes.
Agora, precisamos de um ShipTrackerApp para o nosso projeto.
1package com.mongodb.shiptracker;
2
3import com.mongodb.shiptracker.controller.ShipTrackerController;
4import io.javalin.Javalin;
5
6public class ShipTrackerApp {
7 public static void main(String[] args) {
8 Javalin app = Javalin.create().start(7070);
9
10 ShipTrackerController controller = new ShipTrackerController();
11 controller.registerRoutes(app);
12 }
13}
Aqui criamos nosso aplicativo Javalin e o executamos na porta 7070.
Executando nosso aplicativo
Onde a verdadeira Diversão começa. Vamos compilar e executar nosso projeto com o seguinte:
1mvn clean install
2mvn exec:java -Dexec.mainClass="com.mongodb.shiptracker.ShipTrackerApp"
Agora, podemos inicializar nossas portas.
1curl -X POST http://localhost:7070/ports/init
Com as portas adicionadas, vamos garantir que o endpoint do nosso bote esteja funcionando.
1curl -X POST http://localhost:7070/boats -H "Content-Type: application/json" -d '{"boatId": "BOAT001","startPort": "Miami","endPort": "Lisbon"}'
O que é melhor do que um bote? Dois botes!
1curl -X POST http://localhost:7070/boats -H "Content-Type: application/json" -d '{"boatId": "BOAT002","startPort": "New York","endPort": "Lisbon"}'
Agora que temos algo para rastrear, vamos executar nossa simulação.
1curl -X POST http://localhost:7070/simulate
Em nosso terminal, devemos ver os botes se movendo.
1Simulation step
2Boat BOAT001 moved to new position: (25.821515943344753, -79.86387750300888)
3Boat BOAT002 moved to new position: (40.70257614347035, -73.67282349442259)
E se quisermos executar nossa agregação:
1curl -X GET http://localhost:7070/boats/totalDistance
Devemos obter algo assim:
1[{"_id":"BOAT001","totalDistance":230.4497823966521},{"_id":"BOAT002","totalDistance":21.333333333333417}]
Eba!
Conclusão
Neste tutorial, construímos uma simulação de rastreador de envio para demonstrar como criar e usar coleções de séries temporais no MongoDB. Criamos a coleção de séries temporais com o Java driver Java e realizamos agregações que usaram os recursos de dados de séries temporais e o GeoJSON GeoJSON.
Se você encontrou este tutorial útil, não deixe de conferir mais de nossos artigos sobre como usar o MongoDB com Java Java no Centro do Desenvolvedor. Confira como criar uma playlist criada por AI IA , usando o deep learning4j.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.
Iniciar a conversa

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Artigo

7 coisas que aprendi ao modelar dados para as estatísticas do YouTube


Oct 01, 2024 | 13 min read
Artigo

Criando um aplicativo Flask e MongoDB com aplicativos de contêiner do Azure


Apr 02, 2024 | 8 min read
Tutorial

Arquiteturas de dados em tempo real com o MongoDB Cloud Manager e o Verizon 5G Edge


Aug 28, 2024 | 8 min read
Artigo

Driver Java: migrando do 4.11 a 5.0


Mar 01, 2024 | 3 min read
Sumário