holosphere 1.1.5 → 1.1.7
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/FEDERATION.md +213 -0
- package/README.md +140 -0
- package/babel.config.js +5 -0
- package/examples/README-environmental.md +158 -0
- package/examples/environmentalData.js +380 -0
- package/examples/federation.js +154 -0
- package/examples/queryEnvironmentalData.js +147 -0
- package/examples/references.js +177 -0
- package/federation.js +988 -0
- package/holosphere.d.ts +445 -20
- package/holosphere.js +1083 -998
- package/package.json +3 -6
- package/services/environmentalApi.js +162 -0
- package/services/environmentalApi.test.js +0 -6
- package/test/ai.test.js +268 -76
- package/test/auth.test.js +241 -0
- package/test/delete.test.js +225 -0
- package/test/federation.test.js +163 -356
- package/test/holosphere.test.js +109 -955
- package/test/sea.html +33 -0
- package/test/jest.setup.js +0 -5
- package/test/spacesauth.test.js +0 -335
package/FEDERATION.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# HoloSphere Federation
|
|
2
|
+
|
|
3
|
+
HoloSphere's federation system allows different holons (spaces) to share and access data from each other. Federation creates a relationship between spaces that enables data propagation and cross-space access.
|
|
4
|
+
|
|
5
|
+
## Key Concepts
|
|
6
|
+
|
|
7
|
+
- **Federation Relationship**: A connection between two spaces that allows data to flow between them.
|
|
8
|
+
- **Soul References**: Lightweight references that point to data in its original location (single source of truth).
|
|
9
|
+
- **Notification Flow**: Data notifications flow from source spaces to target spaces that are in the notify list.
|
|
10
|
+
- **Source-Target Relationship**: Each federation sets up a source space (federation list) and a target space (notify list).
|
|
11
|
+
|
|
12
|
+
## Federation Data Flow
|
|
13
|
+
|
|
14
|
+
The HoloSphere federation system works with a clear source-target relationship:
|
|
15
|
+
|
|
16
|
+
1. **Federation List**: When space A federates with space B, space A adds B to its federation list.
|
|
17
|
+
2. **Notify List**: Space B adds space A to its notify list.
|
|
18
|
+
3. **Data Flow**: When space A changes, space B gets notified (but not vice versa unless bidirectional).
|
|
19
|
+
|
|
20
|
+
## Creating Federation
|
|
21
|
+
|
|
22
|
+
Create federation relationships between spaces:
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// Create federation between space1 and space2
|
|
26
|
+
await holoSphere.federate('space1', 'space2');
|
|
27
|
+
|
|
28
|
+
// This sets up:
|
|
29
|
+
// - space1.federation includes space2
|
|
30
|
+
// - space2.notify includes space1
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The bidirectional parameter is largely unused in the current implementation since the federation system naturally sets up the correct notification flow. The default relationship allows space2 to be notified of changes in space1.
|
|
34
|
+
|
|
35
|
+
## Storing and Propagating Data
|
|
36
|
+
|
|
37
|
+
Data must be explicitly propagated to federated spaces:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
const data = {
|
|
41
|
+
id: 'item1',
|
|
42
|
+
title: 'Federation Example',
|
|
43
|
+
value: 42
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Store data in space1
|
|
47
|
+
await holoSphere.put('space1', 'items', data);
|
|
48
|
+
|
|
49
|
+
// Propagate to federated spaces
|
|
50
|
+
await holoSphere.propagate('space1', 'items', data);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
You can also enable automatic propagation in the `put()` method:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
// Store data and automatically propagate
|
|
57
|
+
await holoSphere.put('space1', 'items', data, null, {
|
|
58
|
+
autoPropagate: true
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Accessing Federated Data
|
|
63
|
+
|
|
64
|
+
### Direct Retrieval
|
|
65
|
+
|
|
66
|
+
You can access data directly from any space:
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// Retrieve data from space2 (will resolve reference if it's a reference)
|
|
70
|
+
const data = await holoSphere.get('space2', 'items', 'item1', null, {
|
|
71
|
+
resolveReferences: true // Default is true
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Aggregate Federated Data
|
|
76
|
+
|
|
77
|
+
Use `getFederated()` to get data from multiple federated spaces:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// Get combined data from the local space and all its federated spaces
|
|
81
|
+
const federatedData = await holoSphere.getFederated('space2', 'items', {
|
|
82
|
+
resolveReferences: true, // Default: true
|
|
83
|
+
idField: 'id' // Field to use as the unique identifier
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Soul References
|
|
88
|
+
|
|
89
|
+
HoloSphere uses a simplified reference system based on soul paths:
|
|
90
|
+
|
|
91
|
+
1. A reference contains only an `id` and a `soul` property
|
|
92
|
+
2. The soul path is in the format: `appname/holon/lens/key`
|
|
93
|
+
3. When resolving a reference, HoloSphere follows the soul path to retrieve the original data
|
|
94
|
+
|
|
95
|
+
By default, federation propagation uses references instead of duplicating data. This can be controlled:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
// Propagate with full data copy instead of references
|
|
99
|
+
await holoSphere.propagate('space1', 'items', data, {
|
|
100
|
+
useReferences: false
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Removing Federation
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// Remove federation relationship
|
|
108
|
+
await holoSphere.unfederate('space1', 'space2');
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Complete Example
|
|
112
|
+
|
|
113
|
+
Here's a complete example showing the proper way to set up and use federation:
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
import HoloSphere from './holosphere.js';
|
|
117
|
+
|
|
118
|
+
async function federationExample() {
|
|
119
|
+
const holoSphere = new HoloSphere('example-app');
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const space1 = 'public-space1';
|
|
123
|
+
const space2 = 'public-space2';
|
|
124
|
+
|
|
125
|
+
// Step 1: Create federation relationship
|
|
126
|
+
await holoSphere.federate(space1, space2);
|
|
127
|
+
|
|
128
|
+
// Step 2: Verify federation is set up properly
|
|
129
|
+
const fedInfo1 = await holoSphere.getFederation(space1);
|
|
130
|
+
const fedInfo2 = await holoSphere.getFederation(space2);
|
|
131
|
+
|
|
132
|
+
console.log(`Federation info for ${space1}:`, fedInfo1);
|
|
133
|
+
// Should include: federation: ['space2']
|
|
134
|
+
|
|
135
|
+
console.log(`Federation info for ${space2}:`, fedInfo2);
|
|
136
|
+
// Should include: notify: ['space1']
|
|
137
|
+
|
|
138
|
+
// Step 3: Store data in space1
|
|
139
|
+
const item = {
|
|
140
|
+
id: 'item1',
|
|
141
|
+
title: 'Federation Test',
|
|
142
|
+
value: 42
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await holoSphere.put(space1, 'items', item);
|
|
146
|
+
|
|
147
|
+
// Step 4: Propagate data to federated spaces
|
|
148
|
+
await holoSphere.propagate(space1, 'items', item);
|
|
149
|
+
|
|
150
|
+
// Allow time for propagation
|
|
151
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
152
|
+
|
|
153
|
+
// Step 5: Access data from space2 (resolves reference)
|
|
154
|
+
const itemFromSpace2 = await holoSphere.get(space2, 'items', 'item1');
|
|
155
|
+
console.log('Item from space2:', itemFromSpace2);
|
|
156
|
+
|
|
157
|
+
// Step 6: Update item in space1
|
|
158
|
+
const updatedItem = {
|
|
159
|
+
...item,
|
|
160
|
+
value: 100,
|
|
161
|
+
updated: true
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
await holoSphere.put(space1, 'items', updatedItem);
|
|
165
|
+
|
|
166
|
+
// Since we're using soul references, the update is immediately visible
|
|
167
|
+
// through the reference without needing to propagate again
|
|
168
|
+
|
|
169
|
+
// Verify update is visible through the reference
|
|
170
|
+
const updatedItemFromSpace2 = await holoSphere.get(space2, 'items', 'item1');
|
|
171
|
+
console.log('Updated item from space2:', updatedItemFromSpace2);
|
|
172
|
+
|
|
173
|
+
// Step 7: Clean up
|
|
174
|
+
await holoSphere.unfederate(space1, space2);
|
|
175
|
+
} finally {
|
|
176
|
+
// Always close the HoloSphere instance
|
|
177
|
+
await holoSphere.close();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
federationExample().catch(console.error);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Troubleshooting
|
|
185
|
+
|
|
186
|
+
### Common Issues
|
|
187
|
+
|
|
188
|
+
1. **Federation Relationship**: Make sure to check both the federation list and notify list to understand data flow.
|
|
189
|
+
|
|
190
|
+
2. **Data Propagation**: If data isn't appearing in federated spaces, check:
|
|
191
|
+
- The federation relationship was created correctly
|
|
192
|
+
- The data was propagated explicitly or `autoPropagate` was set to `true`
|
|
193
|
+
- The notify list includes the target space
|
|
194
|
+
|
|
195
|
+
3. **Reference Resolution**: If you're getting reference objects instead of the actual data:
|
|
196
|
+
- Make sure `resolveReferences` is set to `true` (it's the default)
|
|
197
|
+
- Check that the original data still exists at the referenced location
|
|
198
|
+
|
|
199
|
+
4. **Timing Issues**: Data propagation is asynchronous. Add small delays (500-1000ms) between operations to allow propagation to complete.
|
|
200
|
+
|
|
201
|
+
### Best Practices
|
|
202
|
+
|
|
203
|
+
1. **Verify Federation Structure**: After creating a federation, check both spaces to ensure:
|
|
204
|
+
- Source space has the target in its federation list
|
|
205
|
+
- Target space has the source in its notify list
|
|
206
|
+
|
|
207
|
+
2. **Explicit Propagation**: Unless you're using `autoPropagate`, always call `propagate()` explicitly after storing data that should be shared.
|
|
208
|
+
|
|
209
|
+
3. **Choose the Right Propagation Method**:
|
|
210
|
+
- Use `useReferences: true` (default) to keep a single source of truth
|
|
211
|
+
- Use `useReferences: false` only when you need independent copies
|
|
212
|
+
|
|
213
|
+
4. **Cleanup**: Always close the HoloSphere instance when done to prevent resource leaks.
|
package/README.md
CHANGED
|
@@ -329,3 +329,143 @@ Data in HoloSphere is organized by:
|
|
|
329
329
|
|
|
330
330
|
GPL-3.0-or-later
|
|
331
331
|
|
|
332
|
+
# HoloSphere Federation
|
|
333
|
+
|
|
334
|
+
HoloSphere provides a federation system that allows spaces to share data and messages across different holons. This document outlines the federation functionality and how to use it.
|
|
335
|
+
|
|
336
|
+
## Core Federation Features
|
|
337
|
+
|
|
338
|
+
### Space Federation
|
|
339
|
+
- Create relationships between spaces with clear source-target connections
|
|
340
|
+
- Use soul references to maintain a single source of truth
|
|
341
|
+
- Control data propagation between federated spaces
|
|
342
|
+
- Manage notification settings for each space
|
|
343
|
+
|
|
344
|
+
### Message Federation
|
|
345
|
+
- Track messages across federated spaces
|
|
346
|
+
- Maintain message relationships between original and federated copies
|
|
347
|
+
- Update messages consistently across all federated spaces
|
|
348
|
+
|
|
349
|
+
## API Reference
|
|
350
|
+
|
|
351
|
+
### Space Federation
|
|
352
|
+
|
|
353
|
+
#### `federate(spaceId1, spaceId2, password1, password2, bidirectional)`
|
|
354
|
+
Creates a federation relationship between two spaces.
|
|
355
|
+
|
|
356
|
+
```javascript
|
|
357
|
+
await holosphere.federate('space1', 'space2', 'pass1', 'pass2');
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
This sets up:
|
|
361
|
+
- space1.federation includes space2
|
|
362
|
+
- space2.notify includes space1
|
|
363
|
+
|
|
364
|
+
Parameters:
|
|
365
|
+
- `spaceId1`: First space ID (source space)
|
|
366
|
+
- `spaceId2`: Second space ID (target space)
|
|
367
|
+
- `password1`: Optional password for first space
|
|
368
|
+
- `password2`: Optional password for second space
|
|
369
|
+
- `bidirectional`: Whether to set up bidirectional notifications (default: true, but generally not needed)
|
|
370
|
+
|
|
371
|
+
### Data Propagation
|
|
372
|
+
|
|
373
|
+
#### `propagate(holon, lens, data, options)`
|
|
374
|
+
Propagates data to federated spaces.
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
await holosphere.propagate('space1', 'items', data, {
|
|
378
|
+
useReferences: true, // Default: uses soul references
|
|
379
|
+
targetSpaces: ['space2'] // Optional: specific targets
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Parameters:
|
|
384
|
+
- `holon`: The holon identifier
|
|
385
|
+
- `lens`: The lens identifier
|
|
386
|
+
- `data`: The data to propagate
|
|
387
|
+
- `options`: Propagation options
|
|
388
|
+
|
|
389
|
+
Alternatively, you can use auto-propagation:
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
await holosphere.put('space1', 'items', data, null, {
|
|
393
|
+
autoPropagate: true // Enable auto-propagation
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Message Federation
|
|
398
|
+
|
|
399
|
+
#### `federateMessage(originalChatId, messageId, federatedChatId, federatedMessageId, type)`
|
|
400
|
+
Tracks a federated message across different chats.
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
await holosphere.federateMessage('chat1', 'msg1', 'chat2', 'msg2', 'quest');
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### `getFederatedMessages(originalChatId, messageId)`
|
|
407
|
+
Gets all federated messages for a given original message.
|
|
408
|
+
|
|
409
|
+
```javascript
|
|
410
|
+
const messages = await holosphere.getFederatedMessages('chat1', 'msg1');
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### `updateFederatedMessages(originalChatId, messageId, updateCallback)`
|
|
414
|
+
Updates a message across all federated chats.
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
await holosphere.updateFederatedMessages('chat1', 'msg1', async (chatId, messageId) => {
|
|
418
|
+
// Update message in this chat
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Usage Example
|
|
423
|
+
|
|
424
|
+
```javascript
|
|
425
|
+
// Create federation between spaces
|
|
426
|
+
await holosphere.federate('space1', 'space2');
|
|
427
|
+
|
|
428
|
+
// Store data in space1
|
|
429
|
+
const data = { id: 'item1', value: 42 };
|
|
430
|
+
await holosphere.put('space1', 'items', data);
|
|
431
|
+
|
|
432
|
+
// Propagate to federated spaces
|
|
433
|
+
await holosphere.propagate('space1', 'items', data);
|
|
434
|
+
|
|
435
|
+
// Retrieve data from space2 (will resolve the reference)
|
|
436
|
+
const item = await holosphere.get('space2', 'items', 'item1');
|
|
437
|
+
|
|
438
|
+
// Track a federated message
|
|
439
|
+
await holosphere.federateMessage('chat1', 'msg1', 'chat2', 'msg2', 'quest');
|
|
440
|
+
|
|
441
|
+
// Update message across all federated chats
|
|
442
|
+
await holosphere.updateFederatedMessages('chat1', 'msg1', async (chatId, messageId) => {
|
|
443
|
+
await updateMessageInChat(chatId, messageId);
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## Soul References
|
|
448
|
+
|
|
449
|
+
When using the default `useReferences: true` with propagation:
|
|
450
|
+
|
|
451
|
+
1. Only a lightweight reference is stored in the federated space
|
|
452
|
+
2. The reference contains the original item's ID and soul path
|
|
453
|
+
3. When accessed, the reference is automatically resolved to the original data
|
|
454
|
+
4. Changes to the original data are immediately visible through references
|
|
455
|
+
|
|
456
|
+
This maintains a single source of truth while keeping storage efficient.
|
|
457
|
+
|
|
458
|
+
## Federation Structure
|
|
459
|
+
|
|
460
|
+
The federation system uses two key arrays to manage relationships:
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
{
|
|
464
|
+
id: string,
|
|
465
|
+
name: string,
|
|
466
|
+
federation: string[], // Source spaces this holon federates with
|
|
467
|
+
notify: string[], // Target spaces to notify of changes
|
|
468
|
+
timestamp: number
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
package/babel.config.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# HoloSphere Environmental Data Examples
|
|
2
|
+
|
|
3
|
+
This directory contains examples demonstrating how to use HoloSphere for storing, federating, and querying environmental data across different geographical scales.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The examples show how to:
|
|
8
|
+
|
|
9
|
+
1. Store environmental data from various APIs into hierarchical holons
|
|
10
|
+
2. Create federation relationships between holons at different scales
|
|
11
|
+
3. Use soul references to efficiently share data between holons
|
|
12
|
+
4. Query environmental data, including resolving federation references
|
|
13
|
+
5. Compute aggregated metrics across holons
|
|
14
|
+
|
|
15
|
+
## Files
|
|
16
|
+
|
|
17
|
+
- `environmentalData.js` - Shows how to fetch, transform and store environmental data in HoloSphere
|
|
18
|
+
- `queryEnvironmentalData.js` - Demonstrates how to query the stored data across federated holons
|
|
19
|
+
|
|
20
|
+
## Environmental Data Structure
|
|
21
|
+
|
|
22
|
+
The examples organize environmental data into a hierarchical structure:
|
|
23
|
+
|
|
24
|
+
- **City level (H3 resolution 7)**: Air quality data
|
|
25
|
+
- **Region level (H3 resolution 5)**: Carbon sequestration and soil data
|
|
26
|
+
- **Country level (H3 resolution 3)**: Biodiversity data
|
|
27
|
+
|
|
28
|
+
Federation relationships are established between these levels, allowing data to flow from smaller to larger scales while maintaining a single source of truth through soul references.
|
|
29
|
+
|
|
30
|
+
## Data Schemas
|
|
31
|
+
|
|
32
|
+
The examples define several schemas for environmental data:
|
|
33
|
+
|
|
34
|
+
### Air Quality Schema
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"id": "string",
|
|
38
|
+
"holon": "string",
|
|
39
|
+
"timestamp": "number",
|
|
40
|
+
"coordinates": {
|
|
41
|
+
"lat": "number",
|
|
42
|
+
"lon": "number"
|
|
43
|
+
},
|
|
44
|
+
"airQualityIndex": "number",
|
|
45
|
+
"pollutants": {
|
|
46
|
+
"co": "number",
|
|
47
|
+
"no2": "number",
|
|
48
|
+
"o3": "number",
|
|
49
|
+
"pm2_5": "number",
|
|
50
|
+
"pm10": "number",
|
|
51
|
+
"so2": "number"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Carbon Sequestration Schema
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"id": "string",
|
|
60
|
+
"holon": "string",
|
|
61
|
+
"timestamp": "number",
|
|
62
|
+
"coordinates": {
|
|
63
|
+
"lat": "number",
|
|
64
|
+
"lon": "number"
|
|
65
|
+
},
|
|
66
|
+
"carbonSequestration": "number",
|
|
67
|
+
"temperature": "number",
|
|
68
|
+
"precipitation": "number"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Soil Carbon Schema
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"id": "string",
|
|
76
|
+
"holon": "string",
|
|
77
|
+
"timestamp": "number",
|
|
78
|
+
"coordinates": {
|
|
79
|
+
"lat": "number",
|
|
80
|
+
"lon": "number"
|
|
81
|
+
},
|
|
82
|
+
"soilCarbon": "number",
|
|
83
|
+
"soilType": "string",
|
|
84
|
+
"soilDepth": "string"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Biodiversity Schema
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"id": "string",
|
|
92
|
+
"holon": "string",
|
|
93
|
+
"timestamp": "number",
|
|
94
|
+
"countryCode": "string",
|
|
95
|
+
"biodiversityCount": "number",
|
|
96
|
+
"species": [
|
|
97
|
+
{
|
|
98
|
+
"scientificName": "string",
|
|
99
|
+
"count": "number"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Running the Examples
|
|
106
|
+
|
|
107
|
+
1. Make sure you have all required API keys in your `.env` file:
|
|
108
|
+
- `OPENWEATHER_API_KEY` - For air quality data
|
|
109
|
+
- `NOAA_API_KEY` - For climate data
|
|
110
|
+
|
|
111
|
+
2. Run the data storage example:
|
|
112
|
+
```
|
|
113
|
+
node examples/environmentalData.js
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
3. Run the query example:
|
|
117
|
+
```
|
|
118
|
+
node examples/queryEnvironmentalData.js
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Federation in Practice
|
|
122
|
+
|
|
123
|
+
These examples demonstrate several key federation concepts:
|
|
124
|
+
|
|
125
|
+
1. **Hierarchical Data Organization**: Environmental data is organized by geographical scale, with relationships between scales.
|
|
126
|
+
|
|
127
|
+
2. **Soul References**: When propagating data from city to region level, soul references are used instead of duplicating data.
|
|
128
|
+
|
|
129
|
+
3. **Automatic Reference Resolution**: When querying data, references are automatically resolved to their original source.
|
|
130
|
+
|
|
131
|
+
4. **Cross-Scale Aggregation**: The compute function is used to aggregate metrics from different scales.
|
|
132
|
+
|
|
133
|
+
## Example Output
|
|
134
|
+
|
|
135
|
+
Running the query example should produce output similar to:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Querying environmental data for location (41.9, 12.5)
|
|
139
|
+
- City: 8773237a267ffff
|
|
140
|
+
- Region: 852e3ffffffffffffff
|
|
141
|
+
- Country: 832ffffffffffffffff
|
|
142
|
+
|
|
143
|
+
=== CITY LEVEL: Air Quality Data ===
|
|
144
|
+
Latest Air Quality Index: 2
|
|
145
|
+
Pollutant levels:
|
|
146
|
+
- co: 201.94
|
|
147
|
+
- no2: 0.78
|
|
148
|
+
- o3: 68.64
|
|
149
|
+
- pm2_5: 0.5
|
|
150
|
+
- pm10: 0.82
|
|
151
|
+
- so2: 0.52
|
|
152
|
+
|
|
153
|
+
=== REGION LEVEL: Soil Carbon Data ===
|
|
154
|
+
Soil Carbon: 78.4 g/kg
|
|
155
|
+
Soil Type: Clay Loam
|
|
156
|
+
Soil Depth: 0-30cm
|
|
157
|
+
|
|
158
|
+
...
|