From 45ee0a73c0fc407e75f275b9f60f6934966f2691 Mon Sep 17 00:00:00 2001 From: halgari Date: Tue, 11 Jun 2024 22:41:20 -0600 Subject: [PATCH] Handle some features that came up during integration with the app --- src/NexusMods.MnemonicDB.Abstractions/IDb.cs | 6 +++++ .../Models/ModelExtensions.cs | 27 +++++++++++++++++++ .../Query/ObservableDatoms.cs | 25 +++++++++++++++++ .../Query/SliceDescriptor.cs | 13 +++++++++ .../Template.weave | 9 ++++--- .../InMemoryBackend/Snapshot.cs | 1 - src/NexusMods.MnemonicDB/Connection.cs | 1 - src/NexusMods.MnemonicDB/Db.cs | 7 +++++ tests/NexusMods.MnemonicDB.Tests/DbTests.cs | 15 ++++++++--- 9 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 src/NexusMods.MnemonicDB.Abstractions/Models/ModelExtensions.cs diff --git a/src/NexusMods.MnemonicDB.Abstractions/IDb.cs b/src/NexusMods.MnemonicDB.Abstractions/IDb.cs index f23ef8d6..29686e5e 100644 --- a/src/NexusMods.MnemonicDB.Abstractions/IDb.cs +++ b/src/NexusMods.MnemonicDB.Abstractions/IDb.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using NexusMods.MnemonicDB.Abstractions.Attributes; using NexusMods.MnemonicDB.Abstractions.DatomIterators; @@ -77,6 +78,11 @@ public Entities GetReverse(EntityId id, Attribute IEnumerable FindIndexed(Attribute attribute, TValue value); + /// + /// Finds all the entity ids that have the given attribute with the given value. + /// + IEnumerable FindIndexed(ReferenceAttribute attribute, EntityId value); + /// /// Finds all the datoms have the given attribute with the given value. /// diff --git a/src/NexusMods.MnemonicDB.Abstractions/Models/ModelExtensions.cs b/src/NexusMods.MnemonicDB.Abstractions/Models/ModelExtensions.cs new file mode 100644 index 00000000..38e664ba --- /dev/null +++ b/src/NexusMods.MnemonicDB.Abstractions/Models/ModelExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Reactive.Linq; +using DynamicData; +using NexusMods.MnemonicDB.Abstractions.Query; + +namespace NexusMods.MnemonicDB.Abstractions.Models; + +/// +/// Extensions for models +/// +public static class ModelExtensions +{ + /// + /// An observable that emits the revisions of the model, terminates when the model is deleted + /// or otherwise invalid + /// + public static IObservable Revisions(this TModel model) + where TModel : IReadOnlyModel + { + // Pull these out of the model so that we don't keep the model data lying around in memory + var conn = model.Db.Connection; + var id = model.Id; + return conn.ObserveDatoms(id) + .QueryWhenChanged(_ => TModel.Create(conn.Db, id)) + .TakeWhile(m => m.IsValid()); + } +} diff --git a/src/NexusMods.MnemonicDB.Abstractions/Query/ObservableDatoms.cs b/src/NexusMods.MnemonicDB.Abstractions/Query/ObservableDatoms.cs index 732160dc..a5ae6890 100644 --- a/src/NexusMods.MnemonicDB.Abstractions/Query/ObservableDatoms.cs +++ b/src/NexusMods.MnemonicDB.Abstractions/Query/ObservableDatoms.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reactive.Linq; using DynamicData; +using NexusMods.MnemonicDB.Abstractions.Attributes; using NexusMods.MnemonicDB.Abstractions.DatomComparators; using NexusMods.MnemonicDB.Abstractions.DatomIterators; using NexusMods.MnemonicDB.Abstractions.IndexSegments; @@ -37,6 +38,30 @@ public static IObservable> ObserveDatoms(this IConnection conn return conn.ObserveDatoms(SliceDescriptor.Create(id, conn.Registry)); } + /// + /// Observe changes for a given attribue on a given entity id + /// + public static IObservable> ObserveDatoms(this IConnection conn, EntityId id, IAttribute attribute) + { + return conn.ObserveDatoms(SliceDescriptor.Create(id, attribute.GetDbId(conn.Registry.Id), conn.Registry)); + } + + /// + /// Observe changes for datoms that point to the given entity id via the given attribute + /// + public static IObservable> ObserveDatoms(this IConnection conn, ReferenceAttribute attribute, EntityId id) + { + return conn.ObserveDatoms(SliceDescriptor.Create(attribute, id, conn.Registry)); + } + + /// + /// Observe changes for a given attribute + /// + public static IObservable> ObserveDatoms(this IConnection conn, IAttribute attribute) + { + return conn.ObserveDatoms(SliceDescriptor.Create(attribute, conn.Registry)); + } + private static IChangeSet Diff(SortedSet set, IndexSegment updates, SliceDescriptor descriptor, IEqualityComparer comparer) { List>? changes = null; diff --git a/src/NexusMods.MnemonicDB.Abstractions/Query/SliceDescriptor.cs b/src/NexusMods.MnemonicDB.Abstractions/Query/SliceDescriptor.cs index 23d15c89..dc7299c9 100644 --- a/src/NexusMods.MnemonicDB.Abstractions/Query/SliceDescriptor.cs +++ b/src/NexusMods.MnemonicDB.Abstractions/Query/SliceDescriptor.cs @@ -101,6 +101,19 @@ public static SliceDescriptor Create(Attribute + /// Creates a slice descriptor for the given reference attribute and entity that is being pointed to. + /// + public static SliceDescriptor Create(ReferenceAttribute attr, EntityId value, IAttributeRegistry registry) + { + return new SliceDescriptor + { + Index = IndexType.VAETCurrent, + From = Datom(EntityId.MinValueNoPartition, attr, value, TxId.MinValue, false, registry), + To = Datom(EntityId.MaxValueNoPartition, attr, value, TxId.MaxValue, false, registry) + }; + } + /// /// Creates a slice descriptor for the given reference attribute and entity that is being pointed to, this is a /// reverse lookup. diff --git a/src/NexusMods.MnemonicDB.SourceGenerator/Template.weave b/src/NexusMods.MnemonicDB.SourceGenerator/Template.weave index eff3849f..b61922c0 100644 --- a/src/NexusMods.MnemonicDB.SourceGenerator/Template.weave +++ b/src/NexusMods.MnemonicDB.SourceGenerator/Template.weave @@ -34,14 +34,12 @@ public partial class {{= model.Name}} { #region CRUD Methods {{each attr in model.Attributes}} - {{if attr.IsIndexed}} - + {{if attr.IsIndexed || attr.IsReference}} public static IEnumerable<{{= model.Name}}.ReadOnly> FindBy{{= attr.Name}}(__ABSTRACTIONS__.IDb db, {{= attr.HighLevelType.ToDisplayString()}} value) { foreach (var id in db.FindIndexed({{= attr.Name}}, value)) { yield return new {{= model.Name}}.ReadOnly(db, id); } } - {{/if}} {{/each}} @@ -245,6 +243,11 @@ public partial class {{= model.Name}} { /// public __ABSTRACTIONS__.IDb Db { get; } + /// + /// Rebases the entity to the most recent version of the database + /// + public ReadOnly Rebase() => new ReadOnly(Db.Connection.Db, Id); + /// /// Constructs a new ReadOnly model of the entity. /// diff --git a/src/NexusMods.MnemonicDB.Storage/InMemoryBackend/Snapshot.cs b/src/NexusMods.MnemonicDB.Storage/InMemoryBackend/Snapshot.cs index 5797b376..f5c74316 100644 --- a/src/NexusMods.MnemonicDB.Storage/InMemoryBackend/Snapshot.cs +++ b/src/NexusMods.MnemonicDB.Storage/InMemoryBackend/Snapshot.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using Microsoft.Win32; using NexusMods.MnemonicDB.Abstractions; using NexusMods.MnemonicDB.Abstractions.IndexSegments; using NexusMods.MnemonicDB.Abstractions.Query; diff --git a/src/NexusMods.MnemonicDB/Connection.cs b/src/NexusMods.MnemonicDB/Connection.cs index de2891fb..64549bd0 100644 --- a/src/NexusMods.MnemonicDB/Connection.cs +++ b/src/NexusMods.MnemonicDB/Connection.cs @@ -9,7 +9,6 @@ using Microsoft.Extensions.Logging; using NexusMods.MnemonicDB.Abstractions; using NexusMods.MnemonicDB.Abstractions.BuiltInEntities; -using NexusMods.MnemonicDB.Abstractions.ElementComparers; using NexusMods.MnemonicDB.Abstractions.IndexSegments; using NexusMods.MnemonicDB.Abstractions.Internals; using NexusMods.MnemonicDB.Abstractions.Query; diff --git a/src/NexusMods.MnemonicDB/Db.cs b/src/NexusMods.MnemonicDB/Db.cs index f22e0e1a..49286665 100644 --- a/src/NexusMods.MnemonicDB/Db.cs +++ b/src/NexusMods.MnemonicDB/Db.cs @@ -125,6 +125,13 @@ public IEnumerable FindIndexed(Attribute d.E); } + public IEnumerable FindIndexed(ReferenceAttribute attribute, EntityId value) + { + return Snapshot + .Datoms(SliceDescriptor.Create(attribute, value, _registry)) + .Select(d => d.E); + } + public IEnumerable FindIndexedDatoms(Attribute attribute, TValue value) { if (!attribute.IsIndexed) diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.cs b/tests/NexusMods.MnemonicDB.Tests/DbTests.cs index 812670de..96c9ac40 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.cs +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.cs @@ -552,9 +552,6 @@ public async Task CanGetModelRevisions() var loadoutNames = new List(); - - // TODO: re-enable this once we decide on how to handle revisions - /* using var subscription = loadout.Revisions() .Select(l => l.Name) .Finally(() => loadoutNames.Add("DONE")) @@ -578,9 +575,19 @@ public async Task CanGetModelRevisions() loadoutNames.Count.Should().Be(4, "All revisions should be loaded"); loadoutNames.Should().BeEquivalentTo(["Test Loadout", "Update 1", "Update 2", "DONE"]); - */ + } + [Fact] + public async Task CanFindByReference() + { + var loadout = await InsertExampleData(); + foreach (var mod in loadout.Mods) + { + var found = Mod.FindByLoadout(Connection.Db, mod.LoadoutId) + .Select(f => f.Id); + found.Should().Contain(mod.Id, "we can look entities via the value if they are references"); + } } [Fact]