@abi-software/map-utilities 1.6.1-beta.0 → 1.6.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/map-utilities",
3
- "version": "1.6.1-beta.0",
3
+ "version": "1.6.1-beta.2",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -171,10 +171,112 @@ async function queryPathsByDestination(flatmapAPI, knowledgeSource, featureId) {
171
171
  return [];
172
172
  }
173
173
 
174
+ function extractFeatureIds(inputArray) {
175
+ const result = [];
176
+
177
+ for (const itemString of inputArray) {
178
+ const item = JSON.parse(itemString);
179
+
180
+ if (Array.isArray(item) && item.length >= 2) {
181
+ if (Array.isArray(item[1]) && item[1].length === 0) {
182
+ result.push(item[0]);
183
+ }
184
+ }
185
+ }
186
+ return result;
187
+ }
188
+
189
+ // Neuron populations from origin to destination, via
190
+ // Query 24: Neuron populations that have source, via, and destination nodes
191
+ // Query 25: Neuron populations that have source, via, and destination locations
192
+ async function queryPathsByRoute({ flatmapAPI, knowledgeSource, origins, destinations, vias }) {
193
+ const originFeatureIds = extractFeatureIds(origins);
194
+ const destinationFeatureIds = extractFeatureIds(destinations);
195
+ const viaFeatureIds = extractFeatureIds(vias);
196
+
197
+ const paramsF = [
198
+ {
199
+ column: 'source_feature_id',
200
+ value: originFeatureIds,
201
+ ...(originFeatureIds.length === 0 && { negate: true })
202
+ },
203
+ {
204
+ column: 'via_feature_id',
205
+ value: viaFeatureIds,
206
+ ...(viaFeatureIds.length === 0 && { negate: true })
207
+ },
208
+ {
209
+ column: 'dest_feature_id',
210
+ value: destinationFeatureIds,
211
+ ...(destinationFeatureIds.length === 0 && { negate: true })
212
+ }
213
+ ];
214
+
215
+ const params = [
216
+ {
217
+ column: 'source_node_id',
218
+ value: origins,
219
+ ...(origins.length === 0 && { negate: true })
220
+ },
221
+ {
222
+ column: 'via_node_id',
223
+ value: vias,
224
+ ...(vias.length === 0 && { negate: true })
225
+ },
226
+ {
227
+ column: 'dest_node_id',
228
+ value: destinations,
229
+ ...(destinations.length === 0 && { negate: true })
230
+ }
231
+ ];
232
+
233
+ const shouldCallDataF = paramsF.some(param =>
234
+ Array.isArray(param.value) && param.value.length > 0);
235
+
236
+ const promises = [
237
+ competencyQuery({
238
+ flatmapAPI,
239
+ knowledgeSource,
240
+ queryId: 24,
241
+ parameters: params
242
+ })
243
+ ];
244
+
245
+ if (shouldCallDataF) {
246
+ promises.push(
247
+ competencyQuery({
248
+ flatmapAPI,
249
+ knowledgeSource,
250
+ queryId: 25,
251
+ parameters: paramsF
252
+ })
253
+ );
254
+ }
255
+
256
+ const results = await Promise.all(promises);
257
+
258
+ let pathsF = [];
259
+ let data;
260
+ if (shouldCallDataF) {
261
+ const dataF = results[0];
262
+ data = results[1];
263
+ // value => [ 'source_id', 'path_id', 'axon_terminal']
264
+ pathsF = dataF?.results?.values?.map(value => value[1]) || [];
265
+ } else {
266
+ data = results[0];
267
+ }
268
+ // value => [ 'source_id', 'path_id', 'axon_terminal']
269
+ const paths = data?.results?.values?.map(value => value[1]) || [];
270
+ const combined = [...new Set([...pathsF, ...paths])];
271
+
272
+ return combined;
273
+ }
274
+
174
275
  export {
175
276
  competencyQuery,
176
277
  queryAllConnectedPaths,
177
278
  queryPathsByOrigin,
178
279
  queryPathsByViaLocation,
179
280
  queryPathsByDestination,
281
+ queryPathsByRoute,
180
282
  };
@@ -2,6 +2,46 @@
2
2
  // destinations = ilxtr:hasAxonPresynapticElementIn, ilxtr:hasAxonSensorySubcellularElementIn
3
3
  // via = ilxtr:hasAxonLeadingToSensorySubcellularElementIn, ilxtr:hasAxonLocatedIn
4
4
 
5
+ async function query(flatmapAPI, sql, params) {
6
+ const url = `${flatmapAPI}knowledge/query/`;
7
+ const query = { sql, params };
8
+
9
+ try {
10
+ const response = await fetch(url, {
11
+ method: 'POST',
12
+ headers: {
13
+ "Accept": "application/json; charset=utf-8",
14
+ "Content-Type": "application/json"
15
+ },
16
+ body: JSON.stringify(query)
17
+ });
18
+
19
+ if (!response.ok) {
20
+ throw new Error(`Cannot access ${url}`);
21
+ }
22
+
23
+ return await response.json();
24
+ } catch {
25
+ return {
26
+ values: []
27
+ };
28
+ }
29
+ }
30
+
31
+ async function fetchLabels(flatmapAPI, labelledTerms) {
32
+ if (!labelledTerms.length) return [];
33
+
34
+ const data = await query(
35
+ flatmapAPI,
36
+ `select entity, knowledge from knowledge
37
+ where entity in (?${', ?'.repeat(labelledTerms.length - 1)})
38
+ order by source desc`,
39
+ [...labelledTerms]
40
+ );
41
+
42
+ return await data.values;
43
+ }
44
+
5
45
  function filterOrigins(item) {
6
46
  const soma = item["node-phenotypes"]?.["ilxtr:hasSomaLocatedIn"];
7
47
  return Array.isArray(item.connectivity) &&
@@ -66,7 +106,52 @@ function getPhenotypeItems(obj, prop) {
66
106
  return arr;
67
107
  }
68
108
 
69
- function extractOriginItems(knowledge) {
109
+ async function transformResults(flatmapAPI, knowledgeSource, results) {
110
+ const baseResults = Array.from(
111
+ new Map(results.map(item => [JSON.stringify(item), item])).values()
112
+ );
113
+ const terms = baseResults.flat(Infinity);
114
+ const uniqueTerms = [...new Set(terms)];
115
+ const fetchResults = await fetchLabels(flatmapAPI, uniqueTerms);
116
+ // const objectResults = fetchResults.map((item) => JSON.parse(item[1]));
117
+ const objectResults = fetchResults.reduce((arr, item) => {
118
+ const id = item[0];
119
+ const valObj = JSON.parse(item[1]);
120
+ if (valObj.source === knowledgeSource) {
121
+ arr.push({ id, label: valObj.label });
122
+ }
123
+ return arr;
124
+ }, []);
125
+ const nodes = [];
126
+ const formattedResults = baseResults.map((item) => {
127
+ const itemPair = item.flat();
128
+ const labels = [];
129
+ for (let i = 0; i < itemPair.length; i++) {
130
+ const foundObj = objectResults.find((obj) => obj.id === itemPair[i])
131
+ if (foundObj) {
132
+ labels.push(foundObj.label);
133
+ if (i > 0) {
134
+ nodes.push({
135
+ key: [itemPair[i], []],
136
+ label: foundObj.label,
137
+ });
138
+ }
139
+ }
140
+ }
141
+ return {
142
+ key: item,
143
+ label: labels.join(', '),
144
+ };
145
+ });
146
+ // unique results by combining formattedResults and nodes
147
+ // but filter out duplicates based on the labels
148
+ const uniqueResults = [...formattedResults, ...nodes].filter((result, index, self) =>
149
+ index === self.findIndex((r) => r.label === result.label)
150
+ );
151
+ return uniqueResults;
152
+ }
153
+
154
+ async function extractOriginItems(flatmapAPI, knowledgeSource, knowledge) {
70
155
  const results = [];
71
156
  knowledge.forEach(obj => {
72
157
  if (!Array.isArray(obj.connectivity) || obj.connectivity.length === 0) return;
@@ -76,12 +161,10 @@ function extractOriginItems(knowledge) {
76
161
  if (connectivityItems.has(stringifyItem)) results.push(item);
77
162
  });
78
163
  });
79
- return Array.from(
80
- new Map(results.map(item => [JSON.stringify(item), item])).values()
81
- );
164
+ return await transformResults(flatmapAPI, knowledgeSource, results);
82
165
  }
83
166
 
84
- function extractDestinationItems(knowledge) {
167
+ async function extractDestinationItems(flatmapAPI, knowledgeSource, knowledge) {
85
168
  const results = [];
86
169
  knowledge.forEach(obj => {
87
170
  if (!Array.isArray(obj.connectivity) || obj.connectivity.length === 0) return;
@@ -94,12 +177,10 @@ function extractDestinationItems(knowledge) {
94
177
  if (connectivityItems.has(stringifyItem)) results.push(item);
95
178
  });
96
179
  });
97
- return Array.from(
98
- new Map(results.map(item => [JSON.stringify(item), item])).values()
99
- );
180
+ return await transformResults(flatmapAPI, knowledgeSource, results);
100
181
  }
101
182
 
102
- function extractViaItems(knowledge) {
183
+ async function extractViaItems(flatmapAPI, knowledgeSource, knowledge) {
103
184
  const results = [];
104
185
  knowledge.forEach(obj => {
105
186
  if (!Array.isArray(obj.connectivity) || obj.connectivity.length === 0) return;
@@ -112,41 +193,55 @@ function extractViaItems(knowledge) {
112
193
  if (connectivityItems.has(stringifyItem)) results.push(item);
113
194
  });
114
195
  });
115
- return Array.from(
116
- new Map(results.map(item => [JSON.stringify(item), item])).values()
117
- );
196
+ return await transformResults(flatmapAPI, knowledgeSource, results);
118
197
  }
119
198
 
120
- function findPathsByOriginItem(knowledge, originItem) {
199
+ function findPathsByOriginItem(knowledge, originItems) {
121
200
  return knowledge.filter(obj => {
122
201
  if (!Array.isArray(obj.connectivity) || obj.connectivity.length === 0) return false;
123
202
  const origins = getPhenotypeItems(obj, "ilxtr:hasSomaLocatedIn");
124
- return origins.some(item => JSON.stringify(item) === JSON.stringify(originItem));
203
+ return origins.some(item => originItems.map(i => JSON.stringify(i)).includes(JSON.stringify(item)));
125
204
  });
126
205
  }
127
206
 
128
- function findPathsByDestinationItem(knowledge, destinationItem) {
207
+ function findPathsByDestinationItem(knowledge, destinationItems) {
129
208
  return knowledge.filter(obj => {
130
209
  if (!Array.isArray(obj.connectivity) || obj.connectivity.length === 0) return false;
131
210
  const destinations = [
132
211
  ...getPhenotypeItems(obj, "ilxtr:hasAxonPresynapticElementIn"),
133
212
  ...getPhenotypeItems(obj, "ilxtr:hasAxonSensorySubcellularElementIn")
134
213
  ];
135
- return destinations.some(item => JSON.stringify(item) === JSON.stringify(destinationItem));
214
+ return destinations.some(item => destinationItems.map(i => JSON.stringify(i)).includes(JSON.stringify(item)));
136
215
  });
137
216
  }
138
217
 
139
- function findPathsByViaItem(knowledge, viaItem) {
218
+ function findPathsByViaItem(knowledge, viaItems) {
140
219
  return knowledge.filter(obj => {
141
220
  if (!Array.isArray(obj.connectivity) || obj.connectivity.length === 0) return false;
142
221
  const vias = [
143
222
  ...getPhenotypeItems(obj, "ilxtr:hasAxonLeadingToSensorySubcellularElementIn"),
144
223
  ...getPhenotypeItems(obj, "ilxtr:hasAxonLocatedIn")
145
224
  ];
146
- return vias.some(item => JSON.stringify(item) === JSON.stringify(viaItem));
225
+ return vias.some(item => viaItems.map(i => JSON.stringify(i)).includes(JSON.stringify(item)));
147
226
  });
148
227
  }
149
228
 
229
+ async function queryPathsByRouteFromKnowledge({ knowledge, origins, destinations, vias }) {
230
+ let results = knowledge;
231
+
232
+ if (origins.length) {
233
+ results = findPathsByOriginItem(results, origins);
234
+ }
235
+ if (destinations.length) {
236
+ results = findPathsByDestinationItem(results, destinations);
237
+ }
238
+ if (vias.length) {
239
+ results = findPathsByViaItem(results, vias);
240
+ }
241
+
242
+ return results;
243
+ }
244
+
150
245
  export {
151
246
  filterOrigins,
152
247
  filterDestinations,
@@ -157,4 +252,6 @@ export {
157
252
  findPathsByOriginItem,
158
253
  findPathsByDestinationItem,
159
254
  findPathsByViaItem,
255
+ queryPathsByRouteFromKnowledge,
256
+ fetchLabels,
160
257
  }
@@ -14,6 +14,7 @@ import {
14
14
  queryPathsByOrigin,
15
15
  queryPathsByViaLocation,
16
16
  queryPathsByDestination,
17
+ queryPathsByRoute,
17
18
  } from "./CompetencyQueries/CompetencyQueries.js";
18
19
  import {
19
20
  filterOrigins,
@@ -25,6 +26,8 @@ import {
25
26
  findPathsByOriginItem,
26
27
  findPathsByDestinationItem,
27
28
  findPathsByViaItem,
29
+ queryPathsByRouteFromKnowledge,
30
+ fetchLabels,
28
31
  } from "./CompetencyQueries/knowledgeQueries.js";
29
32
 
30
33
  export {
@@ -43,6 +46,7 @@ export {
43
46
  queryPathsByOrigin,
44
47
  queryPathsByViaLocation,
45
48
  queryPathsByDestination,
49
+ queryPathsByRoute,
46
50
  filterOrigins,
47
51
  filterDestinations,
48
52
  filterViaLocations,
@@ -52,4 +56,6 @@ export {
52
56
  findPathsByOriginItem,
53
57
  findPathsByDestinationItem,
54
58
  findPathsByViaItem,
59
+ queryPathsByRouteFromKnowledge,
60
+ fetchLabels,
55
61
  };