<rss version="2.0">
  <channel>
    <title>Hiperspace</title>
    <link>https://www.cepheis.com/hiperspace/blog</link>
    <description><![CDATA[Documentation for Hiperspace ]]></description>
    <item>
      <title>Release vNext 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260424-1</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update to support the presentation Cube data in  Graph views with <strong>Hiperspace.DB</strong>, and minor updated to external references.</p>
<h2>Element enhancement</h2>
<p>The <code>IElement</code> interface of <code>Element&lt;TElement&gt;</code> has been updated to include an <code>ISetSpace</code> reference for the trnasformation of <code>ICubeFact</code> <em>facts</em> to a acyclic directed graph needed to render <strong>Cubes</strong> as a <em>Pivot-Table</em> with <em>drilldown</em> to futher details.</p>
<h2>HiLang enhancement</h2>
<p>Extent expressions do not <em>normally</em> need <code>#id</code> to be recorded beause they are not stored and calculated when needed from an element, but this is not the case for cube measures.  Source editing has been added to carry forward the <code>id</code></p>
<h2>Cube validation</h2>
<p>Keep validation rules have been relaxed to create <strong>cubes</strong> even if it doesn't have any measures.  <strong>Cubes</strong> provide summary information for a single or multiple <em>Dimensions</em>, but sometimes the only <em>measure</em> you need is the number of connections included:</p>
<pre><code>entity Customer = Node () (Id : Int32) {Name : String} [TypeName = "Customer"];
entity Order  = Node (), Edge (From = Customer, To = this, TypeName= "Order"), Edge_ (From = this, To = Product, TypeName = "Ordered" )
  (Id : Int32) {Customer : Customer, Product : Product} [Name = Customer.Name + "-" + Product.Name, TypeName = "Order"];
entity Product = Node () (Id : Int32) {Name : String} [TypeName = "Product"];
</code></pre>
<p><img src="/hiperspace/media/Sites/hiperspace/cube/spread.svg"></p>
<p>If we're not interested in each order, a <strong>Cube</strong>  can be used to simplify the graph to</p>
<p><img src="/hiperspace/media/Sites/hiperspace/cube/summary1.svg"></p>
<p>by changing the model to</p>
<pre><code>@CubeDimension entity Customer = Node () (Id : Int32) {Name : String} [TypeName = "Customer"];
@CubeFact entity Order (Id : Int32) {Customer : Customer, Product : Product};
@CubeDimension entity Product = Node () (Id : Int32) {Name : String} [TypeName = "Product"];
</code></pre>
<p>If any of the <em>dimensions</em> to a <strong>Cube</strong> can be viewed as a <em>Node</em>, the <code>_Cube</code> and <code>_Fact</code> elements become a <code>Node</code> with <em>Drilldown</em> <code>Edge</code> to each dimension.
This change enables <strong>Cube</strong> to be used to simplify a <em>graph</em>, even when the only thing being measured is the number edges being replaced.</p>
]]></description>
      <pubDate>Sat, 02 May 2026 17:55:45 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260424-1</guid>
    </item>
    <item>
      <title>Release 24th April 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260424</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update to support the presentation Cube data in  Graph views with <strong>Hiperspace.DB</strong>, and minor updated to external references.</p>
<h2>Fact enhancement</h2>
<p>A specific <code>ICubeFact&lt;TFact&gt;</code> inteface has been added to include <code>public IEnumerable&lt;TFact&gt; DrillDown(Type dimension);</code> for generic drilldown from one Cube to the next level when viewed in <strong>Hiperspace.DB</strong>.</p>
<p>The <code>ICubeDimension</code> interface now has a <code>GetCube()</code> function to get the named <code>ICubeFact</code> for use with a <strong>PivotTable</strong> view of the <em>Cube</em> through <strong>Hiperspace.DB</strong>.</p>
<h2>SubSpace enhancement</h2>
<p><code>SubSpaceParameters</code> has been enhanced to include <code>TransactionalPolicy</code></p>
<table>
<thead>
<tr>
<th>┃</th>
<th>Policy</th>
<th>┃</th>
<th>Notes</th>
<th>┃</th>
</tr>
</thead>
<tbody>
<tr>
<td>┃</td>
<td>NotTransactional</td>
<td>┃</td>
<td>Indicates that the Space is not transactional</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Transactional</td>
<td>┃</td>
<td>Specifies that a new transaction should be started</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>ParentTransaction</td>
<td>┃</td>
<td>Indicates that the space participates in the transaction of its parent space</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>ContinueTransaction</td>
<td>┃</td>
<td>Transaction is a continuation of a prior (long-running) transaction</td>
<td>┃</td>
</tr>
</tbody>
</table>
<hr>
<h2>Hilang enhancement</h2>
<p>Added support support for Cube aggregation of views in addition to {<em>entity, segment, aspect</em>}.</p>
<p>Consider the model where there are seperate <em>entitites</em> for {<em>FX, FI, EQ</em>} Trades that have different values for Entities, Bonds, Foreign Exchange, but can all be <em>viewed</em> as a Trade that has a booking_id, and a valuation (NPV).  This change adds support for <strong>Cube</strong> analytics of <em>views</em>  combining all trades in all entities that provide the view.</p>
<pre><code>type Banking.TradeBase
(
	Id : String
) 
{
	Book : Banking.Book,
	@CubeMeasure(Aggregate.Sum)
	NPV : Decimal
};

@CubeFact
view Banking.Trade : Banking.TradeBase;

entity Banking.FI.Trade : Banking.TradeBase = Banking.Trade();
entity Banking.FX.Trade : Banking.TradeBase = Banking.Trade();
entity Banking.EQ.Trade : Banking.TradeBase = Banking.Trade();

@CubeDimension
entity Banking.Book ( Id : String ) [ Trades : Banking.Trade (Book = this)];
</code></pre>
<p>When the schema is compiled with <a href="https://www.nuget.org/packages/HiLang">HiLang</a>, <code>Banking.Book</code> has an additional property <code>Trade_Cube</code>  that aggregates NPV over the full set trades that can be <em>viewed</em> as a <code>Banking.Trade</code>.</p>
<h3>Enhanced List support</h3>
<p>Keys in a hilang element can be:</p>
<ul>
<li>A Value including value elements defined in Hilang and primatives (e.g. String, Int32, Guid, etc)</li>
<li>A Reference to an entity (only the Key-part of the referened entity is serialised)</li>
<li>A List of Values (e.g. List&lt;String&gt;) but not a Set&lt;&gt; since a Set order can change</li>
</ul>
<p>Key support for <code>List&lt;&gt;</code> in keys has been extended to include Keys of Lists of Values of Lists of Values, that can be placed in a <code>SortedSet</code>. 	Examples include <a href="https://www.finos.org/common-domain-model">FINIOS CDM</a> that supports complex identifiers that include list of identifiers</p>
<pre><code>value CDM.PartyIdentifier ( identifier : String );
entity CDM.Party ( partyId : CDM.PartyIdentifier );
value CDM.PriceQuantity;
value CDM.AssetIdentifier
(
    identifer : String,
    version : Int32
);
value CDM.Identifer
(
    assignedIdentifier : List&lt;CDM.AssetIdentifier&gt;,
    issuerReference : CDM.Party,
    issuer : String
);
entity CDM.TradeLot
(
    lotIdentifier : List&lt;CDM.Identifer&gt;
)
{
    priceQuantity : List&lt;CDM.PriceQuantity&gt;
};
</code></pre>
<p>In this example <code>CDM.TradeLot</code> has a key that is a 	<code>List&lt;CDM.Identifer&gt;</code> which itself contains a <code>List&lt;CDM.AssetIdentifier&gt;</code>.  Lists are supported in <a href="https://www.nuget.org/packages/HiLang">HiLang</a> models since the they can be unambiguously serialised to a store and retrived later.  <strong>NB</strong> <code>CDM.Identifer</code> contains a reference to <code>CDM.Party</code> which is a <em>entity</em> so only the <em>key</em> is stored, with <code>CDM.Party</code> retrived only when needed.</p>
]]></description>
      <pubDate>Fri, 01 May 2026 19:22:14 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260424</guid>
    </item>
    <item>
      <title>Release 15th April 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260415</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update to support the presentation Cube data in  Graph views with <strong>Hiperspace.DB</strong>, and minor updated to external references.</p>
<h2>Fact enhancement</h2>
<p>Every instance of a <code>_Fact</code> or <code>_Cube</code> element now includes a <code>Facts</code> property that excludes <code>Node</code> that is a view of a fact but have no underlying value.</p>
<h2>Node Enhancement</h2>
<p><code>Node</code> now includes a <code>Measures</code> property, which is a <code>List&lt;MeasureValue&gt;</code> to eliminate the need to separately fetch measures when displaying <code>Cube</code> <code>Node</code> in a <strong>Hiperspace.DB</strong> graph display.  Normally a <em>graph</em> showing "<em>which client Sectors trade which products</em>" would involve connecting the edges <em>Sector-&gt;Client-&gt;Account-&gt;Trade-&gt;Leg-&gt;Asset-&gt;Instrument-&gt;Product</em> or <code>HiperEdge</code> <em>Sector=&gt;Trade=&gt;Product</em>, and then aggregate results.  When <em>Sector</em> and <em>Product</em> are <em><code>@CubeDimension</code></em> and <em>Trade</em> is a <em><code>@CubeFact</code></em> the aggregate values are available directly through <em>Sector-&gt;Trade_Cube-&gt;Product</em> with <em>streaming calculation</em> in <strong>Hiperspace.DB</strong>.  Displaying aggregate values in a graph is so useful for analysis that it is worth extending the <code>Node</code> model to include them as an optional property.
<img src="/hiperspace/media/Sites/hiperspace/cube/hipercube.png"></p>
<blockquote>
<p><strong>NB</strong> This diagram is from a <code>HiperGraph</code> of the Sector "All", Product hierarchy aggregates are connected to the Sector hierarchy (without spider connections)</p>
</blockquote>
<h2>Hilang enhancement</h2>
<ul>
<li>Added support for <code>enum</code> values in messages.</li>
<li>Resolved issue with self-referencing messages <code>message Dimension { Branches : List&lt;Dimension&gt; };</code></li>
<li>Name resolution of types now uses global namespace resolution to avoid clashes.</li>
</ul>
<h2>SubSpace enhancement</h2>
<p>Currently Blazer Web Assembly clients cannot (<em>currently</em>) support threads and therefore cannot perform synchronous IO - <code>FindAsync()</code> and <code>GetAsync()</code>  requests can be routed (<em>over <strong>gRpc</strong></em>) to a remote store, <code>Find()</code> and <code>Get()</code> request are therefore <em>cache-local</em>. the test for <code>OperatingSystem.IsBrowser()</code> has been replaced  with a <code>CachePolicy</code> that default to <em><code>CachingPolicy.Cache</code></em> for Browser and <em><code>CachingPolicy.Space</code></em> otherwise.  This allows <code>message</code> to be used to <em>pre-fetch</em> dependencies on the server-side in addition to browser.</p>
]]></description>
      <pubDate>Wed, 15 Apr 2026 21:55:38 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260415</guid>
    </item>
    <item>
      <title>Release 1st April 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260401</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release includes minor enhancement to the graph functionality when used with <strong>Hiperspace.DB</strong> graph view.  <strong>Hiperspace.DB</strong> provides a <em>graph</em> view that aggregates alll databases, so that graph shows all connections both local and foreign.   In the following example <em>Togaf.Has.WorkPackage</em>  includes a value <em>ProjectKey</em> that could be a stringified <code>SKey</code> reference to a project plan in another database.  When the <em>project</em> is viewed as a graph in  <strong>Hiperspace.DB</strong>, the graph includes paths between a <em>project plan</em> and <em>strategic goals</em>.</p>
<pre><code>segment Togaf.Has.WorkPackage : Togaf.Base 
    = Node        ( SKey = SKey, Name = Name, TypeName = "AF-WorkPackage"),
      Edge        (From = owner, To = this, Name = Name, TypeName = "AF-Has-WorkPackage"),
      Togaf.Edge_ (To = owner, From = this, Name = Name, TypeName = "AF-WorkPackage-For"),
      Togaf.Edges (From = this, To = Project, Name = Name, FromTypeName = "AF-WorkPackage-Plan", ToTypeName = "AF-Plan-WorkPackage"),
      Graph.HiperEdge = StrategicEdges #2675 
{
    ProjectKey      : String #29 
}
[
    "All Togaf.Edges that can be projected as Transitative Togaf.Edges to a Business Goal"
    @Once
    StrategicEdges  = StrategicEdge(this),
    Project         = externalnode(ProjectKey)
];
</code></pre>
<p>For efficiency, the <code>SKey</code> property has been changed from a <a href="https://en.wikipedia.org/wiki/Base64">Base64</a> encoding of the <em>key</em> to add the option of a database prefix.  The <code>Node SKey</code> "Togaf.ascxyz==" will only search for matching items "abcxyz==" in the <em>Togaf</em> database.  Within the <em>Togaf</em> database, only the specific segment is searched because the <em>key</em> includes the <code>#Id</code> of the element set.</p>
<p>the following SQL query would return a table of all <em>project plans</em> and <em>Goals</em> that can be reached through the {component, service, function, process, capability, etc} that are in some way associated with the <em>Work Package</em></p>
<pre><code>SELECT project.Name AS "Project Name"
     , goal.Name AS "Goal Name"
     , project.SKey as "Project Id"
     , goal.SKey as "Goal Id"
FROM   Nodes as project
     , project.Tos as planedge
     , planedge.To as workpackage
     , workpackage.Tos as workedge 
     , workedge.To as goal
WHERE  project.TypeName = 'Plan'
  AND  planedge.TypeName = 'AF-Plan-WorkPackage'
  AND  workpackage.TypeName = 'AF-WorkPackage'
  AND  workedge.TypeName = 'Strategic-Goal'
  AND  goal.TypeName = 'AF-Goal';
</code></pre>
<p>NB the five table join uses <em>implicit</em> joins in  <code>Hiperspace.SQL</code> because <code>project.Tos</code> and <code>workpackage.Tos</code> are <code>Set&lt;Edge&gt;</code> properties that are <em>implicitly</em> filtered by the source <code>Node</code></p>
]]></description>
      <pubDate>Wed, 01 Apr 2026 17:09:37 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260401</guid>
    </item>
    <item>
      <title>Release 20th March 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260320</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release introduces two significant enhancements to hyperspace, to support <a href="https://en.wikipedia.org/wiki/Multiversion_concurrency_control">Mult-version concurrency control</a> in <strong>Hiperspace.DB</strong>, and <a href="https://mermaid.js.org/">Mermaid</a> <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> documentation for <strong>#AI</strong> agents.</p>
<h2>Multi-Version Concurrency Control (MVCC)</h2>
<p>MVCC is rarely needed for normal Hiperspace transactions because it is possible to use <a href="https://en.wikipedia.org/wiki/Command_Query_Responsibility_Segregation">CQRS</a> to apply a change to <em>elements</em> in Hiperspace that are invisible to other sessions (using <code>Horizon</code> filters), but this is not practical for <strong>Cube</strong> aggregation, where it would be possible to have inconsistent aggregates while updates are applied in real-time.</p>
<p>This implementation of MVCC follows the <a href="https://en.wikipedia.org/wiki/SQL">ISO SQL</a> standard that every database change is enrolled in a transaction and only visible to other sessions when the <em>transaction</em> is committed and backed-out on rollback.  There are no nested transactions, since the implementation (in <strong>Hiperspace.DB</strong>) is efficient and combined with the <code>@DeltaIndex</code> functionality for efficient point-in-time access.</p>
<p>For local <em>hiperspaces</em> (e.g. <a href="https://www.nuget.org/packages/Hiperspace.Rocks">Hiperspace.Rocks</a>) transactions are automatic, and <code>Rollback()</code> will return false</p>
<pre><code>public bool Commit() =&gt; Commit(Transaction);
public bool Rollback() =&gt; Rollback(Transaction);
</code></pre>
<h2>MarkDown documentation with HiLang</h2>
<p>The <a href="https://www.nuget.org/packages/HiLang">HILang</a> <em>schema</em> compiler generates a <em>Domain Specific DataBase</em> that can be used in a client application (including web-assembly) for object persistence either to a local hiperspace (e.g. <a href="https://www.nuget.org/packages/Hiperspace.Rocks">Hiperspace.Rocks</a>) or remotely using <strong>Hiperspace.DB</strong> either with <em>lazy loading</em> of reference or <em>eager loading</em> using <code>message</code>.  Message is analogous to stored procedures that execute on the server (using message <code>key</code>) and return full object <code>value</code> in response using a shared DSDB code.</p>
<p>Code is generated during compilation to produce a DSDB with a number of transformations:</p>
<ul>
<li>Segments (many) are specialized for the Element that references them</li>
<li>Aspects (one) are specialized for the Element that references them</li>
<li><code>@CubeFact</code> elements are transformed adding properties for aggregation</li>
<li><code>@CubeFact</code> elements are transformed to provide <code>_Cube</code> aggregates and <code>_Fact</code> cube cell aggregates</li>
<li><code>@CubeDimension</code> elements are transformed to include <em>DrillDown</em> to aggregates via <em>lazy</em> property</li>
</ul>
<p>These transformations are not visible to <strong>AI Agents</strong> because the generated code is ephemeral (<em>using the Indonesian word <strong>hilang</strong></em>).  For <em>agent-based development</em>, the HiLang schema can be generated to markdown files using the <code>%markdown</code> directive in source.</p>
<h3>Schema.md</h3>
<p>For each <code>.hilang</code> schema file a matching <code>.md</code> file is generated containing formatted documentation in a <strong>AI</strong> friendly form, with a <em>mermaid</em> diagram of the data-model.  This documentation file matches the structure of the <code>.hilang</code> schema</p>
<h3>Schema-physical.md</h3>
<p>For each <code>.hilang</code> schema file a matching <code>.md</code> file is generated containing formatted documentation in a <strong>AI</strong> friendly form, with a <em>mermaid</em> diagram of the data-model.  This documentation file matches the generated classes, including all the transformations referenced above</p>
<h3>Schema-cube.md</h3>
<p>This markdown file contains only <code>_Cube</code> elements and the related <em>dimensions</em></p>
<h3>Schema-graph.md</h3>
<p>This markdown file contains only graph (<code>Node</code> and <code>Edge</code>) elements and a diagram of relations</p>
]]></description>
      <pubDate>Fri, 20 Mar 2026 15:54:39 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260320</guid>
    </item>
    <item>
      <title>Release 27th February 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260227</link>
      <description><![CDATA[<h1>Overview</h1>
<h2>Cube Enhancements</h2>
<p>A minor enhancement has been made to fully qualify references to <strong>Hiperspace.Functions</strong>. This prevents naming conflicts when a domain also uses <strong>Functions</strong>.</p>
<p>When one of a Cube’s <strong>Dimensions</strong> is <code>Node</code>, the <code>Node</code> view is added to the Cube along with <code>Edge</code> definitions that represent navigation between the <strong>Dimension</strong> and the Cube. In this release, the naming of these edges has been updated to ensure consistent navigation through a HiperGraph, using a common TypeName convention:</p>
<ul>
<li>TypeName <code>Cube:Drilldown</code> for navigation from a  <em>Dimension</em> to the <em>Cube</em>.</li>
<li>TypeName <code>Cube:Dimension</code> for navigation from the <em>Cube</em> to a <em>Dimension</em>.</li>
</ul>
<h3>Example</h3>
<p>In the <a href="https://github.com/channell/Hiperspace/blob/master/examples/Cube/Cube.hilang">Cube Example</a> <em><strong>Customer Sector</strong></em> and <em><strong>Product</strong></em> are <em>Dimensions</em> that can also be viewed as <code>Nodes</code>. The <em><strong>Contract Cube</strong></em> therefore has <code>Node</code> and <code>Edges</code> added as a views because.</p>
<p><img src="/hiperspace/media/blogs/Blogs/Butterfly/product-model.png"></p>
<ul>
<li><em><strong>Contact</strong></em> (<em>Cube</em>) has one <em><strong>Account</strong></em> and <em><strong>Account</strong></em> has one <em><strong>Customer</strong></em> and <em><strong>Customer</strong></em> has one <em><strong>Sector</strong></em> (<em>Dimension</em>), it is inferred that  <em><strong>Sector</strong></em> is a <em>Dimension</em> of <em>Cube</em> <em><strong>Contact</strong></em>.</li>
<li><em><strong>Contact</strong></em> (<em>Cube</em>) has one <em><strong>Instrument</strong></em> and <em><strong>Instrument</strong></em> has one <em><strong>Product</strong></em>, it is inferred that <em><strong>Product</strong></em> is a <em>Dimension</em> of <em>Cube</em> <em><strong>Contract</strong></em>.</li>
</ul>
<p>When viewed through a <strong>HiperGraph</strong> the <em>Cube</em> becomes a <code>Node</code> that links <em><strong>Sector</strong></em> <em>node</em> to <em><strong>Product</strong></em> <em>Node</em></p>
<p>If you <em>drilldown</em> from <em><strong>Product</strong></em> to <em><strong>Contact_Cube</strong></em> and <em>drilldown</em> to <em><strong>Sector</strong></em>, the shortest <em>HiperEdge</em> from <em><strong>Product</strong></em> to <em><strong>Sector</strong></em> is through the <em><strong>Contract_Cube</strong></em> slice that summarizes the connection between the two <code>Nodes</code>.  We call this combination of <em>HiperGraph</em> and <em>Cube</em> "<em>HiperCube</em>", and is useful for <strong>AI</strong> prompts like <em>"which sector uses most widgets?"</em></p>
<p><img src="/hiperspace/media/blogs/Blogs/Butterfly/product-graph.png"></p>
]]></description>
      <pubDate>Fri, 27 Feb 2026 20:35:06 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260227</guid>
    </item>
    <item>
      <title>Release 14th February 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260214</link>
      <description><![CDATA[<h1>Overview</h1>
<h2>Cube enhancements</h2>
<p><code>@CubeDimension</code> and <code>@CubeHierarchy</code> updated to better support generic access to Cube data from Graph Pivot-Table view.</p>
<ul>
<li><code>@CubeDimension</code> name parameter now refers to the property that provides the name of the element in a Pivot-Table dimension.</li>
<li><code>@CubeHierarchy</code> has additional parameters for <em>Children</em> and <em>Level</em></li>
</ul>
<p>Create GPU Accelerated Graph Search introduced a problem for cubes in that every single permutation of aggregate would be evaluated to produce the Graph cache resulting in:</p>
<ul>
<li>A dense MOLAP cube</li>
<li>Expensive invalidation when cube <em>facts</em> change</li>
</ul>
<p>The  <strong>Cube</strong> model has been split into <em>_Fact</em> entity containing just base aggregates projected from <code>@CubeFact</code> elements, and <em>_Cube</em> elements that are calculated <em>on demand</em> for summaries.</p>
<p><em>Drilldown</em> performed well when then was a very small number of dimensions and hierarchy levels, but poorly when the complexity increases.  The mode has been changed from using <em>Drilldown</em> as an index for aggregate calculation  to a scan of matching <em>facts</em></p>
<h3>Is Fact</h3>
<p><code>IsFact</code> attribute of <em>_Cube</em> and <em>_Fact</em> has been removed, as it is no longer needed.  This is a help for BI access of Cube data via ODATA.</p>
<hr>
<h3>Cube Hierarchy</h3>
<p>Keep hierarchy has been enhanced to include the name of the children property in addition to parent and add level to support aggregation of cube summaries</p>
<p>An example dimension is Organization that is structured as a hierarchy with <em>Parent</em> as a reference to the aggregation line, and <em>Children</em> as a collection of organization units below the current node.  When referenced by a <code>@CubeFact</code> element, the aggregate values of all <code>@CubeMeasure</code>s members show the aggregate value of the <em>node</em> and all <em>child nodes</em></p>
<pre><code>@CubeDimension, CubeHierarchy(Parent, Children, Tier)
entity Organization (Name : String) {Description : String, Parent : Organization, Tier : Int32} [Children : Organization (Parent = this)];
</code></pre>
<p>The generated code implements <code>@CubeHierarchy</code> using the <code>ICubeHierarchy&lt;TEntity&gt;</code> that now includes</p>
<table>
<thead>
<tr>
<th>┃</th>
<th>Name</th>
<th>┃</th>
<th>Notes</th>
<th>┃</th>
</tr>
</thead>
<tbody>
<tr>
<td>┃</td>
<td>Parent</td>
<td>┃</td>
<td>Reference to parent, that must be the same type as the element</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Children</td>
<td>┃</td>
<td>Collection of child nodes that must be the same type as the element</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>AllPartents()</td>
<td>┃</td>
<td>Function that recursively yields the entire hierarchy upto root node</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>AllChildren()</td>
<td>┃</td>
<td>Function that 	recursively  yields every child node and all their child nodes</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Level</td>
<td>┃</td>
<td>Level in Hierarchy for <strong>Pivot Table</strong> views that have separate tabs for each level of the hierarchy</td>
<td>┃</td>
</tr>
</tbody>
</table>
<hr>
<h3>Cube Measure</h3>
<p><code>@CubeMeasure</code> has been enhanced include <a href="https://en.wikipedia.org/wiki/Standard_deviation">Population Standard Deviation</a> and <a href="https://en.wikipedia.org/wiki/Percentile">Ranked Percentile</a> aggregates.  <code>Aggregate.StdDev</code> and <code>Aggregate. Percentile</code> use the <code>Vector</code> pseudo-aggregate member that includes every observation in the <em>_Fact</em> element for precise aggregation at the summary level.</p>
<pre><code>@CubeFact, DeltaIndex 
aspect Valuation {@CubeMeasure(Aggregate.Sum) NPV : Decimal} 
[@CubeMeasure(Aggregate.StdDev) Deviation = NPV, @CubeMeasure(Aggregate.Percentile, 95) PFE = NPV];
</code></pre>
<p>is equivalent to</p>
<table>
<thead>
<tr>
<th>┃</th>
<th>Name</th>
<th>┃</th>
<th>Notes</th>
<th>┃</th>
</tr>
</thead>
<tbody>
<tr>
<td>┃</td>
<td>NPV</td>
<td>┃</td>
<td>Total across dimensions, up through hierarchies</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Deviation</td>
<td>┃</td>
<td>Populate standard deviation for all elements</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>PFE</td>
<td>┃</td>
<td>The top 95% of all simulated scenarios across the entire set at each  level</td>
<td>┃</td>
</tr>
</tbody>
</table>
<p>When the <em>Hiperspace</em> is hosted using <strong>Hiperspace.DB</strong> the <em>_Fact</em> and <em>_Cube</em> elements are automatically recalculated:</p>
<ul>
<li>When a <em>Fact</em> changes the <strong>_Fact</strong> entity is recalculated with its <em>dimension</em> references</li>
<li>When an element that a <em>Fact</em> is dependent on changes the <strong>_Fact</strong> entity is recalculated</li>
<li>When a <strong>Dimension</strong> changes, any <strong>_Cube</strong> elements that reference it are recalculated.</li>
</ul>
<p>Because <strong>_Facts</strong>  and <strong>_Cubes</strong> are recalculated using a <em>delta</em> of changed elements in real-time, it is possible for <strong>Facts</strong> to be calculated from other aggregated <strong>Facts</strong>. Both <em><strong>Facts</strong></em> and  <em><strong>Cubes</strong></em> track history  through time (<em>using <code>GetVersions()</code> functions</em>)</p>
<hr>
<p>Relase also includes updates to reference assemblies</p>
]]></description>
      <pubDate>Sat, 14 Feb 2026 16:09:14 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260214</guid>
    </item>
    <item>
      <title>Release 30th January 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260130</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is focused on improvements to the Cube functionality to provide a foundation for viewing Cube's through Hiperspace.DB's Blazor.   The Blazor explorer uses</p>
<ul>
<li>Cube Dimension Name to display the next of the element in a PivotTable view</li>
<li>Cube Hierarchy Level to enable the PivotTable to view to drilldown through hierarchies of elements</li>
</ul>
<p>For  <em>Portfolio</em> Cube dimension with three levels in the hierarchy, the cube will be displayed in a Pivot -Table as {Portfolio 1, Portfolio 2, Portfolio 3}, and support drilldown from the highest level</p>
<pre><code>@CubeDimension(Id), CubeHierarchy(Parent, Tier), Versioned
entity Cube.Portfolio 
	= Node (SKey = SKey, Name = Id, TypeName = "Portfolio"),
	  Cube.Edges (From = this, To = Parent, FromTypeName = "Portfolio-Parent", ToTypeName = "Portfolio-Child", Name = Id)
(Id : String) 
{ Parent : Cube.Portfolio, Tier : Int32 } 
[Children : Cube.Portfolio (Parent = this)];
</code></pre>
<h2>Hiperspace</h2>
<p><code>CubeDimension</code> has been updated to include a reference to the name that should be used when the Cube is viewed in a Piviot-table view.
<code>CubeHierarchy</code> has been updated to include a level indicator to allow Pivot-table views to navigate through hierarchy of elements</p>
<h2>HiLang</h2>
<p>###empty view enhancement
The view <code>Cube.Edge_</code> and <code>Cube.Edges</code> could be used as helpers to provide additional ways to project <em>elements</em> as graph views, but if they are not referenced elsewhere in the model, they will be pruned and not have any code generation.</p>
<pre><code>"An additional edge helper for Cube.Edges"
view Cube.Edge_ : Edge = Edge();

"Bidirectional Edge, implemented with two Cube.Edges"
view Cube.Edges
    = Edge  (From = From, To = To, Name = Name, TypeName = FromTypeName),
      Cube.Edge_ (From = To, To = From, Name = Name, TypeName = ToTypeName)
(
    From            : Node      #7,
    To              : Node      #8,
    FromTypeName    : String    #9,
    ToTypeName      : String    #10
)
{
    Name        : String    #6,
};
</code></pre>
<p>This presents a problem for the generation of the <code>Edge</code> subspace, since it expects to add a base reference to the pruned view.  A fix has been added to the HiLang compiler to remove the dependency.  The compiler pipeline has been changed to continue process <em>transformations</em> to prevent spurious errors being created from <em>validation</em> that depends on prior <em>transformations</em> .</p>
]]></description>
      <pubDate>Fri, 30 Jan 2026 23:15:26 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260130</guid>
    </item>
    <item>
      <title>Release 17th January 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260117</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release focuses on foundations to support of the migration of data from the current model of using key prefix for exceptions ("00" MetaModel, "0.." versioned, "00.." vector, "000.." sequence), to using the first-byte as a prefix for key-type.  To address this need, <strong>dump / load</strong> functions have been added to <code>SubSpace</code> for JSON data interchange"</p>
<h2>SubSpace</h2>
<p>The JSON format is structured as <code>{ Key : Element { ....}, AsAt : timestamp, Value : Element { ....}}}</code></p>
<ul>
<li>IEnumerable&lt;string&gt; Dump() : <em>dump the content of the hiperspace to JSON</em></li>
<li>Load(IEnumerable&lt;string&gt; source) : <em>load the JSON to Hiperspace</em></li>
</ul>
]]></description>
      <pubDate>Sat, 17 Jan 2026 21:37:20 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260117</guid>
    </item>
    <item>
      <title>Release 31st December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251231</link>
      <description><![CDATA[<h1>Overview</h1>
<p>Change to the <code>GetSequence</code> and <code>UseSequence</code> functions to take a parameter to allow the use of <code>KeyType</code> for Sequences</p>
<p>Extended  <code>Session2(DateTime? AsAt = null)</code>  to <code>Session2(DateTime? AsAt = null, DateTime? DeltaFrom = null)</code> to allow the DeltaFrom parameter to be used in addition to AsAt.</p>
<h2>Obsolete</h2>
<p>Several interfaces function have been marked as <code>Obsolete</code> and will be removed in the next version.</p>
<p>Currently <strong>Hiperspace</strong> uses a key prefix convention to distinguish <em>immutable</em> and <em>versioned</em> elements, <em>VectorSpaces</em> (for nearest neighbor search) and <em>sequences</em></p>
<table>
<thead>
<tr>
<th>│</th>
<th>Prefix</th>
<th>│</th>
<th>Description</th>
<th>│</th>
</tr>
</thead>
<tbody>
<tr>
<td>│</td>
<td>00</td>
<td>│</td>
<td>The meta data dictionary for the <em>Hiperspace</em>, used to ensure that incompatible libraries cannot corrupt a store</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>0...</td>
<td>│</td>
<td>Versioned element that includes a <em>timestamp</em> suffix at the end of the key</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>00...</td>
<td>│</td>
<td>VectorSpace indexes</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>001...</td>
<td>│</td>
<td>Sequence numbers for mutable items</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td></td>
<td>│</td>
<td>Keys without a prefix are immutable elements without version</td>
<td>│</td>
</tr>
</tbody>
</table>
<p>Performance testing of <strong>Hiperspace.DB</strong>  has highlighted that the rebuilding of the <code>MetaMap</code> used to translate between protobuf <em>tag/length/value</em> to <em>tag/value/length</em> for index search can be expensive top build when a store has experienced multiple schema changes.  The <strong>next</strong> version will change the prefix to:</p>
<table>
<thead>
<tr>
<th>│</th>
<th>Prefix</th>
<th>│</th>
<th>Description</th>
<th>│</th>
</tr>
</thead>
<tbody>
<tr>
<td>│</td>
<td>1</td>
<td>│</td>
<td>The meta data dictionary for the <em>Hiperspace</em>, used to ensure that incompatible libraries cannot corrupt a store</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>2</td>
<td>│</td>
<td>cached <code>MetaMap</code> entries for key encode/decode</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>3</td>
<td>│</td>
<td>validated client library <em>fingerprint</em> to avoid the need for <code>MetaMap</code> build</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>4</td>
<td>│</td>
<td>immutable elements without version suffix</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>5</td>
<td>│</td>
<td>Versioned element that includes a <em>timestamp</em> suffix at the end of the key</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>6</td>
<td>│</td>
<td>VectorSpace indexes</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>7</td>
<td>│</td>
<td>Sequence numbers for mutable items</td>
<td>│</td>
</tr>
</tbody>
</table>
<p>The <code>#id</code> for {<code>Node</code>, <code>Edge</code>, <code>VectorNode</code>, <code>HiperEdge</code>,<code>PathMessage</code>} will also be moved to a <em>reserved</em> set of low values with all <em>domain</em> keys moved to higher values</p>
<hr>
<h2>SetSpace</h2>
<p>Added two functions to get a sequence counter from the driver:</p>
<ul>
<li><code>GetSequenceAsync&lt;T&gt;(T element)</code> to get the current sequence number associated with the domain key</li>
<li><code>UseSequenceAsync&lt;T&gt;(T element)</code> to get a new  sequence number for the with the domain key</li>
</ul>
<p><strong>NB</strong> While the main use of Sequence numbers is to provide an alternate (<em>GPGPU friendly</em>) sequence number for a <code>Node</code>, it can be applied to any key value.</p>
<pre><code>// get an order number that is unique within all orders
var orderNo = await space.Orders.UseSequenceAsync (new Order { });
var orderNo = await space.Orders.UseSequenceAsync (new Order.KeyType { });

// get a line number within the owning Order {1,2,3..}
var lineNo = await space.Lines.UseSequenceAsync (new Line { Order = order }); 
var lineNo = await space.Lines.UseSequenceAsync (new Line.KeyType { Order = order.self }); 
</code></pre>
<p><code>Get</code> and <code>GetASync()</code> functions now handle not found errors by returning <code>null</code> for scenarios where a reference object cannot be found.  This is especially useful for views where not found is better handled by <code>Horizon&lt;&gt;</code> filters that</p>
<hr>
<h2>Bind Version</h2>
<p>All versions of Bind Function have been marked as Obsolete, with a preference to use the optimistic locking instead (that include the original AsAt copied to AsWas for comparison).
Versions of Bind functions that include the <code>priorVersion</code> DateTime value use optimistic concurrency control to prevent updates to a version that has been changed by another session</p>
<h2>Nearest</h2>
<p>Hiperspace provides nearest neighbor search using a VectorSpace <code>aspect</code> for geospatial and RAG search.  These functions have been extended to include a maximum distance search</p>
<hr>
<h2>Hilang</h2>
<p>The Hilang ahead of time compiler has been updated to remove indexes that are a subset of the element key, and will not be used</p>
<hr>
<h2>Hiperspace.Rocks</h2>
<p>The <strong>RocksDB</strong> driver has been upgraded to verion 10.4.2.63147</p>
]]></description>
      <pubDate>Wed, 31 Dec 2025 17:15:47 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251231</guid>
    </item>
    <item>
      <title>Release 21st December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251221</link>
      <description><![CDATA[<h1>Overview</h1>
<p>Change method signatures for Path functions to use Route instead of RouteMap for remote calls.</p>
<p>Add Session2 to Subspace to support time-travel sessions over a graph space to support partial refresh of GPU cache for GPU parallel search of Graph methods without the need for recursive cycle detection.</p>
<hr>
<h2>Sequence support</h2>
<p>Sequence numbers are a relational database feature that has largely been replaced by <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUID</a> (<em>especially v7</em>) since they do not require a sequence source and can be allocated within a client.  They still have a use as an alternate key especially with <a href="https://en.wikipedia.org/wiki/General-purpose_computing_on_graphics_processing_units">GPGPU</a> that need primitive points, <em>but do not wish to use the expense of mapping UUID to int128 values that may not be supported by the device</em></p>
<p><strong>Hiperspace</strong> implements <em>sequences</em> as unsigned 64-bit integers (<em>1 to 18,446,744,073,709,551,616</em>), and can be stored for any <em>element</em> key value</p>
<h3>Hiperspace</h3>
<p>Added <code>UseSequenceAsync()</code> for driver implementation, together with proxy methods in {<code>BrowserSpace, FailoverSpace, GenerationSpace, PartitionSpace, SessionSpace</code>}</p>
<h3>Hiperspace.Heap</h3>
<p>Implemented  <code>UseSequenceAsync()</code> in <code>Hiperspace.Heap</code> and added additional driver <code>CacheHeapSpace</code> for use by caches that do not require history to be maintained for elements. <strong>CacheHeapSpace</strong> should <em>only</em> be used by <em>caches</em> since it lacks the ability to reconcile concurrent changes through version inspection.</p>
<p><code>LatestSpace</code> is replaced by <code>CacheHeapSpace</code> and will be removed in a future release.</p>
<h3>Hiperspace.Rocks</h3>
<p>Implemented UseSequenceAsync()<code>in</code>Hiperspace.Rocks<code>and added additional driver</code>CacheRockSpace` for use by caches that do not require history to be maintained for elements. <strong>CacheRockSpace</strong> should <em>only</em> be used by <em>caches</em> since it lacks the ability to reconcile concurrent changes through version inspection.</p>
<p>The underlying RocksDB driver has been updated.</p>
<h3>SubSpace</h3>
<p><code>SubSpace</code> forwards the <code>UseSequenceAsync()</code> function to the underlying driver</p>
<h3>SetSpace</h3>
<p>Added two functions to get a sequence counter from the driver:</p>
<ul>
<li><code>GetSequenceAsync&lt;TEntity&gt;(TEntity? element)</code> to get the current sequence number associated with the domain key</li>
<li><code>UseSequenceAsync&lt;TEntity&gt;(TEntity? element)</code> to get a new  sequence number for the with the domain key</li>
</ul>
<p><strong>NB</strong> While the main use of Sequence numbers is to provide an alternate (<em>GPGPU friendly</em>) sequence number for a <code>Node</code>, it can be applied to any key value.</p>
<pre><code>// get an order number that is unique within all orders
var orderNo = await space.Orders.UseSequenceAsync (new Order { });

// get a line number within the owning Order {1,2,3..}
var lineNo = await space.Lines.UseSequenceAsync (new Line { Order = order }); 
</code></pre>
<hr>
<h3>HiLang</h3>
<p>Added code generation for the SubSpace methods.</p>
<hr>
<h2>OnInitialized</h2>
<p>Addition of <code>virtual void OnInitialized()</code> function to SubSpace to allow domian subspaces to perform additional initialization after the Domain Space has been created by a generic container (e.g. <strong>Hiperspace.DB</strong>)</p>
]]></description>
      <pubDate>Mon, 22 Dec 2025 12:40:54 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251221</guid>
    </item>
    <item>
      <title>Release 10th December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251210</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release introduces GPU optimization for graph searches involving <code>HiperEdge</code> and other vector operations used in nearest-neighbor searches (<em>commonly required when providing information for AI prompts</em>).</p>
<p>It also enhances <em>Hiperspace</em> functions for LINQ queries by adding automatic null-coalescing for values selected from Hiperspace, supporting non-sargable join conditions, and improving performance for multi-table joins and <strong>HiperspaceDB</strong> cube aggregation.</p>
<hr>
<h2>CalculationGPU</h2>
<p><a href="https://en.wikipedia.org/wiki/Graph_theory">Graph Theory</a> is a branch of Mathematics that can be used to describe all knowledge as <code>Node</code> and <code>Edge</code>'s  between them; because there is a simple consistent model across domains it has wide application for visualizing information, but has the downside that many graph views appear as a tangled web of nodes and lines: <em>often</em> conveying complexity, <em>but not</em> clarity.</p>
<p><a href="https://en.wikipedia.org/wiki/Hypergraph">HyperGraph</a>  is a generalization of <a href="https://en.wikipedia.org/wiki/Graph_theory">Graph</a> that replaces the tangle of edges with a <em>hyperedge</em> that encapsulates all the intermediate nodes that are not interesting: I have a <em>cousin</em> (Robert), which can be described as "<code>Me-&gt;(cousin)-&gt;Robert</code>", but <em>cousin</em> is actually a <em>hyperedge</em> that is derived from "<code>me-&gt;mother2-&gt;mother1-&gt;daughter-&gt;son where mother2 is not daughter</code>"</p>
<p><a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a> is a <em>HiperSpace</em> implementation of <em>hyperedge</em> that encapsulates the source of the <em>hyperedge</em> for a simple view of connections <em><strong>or</strong></em> the intermediate nodes in the path. When viewed graphically a <code>HiperEdge</code> view looks like a tree of connections extending out from the subject <code>Node</code>.</p>
<p>Hiperspace provides functions to query  <a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a>s using rules that project {<em>source node type, edge type, end node type</em>} to create <a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a>s whenever needed (<em>e.g. as a member function of a <code>Node</code></em>).</p>
<h3><em>Graph support</em></h3>
<p>This version introduces <code>ICalculationGPU</code> for optional acceleration of Graph Queries using the hardware created for <a href="https://en.wikipedia.org/wiki/Ray_tracing_(graphics)">Ray Tracing</a>,</p>
<h3><em>Calculation support</em></h3>
<p>Search for <em>Nearest</em> node proximity in a <a href="https://en.wikipedia.org/wiki/Vector_space">Vector Space</a> is supported via GPU, together with aggregation of Vectors used for complex calculation.</p>
<hr>
<h3>Null Coalesce</h3>
<p>Consider a cube analytics model with {<em>Sector, Customer, Account, Trade</em>} with dimensions <em>Customer</em> and <em>Sector</em>; with <em>Trade</em> Facts aggregated. Null-coalescing is necessary when querying dimensions associated with a <em>Trade</em> fact.</p>
<pre><code>@CubeDimension, CubeHierarchy(Parent), Versioned
entity Sector 
  (Id : Int32) 
  {Name : String, Parent : Sector} 
  [Children : Sector (Parent = this), Customers : Customer (Sector = this)];

@CubeDimension, DeltaIndex
entity Customer 
  (Id : String) 
  {Sector : Sector}
  [Name : String, Accounts : Account (Customer = this)];
entity Instrument;

entity Account 
  (Id : String) 
  {Name : String, Customer : Customer};

@CubeFact, DeltaIndex
entity Trade : Versioned 
  (id : Int64) 
  {@CubeMeasure(Aggregation.Sum) Quantity : Decimal, Instrument : Instrument, Account : Account};
</code></pre>
<p><img src="/hiperspace/media/Sites/hiperspace/sample/trade-null.png"></p>
<p>The query  <code>from trade in Space.Trades select trade.Account.Customer.Sector</code> provides the <em>Sector</em> for a trade, but if any {<em>Trade.Account, Account.Customer, Customer.Sector</em>} is missing the expression will break.  The work around is to manually add null-coalesce to the query:</p>
<pre><code>from trade in Space.Trades 
select trade.Account ==  null ? null :  
       trade.Account.Customer == null ? null :
       trade.Account.Customer.Sector;
</code></pre>
<p><strong>This release adds coalesce automatically</strong>. This is especially useful when Hiperspace.SQL is used to query a <em>hiperspace</em> from a scripting language where null-coalesce is not available, but we want to access the <em>graph</em> of properties.</p>
<pre><code>select c.Id as SectorId, a.Id as AccountId
from Account as a join 
     Customer as c on a.Customer.Id = c.Id
group by c.Sector.Id, a.Id;
</code></pre>
<p>This query would have <em><strong>previously</strong></em> failed if <code>a.Customer</code> is missing.</p>
<hr>
<h3>Non-Saragable predicates</h3>
<p>In <em>Hiperspace</em> the following query cannot be <a href="https://en.wikipedia.org/wiki/Sargable">Sargable</a> since <em>Customer</em> does not contain a <em>Sector</em> but a <code>KeyRef&lt;Sector.KeyType, Sector&gt;</code> with <em>lazy</em> loading of the element if needed.</p>
<pre><code>select Sector.Name as "Sector Name", c.Name as "Customer Name"
from   Sector join
       Customer on Sector.Name = Customer.Sector.Name
order by Sector.Name, Customer.Name;
</code></pre>
<p>This query would <em><strong>previously</strong></em> have failed since <code>Customer.Sector</code> is a derived property of <em>Customer</em> and cannot be applied to a template for a <code>Find()</code> request of matching <em>Customers</em></p>
<p>This release splits join processing into <a href="https://en.wikipedia.org/wiki/Sargable">Sargable</a> predicates and residual conditions and is constructed dynamically as compiled expressions.</p>
]]></description>
      <pubDate>Wed, 10 Dec 2025 21:31:56 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251210</guid>
    </item>
    <item>
      <title>Release 3rd December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251303</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release adds <code>NotFoundException</code> to distinguish <em>not found</em> from <em>cannot be found</em> conditions. and extends the functionality of <code>@AlternateIndex</code> to support multiple alternate indexes on <em>segments</em> and <em>aspects</em> that are referenced by multiple <em>entities</em>.</p>
<hr>
<h3>Not Found</h3>
<p>Prior to this release <code>Get(...)</code> calls did not distinguish between <em>Not Found</em> and <em>not found because of IO error</em>.  To improve the handling of missing values several changes have been made:</p>
<ul>
<li>Additional Exception class <code>NotFoundException</code></li>
<li><code>KeyRef&lt;&gt;</code> (<em>reference to another element</em>) changed to return <code>null</code> when a value cannot be found</li>
<li><code>RefSingle&lt;&gt;</code> (<em>reference to an aspect</em>) changed to return <code>null</code> when a value cannot be found</li>
</ul>
<hr>
<h3>AlternateIndex</h3>
<p>Alternate indexes are created automatically whenever there is a path from an <em>element</em> from another <em>element</em>, but can be added to support access from a view.  The prime example is <code>Edge</code> which is defined (<em>in the Hilang prelude</em>) as</p>
<pre><code>"edge between nodes"
view Edge 
( /* keys */
    From        : Node,
    To          : Node,
    TypeName    : String
)
{ /* values */
    Name        : String
};
</code></pre>
<p><code>@AlternateIndex</code> enables an <em>element</em> to index the <em>key/value</em> that corresponds to the <code>From</code> key member for indexed access from a <code>Node.Froms</code> extension property</p>
<p>For the model</p>
<pre><code>entity CostCentre (Id : Int32) [Costs : Cost (CostCentre = this)];
aspect Cost {CostCentre : CostCentre, Amount : Decimal};
</code></pre>
<p>with</p>
<pre><code>entity Asset (...) {...} [Cost : Cost];
entity Project (...) {...} [Cost : Cost];
</code></pre>
<p>Concreate elements <code>AssetCost</code> and <code>ProjectCost</code> will create indexes
<code>AssetCostCostCentre.Index</code> and <code>ProjectCostCostCentre.Index</code></p>
<p>Source edit will  change the source to</p>
<pre><code>entity CostCentre (Id : Int32) [Costs : Cost (CostCentre = this)];
aspect Cost 
{
  @AlternateIndex("AssetCost", 42)
  ,AlternateIndex("ProjectCost", 43) 
  CostCentre : CostCentre, Amount : Decimal};
</code></pre>
<p>To ensure the index Id is not used for something else resulting in an incompatible model and store.</p>
<p><em><strong>how does CostCentre know what (Asset/Project/ etc) the Cost is for?</strong></em>
The <code>aspect Cost</code> is transformed to a <code>view</code> that is equvilent to</p>
<pre><code>view Cost (owner : Any) {CostCentre : CostCentre, Amount : Decimal};
</code></pre>
<p>the (<em>C#</em>) hiperspace query</p>
<pre><code>from centre in space.CostCentres 
select centre.Id, (from line in centre.Costs 
                   let asset = line.owner.Is&lt;Asset&gt;() ? Amount : 0 
                   let project = line.owner.Is&lt;Project&gt;() ? Amount : 0 
                   group line by line.CostCentre into totals
                   select new { Projects = totals.Sum(v =&gt; v.asset),
                                Assets = totals.Sum(v =&gt; v.project)})
</code></pre>
<p>Will return the total costs by type for each CostCentre</p>
<h4>Inherited Index</h4>
<p>This model defines an overall <em>trade</em> type with three different implementations for {<em>FI, EQ, FX</em>}  that have different properties for the different asset-classes.   <em>Trade</em> is referenced by <em>Book</em>,  the extension property <em><code>Book.Trades</code></em> returns a collection of <em>Trade</em> that has a <em><code>Book</code></em> equal to the current <em>Book</em>.  For efficient access, an index is created for the <em><code>Trade.Book</code></em> that is inherited by each implementation.</p>
<p>The syntax <code>Banking.FI.Trade : Banking.Trade = Banking.Trade()</code> means <code>Banking.FI.Trade</code>:</p>
<ul>
<li>Inherits <em>keys / values / extensions / properties</em> from <code>Banking.Trade</code> (via <strong><code>:</code></strong>)</li>
<li>Can be viewed as a <code>Banking.Trade</code> (via <strong><code>=</code></strong>)</li>
</ul>
<pre><code>view Banking.Trade (Id : String) 
{Book : Banking.Book};
entity Banking.FI.Trade : Banking.Trade = Banking.Trade();
entity Banking.FX.Trade : Banking.Trade = Banking.Trade();
entity Banking.EQ.Trade : Banking.Trade = Banking.Trade();
entity Banking.Book (Id : String) [Trades : Banking.Trade (Book = this)];
</code></pre>
<p>Adding <code>%ids</code> to the model, with result in a source edit to a <code>#</code> id to each <em>element</em>, <em>key/value</em> and extension property, and <code>@AlternateIndex</code> property for each generated concrete index.</p>
<pre><code>view Banking.Trade #45 (Id : String) 
{@AlternateIndex("Banking.EQ.Trade", 52) 
 ,AlternateIndex("Banking.FI.Trade", 48)
 ,AlternateIndex("Banking.FX.Trade", 50) 
 Book : Banking.Book};
entity Banking.FI.Trade : Banking.Trade = Banking.Trade() #49;
entity Banking.FX.Trade : Banking.Trade = Banking.Trade() #51;
entity Banking.EQ.Trade : Banking.Trade = Banking.Trade() #53;
entity Banking.Book #47 (Id : String #1) [Trades : Banking.Trade (Book = this) #54];
</code></pre>
<p>subsequent compilation of the model will result in the indexes using the same <em>Id</em> value when stored.<br>
<strong>NB</strong> the <code>#id</code> can be of any value, but can never to reused for a different purpose once used with a <em>Hiperspace</em>.</p>
<hr>
<h3>Source Editing</h3>
<p>When the directive <code>%ids</code> is added to a hilang model, the source code is edited to add <code>#id</code> values to ensure that the schema can be evolved without the risk of introducing incompatible changes.</p>
<p><em><strong>know issue</strong></em>: Source editing lacks the context of other edits to a line of <code>hilang</code> source <code>@AlternateIndex("Banking.EQ.Trade", 52) @AlternateIndex("Banking.FI.Trade", 48) Book : Banking.Book</code> will create a syntax error the next time the <em>schema</em> is compiled because <code>@</code> is the prefix for a comma-separated list of one-or-more attributes.  The code needs to be edited to change subsequent <code>@</code> to <code>,</code></p>
]]></description>
      <pubDate>Sun, 07 Dec 2025 15:45:23 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251303</guid>
    </item>
    <item>
      <title>Release 24th November 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251124</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release adds the <code>@AlternateIndex</code> property to explicitly create an index for views and ad hoc queries that do not <em>or cannot</em> be defined in the normal way of using an access path.</p>
<h3>Path Index</h3>
<p>Normally indexes are created in <code>Hiperspace</code> by defining an extent that uses an attribute to retrieve a list of elements.  The following hilang model defines a <em>Person</em> with a <em>Parent</em> and an extension <em>Children</em> that retrieves all <em>Person</em> that have a <em>Parent</em> that refers to this <em>person</em>.</p>
<pre><code>entity Person (Id : Guid) {Parent : Person} [Children : Person (Parent = this)];
</code></pre>
<p>The <em>Children</em> extent causes an index to be created on <em>Parent</em> so that access to <em>Children</em> is fast and does not require an scan of all <em>Person</em> elements</p>
<h3>Alternate Index</h3>
<p>The following model defines two entities {<em>Product, Order</em>} that can be viewed as <strong>nodes</strong> in a <strong>graph</strong>. The segment <em>Item</em> defined the details of the <em>Order</em> with reference to the <em>Product</em> being ordered.</p>
<pre><code>entity Product = Node(...) (Id : Guid);
entity Order = Node (...) (Id : Guid) [Items : Item];
segment Item = Edges (From = owner, To = Product, FromType = "Purchase", ToType ="Purchased")
(Line : Int32) 
{
  @AlternateIndex
  Product : Product,
  Quantity : Decimal
};
</code></pre>
<p>In a <strong>UML</strong> diagram,  <em>Order</em> aggregates <em>Item</em>, and <em>Item</em> is associated with <em>Product</em>.  In a <strong>Graph</strong> diagram <em>Order</em> and <em>Product</em> are both <code>Nodes</code> with two <code>Edge</code> between them for "Purchase" and "Purchased".</p>
<p><img src="/hiperspace/media/Sites/hiperspace/sample/orderitem.png"></p>
<p><em>Product</em> cannot refer to <em>Item</em> because it is a <code>segment</code> of its owner (<em>in this case Order</em>), and cannot therefore define a <em>path</em> from <em>product</em> to <em>Item</em>. When viewed as a <strong>graph</strong> the Order-&gt;(Purchase)-&gt;Product <code>Edge</code> uses the <em>Item</em> key to find all edges, but the Product-&gt;(Purchased)-&gt;Order <code>Edge</code> needs an <code>@AlternateIndex</code> to avoid a scan of all <em>Items</em>.</p>
<hr>
<h2>Result&lt;&gt; enhancement</h2>
<p>The Hiperspace <a href="https://github.com/channell/Hiperspace/blob/master/src/Hiperspace/Result.cs">Result</a> type has been updated to include <em>Rust-Style</em> continuation monads that can optionally follow a <code>.Bind()</code> (<em>bind an element with hiperspace</em>) with:</p>
<ul>
<li><code>.Then</code> chaining function</li>
<li><code>.Else</code> error handling function</li>
</ul>
<pre><code>write.Roles
    .Bind(new Role
    {
        Name = role,
        RoleName = role,
    })
    .Then(r =&gt;
    {
        Log?.LogInformation($"Added missing role {r.Name}");
        return Result.Ok(r);
    })
    .Else(r =&gt; Log?.LogError($"Failed to bind role {r.Value.Name} with status {r.Status} and reason {r.Reason}"));
</code></pre>
]]></description>
      <pubDate>Mon, 24 Nov 2025 20:12:29 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251124</guid>
    </item>
    <item>
      <title>Release 15th November 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251115</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This version updates to .NET 10 and modernizes the code to use <code>name != null</code> and <code>ReferenceEquals(name, null)</code>
to <code>name is not null</code>.</p>
<p>The <code>is null</code> and <code>is not null</code> patterns are <strong>billed</strong> as</p>
<blockquote>
<p>provide a clearer and more concise way to check for null values in C#. This change enhances readability and aligns with current C# best practices.</p>
</blockquote>
<p>but the main advantage is that:</p>
<ul>
<li>You don't need to include <code>ReferenceEquals</code> in any <code>operator ==</code> overload.</li>
<li><code>name is not null</code> is the same for reference (<em>class</em>) and value (<em>struct</em>) objects instead of <code>ReferenceEquals(name, null)</code> for classes and <code>!name.HasValue</code> for nullable values.</li>
</ul>
<h2>References</h2>
<p>Reference libraries have also been updated</p>
]]></description>
      <pubDate>Sat, 15 Nov 2025 17:12:48 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251115</guid>
    </item>
    <item>
      <title>Release 6th November 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251106</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release builds on the <a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a> functionality provided by <strong>message</strong> to execute queries on a remote <strong>Hiperspace.DB</strong> and return the full set of <em>elements</em> back to a client without the need to return every intermediate object necessary.  The prime example is the <strong>Hiperspace.DB</strong> opening page, which displays a summary bar-graphs of the number of <strong>nodes</strong> and <strong>edges</strong> in each database, and validates that <em>changes</em> to the (compiled) schema for databases does not change the definition of an already stored <em>element set.</em></p>
<p>The <em>graph-view</em> functionality of <strong>Hiperspace.DB</strong> uses <code>HiperEdge</code> functions to recursively search (<em>in parallel</em>) all <code>Node</code> types that satisfy the view criteria and display as a navigable <a href="https://en.wikipedia.org/wiki/SVG">SVG</a> graph of connections.  These capabilities use the <a href="https://github.com/channell/Hiperspace/blob/master/src/Hiperspace/Graph/GraphFunctions.cs"><code>GraphFunctions</code></a> to search Nodes using message-keys <em>sent</em> to the server and message-value <em>provided</em> on completion together with rendering information for <code>Node</code> <em>shapes</em> and <em>color</em>.</p>
<p>To support these use-cases, a couple of enhancements have been added to <a href="https://www.nuget.org/packages/Hiperspace/">Hiperspace</a> and <a href="https://www.nuget.org/packages/HiLang">HiLang</a> to better support the functions.</p>
<h3>SubSpace</h3>
<p><code>IServiceProvider</code> is  an interface on the .NET Platform to inject <em>Services</em> into components (<a href="https://en.wikipedia.org/wiki/Inversion_of_control">IoC</a>) at runtime.  A ServiceProvider property has been added to <code>SubSpace</code> to enable messages to enlist functionality when a message is received, and a parameter added to <code>SubSpaceParameters</code> for <em>domain space</em> construction.</p>
<p><code>FindPaths</code> and <code>FindPathsAsync</code> functions now have <em>domain-specific</em> implementations that use a <em><strong>message</strong></em> to transmit the call through to a server where the parallel recursive search can be efficiently executed close the data, using many of the cores of the server.</p>
<h3>Node</h3>
<p>The <code>Node</code> element has been extended to include additional functions that provide <code>HiperEdge</code>  search for related nodes across transitive edges</p>
<table>
<thead>
<tr>
<th>│</th>
<th>Name</th>
<th>│</th>
<th>Description</th>
<th>│</th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>━━━━━━━━</td>
<td>+</td>
<td>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</td>
<td>+</td>
</tr>
<tr>
<td>│</td>
<td>HiperEdges</td>
<td>│</td>
<td>Treat the TypeName parameter as a HiperEdge and find all matching</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>HiperEdgesAsync</td>
<td>│</td>
<td>as above, but non-blocking (especially for Blazor web clients)</td>
<td>│</td>
</tr>
</tbody>
</table>
<pre><code>        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;The Edge type name&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public HashSet&lt;HiperEdge&gt; HiperEdges(string TypeName, int? length = null, HashSet&lt;string&gt;? targets = null)

        /// &lt;summary&gt;
        /// Create an inline HiperName from TypeNames and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="HiperName"&gt;The name of the HiperEdge being infered from the Edge TyopeNames&lt;/param&gt;
        /// &lt;param name="TypeNames"&gt;the set of Edge TypeNames that make up this HiperEdge&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public HashSet&lt;HiperEdge&gt; HiperEdges(string HiperName, IEnumerable&lt;string&gt; TypeNames, int? length = null, HashSet&lt;string&gt;? targets = null)

        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;the name given to this transitative hiperedge&lt;/param&gt;
        /// &lt;param name="rules"&gt;the set of meta edges (start-node type, end-node type, edge type) rules that define the transitative path&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public HashSet&lt;HiperEdge&gt; HiperEdges(string TypeName, HashSet&lt;Rule&gt; rules, int? length = null, HashSet&lt;string&gt;? targets = null)

        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;The Edge type name&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public async Task&lt;HashSet&lt;Graph.HiperEdge&gt;&gt; HiperEdgesAsync(string TypeName, int? length = null, HashSet&lt;string&gt;? targets = null, CancellationToken cancellationToken = default)

        /// &lt;summary&gt;
        /// Create an inline HiperName from TypeNames and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="HiperName"&gt;The name of the HiperEdge being infered from the Edge TyopeNames&lt;/param&gt;
        /// &lt;param name="TypeNames"&gt;the set of Edge TypeNames that make up this HiperEdge&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public async Task&lt;HashSet&lt;HiperEdge&gt;&gt; HiperEdgesAsync(string HiperName, IEnumerable&lt;string&gt; TypeNames, int? length = null, HashSet&lt;string&gt;? targets = null, CancellationToken cancellationToken = default)

        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;the name given to this transitative hiperedge&lt;/param&gt;
        /// &lt;param name="rules"&gt;the set of meta edges (start-node type, end-node type, edge type) rules that define the transitative path&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public async Task&lt;HashSet&lt;Graph.HiperEdge&gt;&gt; HiperEdgesAsync(string TypeName, HashSet&lt;Rule&gt; rules, int? length = null, HashSet&lt;string&gt;? targets = null, CancellationToken cancellationToken = default)
</code></pre>
<p>The <a href="https://github.com/channell/Hiperspace/blob/master/examples/CousinProblem/Test.cs">Test cases</a> demonstrate the use of these functions to provide <em>server-side</em> search for relations. The first example treats "Child" as a transitive <code>HiperEdge</code>, while the second infers an "Ancestors" <code>HiperEdge</code> from either <em>Mother</em> or <em>Father</em>.</p>
<pre><code>                var descendants = node.HiperEdges("Child");
                var ancestors = node.HiperEdges("Ancestors", new[] {"Mother", "Father" });
</code></pre>
]]></description>
      <pubDate>Sat, 08 Nov 2025 13:03:10 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251106</guid>
    </item>
    <item>
      <title>Release 23rd October 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251023</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release extends the <strong>message</strong> functionality to behave more like a stored procedure call,
binding all <em>entity/segment/aspect/view</em> results to the local subspace when it completes.</p>
<p>Initially the <code>RemoteLabel</code>
flag was used to indicate which tier should invoke the message, and return results.  Test with <strong>Hiperspace.DB</strong>
highlighted that the flag is not needed - the message is now executed by the lowest <em>Hiperspace</em> that can provide a value.</p>
]]></description>
      <pubDate>Thu, 23 Oct 2025 19:03:16 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251023</guid>
    </item>
    <item>
      <title>Release 20th October 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251020</link>
      <description><![CDATA[<p><a href="https://www.cepheis.com/hiperspace/20251020">https://www.cepheis.com/hiperspace/20251020</a></p>
<h2>Overview</h2>
<p>This release introduced the <strong>message</strong>  element for client/server scenarios where you might want to run a  query on a server and transfer only the final results to a client.  This is especially useful for whole graph queries that select every element that is related to the subject <em><strong>node</strong></em> for a <em>butterfly</em> view.</p>
<hr>
<h2>Message</h2>
<p>The <a href="https://www.cepheis.com/hiperspace/message">HiLang Message</a> page goes into detail about the structure of <em>messages</em>.. but it is worth referring to an example for clarity. The <code>CustomerCube</code> <em>message</em>  takes a reference to a <em>Customer</em> and returns all the information need to display a <em>hipercube</em> in a <a href="https://webassembly.org/">Web Assembly</a> browser client, transfered with <a href="https://protobuf.dev/">Protobuf</a> compression between client and server</p>
<pre><code>message API.CustomerCube
( 
    Customer        : Acc.Customer #1
)
{
    CustomerTree    : List&lt;Acc.Customer&gt; #2,
    Sector          : List&lt;Acc.Sector&gt; #3,
    Accounts        : List&lt;Acc.Account&gt; #4 ,

    "Using the generated cube element from Transaction and its dimensions"
    Cube            : List&lt;Acc.Transaction_Cube&gt; #5,

    "Using the generated cube drilldown from cube"
    Drilldown       : List&lt;Acc.Transaction_CubeDrillDown&gt; #6
};
</code></pre>
<p><strong>NB</strong> <code>Cube            : List&lt;Acc.Transaction_Cube&gt;</code> is a <em>physical</em> element, generated from the <code>Transaction</code> <em>@Fact</em> property, and including the <em>@Dimension</em>  {<em>Customer, Account, Sector</em>} and <em>@Measure</em> analysis properties</p>
<p>When the schema is compiled with <a href="https://www.nuget.org/packages/HiLang/">HiLang</a>, implementations are not provided for</p>
<pre><code>namespace API
{
    public partial class CustomerCube : IMessage
    {
        public Task&lt;IMessage&gt; InvokeAsync(CancellationToken token = default)
        {
        }
        public async IAsyncEnumerable&lt;IMessage&gt; InvokeStreamAsync([EnumeratorCancellation]CancellationToken token = default)
        {
            yield return await InvokeAsync(token);
        }
    }
}
</code></pre>
<p>which must be added to the schema project to provide implementation for the server-side functions</p>
<hr>
<h3>HiperSpace</h3>
<p><code>HiperSpace</code> has two additional methods that transfer messages through the layers of Hiperspaces, until a server <code>SubSpace</code> is found that will execute the message, and return the value</p>
<pre><code>       public virtual Task&lt;byte[]&gt; InvokeAsync(byte[] key, CancellationToken token = default) =&gt; Task.FromResult(key);
       public virtual IAsyncEnumerable&lt;byte[]&gt; InvokeStreamAsync(byte[] key, CancellationToken token = default) =&gt; new byte[][] { key }.ToAsyncEnumerable();
</code></pre>
<h3>SubSpace</h3>
<p><code>SubSpace</code> has the above functions (<em>because they also implement</em> <code>HiperSpace</code>), plus the domain specific functions needed for code like `var msg = await space.InvokeAsync(new CustomerCube ); to be executed on a client.</p>
<pre><code>       public async Task&lt;TMessage&gt; InvokeAsync&lt;TMessage&gt;(TMessage item, CancellationToken token = default) 
           where TMessage : class, IMessage
       public async IAsyncEnumerable&lt;TMessage&gt; InvokeStreamAsync&lt;TMessage&gt;(TMessage item, [EnumeratorCancellation]CancellationToken token = default) 
</code></pre>
<p>There are no equivalent synchronous messages handling functions because the only reason for using messages is to perform query processing on a server, and Blazor client cannot use any blocking operations.</p>
<p>With this release the <em>obsolete</em> function <code>public abstract object? Get(string sid);</code>  has been removed, and <code>Get&lt;object?&gt;(string sid)</code> should be used instead.</p>
<hr>
<h4>Domain implementation</h4>
<p>The Domain implementation of <code>SubSpace</code> (<em>generated by HiLang</em>) includes logic to execute the message logic in the first <em>domain Space</em> that has the <code>RemoteLabel</code> set to true.  Generated HiLang schema will not compile until the <code>InvokeAsync</code> and <code>InvokeStreamAsync</code> functions have been added by the implementor.</p>
<hr>
<h3>HiLang</h3>
<p>HiLang <code>segment</code> (<em>many</em>) and <code>aspect</code> (<em>optionally one</em>) can be applied to any <code>entity</code> to extend the definition of the entity with information that is versioned separately.  Consider the example of <a href="https://github.com/channell/Hiperspace/blob/master/examples/TOGAF/TOGAF.hilang">TOGAF</a> where every entity type can include any  number of <em><strong>Gaps</strong></em> each of which have separate storage and references to their <code>owner</code> with navigation from the <code>owner</code> using the <em><strong>Gap</strong></em> property.</p>
<p><a href="https://www.nuget.org/packages/HiLang/">HiLang</a> Generates {<em>ServiceGap, DriverGap, ActorGap, FunctionGap, CapabilityGap, ProductGap, GoalGap, ObjectiveGap, MeasureGap, ActivityGap, EventGap, ProcessGap, ControlGap, ValueStreamGap, CourseOfActionGap, EntityGap, LogicalGap, PhysicalGap, SystemGap, ComponentGap, DeployedGap, PlatformGap, HostGap, InstanceGap</em>} to provide storage for entity specific versions of the <em><strong>Gap</strong></em>, and <em>View</em> to bring togther every different implementation for query together.</p>
<p>When <em>segment</em>/<em>aspect</em> is only used in a single comtext, we can end-up with very long segment implementation names like <code>CustomerAccountTransaction</code> when <code>Customer</code> has many <code>Account</code> <em>segments</em> , which has many <code>Transaction</code> segments.  To minimise the generated classes, an additional <em>step</em> has been added to HiLang to remove the generated prefix when a segment is only used in one context.</p>
<p>This version changes <code>CustomerAccountTransaction</code>  to <code>Transaction</code>.  If we subsequently need to add the segment to another <em>entity</em>, the prefix would be added back to distinguish different owners of <code>Transaction</code>.</p>
<p><strong>NB</strong> Hiperspace does not  store the name of the <em>element</em> with every instance (only the #id is stored), so any change will be transparent.</p>
<hr>
<h3>LatestSpace</h3>
<p><code>LatestSpace</code> is a specialisation fo HeapSpace, that discards history to reduce memory usage.  the main use-case is with  Blazor Web-Assembly clients that have no interest in the history of an Element, but need to hold the durable representation for synchronization at the end of a session.</p>
<h2>References</h2>
<p>References to other libraries have been updated</p>
]]></description>
      <pubDate>Mon, 20 Oct 2025 12:00:27 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251020</guid>
    </item>
    <item>
      <title>Release 23rd September 2025</title>
      <link>https://www.cepheis.com/hiperspace/20250923</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release introduces three new features for references to views, the <code>Any</code> polymorphic union type and <code>Get&lt;TEntity&gt;(string sid)</code> function.   Together they provide the <code>Hiperspace.DB</code> functionality to combine any number of Hiperspaces in a single  Graph view without duplication or copying.</p>
<hr>
<h3>View References</h3>
<p>Views in Hiperspace are generated to combine all <em>entities, segments, aspects and views</em> that can be projected as the view, but also provide an additional role for sub-types and complex associations.</p>
<p>All references in <code>Hiperspace</code> can be navigated to either <em>elements</em> key or an index that matches the predicate used to reference it. The <a href="https://www.cepheis.com/hiperspace/20250807">last release</a> completed the optimization of view access to parts, this release adds indexing to efficiently access view items with criteria other than the key.</p>
<h4>Indexed views</h4>
<p>Consider the example of a Trading problem where <strong>Trades</strong> are booked in  a <strong>Book</strong>, but each trade type {Fixed Income, Forex, Equity}  can have specific properties for the product traded.   When the view is referenced by a member other than the key (<em>Book in this example</em>), an index is created to efficiently access the set of matching values.</p>
<pre><code>view Banking.Trade (Id : String) {Book : Banking.Book};
entity Banking.FI.Trade : Banking.Trade = Banking.Trade();
entity Banking.FX.Trade : Banking.Trade = Banking.Trade();
entity Banking.EQ.Trade : Banking.Trade = Banking.Trade();
entity Banking.Book (Id : String) [Trades : Banking.Trade (Book = this)];
</code></pre>
<p><code>Trades : Trade (Book = this)</code>selects the set of Trade that have a reference to <code>this</code> Book.  This access causes an index to be created in the view <code>Trade</code> that is inherited by each implementation.</p>
<h4>Sub type</h4>
<p><img src="/hiperspace/media/Sites/hiperspace/diagrams/trade.svg"></p>
<p>This <a href="https://en.wikipedia.org/wiki/Unified_Modeling_Language">UML</a> diagram represents the logical view of the <code>.hilang</code> schema above.  When a relational database is used, one of three strategies are normally followed:</p>
<ul>
<li><strong>Direct</strong>: Each entity {Trade, FI_Trade, FX_Trade, EQ_Trade} is mapped separately, and joined as needed</li>
<li><strong>Down</strong> denormalization: Properties of the parent are duplicated in each of the sub-types and with context specific joining</li>
<li><strong>Up</strong> denormalization: Properties of each sub-type are added to the base type (optionally with <em>aliases</em> for duplicate names)</li>
</ul>
<p>Hiperspace does not need denormalization the elements because types can inherit from a base type.  The code <code>entity FX_Trade : Trade = Trade;</code> states that the <em>entity</em> <strong>FX_Trade</strong> <em>inherits</em>(:) from <strong>Trade</strong> <em>and can be viewed</em>(=) as a <strong>Trade</strong></p>
<p>In the example above, each sub-type will be queried in parallel using the index on the <strong>Book</strong> reference</p>
<h5>Modelled as Aspect</h5>
<p>The schema could also have been modelled using <em>aspect</em> rather than <em>realization</em>, depending on business  requirement, e.g.</p>
<ul>
<li><em>Convertible</em> where final exchange is either equity or cash</li>
<li><em>Hybrid</em> where cashflow is calculated from either from a yield-curve or equity price</li>
<li><em>Participation</em> where the equity price uses FX exchange rates</li>
</ul>
<p><img src="/hiperspace/media/Sites/hiperspace/diagrams/trade2.svg"></p>
<pre><code>entity Banking.Trade (Id : String) {Book : Banking.Book} 
	[FI : Banking.FI.Trade, FX : Banking.FX.Trade, EQ : Banking.EQ.Trade];
aspect Banking.FI.Trade;
aspect Banking.FX.Trade;
aspect Banking.EQ.Trade;
entity Banking.Book (Id : String) [Trades : Banking.Trade (Book = this)];
</code></pre>
<p><strong>Aspects</strong> <em>appear</em> to be properties of the <em>owning</em>  entity,  so the choice has minimal impact on usage ( <code>Banking.FI.Trade { YieldCurve = ... }</code> vs <code>Banking.Trade { FI = new Banking.FI.Trade { YieldCurve = ...}}</code>)</p>
<h4>Segment / Aspect reference</h4>
<p>Segments and Aspects are <em>owned</em> by the <code>entity, segment, aspect</code> that references them as an extension.  They appear (*to users *) as a set property for segments, and  value property for aspects, with the <code>owner</code> added <em>transparently</em> when a value is assigned.</p>
<p>&lt;img src="https://www.cepheis.com/hiperspace/media/Sites/hiperspace/diagrams/segment.svg"&gt;</p>
<pre><code>entity Customer [Accounts : Has];
segment Has (Account : Account);
entity Account [HasCustomer : Has (Account = this)];
</code></pre>
<p><code>HiLang</code> translates the segment <code>Has</code> into an element <code>CustomerHas</code> (adding the key <code>owner : Customer</code>), and the segment <code>Has</code> is transformed into a <em>view</em> that <code>CustomerHas</code> provides.  If there are no other implementations of <code>Has</code> (<em>e.g. for a sub-account of Account</em>) the view is <em>pruned</em> and not generated.</p>
<p>In this example <code>Has</code> is referenced by <code>Account</code> to provide a link from <code>Account</code> to <code>Customer</code> using the <code>HasCustomerIndex</code>.  As a <code>segment</code>/<code>aspect</code> can be applied to any stored element, <code>Has</code> view includes  the key <code>owner : Any</code> which in this case can be <em>cast</em> to <code>Customer</code>.</p>
<hr>
<h3>Any</h3>
<p>The new type <code>Any</code> has been introduced to support <code>segment</code>/<code>aspect</code> views, and can be converted to any of the domain types like a <code>union</code> in F#.  Unlike <code>object</code>, <code>Any</code> can be serialized and used anywhere in the schema, when a generic reference is required.  <code>Any</code> is code generated by <code>HiLang</code> to include a constructor for the each of the domain types, together will <em>cast</em> operators to convert to each of the domain types.  Elements also include a <em>cast</em> operator to convert to an <code>Any</code> when assigned.</p>
<p><code>Any</code> uses constructors and conversion operators  to behave like a base type for any domain element <code>Any value = customer</code> <em>will use Customer’s operator Any function</em>, <code>Customer? value = any</code> <em>will use Any’s operator Customer function</em> to return the value or <code>null</code>.  Explicit cast functions are crerted for each <em>element</em> that could be stored.. for the <em>Customer</em> type it includes</p>
<ul>
<li><strong>Customer()</strong> : cast the Any to a customer or null</li>
<li><strong>CustomerAsync()</strong> : async function to return the value (<em>especially usefull for WebAssembly where network read may be required</em>)</li>
</ul>
<p><code>Any</code> includes the functions</p>
<ul>
<li><code>Type AnyType()</code>to inspect the content of the <code>Any</code> class for match/switch statement</li>
<li><code>bool Is&lt;T&gt;()</code> : returns true if the content of the <code>Any</code> is of type <code>T</code></li>
<li><code>T? As&lt;T&gt;()</code> : returns the value of the <code>Any</code> cast to the type <code>T</code></li>
<li><code>Task&lt;T?&gt; AsAsync&lt;T&gt;()</code> : returns the value of the <code>Any</code> cast to the type <code>T</code> using an async network call if necassary</li>
</ul>
<hr>
<h3>Get(string sid)</h3>
<p><code>SubSpace</code> provides an <code>object? Get (string sid)</code> function that can be used to get an element in Hiperspace without knowing the type, just using the stringified key of the element.  This function is now obsolete and has been replaced by <code>TEntity Get&lt;TEntity&gt;(string sid)</code> that allows the desired type to be retrieved.</p>
<p>This method was added for <code>Hiperspace.DB</code> that includes <code>GraphSpace</code> to aggregate <code>Node</code>/<code>Edge</code>/<code>HiperEdge</code> across domains.  For <code>GraphSpace</code> it is necessary to call <code>Get()</code> with the <code>SKey</code> of a <code>Node</code> and then cast to <code>Node</code> which was not possible with <code>object</code> because <em>elements</em> do not need be viewable as a <code>Node</code>.  <code>GraphSpace</code> uses <code>Get&lt;Node&gt;(skey)</code> for the lookup.</p>
]]></description>
      <pubDate>Mon, 20 Oct 2025 11:41:51 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20250923</guid>
    </item>
    <item>
      <title>Release 8th August 2025</title>
      <link>https://www.cepheis.com/hiperspace/20250807</link>
      <description><![CDATA[<h2>Overview</h2>
<p>Partition elimination is an advanced feature in SQL relational databases, that skip access to a partition view when the optimizer can determine that it will not return any values.  This release adds the feature to Hiperspace views to skip <code>SetSpace</code> access that is not needed, <em>most commonly for Graph queries</em></p>
<h3>SQL Views</h3>
<pre><code>/* for the view */
CREATE VIEW costs AS
SELECT cost, 'Sales' as area FROM sales_costs UNION
SELECT cost, 'Operations' as area FROM ops_costs UNION
SELECT cost, 'Assets' as area FROM asset_costs ;

/* queried as */
SELECT cost FROM costs WHERE area = 'Sales';

/* will skip the query ops_costs and asset_costs since it will return no rows */
</code></pre>
<h3>Hiperspace Views</h3>
<p>Hiperspace views provide the same function, but rather than define a view separately, the view definition is part <code>element</code> definition, with the view  being created as a union of all the <code>elements</code> { <code>entity</code>, <code>segment</code>, <code>aspect</code>, <code>view</code> } that provide the <code>view</code>.</p>
<pre><code>segment Togaf.Has.Requirement : Togaf.Base 
    = Node        ( SKey = SKey, Name = Name, TypeName = "AF-Requirement"),
      Edges       (From = owner, To = this, Name = Name, FromTypeName = "AF-Has-Requirement", ToTypeName = "AF-Requirement-For") ;
</code></pre>
<p>defines a <code>segment</code> named <code>Togaf.Has.Requirement</code> <em>that can be viewed</em> as a <code>Node</code> with the <code>TypeName</code> "AF-Requirement".  Within the <a href="https://github.com/channell/Hiperspace/blob/master/examples/TOGAF/TOGAF.hilang">TOGAF</a> sample there are
<strong>201</strong>  elements that <em>can be viewed</em> as a <code>Node</code>, all of which are included in the <code>SubSpace</code> view <code>Nodes</code>.</p>
<p>Prior to this release a LINQ query <code>from node in Nodes where node.TypeName  == "AF-Requirement";</code> would scan all <strong>201</strong> <code>SetSpace</code> before filtering to include only <em>requirements</em>, but will now skip the scan of the other <strong>200</strong> types.  This is especially useful for graph data-explorer browsers that will typically search for a single source node type</p>
<p>This is implemented with the <code>SetSpace&lt;&gt;</code> function <code>public virtual bool IsSkippable(object template) =&gt; false;</code> that is overridden in <strong>HiLang</strong> generated code, and used by all <code>View</code> Sets</p>
<h2>RefSet&lt;&gt;</h2>
<p>The <code>RefSet&lt;&gt;</code> collection of references to segments or other entities (via index) has been updated to include <code>AddAsync</code>  to simplify the addition of a segment reference when used from a web-assembly client.</p>
]]></description>
      <pubDate>Thu, 07 Aug 2025 20:45:22 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20250807</guid>
    </item>
  </channel>
</rss>