This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-nitride-cil/src/Nitride/Entities/CreateOrUpdateIndex.cs

115 lines
3.7 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using Gallium;
using Serilog;
namespace Nitride.Entities;
/// <summary>
/// A Nitride operation that creates and merges entities that are intended
/// to be indexes of another entity. Examples of this would be year and month
/// archive pages for a blog or a tag/category pages for associated data. This
/// uses the scanner to determine how many index entities are needed and then
/// merges existing entities with their data or creates new indexes for ones
/// that don't already have an index.
/// </summary>
/// <remarks>
/// This makes the assumption that there is one index per page.
/// </remarks>
[WithProperties]
public partial class CreateOrUpdateIndex : OperationBase
{
private readonly ILogger logger;
private readonly IValidator<CreateOrUpdateIndex> validator;
public CreateOrUpdateIndex(ILogger logger, IValidator<CreateOrUpdateIndex> validator)
{
this.validator = validator;
this.logger = logger.ForContext(typeof(CreateOrUpdateIndex));
}
/// <summary>
/// Creates an index for a given key. This will not be called for any
/// index that has been already created.
/// </summary>
public Func<string, IList<Entity>, Entity> CreateIndex { get; set; } = null!;
/// <summary>
/// Gets or sets the function to retrieve the key from an existing
/// index page. If this returns null, then the entity is considered not
/// to be an index page.
/// </summary>
public Func<Entity, string?> GetIndexKey { get; set; } = null!;
/// <summary>
/// Gets or sets the scanner that provides the keys.
/// </summary>
public EntityScanner Scanner { get; set; } = null!;
/// <summary>
/// Updates an existing index entity to include new information.
/// </summary>
public Func<Entity, string, IEnumerable<Entity>, Entity> UpdateIndex { get; set; } = null!;
/// <inheritdoc />
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
{
// Make sure we have sane data.
this.validator.ValidateAndThrow(this);
// Get the list of all the scanned entities.
var scanned = this.Scanner.GetScannedResults().ToDictionary(x => x.Key, x => x.Value);
// We loop through the results and look for index entities. Any one we
// find, we update with the existing entries. If we get to the end and
// still have any left over, we create those pages.
HashSet<string> existing = new();
foreach (Entity? entity in input)
{
// See if this entity is an index for anything.
string? key = this.GetIndexKey(entity);
if (key == null)
{
// Not an index page, we don't need to pay attention.
yield return entity;
}
else
{
// This is an existing entity page that needs to be updated.
IEnumerable<Entity> entries =
scanned.TryGetValue(key, out List<Entity>? list) ? list : Array.Empty<Entity>();
existing.Add(key);
yield return this.UpdateIndex(entity, key, entries);
}
}
// Once we're done with the list, we need to create the missing indexes.
foreach (string? key in scanned.Keys)
{
if (existing.Contains(key))
{
continue;
}
yield return this.CreateIndex(key, scanned[key]);
}
// Report the results.
this.logger.Debug(
"Found {Old:N0} and created {New:N0} index pages for {Keys:N0} keys",
existing.Count,
scanned.Count - existing.Count,
scanned.Keys.Count());
}
}