Mutable Properties

Facts are immutable. But sometimes we want to record the values of properties that can change over time. To do so, define a new fact type representing a change to that value.

The name of a site should be allowed to change. That is why we didn't include it as a field of the Site fact. To model this, define a SiteName fact that refers to the site and stores the new value. It should also refer to past SiteName facts that it replaces.

[FactType("Blog.Site.Name")]
public record SiteName(Site site, string value, SiteName[] prior) { }

Renderer.RenderTypes(typeof(SiteName))
%0 Blog.Site.Name Blog.Site.Name Blog.Site.Name->Blog.Site.Name prior Blog.Site Blog.Site Blog.Site.Name->Blog.Site site Jinaga.User Jinaga.User Blog.Site->Jinaga.User creator

When we create the first instance of the site name fact, we have no prior names to replace. So we pass in an empty array.

var siteName0 = await jinagaClient.Fact(new SiteName(site, "My Site", []));

jinagaClient.RenderFacts(siteName0)
%0 eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== Jinaga.User publicKey --- FAKE USER --- fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== Blog.Site createdAt 2024-05-18T20:32:02.... fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== creator Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA== Blog.Site.Name value My Site Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site

If the user changes the name of the site, then we record that with a new fact that replaces the first one.

var siteName1 = await jinagaClient.Fact(new SiteName(site, "My Blog", [siteName0]));

jinagaClient.RenderFacts(siteName1)
%0 eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== Jinaga.User publicKey --- FAKE USER --- fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== Blog.Site createdAt 2024-05-18T20:32:02.... fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== creator Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA== Blog.Site.Name value My Site Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site 4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww== Blog.Site.Name value My Blog 4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site 4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww==->Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA== prior

If they change it again, we only include the most recent value that we are replacing. There is no need to list all of the past values, since some of them have already been replaced. This forms a chain of values that the property took on over time.

var siteName2 = await jinagaClient.Fact(new SiteName(site, "My Journal", [siteName1]));

jinagaClient.RenderFacts(siteName2)
%0 eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== Jinaga.User publicKey --- FAKE USER --- fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== Blog.Site createdAt 2024-05-18T20:32:02.... fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== creator Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA== Blog.Site.Name value My Site Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site 4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww== Blog.Site.Name value My Blog 4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site 4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww==->Ks00prnUjVMTiazHRnLzVWWuurH0Xc2djtNbTz93TW9svgXu/GNCVq+XV2daZYpv2GZtJGyP+W6AmrITEZMjeA== prior y4JvMVeVi8615eRRaFgAvebiJTq5M71SNju8TfQfaP0mInGfxJV5dpACeWQdt2P01LwXmBxlxmS9pQoowEMFMw== Blog.Site.Name value My Journal y4JvMVeVi8615eRRaFgAvebiJTq5M71SNju8TfQfaP0mInGfxJV5dpACeWQdt2P01LwXmBxlxmS9pQoowEMFMw==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site y4JvMVeVi8615eRRaFgAvebiJTq5M71SNju8TfQfaP0mInGfxJV5dpACeWQdt2P01LwXmBxlxmS9pQoowEMFMw==->4xLWBu6uW4rV5faLluXmKSSwfTHzGN4um6W8+FbrZM4F67vHlXz2j/z3+1adGVxtzSIurTtHoOwcX8vT/HDxww== prior

To find the current name of a site, we look for site names that have not been replaced. Let's take this in two parts.

First, look for all of the names of a site. This will include all past values.

var namesOfSite = Given<Site>.Match((site, facts) =>
    from name in facts.OfType<SiteName>()
    where name.site == site
    select name);

var names = await jinagaClient.Query(namesOfSite, site);

names.Count()
3

Second, let's filter this history. We only want the names for which there is no next value.

namesOfSite = Given<Site>.Match((site, facts) =>
    from name in facts.OfType<SiteName>()
    where name.site == site
    // Filter out names for which a next name exists
    where !facts.Any<SiteName>(next => next.prior.Contains(name))
    select name);

names = await jinagaClient.Query(namesOfSite, site);

names.Count()
1
names.Single().value
My Journal

Continue With

Conflicts

Jinaga is a product of Jinaga LLC.

Michael L Perry, President