Skip to content

Commit

Permalink
Merge pull request #103 from mongodb-developer:main
Browse files Browse the repository at this point in the history
Main
  • Loading branch information
ctcac00 authored Mar 12, 2024
2 parents 65b1285 + ffa6006 commit b298685
Show file tree
Hide file tree
Showing 25 changed files with 484 additions and 71 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ env:
ATLAS_CHART_ID_EVENT_PROD: ${{ secrets.ATLAS_CHART_ID_EVENT_PROD }}
ATLAS_CHART_ID_PLAYER_PROD: ${{ secrets.ATLAS_CHART_ID_PLAYER_PROD }}
ATLAS_CHART_ID_HOME_PROD: ${{ secrets.ATLAS_CHART_ID_HOME_PROD }}
ATLAS_CHART_ID_SIMILAR_PROD: ${{ secrets.ATLAS_CHART_ID_SIMILAR_PROD }}

CONNECTION_STRING_STAGING: ${{ secrets.CONNECTION_STRING_STAGING }}
REST_SERVICE_IP_STAGING: ${{ secrets.REST_SERVICE_IP_STAGING }}
Expand All @@ -29,6 +30,7 @@ env:
ATLAS_CHART_ID_EVENT_STAGING: ${{ secrets.ATLAS_CHART_ID_EVENT_STAGING }}
ATLAS_CHART_ID_PLAYER_STAGING: ${{ secrets.ATLAS_CHART_ID_PLAYER_STAGING}}
ATLAS_CHART_ID_HOME_STAGING: ${{ secrets.ATLAS_CHART_ID_HOME_STAGING }}
ATLAS_CHART_ID_SIMILAR_STAGING: ${{ secrets.ATLAS_CHART_ID_SIMILAR_STAGING }}

jobs:
deployment:
Expand All @@ -49,6 +51,7 @@ jobs:
echo "ATLAS_CHART_ID_EVENT=${ATLAS_CHART_ID_EVENT_STAGING}" >> $GITHUB_ENV
echo "ATLAS_CHART_ID_PLAYER=${ATLAS_CHART_ID_PLAYER_STAGING}" >> $GITHUB_ENV
echo "ATLAS_CHART_ID_HOME=${ATLAS_CHART_ID_HOME_STAGING}" >> $GITHUB_ENV
echo "ATLAS_CHART_ID_SIMILAR=${ATLAS_CHART_ID_SIMILAR_STAGING}" >> $GITHUB_ENV
echo "GAME_CLIENT_PORT=${GAME_CLIENT_PORT}" >> $GITHUB_ENV
Expand All @@ -63,6 +66,7 @@ jobs:
echo "ATLAS_CHART_ID_EVENT=${ATLAS_CHART_ID_EVENT_PROD}" >> $GITHUB_ENV
echo "ATLAS_CHART_ID_PLAYER=${ATLAS_CHART_ID_PLAYER_PROD}" >> $GITHUB_ENV
echo "ATLAS_CHART_ID_HOME=${ATLAS_CHART_ID_HOME_PROD}" >> $GITHUB_ENV
echo "ATLAS_CHART_ID_SIMILAR=${ATLAS_CHART_ID_SIMILAR_PROD}" >> $GITHUB_ENV
echo "GAME_CLIENT_PORT=${GAME_CLIENT_PORT}" >> $GITHUB_ENV
Expand Down Expand Up @@ -116,6 +120,7 @@ jobs:
echo "ATLAS_CHART_ID_PLAYER=${{ env.ATLAS_CHART_ID_PLAYER }}" >> ./website/.env
echo "ATLAS_CHART_ID_HOME=${{ env.ATLAS_CHART_ID_HOME }}" >> ./website/.env
echo "ATLAS_CHART_ID_HOME=${{ env.ATLAS_CHART_ID_HOME }}" >> ./website/.env
echo "ATLAS_CHART_ID_SIMILAR=${{ env.ATLAS_CHART_ID_SIMILAR }}" >> ./website/.env
echo "GAME_CLIENT_PORT=${{ env.GAME_CLIENT_PORT }}" >> ./website/.env
Expand Down
26 changes: 26 additions & 0 deletions deployment/game_database/vector_index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Create the following atlas vector search index:

// ==========================
// named: vector_index
// collection: recordings
// ==========================
{
"fields": [
{
"path": "Player.Nickname",
"type": "filter"
},
{
"numDimensions": 589,
"path": "speed_vector",
"similarity": "euclidean",
"type": "vector"
},
{
"numDimensions": 588,
"path": "accel_vector",
"similarity": "euclidean",
"type": "vector"
}
]
}
122 changes: 122 additions & 0 deletions rest_service/Controllers/RecordingsController.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using MongoDB.Driver;
using RestService.Dtos.RequestObjects;
using RestService.Dtos.ResponseObjects;
using RestService.Entities;
using RestService.Exceptions;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;

namespace RestService.Controllers;

Expand Down Expand Up @@ -38,6 +41,7 @@ public async Task<IActionResult> PostRecording([FromBody] RecordingRequest recor

var newRecording = new Recording()
{
Id = ObjectId.GenerateNewId(),
SessionStatisticsPlain = recordingRequest.SessionStatisticsPlain,
DateTime = DateTime.UtcNow,
Player = new RecordingPlayer { Name = recordingRequest.PlayerName },
Expand All @@ -53,6 +57,15 @@ public async Task<IActionResult> PostRecording([FromBody] RecordingRequest recor
}).ToList()
};

// Calculate vectors
try
{
newRecording.SpeedVector = CalculateSpeedVector(newRecording.Snapshots);
newRecording.AccelVector = CalculateAcceleration(newRecording.SpeedVector);
} catch (Exception) {
// Favor persisting Recording over setting vectors
}

try
{
await AddLocation(newRecording);
Expand Down Expand Up @@ -129,4 +142,113 @@ private async Task AddPlayer(Recording recording)
throw new MultiplePlayersFoundException();
}
}

private static double[] CalculateSpeedVector(List<Snapshot> snapshots)
{
long vectorSize = snapshots.Count - 1;
if (vectorSize != 589) // 590 represents movements in 60s
return Array.Empty<double>();

double[] speed = new double[vectorSize];
for (int i = 0; i < vectorSize; i++)
{
double speedX = snapshots[i + 1].Position.X - snapshots[i].Position.X;
//double Y = snapshots[i + 1].Position.X - snapshots[i].Position.X;
double speedY = snapshots[i + 1].Position.Z - snapshots[i].Position.Z;
speed[i] = Math.Sqrt(Math.Pow(speedX, 2) + Math.Pow(speedY, 2));
}

return speed;
}

private static double[] CalculateAcceleration(double[] speedVector)
{
long vectorSize = speedVector.Length - 1;
if (speedVector.Length != 589) // speed vector should be 1 less 60s run
return Array.Empty<double>();

double dt = 1; // Assuming a constant time step of 1 unit.
double[] accelVector = new double[vectorSize];

for (int i = 0; i < speedVector.Length - 1; i++)
accelVector[i] = (speedVector[i + 1] - speedVector[i]) / dt;

return accelVector;
}

[HttpGet("similarBySpeed", Name = "GetSimilarBySpeed")]
public async Task<List<SimilarRecordingResponse>> SimilarBySpeed([FromQuery] PlayerRequest playerRequest)
{
// Get the highest scoring run for this player
Recording topRecording = _recordingsCollection
.Find(r => r.Player.Name.Equals(playerRequest.Name))
.SortByDescending(r => r.SessionStatisticsPlain.Score)
.Limit(1).ToList().First();

// Now get similar recordings
List<Recording> similarRecordings = _recordingsCollection.Aggregate()
.VectorSearch(
r => r.SpeedVector,
topRecording.SpeedVector,
3,
new VectorSearchOptions<Recording>()
{
IndexName = "vector_index",
NumberOfCandidates = 1000,
Filter = Builders<Recording>.Filter
.Where(r => !r.Player.Name.Equals(playerRequest.Name))
})
.ToList();

// Return this player's top recording + top similar
List<SimilarRecordingResponse> response = new()
{
new SimilarRecordingResponse(topRecording)
};
response.AddRange(
similarRecordings
.Select(r => new SimilarRecordingResponse(r))
.ToList());

return response;
}


[HttpGet("similarByAcceleration", Name = "GetSimilarByAcceleration")]
public async Task<List<SimilarRecordingResponse>> SimilarByAcceleration([FromQuery] PlayerRequest playerRequest)
{
// Get the highest scoring run for this player
Recording topRecording = _recordingsCollection
.Find(r => r.Player.Name.Equals(playerRequest.Name))
.SortByDescending(r => r.SessionStatisticsPlain.Score)
.Limit(1).ToList().First();

// Now get similar recordings
List<Recording> similarRecordings = _recordingsCollection.Aggregate()
.VectorSearch(
r => r.AccelVector,
topRecording.AccelVector,
3,
new VectorSearchOptions<Recording>()
{
IndexName = "vector_index",
NumberOfCandidates = 1000,
Filter = Builders<Recording>.Filter
.Where(r => !r.Player.Name.Equals(playerRequest.Name))
})
.ToList();

// Return this player's top recording + top similar
List<SimilarRecordingResponse> response = new()
{
new SimilarRecordingResponse(topRecording)
};
response.AddRange(
similarRecordings
.Select(r => new SimilarRecordingResponse(r))
.ToList());

return response;
}

}
5 changes: 1 addition & 4 deletions rest_service/Dtos/RequestObjects/RecordingRequest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using RestService.Entities;
using RestService.Entities;

namespace RestService.Dtos.RequestObjects;

[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")]
public class RecordingRequest
{
public SessionStatisticsPlain? SessionStatisticsPlain { get; set; }
Expand Down
3 changes: 0 additions & 3 deletions rest_service/Dtos/RequestObjects/SnapshotRequest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using RestService.Entities;

namespace RestService.Dtos.RequestObjects;

[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class SnapshotRequest
{
public Position? Position { get; set; }
Expand Down
1 change: 0 additions & 1 deletion rest_service/Dtos/ResponseObjects/ConfigResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

namespace RestService.Dtos.ResponseObjects;

[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
public class ConfigResponse
{
public float RoundDuration { get; set; }
Expand Down
12 changes: 0 additions & 12 deletions rest_service/Dtos/ResponseObjects/PlayerResponse.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using RestService.Entities;

namespace RestService.Dtos.ResponseObjects;

[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
public class PlayerResponse
{
[JsonProperty("_id")] public string? Id { get; set; }
Expand All @@ -22,13 +19,4 @@ public PlayerResponse(Player player)
Team = player.Team;
Location = player.Location;
}

/*
public PlayerResponse(PlayerUnique player)
{
Id = player.Id.ToString();
Name = player.Name;
Location = player.Location;
}
*/
}
22 changes: 22 additions & 0 deletions rest_service/Dtos/ResponseObjects/SimilarRecordingResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Newtonsoft.Json;
using RestService.Entities;

namespace RestService.Dtos.ResponseObjects;

public class SimilarRecordingResponse
{
[JsonProperty("_id")]
public string? Id { get; set; }
[JsonProperty("sessionStatisticsPlain")]
public SessionStatisticsPlain? SessionStatisticsPlain { get; set; }
[JsonProperty("name")]
public string? Name { get; set; }

public SimilarRecordingResponse(Recording recording)
{
Id = recording.Id.ToString();
SessionStatisticsPlain = recording.SessionStatisticsPlain;
Name = recording.Player.Name;
}
}

4 changes: 1 addition & 3 deletions rest_service/Entities/Config.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace RestService.Entities;

[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[BsonIgnoreExtraElements]
public class Config
{
[BsonElement("_id")] public ObjectId? Id { get; set; }
Expand Down
4 changes: 1 addition & 3 deletions rest_service/Entities/Event.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using MongoDB.Bson.Serialization.Attributes;

namespace RestService.Entities;

[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
[SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")]
[BsonIgnoreExtraElements]
public class Event
{
[BsonElement("_id")] public string? Id { get; set; }
Expand Down
7 changes: 1 addition & 6 deletions rest_service/Entities/Player.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
using System.Diagnostics.CodeAnalysis;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using RestService.Dtos.RequestObjects;

namespace RestService.Entities;

[SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")]
[BsonIgnoreExtraElements]
public class Player
{
[BsonElement("_id")] public ObjectId? Id { get; set; }
[BsonElement("Nickname")] public string? Name { get; set; }
[BsonElement("TeamName")] public string? Team { get; set; }
[BsonElement("Email")] public string? Email { get; set; }

[JsonProperty("location")]
[BsonElement("location")]
public string? Location { get; set; }
}
18 changes: 7 additions & 11 deletions rest_service/Entities/PlayerUnique.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;

namespace RestService.Entities;

namespace RestService.Entities
[BsonIgnoreExtraElements]
public class PlayerUnique
{
public class PlayerUnique
{
[BsonId]
[JsonProperty("Name")]
public string Name { get; set; }
[BsonId]
public string Name { get; set; }

[JsonProperty("location")]
[BsonElement("location")]
public string? Location { get; set; }
}
[BsonElement("location")]
public string? Location { get; set; }
}
10 changes: 5 additions & 5 deletions rest_service/Entities/Position.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using MongoDB.Bson.Serialization.Attributes;

namespace RestService.Entities;

[SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")]
[BsonIgnoreExtraElements]
public class Position
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Loading

0 comments on commit b298685

Please sign in to comment.