A Go library for managing video markers in MongoDB with built-in support for tags, events, categories, and OpenTelemetry tracing.
A markers library for the Kerberos video surveillance platform that provides a unified interface for creating and persisting video annotations with optimized bulk operations and multi-tenant support.
- MongoDB Integration: Full MongoDB support with optimized bulk write operations
- Multi-tenant Support: Organization-scoped markers with data isolation
- OpenTelemetry Tracing: Built-in distributed tracing for observability
- Rich Metadata: Support for tags, events, and categories on markers
- Option Collections: Automatic management of unique marker/tag/event/category options per organization
- Range Tracking: Time range documents for efficient filtering and querying
- Media Linking: Automatic linking of markers to media documents
- Production Ready: Optimized for high-performance video annotation applications
go get github.com/uug-ai/markerspackage main
import (
"context"
"log"
"github.com/uug-ai/markers/pkg/markers"
"github.com/uug-ai/models/pkg/models"
"github.com/uug-ai/trace/pkg/opentelemetry"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// Connect to MongoDB
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}
// Initialize tracer
tracer := opentelemetry.NewTracer("markers-service")
ctx := context.Background()
// Create marker instance
m := markers.New()
// Define a marker
marker := models.Marker{
Name: "Person Detected",
StartTimestamp: 1708790400,
EndTimestamp: 1708790410,
OrganisationId: "org-123",
DeviceId: "camera-001",
GroupId: "group-001",
Tags: []models.Tag{{Name: "security"}},
Events: []models.Event{{Name: "motion", StartTimestamp: 1708790400}},
Categories: []models.Category{{Name: "surveillance"}},
}
// Create marker in MongoDB (mediaIds are optional)
result, err := m.Create(ctx, tracer, client, marker, "media-id-here")
if err != nil {
log.Fatal(err)
}
log.Printf("Created marker with ID: %s", result.Id.Hex())
}Markers are annotations applied to video segments containing:
- Name: Required identifier for the marker (e.g., "Person Detected", "Vehicle Entry")
- Timestamps: Start and end timestamps defining the marker's time range
- Duration: Automatically calculated from timestamps
- Tags: Additional labels for categorization
- Events: Time-stamped events within the marker
- Categories: Hierarchical classification
- Organization/Device/Group: Multi-tenant identifiers
Each marker creation follows this pattern:
- Initialize the Marker instance using
markers.New() - Build the marker model with required fields (Name is mandatory)
- Call
Create()with context, tracer, MongoDB client, marker, and optional mediaIds (variadic...string) - Handle the returned marker with its generated ID
The library manages multiple collections for optimal query performance:
| Collection | Purpose |
|---|---|
markers |
Primary marker storage |
marker_options |
Unique marker names per organization |
marker_option_ranges |
Time ranges for each marker name |
marker_tag_options |
Unique tag names per organization |
marker_tag_option_ranges |
Time ranges for each tag |
marker_event_options |
Unique event names per organization |
marker_event_option_ranges |
Time ranges for each event |
marker_category_options |
Unique category names per organization |
media |
Updated with marker/tag/event names |
package main
import (
"context"
"log"
"github.com/uug-ai/markers/pkg/markers"
"github.com/uug-ai/models/pkg/models"
)
func main() {
m := markers.New()
marker := models.Marker{
Name: "Motion Detected",
StartTimestamp: 1708790400,
EndTimestamp: 1708790420,
OrganisationId: "org-123",
DeviceId: "camera-001",
}
// No mediaIds - skip media linking
result, err := m.Create(ctx, tracer, mongoClient, marker)
if err != nil {
log.Fatal(err)
}
log.Printf("Marker created: %s", result.Id.Hex())
}marker := models.Marker{
Name: "Security Alert",
StartTimestamp: 1708790400,
EndTimestamp: 1708790500,
OrganisationId: "org-123",
DeviceId: "camera-001",
GroupId: "entrance-group",
Tags: []models.Tag{
{Name: "high-priority"},
{Name: "entrance"},
},
Events: []models.Event{
{Name: "door-opened", StartTimestamp: 1708790420, EndTimestamp: 1708790425},
{Name: "person-entered", StartTimestamp: 1708790430, EndTimestamp: 1708790435},
},
Categories: []models.Category{
{Name: "security"},
{Name: "access-control"},
},
}
result, err := m.Create(ctx, tracer, client, marker, "media-object-id")When mediaIds are provided, the marker's names, tags, and events are automatically added to the corresponding media documents:
// Link to a single media document
result, err := m.Create(ctx, tracer, client, marker, "64a1b2c3d4e5f6789012abcd")
// Link to multiple media documents
result, err := m.Create(ctx, tracer, client, marker, "media-1", "media-2", "media-3")
// Skip media linking (no mediaIds)
result, err := m.Create(ctx, tracer, client, marker)The media document is updated using $addToSet to ensure uniqueness, and only if the marker's timestamp falls within the media's time range.
.
├── pkg/
│ └── markers/ # Core markers implementation
│ ├── main.go # Marker struct and Create() method
│ └── mongodb.go # MongoDB persistence layer
├── main.go # Entry point
├── go.mod
├── go.sum
├── Dockerfile
└── README.md
The library uses the following default configuration:
DatabaseName = "Kerberos"
TIMEOUT = 10 * time.SecondYou can configure the MongoDB connection using environment variables:
# MongoDB
MONGODB_URI=mongodb://localhost:27017
DATABASE_NAME=Kerberos
# Tracing
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_SERVICE_NAME=markers-serviceThe library validates marker data before persistence:
- Name: Required field - markers must have a name
- Timestamps: Used to calculate duration automatically
Validation is performed in the Create() method:
if marker.Name == "" {
return models.Marker{}, errors.New("marker name is required")
}The library provides clear error handling at each stage:
// Create marker (omit mediaIds to skip media linking)
result, err := m.Create(ctx, tracer, client, marker)
if err != nil {
// Validation error or database error
log.Printf("Failed to create marker: %v", err)
return
}
// Marker created successfully
log.Printf("Created marker: %s", result.Id.Hex())Common error scenarios:
- Missing required
Namefield - MongoDB connection issues
- Invalid
mediaIdformat (when provided) - Bulk write failures for option collections
Run the test suite:
go test ./...Run tests with coverage:
go test -cover ./...Run tests for specific components:
# Markers tests
go test ./pkg/markers -vContributions are welcome! When adding new features, please follow the existing patterns demonstrated in this repository.
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Follow the existing code patterns
- Add comprehensive tests for your changes
- Ensure all tests pass:
go test ./... - Commit your changes following Conventional Commits
- Push to your branch (
git push origin feat/amazing-feature) - Open a Pull Request
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, types
Scopes:
markers- Core markers functionalitymongodb- MongoDB persistence layeroptions- Marker options collectionsdocs- Documentation updatestests- Test updates
Examples:
feat(markers): add bulk marker creation support
fix(mongodb): correct timeout handling in bulk writes
docs(readme): update usage examples
refactor(options): optimize deduplication logic
test(markers): add validation error tests
This project is licensed under the MIT License - see the LICENSE file for details.
This project uses the following key libraries:
- mongo-driver - Official MongoDB Go driver
- uug-ai/models - Shared model types including Marker struct
- uug-ai/trace - OpenTelemetry tracing utilities
See go.mod for the complete list of dependencies.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: See inline code comments and examples above
Built with ❤️ by UUG.AI