Skip to content

Primary Metadata System

Fresh

The primary metadata system provides immediate document identity without requiring a full parse of all quads. It extracts the four most important facts about a document during parsing: subject, type, label, and comment.

The Dual-Layer Architecture

MD-LD's parse result exposes document identity at two levels:

LayerFieldPurposeUse Cases
Canonical IdentityprimarySubjectAppend routing, storage, synchronizationappend(), file placement, authority validation
Semantic SurfaceprimaryUI, indexing, navigation, agent orientationDashboards, search, previews, timelines

These two layers serve different needs in different parts of a system.

primarySubject

result.primarySubject is the string IRI of the first non-fragment subject declaration in the document.

markdown
[my] <tag:you@example.com,2026:>

# Alice {=my:alice .prov:Person label}

In this document, result.primarySubject is "tag:you@example.com,2026:alice".

Fragment subjects are excluded:

markdown
# Document {=my:doc .my:Doc label}
## Section {=#section}

primarySubject is my:doc, not my:doc#section. The fragment is a structural subdivision, not the canonical identity.

Why primarySubject Matters

The primarySubject is the canonical address for the document's main entity. It tells you:

  • Which IRI to use when storing this document in a content-addressed system
  • Which IRI to check for conflicts when appending to a knowledge base
  • Which authority (email address or domain) produced this document

Applications that route documents by their primary entity (e.g., "store all documents about my:alice in the same shard") use primarySubject directly.

primary Object

result.primary is an object with four fields extracted from the first occurrence of each type during parsing:

javascript
primary: {
  subject: string | null,   // First non-fragment subject declaration
  type:    string | null,   // First rdf:type declaration
  label:   string | null,   // First rdfs:label literal
  comment: string | null    // First rdfs:comment literal
}

Extraction Rules

Each field is set by the first occurrence of the relevant construct:

  • subject — set by the first {=IRI} that is not a fragment
  • type — set by the first .Class annotation in the document
  • label — set by the first {label} or {rdfs:label} literal
  • comment — set by the first {comment} or {rdfs:comment} literal

Once set, none of these fields change, regardless of how many subjects, types, or labels appear later in the document.

Example

markdown
[my] <tag:you@example.com,2026:>

# Alice {=my:alice .prov:Person label}

> The best engineer in the team. {comment}

[alice@example.com] {my:email}

Result:

javascript
{
  primarySubject: 'tag:you@example.com,2026:alice',
  primary: {
    subject: 'tag:you@example.com,2026:alice',
    type:    'http://www.w3.org/ns/prov#Person',
    label:   'Alice',
    comment: 'The best engineer in the team.'
  }
}

Use Cases

Quick UI Rendering

Without iterating all quads, you can immediately display a document's title, type badge, and description:

javascript
const { primary } = parse({ text: document });

// Render a document card
const card = {
  title:       primary.label   || primary.subject || 'Unnamed',
  type:        primary.type    || 'Unknown type',
  description: primary.comment || ''
};

Search Indexing

Index documents by their primary metadata for full-text search:

javascript
const results = documents.map(text => {
  const { primary } = parse({ text });
  return {
    iri:     primary.subject,
    title:   primary.label,
    type:    primary.type,
    summary: primary.comment
  };
});

Agent Orientation

For LLM agents processing a knowledge base, primary provides immediate orientation without requiring quad traversal:

javascript
const { primary } = parse({ text: knowledgeDocument });

const systemMessage = `
This document is about: ${primary.label}
Type: ${primary.type}
Description: ${primary.comment}
IRI: ${primary.subject}
`;

Stream Routing

When documents arrive in a stream, route them by type using primary.type:

javascript
documents.forEach(text => {
  const { primary } = parse({ text });

  switch (primary.type) {
    case 'http://www.w3.org/ns/prov#Activity':
      activityQueue.push(text);
      break;
    case 'http://www.w3.org/ns/prov#Person':
      personStore.upsert(primary.subject, text);
      break;
    default:
      generalQueue.push(text);
  }
});

Merge Behavior

When merging multiple documents, primary becomes an array (one object per document):

javascript
const merged = merge([doc1, doc2, doc3]);

// Array of primary objects in merge order
console.log(merged.primary);
// [ { subject: ..., type: ..., label: ..., comment: ... }, ... ]

// Array of canonical IRIs in merge order
console.log(merged.primarySubjects);
// [ 'tag:...alice', 'tag:...bob', ... ]

Relationship to the Full Quad Graph

primary is a shortcut, not a substitute. It extracts the first occurrence of each field for convenience. For complete document identity — including all types, all labels, all comments — iterate result.quads directly.

primary answers the question: "What is this document fundamentally about?" The full quad graph answers: "What does this document say in detail?"