Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/collection-model/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ interface CollectionProps {
calculated_storage_size: number | undefined;
index_count: number | undefined;
index_size: number | undefined;
bucket_count: number | undefined;
avg_bucket_size: number | undefined;
isTimeSeries: boolean;
isView: boolean;
/** Only relevant for a view and identifies collection/view from which this view was created. */
Expand Down
2 changes: 2 additions & 0 deletions packages/collection-model/lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ const CollectionModel = AmpersandModel.extend(debounceActions(['fetch']), {
free_storage_size: 'number',
index_count: 'number',
index_size: 'number',
bucket_count: 'number',
avg_bucket_size: 'number',
},
derived: {
ns: {
Expand Down
50 changes: 50 additions & 0 deletions packages/data-service/src/data-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,30 @@ class DataServiceImpl extends WithLogContext implements DataService {
},
},
nindexes: { $max: '$storageStats.nindexes' },
numBuckets: {
$first: {
$ifNull: [
'$storageStats.timeseries.bucketCount',
'$storageStats.numBuckets',
],
},
},
totalBucketSize: {
$first: {
$ifNull: [
{
$multiply: [
'$storageStats.timeseries.avgBucketSize',
'$storageStats.timeseries.bucketCount',
],
},
'$storageStats.totalBucketSize',
],
},
},
avgBucketSizeFromStats: {
$first: '$storageStats.timeseries.avgBucketSize',
},
},
},
{
Expand All @@ -1230,6 +1254,30 @@ class DataServiceImpl extends WithLogContext implements DataService {
else: 0,
},
},
// `avgBucketSize` is the average bucket size for time series collections
avgBucketSize: {
$cond: {
if: { $ne: ['$avgBucketSizeFromStats', null] },
then: '$avgBucketSizeFromStats',
else: {
$cond: {
if: {
$and: [
{ $ne: ['$numBuckets', null] },
{ $ne: ['$numBuckets', 0] },
],
},
then: {
$divide: [
{ $toDouble: '$totalBucketSize' },
{ $toDouble: '$numBuckets' },
],
},
else: null,
},
},
},
},
},
},
],
Expand Down Expand Up @@ -2995,6 +3043,8 @@ class DataServiceImpl extends WithLogContext implements DataService {
free_storage_size: data.freeStorageSize ?? 0,
index_count: data.nindexes ?? 0,
index_size: data.totalIndexSize ?? 0,
bucket_count: data.numBuckets,
avg_bucket_size: data.avgBucketSize,
};
}

Expand Down
2 changes: 2 additions & 0 deletions packages/data-service/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface CollectionStats {
free_storage_size: number;
index_count: number;
index_size: number;
bucket_count?: number;
avg_bucket_size?: number;
}

export interface CollStatsIndexDetails {
Expand Down
48 changes: 40 additions & 8 deletions packages/databases-collections-list/src/collections.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ function createCollection(
index_count: 15,
index_size: 16,
calculated_storage_size: undefined,
bucket_count: undefined,
avg_bucket_size: undefined,
...props,
};

Expand Down Expand Up @@ -103,9 +105,11 @@ const colls: CollectionProps[] = [
storage_size: 5000,
document_count: undefined,
avg_document_size: undefined,
index_size: undefined,
index_size: 2000,
type: 'timeseries',
index_count: undefined,
index_count: 2,
bucket_count: 50,
avg_bucket_size: 100,
properties: [{ id: 'timeseries' }],
}),
createCollection('qux', {
Expand Down Expand Up @@ -405,9 +409,9 @@ describe('Collections', () => {
});

await testSortColumn(screen, 'collections-list', 'Indexes', [
['5', '0', '-', '-', '5', '1', '11', '3', '5', '17'],
['17', '11', '5', '5', '5', '3', '1', '0', '-', '-'],
['0', '1', '3', '5', '5', '5', '11', '17', '-', '-'],
['5', '0', '-', '2', '5', '1', '11', '3', '5', '17'],
['17', '11', '5', '5', '5', '3', '2', '1', '0', '-'],
['0', '1', '2', '3', '5', '5', '5', '11', '17', '-'],
]);
});

Expand All @@ -421,7 +425,7 @@ describe('Collections', () => {
'500.00 B',
'0 B',
'-',
'-',
'2.00 kB',
'17.00 kB',
'10.00 MB',
'555.00 B',
Expand All @@ -435,23 +439,23 @@ describe('Collections', () => {
'200.00 kB',
'123.46 kB',
'17.00 kB',
'2.00 kB',
'555.00 B',
'500.00 B',
'0 B',
'-',
'-',
],
[
'0 B',
'500.00 B',
'555.00 B',
'2.00 kB',
'17.00 kB',
'123.46 kB',
'200.00 kB',
'333.33 kB',
'10.00 MB',
'-',
'-',
],
]);
});
Expand Down Expand Up @@ -555,6 +559,34 @@ describe('Collections', () => {
);
});

it('renders a tooltip for timeseries badge with bucket stats', async function () {
renderCollectionsList({
collections: colls,
});

const result = inspectTable(screen, 'collections-list');
const badge = result.trs[3].querySelector(
'[data-testid="collection-badge-timeseries"]'
);
expect(badge).to.exist;

userEvent.hover(badge as Element);
await waitFor(
function () {
expect(screen.getByRole('tooltip')).to.exist;
},
{
timeout: 5000,
}
);

const tooltipText = screen.getByRole('tooltip').textContent;
expect(tooltipText).to.include('Bucket count:');
expect(tooltipText).to.include('50');
expect(tooltipText).to.include('Avg. bucket size:');
expect(tooltipText).to.include('100.00 B');
});

it('renders a tooltip for storage size cell with storage breakdown and data size', async function () {
renderCollectionsList({
collections: colls,
Expand Down
31 changes: 27 additions & 4 deletions packages/databases-collections-list/src/collections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,31 @@ function collectionPropertyToBadge(
};
case 'capped':
return { id, name: id, variant: 'darkgray' };
case 'timeseries':
return { id, name: id, variant: 'darkgray', icon: 'TimeSeries' };
case 'timeseries': {
let hint: React.ReactNode = undefined;
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Redundant initialization. In TypeScript/JavaScript, variables without explicit initialization are already undefined. Remove = undefined for cleaner code.

Suggested change
let hint: React.ReactNode = undefined;
let hint: React.ReactNode;

Copilot uses AI. Check for mistakes.
if (
collection.bucket_count !== undefined ||
collection.avg_bucket_size !== undefined
) {
hint = (
<>
{collection.bucket_count !== undefined && (
<div>
<strong>Bucket count:</strong>{' '}
{compactNumber(collection.bucket_count)}
</div>
)}
{collection.avg_bucket_size !== undefined && (
<div>
<strong>Avg. bucket size:</strong>{' '}
{compactBytes(collection.avg_bucket_size)}
</div>
)}
</>
);
}
return { id, name: id, variant: 'darkgray', icon: 'TimeSeries', hint };
}
case 'fle2':
return {
id,
Expand Down Expand Up @@ -415,7 +438,7 @@ function collectionColumns({
}

const type = collection.type as string;
if (type === 'view' || type === 'timeseries') {
if (type === 'view') {
return '-';
}

Expand All @@ -436,7 +459,7 @@ function collectionColumns({
return <Placeholder maxChar={10}></Placeholder>;
}

if (collection.type === 'view' || collection.type === 'timeseries') {
if (collection.type === 'view') {
return '-';
}

Expand Down
Loading