324 lines
13 KiB
C#

using MemoryPack;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetDependencyGraph
{
[BsonIgnoreExtraElements]
[MemoryPackable]
public partial class AssetIdentify
{
public string Path = null!;
public string AssetType = null!;
public string Guid;
public string Md5;
}
public sealed class AssetIdentifyJsonConverter : JsonConverter<AssetIdentify>
{
static JsonSerializerOptions serializerOptions = new JsonSerializerOptions() { IncludeFields = true };
public override AssetIdentify? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<AssetIdentify>(reader.GetString()!, serializerOptions);
}
public override void Write(Utf8JsonWriter writer, AssetIdentify value, JsonSerializerOptions options)
{
writer.WriteStringValue(JsonSerializer.Serialize(value, serializerOptions));
}
public override AssetIdentify ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return Read(ref reader, typeToConvert, serializerOptions)!;
}
public override void WriteAsPropertyName(Utf8JsonWriter writer, [DisallowNull] AssetIdentify value, JsonSerializerOptions options)
{
writer.WritePropertyName(JsonSerializer.Serialize(value, serializerOptions));
}
}
[BsonIgnoreExtraElements]
[MemoryPackable]
public partial class AssetNode
{
public AssetIdentify Self = null!;
public string AssetType = null!;
[JsonIgnore]
[MemoryPackIgnore]
public ConcurrentBag<AssetIdentify> Dependencies = new();
[JsonIgnore]
[MemoryPackIgnore]
public ConcurrentBag<AssetIdentify> Dependent = new();
[AllowNull]
public HashSet<AssetIdentify> DependencySet;
[AllowNull]
public HashSet<AssetIdentify> DependentSet;
}
public sealed class AssetNodeJsonConverter : JsonConverter<AssetNode>
{
static JsonSerializerOptions serializerOptions = new JsonSerializerOptions()
{
IncludeFields = true,
Converters = { new AssetIdentifyJsonConverter() }
};
public override AssetNode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<AssetNode>(reader.GetString()!, serializerOptions);
}
public override void Write(Utf8JsonWriter writer, AssetNode value, JsonSerializerOptions options)
{
writer.WriteStringValue(JsonSerializer.Serialize(value, serializerOptions));
}
}
[BsonIgnoreExtraElements]
public sealed class FolderNode : AssetNode
{
}
[BsonIgnoreExtraElements]
public sealed class PackageNode : AssetNode
{
}
public class AssetDependencyGraphDB
{
MongoClient client;
IMongoCollection<FolderNode> FolderNodes;
IMongoCollection<PackageNode> PackageNodes;
IMongoCollection<AssetNode> AssetNodes;
Dictionary<string, AssetNode> findCacheDic = new();
public AssetDependencyGraphDB(string user, string passwd, string ip)
{
MongoClientSettings settings;
if (string.IsNullOrWhiteSpace(user) && !string.IsNullOrEmpty(ip))
{
settings = MongoClientSettings.FromUrl(new MongoUrl($"mongodb://{ip}:27017/"));
}
else
{
settings = MongoClientSettings.FromUrl(new MongoUrl($"mongodb://{user}:{passwd}@{ip}:27017/"));
}
settings.ConnectTimeout = TimeSpan.FromSeconds(5);
settings.MinConnectionPoolSize = 1;
settings.MaxConnectionPoolSize = 25;
client = new MongoClient(settings);
var db = client.GetDatabase("assetgraph");
FolderNodes = db.GetCollection<FolderNode>("folder_nodes");
PackageNodes = db.GetCollection<PackageNode>("package_nodes");
AssetNodes = db.GetCollection<AssetNode>("asset_nodes");
}
public void Clean()
{
client.DropDatabase("assetgraph");
var db = client.GetDatabase("assetgraph");
FolderNodes = db.GetCollection<FolderNode>("folder_nodes");
PackageNodes = db.GetCollection<PackageNode>("package_nodes");
AssetNodes = db.GetCollection<AssetNode>("asset_nodes");
}
public void Insert<T>(T node) where T : AssetNode
{
switch (node)
{
case FolderNode folderNode:
{
FolderNodes.InsertOne(folderNode);
break;
}
case PackageNode packageNode:
{
PackageNodes.InsertOne(packageNode);
break;
}
case AssetNode assetNode:
{
AssetNodes.InsertOne(assetNode);
break;
}
default:
break;
}
}
public void UpdateOrInsert<T>(T node) where T : AssetNode
{
switch (node)
{
case FolderNode folderNode:
{
var filter = Builders<FolderNode>.Filter.And(
Builders<FolderNode>.Filter.Eq(fn => fn.Self.Path, node.Self.Path)
);
var found = FolderNodes.Find(filter);
if (found == null || found.CountDocuments() == 0)
{
FolderNodes.InsertOne(folderNode);
}
else
{
var result = FolderNodes.UpdateOne(filter, Builders<FolderNode>.Update.Combine(
Builders<FolderNode>.Update.Set(fn => fn.Self, folderNode.Self),
Builders<FolderNode>.Update.Set(fn => fn.AssetType, folderNode.AssetType),
Builders<FolderNode>.Update.Set(fn => fn.Dependencies, folderNode.Dependencies),
Builders<FolderNode>.Update.Set(fn => fn.Dependent, folderNode.Dependent)
));
}
break;
}
case PackageNode packageNode:
{
var filter = Builders<PackageNode>.Filter.And(
Builders<PackageNode>.Filter.Eq(fn => fn.Self.Path, node.Self.Path)
);
var found = PackageNodes.Find(filter);
if (found == null || found.CountDocuments() == 0)
{
PackageNodes.InsertOne(packageNode);
}
else
{
var result = PackageNodes.UpdateOne(filter, Builders<PackageNode>.Update.Combine(
Builders<PackageNode>.Update.Set(fn => fn.Self, packageNode.Self),
Builders<PackageNode>.Update.Set(fn => fn.AssetType, packageNode.AssetType),
Builders<PackageNode>.Update.Set(fn => fn.Dependencies, packageNode.Dependencies),
Builders<PackageNode>.Update.Set(fn => fn.Dependent, packageNode.Dependent)
));
}
break;
}
case AssetNode assetNode:
{
var filter = Builders<AssetNode>.Filter.And(
Builders<AssetNode>.Filter.Eq(fn => fn.Self.Path, node.Self.Path)
);
var found = AssetNodes.Find(filter);
if (found == null || found.CountDocuments() == 0)
{
AssetNodes.InsertOne(assetNode);
}
else
{
var result = AssetNodes.UpdateOne(filter, Builders<AssetNode>.Update.Combine(
Builders<AssetNode>.Update.Set(fn => fn.Self, assetNode.Self),
Builders<AssetNode>.Update.Set(fn => fn.AssetType, assetNode.AssetType),
Builders<AssetNode>.Update.Set(fn => fn.Dependencies, assetNode.Dependencies),
Builders<AssetNode>.Update.Set(fn => fn.Dependent, assetNode.Dependent)
));
}
break;
}
default:
break;
}
}
public void Delete<T>(T node) where T : AssetNode
{
switch (node)
{
case FolderNode folderNode:
{
var filter = Builders<FolderNode>.Filter.And(
Builders<FolderNode>.Filter.Eq(fn => fn.Self.Path, node.Self.Path)
);
var found = FolderNodes.Find(filter);
if (found != null && found.CountDocuments() == 0)
{
// TODO: del ref dep
FolderNodes.DeleteOne(filter);
}
break;
}
case PackageNode packageNode:
{
var filter = Builders<PackageNode>.Filter.And(
Builders<PackageNode>.Filter.Eq(fn => fn.Self.Path, node.Self.Path)
);
var found = PackageNodes.Find(filter);
if (found != null && found.CountDocuments() == 0)
{
// TODO: del ref dep
PackageNodes.DeleteOne(filter);
}
break;
}
case AssetNode assetNode:
{
var filter = Builders<AssetNode>.Filter.And(
Builders<AssetNode>.Filter.Eq(fn => fn.Self.Path, node.Self.Path)
);
var found = AssetNodes.Find(filter);
if (found != null && found.CountDocuments() == 0)
{
// TODO: del ref dep
AssetNodes.DeleteOne(filter);
}
break;
}
default:
break;
}
}
public AssetNode Find(string path)
{
if (findCacheDic.TryGetValue(path, out var assetNode))
{
return assetNode;
}
var filter = Builders<AssetNode>.Filter.And(
Builders<AssetNode>.Filter.Eq(fn => fn.Self.Path, path)
);
var found = AssetNodes.Find(filter);
if (found != null && found.CountDocuments() != 0)
{
assetNode = found.First();
findCacheDic[path] = assetNode;
return assetNode;
}
var filter1 = Builders<PackageNode>.Filter.And(
Builders<PackageNode>.Filter.Eq(fn => fn.Self.Path, path)
);
var found1 = PackageNodes.Find(filter1);
if (found1 != null && found1.CountDocuments() != 0)
{
assetNode = found1.First();
findCacheDic[path] = assetNode;
return assetNode;
}
var filter2 = Builders<FolderNode>.Filter.And(
Builders<FolderNode>.Filter.Eq(fn => fn.Self.Path, path)
);
var found2 = FolderNodes.Find(filter2);
if (found2 != null && found2.CountDocuments() != 0)
{
assetNode = found2.First();
findCacheDic[path] = assetNode;
return assetNode;
}
return null!;
}
}
}