Skip to content

Commit

Permalink
Add database path (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
Atralupus authored May 8, 2024
1 parent 6f08fe5 commit 1d4c28c
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 64 deletions.
2 changes: 1 addition & 1 deletion Mimir.Worker/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"Configuration": {
"MongoDbConnectionString": "mongodb://rootuser:rootpass@localhost:27017",
"DatabaseName": "mimir",
"DatabaseName": "odin",
"HeadlessEndpoint": "https://9c-main-full-state.nine-chronicles.com/graphql"
}
}
16 changes: 10 additions & 6 deletions Mimir/Controllers/AgentController.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using Libplanet.Crypto;
using Microsoft.AspNetCore.Mvc;
using Mimir.Models.Agent;
using Libplanet.Crypto;
using Mimir.Services;
using Mimir.Util;

namespace Mimir.Controllers;

[ApiController]
[Route("agent")]
[Route("{network}/agent")]
public class AccountController : ControllerBase
{
[HttpGet("{agentAddress}/avatars")]
public async Task<AvatarsResponse> GetAvatars(string agentAddress, IStateService stateService)
public async Task<AvatarsResponse> GetAvatars(
string network,
string agentAddress,
IStateService stateService
)
{
var stateGetter = new StateGetter(stateService);
var avatars = await stateGetter.GetAvatarStatesAsync(new Address(agentAddress));
Expand All @@ -21,8 +25,8 @@ public async Task<AvatarsResponse> GetAvatars(string agentAddress, IStateService
return new AvatarsResponse([]);
}

return new AvatarsResponse(avatars
.Select(e => new Avatar(e.address.ToString(), e.name, e.level))
.ToList());
return new AvatarsResponse(
avatars.Select(e => new Avatar(e.address.ToString(), e.name, e.level)).ToList()
);
}
}
16 changes: 9 additions & 7 deletions Mimir/Controllers/ArenaController.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
using Libplanet.Crypto;
using Microsoft.AspNetCore.Mvc;
using Nekoyume.TableData;
using Mimir.Arena;
using Mimir.Models.Arena;
using Mimir.Repositories;
using Mimir.Services;
using Mimir.Util;
using Nekoyume.TableData;

namespace Mimir.Controllers;

[ApiController]
[Route("arena")]
[Route("{network}/arena")]
public class ArenaController(ArenaRankingRepository arenaRankingRepository) : ControllerBase
{
[HttpGet("ranking/{avatarAddress}/rank")]
public async Task<long> GetRankByAvatarAddress(string avatarAddress)
public async Task<long> GetRankByAvatarAddress(string network, string avatarAddress)
{
var rank = await arenaRankingRepository.GetRankByAvatarAddress(avatarAddress);
var rank = await arenaRankingRepository.GetRankByAvatarAddress(network, avatarAddress);
if (rank == 0)
{
Response.StatusCode = StatusCodes.Status404NotFound;
Expand All @@ -27,15 +27,17 @@ public async Task<long> GetRankByAvatarAddress(string avatarAddress)
}

[HttpGet("ranking")]
public async Task<List<ArenaRanking>> GetRanking(int limit, int offset)
public async Task<List<ArenaRanking>> GetRanking(string network, int limit, int offset)
{
return await arenaRankingRepository.GetRanking(limit, offset);
return await arenaRankingRepository.GetRanking(network, limit, offset);
}

[HttpPost("simulate")]
public async Task<ArenaSimulateResponse> Simulate(
string network,
[FromBody] ArenaSimulateRequest arenaSimulateRequest,
IStateService stateService)
IStateService stateService
)
{
var stateGetter = new StateGetter(stateService);

Expand Down
6 changes: 3 additions & 3 deletions Mimir/Controllers/AvatarController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace Mimir.Controllers;

[ApiController]
[Route("avatars")]
[Route("{network}/avatars")]
public class AvatarController(AvatarRepository avatarRepository) : ControllerBase
{
[HttpGet("{avatarAddress}/inventory")]
public Inventory? GetInventory(string avatarAddress)
public Inventory? GetInventory(string network, string avatarAddress)
{
var inventory = avatarRepository.GetInventory(avatarAddress);
var inventory = avatarRepository.GetInventory(network, avatarAddress);
if (inventory is null)
{
Response.StatusCode = StatusCodes.Status404NotFound;
Expand Down
1 change: 0 additions & 1 deletion Mimir/Options/DatabaseOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ namespace Mimir.Options;
public class DatabaseOption
{
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
82 changes: 56 additions & 26 deletions Mimir/Repositories/ArenaRankingRespository.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
using MongoDB.Bson;
using MongoDB.Driver;
using Mimir.Models.Agent;
using Mimir.Models.Arena;
using Mimir.Services;
using MongoDB.Bson;
using MongoDB.Driver;

namespace Mimir.Repositories;

public class ArenaRankingRepository(MongoDBCollectionService mongoDBCollectionService, IStateService stateService)
public class ArenaRankingRepository : BaseRepository<BsonDocument>
{
private readonly IMongoCollection<dynamic> _arenaCollection =
mongoDBCollectionService.GetCollection<dynamic>("arena");
private readonly CpRepository _cpRepository = new(stateService);
private readonly CpRepository _cpRepository;

public ArenaRankingRepository(
MongoDBCollectionService mongoDBCollectionService,
IStateService stateService
)
: base(mongoDBCollectionService)
{
_cpRepository = new CpRepository(stateService);
}

protected override string GetCollectionName()
{
return "arena";
}

public async Task<long> GetRankByAvatarAddress(string avatarAddress)
public async Task<long> GetRankByAvatarAddress(string network, string avatarAddress)
{
var collection = GetCollection(network);

var pipelines = new BsonDocument[]
{
new("$sort", new BsonDocument("Score.Score", -1)),
Expand All @@ -22,35 +36,41 @@ public async Task<long> GetRankByAvatarAddress(string avatarAddress)
new BsonDocument
{
{ "_id", BsonNull.Value },
{ "docs", new BsonDocument("$push", "$$ROOT") },
{ "docs", new BsonDocument("$push", "$$ROOT") }
}
),
new(
"$unwind",
new BsonDocument { { "path", "$docs" }, { "includeArrayIndex", "Rank" }, }
new BsonDocument { { "path", "$docs" }, { "includeArrayIndex", "Rank" } }
),
new("$match", new BsonDocument("docs.AvatarAddress", avatarAddress)),
new("$match", new BsonDocument("docs.AvatarAddress", avatarAddress))
};
var aggregation = await _arenaCollection.Aggregate<dynamic>(pipelines).ToListAsync();
return aggregation.Count == 0
? 0
: (long)aggregation.First().Rank;

var aggregation = await collection.Aggregate<dynamic>(pipelines).ToListAsync();
return aggregation.Count == 0 ? 0 : (long)aggregation.First().Rank;
}

public async Task<List<ArenaRanking>> GetRanking(long limit, long offset)
public async Task<List<ArenaRanking>> GetRanking(string network, long limit, long offset)
{
var collection = GetCollection(network);

var pipelines = new[]
{
@"{ $setWindowFields: { partitionBy: '', sortBy: { 'Score.Score': -1 }, output: { Rank: { $rank: {} } } } }",
$@"{{ $skip: {offset} }}",
$@"{{ $limit: {limit} }}",
@"{ $lookup: { from: 'avatars', localField: 'AvatarAddress', foreignField: 'Avatar.address', as: 'Avatar' } }",
@"{ $unwind: { path: '$Avatar', preserveNullAndEmptyArrays: true } }",
@"{ $unset: ['Avatar.Avatar.inventory', 'Avatar.Avatar.mailBox', 'Avatar.Avatar.stageMap', 'Avatar.Avatar.monsterMap', 'Avatar.Avatar.itemMap', 'Avatar.Avatar.eventMap'] }",
}.Select(BsonDocument.Parse).ToArray();
var aggregation = _arenaCollection.Aggregate<BsonDocument>(pipelines).ToList();
var arenaRankings = await Task.WhenAll(aggregation.OfType<BsonDocument>().Select(BuildArenaRankingFromDocument));
return [.. arenaRankings];
@"{ $unset: ['Avatar.Avatar.inventory', 'Avatar.Avatar.mailBox', 'Avatar.Avatar.stageMap', 'Avatar.Avatar.monsterMap', 'Avatar.Avatar.itemMap', 'Avatar.Avatar.eventMap'] }"
}
.Select(BsonDocument.Parse)
.ToArray();

var aggregation = collection.Aggregate<BsonDocument>(pipelines).ToList();
var arenaRankings = await Task.WhenAll(
aggregation.OfType<BsonDocument>().Select(BuildArenaRankingFromDocument)
);
return arenaRankings.ToList();
}

private async Task<ArenaRanking> BuildArenaRankingFromDocument(BsonDocument document)
Expand Down Expand Up @@ -80,13 +100,23 @@ private async Task<ArenaRanking> BuildArenaRankingFromDocument(BsonDocument docu
arenaRanking.Avatar = avatar;

var characterId = document["Avatar"]["Avatar"]["characterId"].AsInt32;
var equipmentIds = document["Avatar"]["ItemSlot"]["Equipments"].AsBsonArray.Select(x => x.AsString);
var costumeIds = document["Avatar"]["ItemSlot"]["Costumes"].AsBsonArray.Select(x => x.AsString);
var runeSlots = document["Avatar"]["RuneSlot"].AsBsonArray.Select(rune =>
(rune["RuneId"].AsInt32, rune["Level"].AsInt32)
);
var equipmentIds = document["Avatar"]
["ItemSlot"]["Equipments"]
.AsBsonArray.Select(x => x.AsString);
var costumeIds = document["Avatar"]
["ItemSlot"]["Costumes"]
.AsBsonArray.Select(x => x.AsString);
var runeSlots = document["Avatar"]
["RuneSlot"]
.AsBsonArray.Select(rune => (rune["RuneId"].AsInt32, rune["Level"].AsInt32));

var cp = await _cpRepository.CalculateCp(avatar, characterId, equipmentIds, costumeIds, runeSlots);
var cp = await _cpRepository.CalculateCp(
avatar,
characterId,
equipmentIds,
costumeIds,
runeSlots
);
arenaRanking.CP = cp;
Console.WriteLine($"CP Calculate {arenaRanking.ArenaAddress}");

Expand Down
32 changes: 17 additions & 15 deletions Mimir/Repositories/AvatarRepository.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
using MongoDB.Bson;
using MongoDB.Driver;
using Mimir.Models.Avatar;
using Mimir.Services;
using MongoDB.Bson;
using MongoDB.Driver;

namespace Mimir.Repositories;

public class AvatarRepository(MongoDBCollectionService mongoDBCollectionService)
public class AvatarRepository : BaseRepository<BsonDocument>
{
private readonly IMongoCollection<BsonDocument> _avatarsCollection =
mongoDBCollectionService.GetCollection<BsonDocument>("avatars");
public AvatarRepository(MongoDBCollectionService mongoDBCollectionService)
: base(mongoDBCollectionService) { }

public Inventory? GetInventory(string avatarAddress)
protected override string GetCollectionName()
{
var filter = Builders<BsonDocument>.Filter.Eq(f => f["Avatar"]["address"], avatarAddress);
var projection = Builders<BsonDocument>.Projection.Include(f => f["Avatar"]["inventory"]["Equipments"]);
var document = _avatarsCollection.Find(filter).Project(projection).FirstOrDefault();
if (document is null)
{
return null;
}

return new Inventory(document["Avatar"]["inventory"]);
return "avatars";
}

public Inventory? GetInventory(string network, string avatarAddress)
{
var collection = GetCollection(network);
var filter = Builders<BsonDocument>.Filter.Eq("Avatar.address", avatarAddress);
var projection = Builders<BsonDocument>.Projection.Include("Avatar.inventory.Equipments");
var document = collection.Find(filter).Project(projection).FirstOrDefault();

return document is null ? null : new Inventory(document["Avatar"]["inventory"]);
}
}
48 changes: 48 additions & 0 deletions Mimir/Repositories/BaseRepositories.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using Mimir.Services;
using MongoDB.Bson;
using MongoDB.Driver;

namespace Mimir.Repositories;

public abstract class BaseRepository<T>
{
private readonly MongoDBCollectionService _mongoDBCollectionService;
private readonly Dictionary<string, IMongoCollection<T>> _collections = new();

protected BaseRepository(MongoDBCollectionService mongoDBCollectionService)
{
_mongoDBCollectionService = mongoDBCollectionService;

var networks = new[] { "heimdall", "odin" };
var collectionName = GetCollectionName();

foreach (var network in networks)
{
string databaseName = network switch
{
"heimdall" => "heimdall",
"odin" => "odin",
_ => throw new ArgumentException("Invalid network name", nameof(network))
};

var collection = _mongoDBCollectionService.GetCollection<T>(
collectionName,
databaseName
);
_collections[network] = collection;
}
}

protected abstract string GetCollectionName();

protected IMongoCollection<T> GetCollection(string network)
{
if (_collections.TryGetValue(network, out var collection))
{
return collection;
}

throw new ArgumentException("Invalid network name", nameof(network));
}
}
4 changes: 2 additions & 2 deletions Mimir/Services/MongoDBCollectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ public class MongoDBCollectionService(IOptions<DatabaseOption> databaseOption)
{
private readonly IOptions<DatabaseOption> _databaseOption = databaseOption;

public IMongoCollection<T> GetCollection<T>(string collectionName)
public IMongoCollection<T> GetCollection<T>(string collectionName, string databaseName)
{
var client = new MongoClient(_databaseOption.Value.ConnectionString);
var database = client.GetDatabase(_databaseOption.Value.DatabaseName);
var database = client.GetDatabase(databaseName);
return database.GetCollection<T>(collectionName);
}
}
3 changes: 3 additions & 0 deletions Mimir/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"Microsoft.AspNetCore": "Warning"
}
},
"Database": {
"ConnectionString": "mongodb://rootuser:rootpass@localhost:27017"
},
"StateService": {
"HeadlessEndpoint": "https://9c-main-full-state.nine-chronicles.com/graphql"
}
Expand Down
6 changes: 3 additions & 3 deletions Mimir/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
}
},
"AllowedHosts": "*",
"Database": {
"ConnectionString": "mongodb://rootuser:rootpass@localhost:27017"
},
"StateService": {
"HeadlessEndpoint": "https://9c-main-full-state.nine-chronicles.com/graphql"
},
"DataProvider": {
"Endpoint": "http://heimdall-dp.9c.gg/graphql"
}
}

0 comments on commit 1d4c28c

Please sign in to comment.