Skip to content

Commit 7e35bb9

Browse files
committed
V2 object layer: read V_eta's new statement names, and the old ones
V_eta renamed the statement family on 2026-10-08 (did-schema V_eta_tenets.md, T2 amendment): subject_statement/assertion/interaction/ observation/manipulation/calculation drop subject_, the edge subject_id becomes entity_id, data_type becomes value. The readers accept both, so a dataset written before the rename still reads: ndi.v2.vetaName and vetaAliases name the pairs; blockOf, edgeIds, classChain and directParents fall back; isaQuery, entityQuery, fieldQuery and termQuery search both. ndi.data_type becomes ndi.value. The Haley import asks the schema by the new name; ndi.migrate's second pass is not changed (its CI is pinned to the pre-#73 schema). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxKLA8Tbf9cpvDe7fuVeKf
1 parent aeac916 commit 7e35bb9

31 files changed

Lines changed: 339 additions & 174 deletions

‎src/ndi/+ndi/+file/+navigator/bodies.m‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
channels = S.database_search(ndi.query('', 'isa', 'acquisition_channels', '') & ...
124124
ndi.query('', 'depends_on', 'acquisition_system_id', systems{a}.id()) & inSession);
125125
for c = 1:numel(channels)
126-
statements = S.database_search(ndi.query('', 'isa', 'subject_statement', '') & ...
126+
statements = S.database_search(ndi.v2.isaQuery('statement') & ...
127127
ndi.query('', 'depends_on', 'acquisition_channels_id', channels{c}.id()) & inSession);
128128
for t = 1:numel(statements)
129129
st = statements{t};
@@ -208,16 +208,17 @@
208208
function pm = statementProbemap(~, st, sysName)
209209
p = st.document_properties;
210210
type = '';
211-
if isfield(p, 'subject_interaction') && isfield(p.subject_interaction, 'method') ...
212-
&& isstruct(p.subject_interaction.method) && isfield(p.subject_interaction.method, 'name')
213-
type = char(p.subject_interaction.method.name);
211+
m = ndi.v2.blockOf(p, 'interaction', 'method', []);
212+
if isstruct(m) && isfield(m, 'name')
213+
type = char(m.name);
214214
end
215215
if isempty(type)
216216
error('NDI:navigator:bodies:noMethod', ...
217217
['Recording statement %s has no `method`, which names the probe ' ...
218218
'type (e.g. brightfield-imaging).'], st.id());
219219
end
220-
subject = st.dependency_value('subject_id');
220+
subject = ndi.v2.edgeIds(p, 'entity_id');
221+
if isempty(subject), subject = ''; else, subject = subject{1}; end
221222
pm = ndi.epoch.epochprobemap_daqsystem(sysName, 1, type, [sysName ':image1'], subject);
222223
end
223224
end

‎src/ndi/+ndi/+setup/+V2/schemaHasField.m‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,17 @@
1414
end
1515
tf = false;
1616
p = getenv('DID_SCHEMA_PATH');
17-
f = fullfile(p, [className '.json']);
18-
if isempty(p) || ~isfile(f)
17+
% a class renamed 2026-10-08 is looked up under whichever name the schema
18+
% in use has (ndi.v2.vetaAliases: 'statement' or 'subject_statement')
19+
names = ndi.v2.vetaAliases(className);
20+
f = '';
21+
for k = 1:numel(names)
22+
if ~isempty(p) && isfile(fullfile(p, [names{k} '.json']))
23+
f = fullfile(p, [names{k} '.json']);
24+
break;
25+
end
26+
end
27+
if isempty(f)
1928
return;
2029
end
2130
d = jsondecode(fileread(f));

‎src/ndi/+ndi/+setup/+conv/+haley/assertionDocuments.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
strains(list{k}.key) = list{k};
6161
end
6262
eachMember = struct();
63-
if ndi.setup.V2.schemaHasField('subject_statement', 'distributive')
63+
if ndi.setup.V2.schemaHasField('statement', 'distributive')
6464
eachMember.distributive = true;
6565
end
6666

‎src/ndi/+ndi/+setup/+conv/+haley/manipulationDocuments.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
refs = getOr(options.Relations, 'timeReferenceIds', containers.Map());
9797
spans = getOr(options.Relations, 'assaySpans', containers.Map());
9898
eachMember = struct();
99-
if ndi.setup.V2.schemaHasField('subject_statement', 'distributive')
99+
if ndi.setup.V2.schemaHasField('statement', 'distributive')
100100
eachMember.distributive = true;
101101
end
102102

‎src/ndi/+ndi/+v2/blockOf.m‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33

44
if nargin < 4, default = []; end
55
v = default;
6-
if isfield(p, block) && isstruct(p.(block)) && isfield(p.(block), field)
7-
v = p.(block).(field);
6+
% a block renamed 2026-10-08 is read under either name (ndi.v2.vetaAliases)
7+
names = ndi.v2.vetaAliases(block);
8+
for k = 1:numel(names)
9+
if isfield(p, names{k}) && isstruct(p.(names{k})) && isfield(p.(names{k}), field)
10+
v = p.(names{k}).(field);
11+
return;
12+
end
813
end
914
end

‎src/ndi/+ndi/+v2/classChain.m‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
name = char(p.document_class.class_name);
1212
chain = {name};
1313
try
14-
chain = [{name}, reshape(did2.schema.cache.shared().superclasses(name), 1, [])];
14+
chain = ndi.v2.vetaName([{name}, reshape(did2.schema.cache.shared().superclasses(name), 1, [])]);
1515
return;
1616
catch
1717
end
@@ -24,4 +24,5 @@
2424
end
2525
end
2626
end
27+
chain = ndi.v2.vetaName(chain); % names as of 2026-10-08 (ndi.v2.vetaName)
2728
end

‎src/ndi/+ndi/+v2/directParents.m‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
try
99
s = did2.schema.cache.shared().getClass(className);
1010
catch
11+
s = [];
12+
end
13+
if isempty(s)
14+
% a class renamed 2026-10-08, asked for by the name the schema in use lacks
15+
alias = setdiff(ndi.v2.vetaAliases(className), {char(className)});
16+
try
17+
if ~isempty(alias), s = did2.schema.cache.shared().getClass(alias{1}); end
18+
catch
19+
end
20+
end
21+
if isempty(s)
1122
return;
1223
end
1324
if ~isfield(s, 'document_class') || ~isfield(s.document_class, 'superclasses')
@@ -20,4 +31,5 @@
2031
parents{end+1} = char(sc{k}.class_name); %#ok<AGROW>
2132
end
2233
end
34+
parents = ndi.v2.vetaName(parents); % names as of 2026-10-08
2335
end

‎src/ndi/+ndi/+v2/edgeIds.m‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@
2424
v = char(v);
2525
if ~isempty(v), ids{end+1} = v; end %#ok<AGROW>
2626
end
27+
if isempty(ids) && strcmp(name, 'entity_id')
28+
% a statement written before 2026-10-08 names what it is about `subject_id`
29+
ids = ndi.v2.edgeIds(p, 'subject_id');
30+
end
2731
end

‎src/ndi/+ndi/+v2/entityQuery.m‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
function q = entityQuery(id)
2+
%ENTITYQUERY Statements about the entity ID: its `entity_id` (or, written before 2026-10-08, `subject_id`) edge.
3+
%
4+
% Q = ndi.v2.entityQuery(ID) is ndi.query('', 'depends_on', 'entity_id', ID)
5+
% OR the same for `subject_id`, the edge's name until 2026-10-08.
6+
%
7+
% See also ndi.v2.isaQuery, ndi.v2.vetaAliases.
8+
9+
q = ndi.query('', 'depends_on', 'entity_id', id) | ndi.query('', 'depends_on', 'subject_id', id);
10+
end

‎src/ndi/+ndi/+v2/fieldQuery.m‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function q = fieldQuery(path, op, value)
2+
%FIELDQUERY A field query on a V_eta block under its current or its old name.
3+
%
4+
% Q = ndi.v2.fieldQuery('statement.variable.name', 'exact_string', V) is the
5+
% ndi.query on that path OR on 'subject_statement.variable.name', the
6+
% block's name until 2026-10-08 (ndi.v2.vetaAliases on the first segment).
7+
%
8+
% See also ndi.v2.termQuery, ndi.v2.isaQuery.
9+
10+
parts = strsplit(char(path), '.');
11+
names = ndi.v2.vetaAliases(parts{1});
12+
q = [];
13+
for k = 1:numel(names)
14+
p = strjoin([names(k), parts(2:end)], '.');
15+
one = ndi.query(p, op, value, '');
16+
if isempty(q), q = one; else, q = q | one; end
17+
end
18+
end

0 commit comments

Comments
 (0)