using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using BuildFeed.Model.View; using MongoDB.Bson; using MongoDB.Driver; namespace BuildFeed.Model { public partial class BuildRepository { public const int CURRENT_LONG_TERM = (int)ProjectFamily.Redstone; public const int CURRENT_RELEASE = (int)ProjectFamily.Redstone5; public const int CURRENT_XBOX = (int)ProjectFamily.Redstone5; private const string BUILD_COLLECTION_NAME = "builds"; private static readonly BsonDocument sortByAddedDate = new BsonDocument(nameof(Build.Added), -1); private static readonly BsonDocument sortByCompileDate = new BsonDocument(nameof(Build.BuildTime), -1); private static readonly BsonDocument sortByLeakedDate = new BsonDocument(nameof(Build.LeakDate), -1); private static readonly BsonDocument sortByOrder = new BsonDocument { new BsonElement(nameof(Build.MajorVersion), -1), new BsonElement(nameof(Build.MinorVersion), -1), new BsonElement(nameof(Build.Number), -1), new BsonElement(nameof(Build.Revision), -1), new BsonElement(nameof(Build.BuildTime), -1) }; private readonly IMongoCollection _buildCollection; public BuildRepository() { var settings = new MongoClientSettings { Server = new MongoServerAddress(MongoConfig.Host, MongoConfig.Port) }; if (!string.IsNullOrEmpty(MongoConfig.Username) && !string.IsNullOrEmpty(MongoConfig.Password)) { settings.Credential = MongoCredential.CreateCredential(MongoConfig.Database, MongoConfig.Username, MongoConfig.Password); } var dbClient = new MongoClient(settings); IMongoDatabase buildDatabase = dbClient.GetDatabase(MongoConfig.Database); _buildCollection = buildDatabase.GetCollection(BUILD_COLLECTION_NAME); } public async Task SetupIndexes() { var indexes = await (await _buildCollection.Indexes.ListAsync()).ToListAsync(); if (indexes.All(i => i["name"] != "_idx_group")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Combine(Builders.IndexKeys.Descending(b => b.MajorVersion), Builders.IndexKeys.Descending(b => b.MinorVersion), Builders.IndexKeys.Descending(b => b.Number), Builders.IndexKeys.Descending(b => b.Revision)), new CreateIndexOptions { Name = "_idx_group" })); } if (indexes.All(i => i["name"] != "_idx_legacy")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Ascending(b => b.LegacyId), new CreateIndexOptions { Name = "_idx_legacy" })); } if (indexes.All(i => i["name"] != "_idx_lab")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Ascending(b => b.Lab), new CreateIndexOptions { Name = "_idx_lab" })); } if (indexes.All(i => i["name"] != "_idx_date")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Descending(b => b.BuildTime), new CreateIndexOptions { Name = "_idx_date" })); } if (indexes.All(i => i["name"] != "_idx_bstr")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Ascending(b => b.FullBuildString), new CreateIndexOptions { Name = "_idx_bstr" })); } if (indexes.All(i => i["name"] != "_idx_alt_bstr")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Ascending(b => b.AlternateBuildString), new CreateIndexOptions { Name = "_idx_alt_bstr" })); } if (indexes.All(i => i["name"] != "_idx_source")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Ascending(b => b.SourceType), new CreateIndexOptions { Name = "_idx_source" })); } if (indexes.All(i => i["name"] != "_idx_family")) { await _buildCollection.Indexes.CreateOneAsync(new CreateIndexModel( Builders.IndexKeys.Ascending(b => b.Family), new CreateIndexOptions { Name = "_idx_family" })); } } [DataObjectMethod(DataObjectMethodType.Select, true)] public async Task> Select() => await _buildCollection.Find(new BsonDocument()).ToListAsync(); [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task SelectById(Guid id) => await _buildCollection.Find(Builders.Filter.Eq(b => b.Id, id)).SingleOrDefaultAsync(); [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task SelectByLegacyId(long id) => await _buildCollection .Find(Builders.Filter.Eq(b => b.LegacyId, id)) .SingleOrDefaultAsync(); [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task> SelectBuildsByOrder(int limit = -1, int skip = 0) { var query = _buildCollection.Find(new BsonDocument()).Sort(sortByOrder).Skip(skip); if (limit > 0) { query = query.Limit(limit); } return await query.ToListAsync(); } [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task> SelectFrontPage() { var families = new Dictionary(); var query = _buildCollection.Aggregate(new AggregateOptions { AllowDiskUse = true }) .Match(new BsonDocument { { "$or", new BsonArray { new BsonDocument { { nameof(Build.Family), new BsonDocument { { "$gte", CURRENT_RELEASE } } } }, new BsonDocument { { nameof(Build.Family), CURRENT_LONG_TERM } } } } }) .Group(new BsonDocument { { "_id", new BsonDocument { { nameof(Build.Family), $"${nameof(Build.Family)}" }, { nameof(Build.LabUrl), $"${nameof(Build.LabUrl)}" }, { nameof(Build.SourceType), $"${nameof(Build.SourceType)}" } } }, { "items", new BsonDocument { { "$push", new BsonDocument { { nameof(Build.Id), "$_id" }, { nameof(Build.MajorVersion), $"${nameof(Build.MajorVersion)}" }, { nameof(Build.MinorVersion), $"${nameof(Build.MinorVersion)}" }, { nameof(Build.Number), $"${nameof(Build.Number)}" }, { nameof(Build.Revision), $"${nameof(Build.Revision)}" }, { nameof(Build.Lab), $"${nameof(Build.Lab)}" }, { nameof(Build.BuildTime), $"${nameof(Build.BuildTime)}" } } } } } }); var dbResults = await query.ToListAsync(); var results = (from g in dbResults select new { Key = new { Family = (ProjectFamily)g["_id"].AsBsonDocument[nameof(Build.Family)].AsInt32, LabUrl = g["_id"].AsBsonDocument[nameof(Build.LabUrl)].AsString, SourceType = (TypeOfSource)g["_id"].AsBsonDocument[nameof(Build.SourceType)].AsInt32 }, Items = from i in g["items"].AsBsonArray select new FrontPageBuild { Id = i[nameof(Build.Id)].AsGuid, MajorVersion = (uint)i[nameof(Build.MajorVersion)].AsInt32, MinorVersion = (uint)i[nameof(Build.MinorVersion)].AsInt32, Number = (uint)i[nameof(Build.Number)].AsInt32, Revision = (uint?)i[nameof(Build.Revision)].AsNullableInt32, Lab = i[nameof(Build.Lab)].AsString, BuildTime = i[nameof(Build.BuildTime)].ToNullableUniversalTime() } }).ToArray(); IEnumerable listOfFamilies = results.GroupBy(g => g.Key.Family).Select(g => g.Key).OrderByDescending(k => k); foreach (ProjectFamily family in listOfFamilies) { var fp = new FrontPage { CurrentCanary = results.Where(g => (g.Key.Family == family) && !g.Key.LabUrl.Contains("xbox") && !g.Key.LabUrl.Contains("analog")) .SelectMany(g => g.Items) .OrderByDescending(b => b.BuildTime) .FirstOrDefault(), CurrentInsider = results .Where(g => (g.Key.Family == family) && !g.Key.LabUrl.Contains("xbox") && !g.Key.LabUrl.Contains("analog") && ((g.Key.SourceType == TypeOfSource.PublicRelease) || (g.Key.SourceType == TypeOfSource.UpdateGDR))) .SelectMany(g => g.Items) .OrderByDescending(b => b.BuildTime) .FirstOrDefault(), CurrentRelease = results .Where(g => ((int)g.Key.Family <= CURRENT_RELEASE) && (g.Key.Family == family) && g.Key.LabUrl.Contains("_release") && !g.Key.LabUrl.Contains("xbox") && !g.Key.LabUrl.Contains("analog") && ((g.Key.SourceType == TypeOfSource.PublicRelease) || (g.Key.SourceType == TypeOfSource.UpdateGDR))) .SelectMany(g => g.Items) .OrderByDescending(b => b.BuildTime) .FirstOrDefault(), CurrentXbox = results.Where(g => ((int)g.Key.Family >= CURRENT_XBOX) && (g.Key.Family == family) && g.Key.LabUrl.Contains("xbox")) .SelectMany(g => g.Items) .OrderByDescending(b => b.BuildTime) .FirstOrDefault(), CurrentAnalog = results.Where(g => ((int)g.Key.Family >= CURRENT_RELEASE) && (g.Key.Family == family) && g.Key.LabUrl.Contains("analog")) .SelectMany(g => g.Items) .OrderByDescending(b => b.BuildTime) .FirstOrDefault() }; families.Add(family, fp); } return families; } [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task> SelectBuildsByStringSearch(string term, int limit = -1) { var query = _buildCollection.Aggregate() .Match(b => b.FullBuildString != null) .Match(b => b.FullBuildString != "") .Match(b => b.FullBuildString.ToLower().Contains(term.ToLower())); if (limit > 0) { query = query.Limit(limit); } return await query.ToListAsync(); } [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task SelectBuildByFullBuildString(string build) => await _buildCollection .Find(Builders.Filter.Eq(b => b.FullBuildString, build)) .SingleOrDefaultAsync(); [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task> SelectBuildsByCompileDate(int limit = -1, int skip = 0) { var query = _buildCollection.Find(new BsonDocument()).Sort(sortByCompileDate).Skip(skip); if (limit > 0) { query = query.Limit(limit); } return await query.ToListAsync(); } [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task> SelectBuildsByAddedDate(int limit = -1, int skip = 0) { var query = _buildCollection.Find(new BsonDocument()).Sort(sortByAddedDate).Skip(skip); if (limit > 0) { query = query.Limit(limit); } return await query.ToListAsync(); } [DataObjectMethod(DataObjectMethodType.Select, false)] public async Task> SelectBuildsByLeakedDate(int limit = -1, int skip = 0) { var query = _buildCollection.Find(new BsonDocument()).Sort(sortByLeakedDate).Skip(skip); if (limit > 0) { query = query.Limit(limit); } return await query.ToListAsync(); } [DataObjectMethod(DataObjectMethodType.Insert, true)] public async Task Insert(Build item) { item.Id = Guid.NewGuid(); item.RegenerateCachedProperties(); await _buildCollection.InsertOneAsync(item); } [DataObjectMethod(DataObjectMethodType.Insert, false)] public async Task InsertAll(IEnumerable items) { var generatedItems = new List(); foreach (Build item in items) { item.Id = Guid.NewGuid(); item.RegenerateCachedProperties(); generatedItems.Add(item); } await _buildCollection.InsertManyAsync(generatedItems); } [DataObjectMethod(DataObjectMethodType.Update, true)] public async Task Update(Build item) { Build old = await SelectById(item.Id); item.Added = old.Added; item.Modified = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc); item.RegenerateCachedProperties(); await _buildCollection.ReplaceOneAsync(Builders.Filter.Eq(b => b.Id, item.Id), item); } public async Task RegenerateCachedProperties() { var builds = await Select(); foreach (Build bd in builds) { bd.RegenerateCachedProperties(); await _buildCollection.ReplaceOneAsync(Builders.Filter.Eq(b => b.Id, bd.Id), bd); } } [DataObjectMethod(DataObjectMethodType.Delete, true)] public async Task DeleteById(Guid id) { await _buildCollection.DeleteOneAsync(Builders.Filter.Eq(b => b.Id, id)); } } }