- 8 minutes to read

Saved Views: Technical Specifications

New .7.x

This page defines the technical data structure, storage architecture, versioning strategy, and conflict resolution patterns for Mapify Saved Views. It is intended for developers, database administrators, and architects implementing or integrating with this feature.

Back to Saved Views


Data Structure: JSON Schema

Saved Views are stored as JSON objects. Each Saved View represents a complete snapshot of graph state including filters, layout, and metadata.

Required Fields

Field Type Description Example
viewId GUID Unique identifier. Generated on creation. "a3f8d7c2-4b1e-4a9d-8e3f-7c6b5a4d3e2f"
viewName String (1–255 chars) User-provided display name. Unique per user for personal views; globally unique for shared views. "GDPR Compliance Audit Q1 2026"
createdBy String (email or userId) User who created the view. Used for ownership and permissions. "user@nodinite.com"
createdDate ISO 8601 DateTime Timestamp when view was created (UTC). "2026-01-15T10:30:00Z"
visibility Enum: "personal" | "shared" Determines access scope. "shared"
filters Object Active filter criteria applied to the graph. See Filters Object Schema below. { "domains": ["CRM"], "entityTypes": ["Integration"] }
layout Object Graph visualization settings (2D/3D, zoom, camera position). See Layout Object Schema below. { "mode": "2D", "zoom": 1.0 }
schemaVersion String (semantic version) Schema version for backward compatibility. Current version: "1.0.0". "1.0.0"

Optional Fields

Field Type Description Default
description String (0–1000 chars) User-provided context explaining the view's purpose. null
lastModifiedBy String (email or userId) User who last updated the view. null
lastModifiedDate ISO 8601 DateTime Timestamp of last modification (UTC). null
selectedEntities Array of GUIDs Specific entity IDs to highlight or include. Empty = all entities matching filters. []
sharedWith Array of Strings Team names or user groups with access. Only applies to shared views. Empty = all authenticated users. []
tags Array of Strings User-defined tags for categorization (e.g., "compliance", "onboarding"). []
isFavorite Boolean User-specific flag to pin view to top of dropdown. false

Filters Object Schema

{
  "domains": ["String"],
  "complianceTags": ["String"],
  "entityTypes": ["String"],
  "owners": ["String"],
  "systems": ["String"],
  "healthStatus": ["String"],
  "customMetadata": { "fieldName": "fieldValue" },
  "dateRange": {
    "field": "String",
    "from": "ISO 8601 DateTime",
    "to": "ISO 8601 DateTime"
  },
  "searchQuery": "String"
}

Layout Object Schema

{
  "mode": "2D"|"3D",
  "groupBy": "domain"|"owner"|"system"|"entityType"|null,
  "zoom": 1.0,
  "centerX": 0,
  "centerY": 0,
  "centerZ": 0,
  "rotationX": 0,
  "rotationY": 0,
  "colorScheme": {
    "compliant": "#28a745",
    "reviewNeeded": "#ffc107",
    "nonCompliant": "#dc3545"
  },
  "showLabels": true,
  "showEdges": true,
  "nodeSize": "small"|"medium"|"large",
  "edgeThickness": "thin"|"medium"|"thick"
}

Complete Example: Saved View JSON

{
  "viewId": "a3f8d7c2-4b1e-4a9d-8e3f-7c6b5a4d3e2f",
  "viewName": "GDPR Compliance Audit Q1 2026",
  "description": "Quarterly GDPR audit covering Customer Data, CRM, and Marketing integrations.",
  "createdBy": "user@nodinite.com",
  "createdDate": "2026-01-15T10:30:00Z",
  "lastModifiedBy": "admin@nodinite.com",
  "lastModifiedDate": "2026-01-19T14:45:00Z",
  "visibility": "shared",
  "schemaVersion": "1.0.0",
  "filters": {
    "domains": ["Customer Data", "CRM", "Marketing"],
    "complianceTags": ["GDPR", "Personal Data", "EU Region"],
    "entityTypes": ["Integration", "Service", "Resource"],
    "owners": ["Data Protection Team"],
    "healthStatus": ["Healthy", "Warning"],
    "dateRange": {
      "field": "lastModified",
      "from": "2025-10-20T00:00:00Z",
      "to": "2026-01-19T23:59:59Z"
    }
  },
  "layout": {
    "mode": "2D",
    "groupBy": "domain",
    "zoom": 1.0,
    "centerX": 0,
    "centerY": 0,
    "showLabels": true,
    "showEdges": true,
    "nodeSize": "medium"
  },
  "selectedEntities": [],
  "sharedWith": ["Data Protection Team", "Auditors"],
  "tags": ["compliance", "GDPR", "quarterly"],
  "isFavorite": false
}

Storage Architecture

Saved Views require persistent storage with multi-user access, version control, and performance optimization.

Option 1: Browser LocalStorage

Pros: Zero server load, instant access, offline support, simple implementation.

Cons: No cross-device sync, no shared views, 5–10 MB limit, no backup, security risks.

Verdict: Not recommended for enterprise deployments.

Database Schema (SQL Server):

CREATE TABLE MapifyViews (
    ViewId          UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
    ViewName        NVARCHAR(255)    NOT NULL,
    Description     NVARCHAR(1000)   NULL,
    CreatedBy       NVARCHAR(255)    NOT NULL,
    CreatedDate     DATETIME2        NOT NULL DEFAULT GETUTCDATE(),
    LastModifiedBy  NVARCHAR(255)    NULL,
    LastModifiedDate DATETIME2       NULL,
    Visibility      NVARCHAR(20)     NOT NULL CHECK (Visibility IN ('personal', 'shared')),
    SchemaVersion   NVARCHAR(20)     NOT NULL DEFAULT '1.0.0',
    ViewData        NVARCHAR(MAX)    NOT NULL,  -- JSON blob
    SharedWith      NVARCHAR(MAX)    NULL,      -- JSON array of team names
    Tags            NVARCHAR(MAX)    NULL,      -- JSON array of tags
    IsFavorite      BIT              DEFAULT 0,
    IsDeleted       BIT              DEFAULT 0,

    INDEX IX_MapifyViews_CreatedBy  (CreatedBy),
    INDEX IX_MapifyViews_Visibility (Visibility),
    INDEX IX_MapifyViews_CreatedDate (CreatedDate DESC)
);

Web API Endpoints:

  • GET /api/mapify/views – List all views (personal + shared) for current user
  • GET /api/mapify/views/{viewId} – Load specific view by ID
  • POST /api/mapify/views – Create new view
  • PUT /api/mapify/views/{viewId} – Update existing view
  • DELETE /api/mapify/views/{viewId} – Soft-delete (set IsDeleted = 1)

Permissions Model:

  • Personal views: Only creator can read, update, delete
  • Shared views: All authenticated users can read; only creator and admins can update/delete

Option 3: Hybrid (LocalStorage + Database Sync)

Pros: Instant personal view access + shared view collaboration + offline support for personal views.

Cons: Sync complexity, partial backup, potential user confusion about which views are local vs server-side.

Verdict: Consider for advanced mobile or disconnected-environment scenarios only.

Performance Optimization

// Efficient query: filter at database level, project only needed fields
var views = _database.MapifyViews
    .Where(v => v.Visibility == "shared" || v.CreatedBy == currentUserId)
    .Where(v => !v.IsDeleted)
    .OrderByDescending(v => v.CreatedDate)
    .Take(50)
    .Select(v => new { v.ViewId, v.ViewName, v.CreatedDate })
    .ToList();

Expected performance targets:

  • List views: <100ms for 1,000 views
  • Load specific view: <200ms (including network latency)
  • Save new view: <300ms (including database write + indexing)

View ID Naming Conventions

Format: GUIDs — Globally Unique Identifiers.

Generation: NEWID() in SQL Server or Guid.NewGuid() in C#.

Why GUIDs?

  • Globally unique — no collision risk even with offline creation and later sync
  • Non-sequential — cannot guess other users' view IDs (security)
  • Standardized — compatible with REST APIs and distributed systems

Avoid:

  • Sequential integers — risk of ID collisions in distributed environments
  • User-defined strings — risk of naming conflicts and special character issues

Schema Versioning Strategy

As Mapify evolves, the Saved Views schema requires updates. A robust versioning strategy ensures backward compatibility.

Every Saved View includes schemaVersion using semantic versioning ("MAJOR.MINOR.PATCH"):

Version Change Example Changes Allowed Migration Strategy
PATCH 1.0.0 → 1.0.1 Bug fixes only; no schema changes None — old views load unchanged
MINOR 1.0.0 → 1.1.0 New optional fields added Automatic — API adds default values on load
MAJOR 1.0.0 → 2.0.0 Breaking changes (field removal, type changes) Manual or automated migration script required

Example Migration: Adding tags Field (v1.0.0 → v1.1.0)

public SavedView LoadView(Guid viewId)
{
    var view = _database.GetView(viewId);

    if (view.SchemaVersion == "1.0.0")
    {
        view.Tags = new List<string>();
        view.SchemaVersion = "1.1.0";
        _database.UpdateView(view);
    }

    return view;
}

Example Migration: Removing Deprecated Field (v1.x → v2.0.0)

-- Backup before migration
SELECT * INTO MapifyViews_Backup_v1 FROM MapifyViews;

-- Apply schema change
UPDATE MapifyViews
SET SchemaVersion = '2.0.0',
    ViewData = JSON_MODIFY(ViewData, '$.layout.colorScheme', NULL)
WHERE SchemaVersion LIKE '1.%';

Deprecation Policy:

  • Maintain backward compatibility for 2 major versions
  • Display in-app notification when user loads deprecated-schema view
  • Notify users 30 days before breaking changes with export option to back up views

Multi-User Conflict Resolution

Scenario 1: Simultaneous Edits to Shared View

Solution: Optimistic Locking with Last-Write-Wins

  1. Each view stores lastModifiedDate
  2. On update, API checks if server timestamp matches client's cached version
  3. Timestamps match → Allow update
  4. Timestamps differ → Reject with conflict error

Example API response (conflict):

{
  "success": false,
  "error": "Conflict detected. This view was modified by admin@nodinite.com at 2026-01-19 15:30:00.",
  "conflictDetails": {
    "serverLastModifiedBy": "admin@nodinite.com",
    "serverLastModifiedDate": "2026-01-19T15:30:00Z",
    "clientLastModifiedDate": "2026-01-19T15:25:00Z"
  }
}

UX: Offer "Reload View" or "Save as New Personal View" to prevent data loss.

Scenario 2: View Deleted While User is Editing

Solution: Soft Deletes + Graceful Degradation

Views are never permanently deleted. IsDeleted = 1 hides them from UI while retaining data for 90 days. The update API checks IsDeleted before allowing updates.

UX: "This view has been deleted. Would you like to save your changes as a new view?"

Scenario 3: Ownership Transfer

When a creator leaves the organization, administrators can reassign ownership:

UPDATE MapifyViews
SET CreatedBy = 'new-owner@nodinite.com',
    LastModifiedBy = 'admin@nodinite.com',
    LastModifiedDate = GETUTCDATE()
WHERE ViewId = '...' AND Visibility = 'shared';

Transfer options: specific user, team/group, or system view (admin-managed only with no individual owner).