Drive analytics
Drive Analytics provides automated monitoring and insights into document drive operations within Powerhouse applications. This system tracks user interactions, document modifications, and drive activity to help developers understand usage patterns and system performance.
Overview
The Drive Analytics system consists of two specialized processors that automatically collect metrics from document drives:
- Drive Analytics Processor: Tracks file and folder operations (creation, deletion, moves, etc.)
- Document Analytics Processor: Tracks document content changes and state modifications
These processors run in the background, converting operations into structured time-series data that can be queried and visualized in real-time.
Available Metrics in Connect
Connect applications have Drive Analytics enabled by default through the ReactorAnalyticsProvider
. When enabled, the system automatically tracks:
Drive Operations Metrics
- File Creation: New documents added to drives
- Folder Creation: New directories created
- File Updates: Document content modifications
- Node Updates: Metadata changes
- File Moves: Documents relocated between folders
- File Copies: Document duplication
- File Deletions: Documents removed from drives
Document Operations Metrics
- State Changes: Document model state modifications
Data Sources and Structure
Drive Analytics organizes data using hierarchical source paths that allow precise querying of different analytics contexts:
Drive Analytics Sources
Pattern: ph/drive/{driveId}/{branch}/{scope}
- driveId: Unique identifier for the document drive
- branch: Branch name (e.g., "main", "dev")
- scope: Operation scope ("global" for shared operations, "local" for device-specific)
Example: ph/drive/abc123/main/global
Document Analytics Sources
Pattern: ph/doc/{driveId}/{documentId}/{branch}/{scope}
- driveId: Drive containing the document
- documentId: Specific document identifier
- branch: Branch name
- scope: Operation scope
Example: ph/doc/abc123/doc456/main/global
Available Metrics
DriveOperations
Tracks file system operations within drives:
- Value: Always 1 (counter metric)
- Purpose: Count drive-level operations like file creation, deletion, moves
- Source Pattern:
ph/drive/*
DocumentOperations
Tracks document content and state changes:
- Value: Always 1 (counter metric)
- Purpose: Count document-specific operations like state changes
- Source Pattern:
ph/doc/*
Complete Dimensions Reference
Drive Analytics Dimensions
1. Drive Dimension
Pattern: ph/drive/{driveId}/{branch}/{scope}/{revision}
Purpose: Identifies the drive context with revision information
// Examples
"ph/drive/abc123/main/global/42";
"ph/drive/my-drive/feature-branch/local/15";
2. Operation Dimension
Pattern: ph/drive/operation/{operationType}/{operationIndex}
Purpose: Identifies specific operation types and their sequence
Available Operation Types:
- ADD_FILE: Create new file
- ADD_FOLDER: Create new folder
- UPDATE_FILE: Modify file content
- UPDATE_NODE: Modify node metadata
- MOVE_NODE: Move file/folder to different location
- COPY_NODE: Duplicate existing file/folder
- DELETE_NODE: Remove file/folder
// Examples
"ph/drive/operation/ADD_FILE/5";
"ph/drive/operation/DELETE_NODE/23";
"ph/drive/operation/MOVE_NODE/12";
3. Target Dimension
Pattern: ph/drive/target/{targetType}/{targetId}
Purpose: Identifies what was targeted by the operation
Target Types:
- DRIVE: Operation affects the drive itself
- NODE: Operation affects a specific file/folder
// Examples
"ph/drive/target/DRIVE/abc123";
"ph/drive/target/NODE/file456";
"ph/drive/target/NODE/folder789";
4. Action Type Dimension
Pattern: ph/drive/actionType/{actionType}/{targetId}
Purpose: Categorizes operations by their effect
Action Types:
- CREATED: New items added (ADD_FILE, ADD_FOLDER)
- DUPLICATED: Items copied (COPY_NODE)
- UPDATED: Existing items modified (UPDATE_FILE, UPDATE_NODE)
- MOVED: Items relocated (MOVE_NODE)
- REMOVED: Items deleted (DELETE_NODE)
// Examples
"ph/drive/actionType/CREATED/file123";
"ph/drive/actionType/MOVED/folder456";
"ph/drive/actionType/REMOVED/doc789";
Document Analytics Dimensions
1. Drive Dimension
Pattern: ph/doc/drive/{driveId}/{branch}/{scope}/{revision}
Purpose: Drive context for document operations
// Examples
"ph/doc/drive/abc123/main/global/42";
2. Operation Dimension
Pattern: ph/doc/operation/{operationType}/{operationIndex}
Purpose: Document-specific operation identification
// Examples (document model operations vary by document type)
"ph/doc/operation/SET_STATE/15";
"ph/doc/operation/ADD_ITEM/8";
"ph/doc/operation/UPDATE_PROPERTY/22";
3. Target Dimension
Pattern: ph/doc/target/{driveId}/{targetType}/{documentId}
Purpose: Document target identification
Target Types:
- DRIVE: Document is the drive document itself (driveId === documentId)
- NODE: Document is a regular document within the drive
// Examples
"ph/doc/target/abc123/DRIVE/abc123"; // Drive document
"ph/doc/target/abc123/NODE/doc456"; // Regular document
Query Parameters
Time Range
- start: DateTime object for query start time
- end: DateTime object for query end time
- granularity: Time bucketing (Total, Hourly, Daily, Weekly, Monthly)
Filtering with Select
Use the select
parameter to filter by specific dimension values:
select: {
// Filter by specific drives
drive: [
AnalyticsPath.fromString("ph/drive/abc123"),
AnalyticsPath.fromString("ph/drive/xyz789")
],
// Filter by operation types
operation: [
AnalyticsPath.fromString("ph/drive/operation/ADD_FILE"),
AnalyticsPath.fromString("ph/drive/operation/UPDATE_FILE")
],
// Filter by action types
actionType: [
AnalyticsPath.fromString("ph/drive/actionType/CREATED"),
AnalyticsPath.fromString("ph/drive/actionType/UPDATED")
],
// Filter by targets
target: [
AnalyticsPath.fromString("ph/drive/target/NODE")
]
}
Level of Detail (LOD)
Control how deeply dimensions are grouped:
lod: {
drive: 1, // Group by drive only (ignore branch/scope/revision)
operation: 1, // Group by operation type only (ignore index)
actionType: 1, // Group by action type only (ignore target ID)
target: 1 // Group by target type only (ignore target ID)
}
Querying Analytics Data
Using the useAnalyticsQuery Hook
The primary way to access drive analytics is through the useAnalyticsQuery
hook:
import {
useAnalyticsQuery,
AnalyticsGranularity,
AnalyticsPath,
DateTime,
} from "@powerhousedao/reactor-browser/analytics";
function DriveUsageChart({ driveId }: { driveId: string }) {
const { data, isLoading } = useAnalyticsQuery({
start: DateTime.now().minus({ days: 7 }),
end: DateTime.now(),
granularity: AnalyticsGranularity.Daily,
metrics: ["DriveOperations"],
select: {
drive: [AnalyticsPath.fromString(`ph/drive/${driveId}`)],
actionType: [
AnalyticsPath.fromString("ph/drive/actionType/CREATED"),
AnalyticsPath.fromString("ph/drive/actionType/UPDATED"),
AnalyticsPath.fromString("ph/drive/actionType/REMOVED"),
],
},
lod: {
drive: 1,
actionType: 1,
},
});
if (isLoading) return <div>Loading analytics...</div>;
return (
<div>
{/* Render your chart using the analytics data */}
{data?.rows.map((row) => (
<div key={row.metric}>
{row.metric}: {row.value}
</div>
))}
</div>
);
}
Using the useDriveAnalytics Hook
For common drive analytics queries, use the specialized useDriveAnalytics
hook:
import { useDriveAnalytics } from "@powerhousedao/common/drive-analytics";
import { AnalyticsGranularity } from "@powerhousedao/reactor-browser/analytics";
function DriveInsights({ driveIds }: { driveIds: string[] }) {
const analytics = useDriveAnalytics({
filters: {
driveId: driveIds,
operation: ["ADD_FILE", "UPDATE_FILE", "DELETE_NODE"],
actionType: ["CREATED", "UPDATED", "REMOVED"],
},
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), // 7 days ago
to: new Date().toISOString(),
granularity: AnalyticsGranularity.Daily,
levelOfDetail: { drive: 1, operation: 1 },
});
if (analytics.isLoading) return <div>Loading...</div>;
return (
<div>
<h3>Drive Activity Summary</h3>
{analytics.data?.rows.map((row, index) => (
<div key={index}>
<strong>
{row.dimensions.find((d) => d.name === "actionType")?.path}
</strong>
: {row.value}
</div>
))}
</div>
);
}
Using the useDocumentAnalytics Hook
For document-specific analytics queries, use the useDocumentAnalytics
hook:
import { useDocumentAnalytics } from "@powerhousedao/common/drive-analytics";
import { AnalyticsGranularity } from "@powerhousedao/reactor-browser/analytics";
function DocumentInsights({
driveId,
documentIds,
}: {
driveId: string;
documentIds: string[];
}) {
const analytics = useDocumentAnalytics({
filters: {
driveId: [driveId],
documentId: documentIds,
target: ["NODE"], // Focus on document nodes vs drive documents
branch: ["main"],
scope: ["global"],
},
from: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // 24 hours ago
to: new Date().toISOString(),
granularity: AnalyticsGranularity.Hourly,
levelOfDetail: {
drive: 1,
operation: 1,
target: 1,
},
});
if (analytics.isLoading) return <div>Loading...</div>;
return (
<div>
<h3>Document Activity Summary</h3>
{analytics.data?.rows.map((row, index) => (
<div key={index}>Document Operations: {row.value}</div>
))}
</div>
);
}
Advanced Query Examples
Filter by Multiple Criteria
// Get file creations and updates for specific drives in the last 24 hours
const { data } = useAnalyticsQuery({
start: DateTime.now().minus({ hours: 24 }),
end: DateTime.now(),
granularity: AnalyticsGranularity.Hourly,
metrics: ["DriveOperations"],
select: {
drive: [
AnalyticsPath.fromString("ph/drive/project-a"),
AnalyticsPath.fromString("ph/drive/project-b"),
],
operation: [
AnalyticsPath.fromString("ph/drive/operation/ADD_FILE"),
AnalyticsPath.fromString("ph/drive/operation/UPDATE_FILE"),
],
target: [AnalyticsPath.fromString("ph/drive/target/NODE")],
},
lod: {
drive: 1,
operation: 1,
},
});
Compare Document vs Drive Operations
// Using the specialized hooks for easier comparison
const driveOps = useDriveAnalytics({
filters: { driveId: [driveId] },
from: DateTime.now().minus({ days: 1 }).toISO(),
to: DateTime.now().toISO(),
granularity: AnalyticsGranularity.Total,
});
const docOps = useDocumentAnalytics({
filters: { driveId: [driveId] },
from: DateTime.now().minus({ days: 1 }).toISO(),
to: DateTime.now().toISO(),
granularity: AnalyticsGranularity.Total,
});
// Or using useAnalyticsQuery directly
const driveOpsQuery = useAnalyticsQuery({
start: DateTime.now().minus({ days: 1 }),
end: DateTime.now(),
granularity: AnalyticsGranularity.Total,
metrics: ["DriveOperations"],
select: {
drive: [AnalyticsPath.fromString(`ph/drive/${driveId}`)],
},
});
const docOpsQuery = useAnalyticsQuery({
start: DateTime.now().minus({ days: 1 }),
end: DateTime.now(),
granularity: AnalyticsGranularity.Total,
metrics: ["DocumentOperations"],
select: {
drive: [AnalyticsPath.fromString(`ph/doc/drive/${driveId}`)],
},
});
Real-time Activity Monitoring
// Monitor specific drive for real-time updates
const { data } = useAnalyticsQuery(
{
start: DateTime.now().minus({ minutes: 10 }),
end: DateTime.now(),
granularity: AnalyticsGranularity.Total,
metrics: ["DriveOperations"],
select: {
drive: [AnalyticsPath.fromString(`ph/drive/${driveId}`)],
},
},
{
sources: [AnalyticsPath.fromString(`ph/drive/${driveId}`)],
refetchInterval: 5000, // Poll every 5 seconds
},
);
Real-time Updates
Analytics queries can automatically update when new data is available by specifying sources:
const { data } = useAnalyticsQuery(
{
start: DateTime.now().minus({ hours: 1 }),
end: DateTime.now(),
granularity: AnalyticsGranularity.Total,
metrics: ["DriveOperations"],
},
{
sources: [AnalyticsPath.fromString(`ph/drive/${driveId}`)],
},
);
// This query will automatically refetch when new operations occur in the specified drive
Configuration in Connect
Drive Analytics is automatically enabled in Connect applications through feature flags:
// In apps/connect/src/context/reactor-analytics.tsx
export function ReactorAnalyticsProvider({ children }: PropsWithChildren) {
return (
<AnalyticsProvider options={{ databaseName: "connect:analytics" }}>
{connectConfig.analytics.driveAnalyticsEnabled && (
<DriveAnalyticsProcessor />
)}
{children}
</AnalyticsProvider>
);
}