Get People Slim

Overview

When retrieving large numbers of people, fetching complete PersonInfo objects with all properties can be inefficient. The “slim” approach uses MongoDB aggregation to return only the specific fields you need, dramatically reducing response size and improving performance.

This technique is essential for:

  • Populating dropdown lists or search results
  • Exporting data to external systems
  • Building reports that need specific fields
  • Mobile applications with bandwidth constraints

When to Use Slim Queries

Scenario Use Standard Query Use Slim Query
Edit a single person ✅
Display person details ✅
Populate a selection list ✅
Export 1000+ records ✅
API with bandwidth limits ✅
Mobile app data sync ✅

Basic Slim Query

Retrieve only essential fields (Key, CommonName, Metadata):

// Step 1: Build the aggregation pipeline
var match = new BsonDocument 
{ 
    { "$match", new BsonDocument { { "_t", "Person" } } } 
};

var project = new BsonDocument 
{ 
    { "$project", new BsonDocument 
        { 
            { "_id", "$_id" },           // Person's unique key
            { "CommonName", "$CommonName" }, // Display name
            { "Metadata", "$Metadata" }      // Custom form data
        } 
    } 
};

// Step 2: Execute the aggregation (30 second timeout)
var people = await client.AggregateAsync(
    currentInstance, 
    "KeepObjects", 
    new[] { match, project }, 
    30);

// Step 3: Process results
foreach (var doc in people)
{
    string key = doc["_id"].AsString;
    string name = doc["CommonName"].AsString;
    Console.WriteLine($"{name} (Key: {key})");
}

Common Projection Fields

Field MongoDB Path Description
Key _id Unique identifier
CommonName CommonName Display name of objects
GivenName GivenName First name of Person
Surname Surname Last name of Person
Metadata Metadata Custom form data array
Tags Tags Tag string array
Monikers Monikers External ID array
CardAssignments CardAssignments Credential array
ObjectLinks ObjectLinks Linked objects (access levels, badge types)
MacAddress MacAddress MacAddress of Controllers

Slim Query Examples

Names Only (For Dropdown Lists)

var match = new BsonDocument 
{ 
    { "$match", new BsonDocument { { "_t", "Person" } } } 
};

var project = new BsonDocument 
{ 
    { "$project", new BsonDocument 
        { 
            { "_id", 1 },
            { "CommonName", 1 },
            { "GivenName", 1 },
            { "Surname", 1 }
        } 
    } 
};

var sort = new BsonDocument 
{ 
    { "$sort", new BsonDocument { { "Surname", 1 }, { "GivenName", 1 } } }  // Sort by name
};

var people = await client.AggregateAsync(
    currentInstance, 
    "KeepObjects", 
    new[] { match, project, sort }, 
    30);

People with Specific Tag

var match = new BsonDocument 
{ 
    { "$match", new BsonDocument 
        { 
            { "_t", "Person" },
            { "Tags", "vip" }  // Only people with vip tag
        } 
    } 
};

var project = new BsonDocument 
{ 
    { "$project", new BsonDocument 
        { 
            { "_id", 1 },
            { "CommonName", 1 },
            { "Tags", 1 }
        } 
    } 
};

var vipPeople = await client.AggregateAsync(
    currentInstance, 
    "KeepObjects", 
    new[] { match, project }, 
    30);

People with Card Information

var match = new BsonDocument 
{ 
    { "$match", new BsonDocument 
        { 
            { "_t", "Person" },
            { "CardAssignments", new BsonDocument { { "$ne", BsonNull.Value } } }
        } 
    } 
};

var project = new BsonDocument 
{ 
    { "$project", new BsonDocument 
        { 
            { "_id", 1 },
            { "CommonName", 1 },
            { "CardAssignments.DisplayCardNumber", 1 },
            { "CardAssignments.ExpiresOn", 1 },
            { "CardAssignments.IsDisabled", 1 }
        } 
    } 
};

var peopleWithCards = await client.AggregateAsync(
    currentInstance, 
    "KeepObjects", 
    new[] { match, project }, 
    30);

Count People by Tag

var pipeline = new BsonDocument[]
{
    new BsonDocument { { "$match", new BsonDocument { { "_t", "Person" } } } },
    new BsonDocument { { "$unwind", "$Tags" } },
    new BsonDocument { { "$group", new BsonDocument 
        { 
            { "_id", "$Tags" },
            { "count", new BsonDocument { { "$sum", 1 } } }
        } 
    }},
    new BsonDocument { { "$sort", new BsonDocument { { "count", -1 } } } }
};

var tagCounts = await client.AggregateAsync(
    currentInstance, 
    "KeepObjects", 
    pipeline, 
    30);

foreach (var doc in tagCounts)
{
    Console.WriteLine($"Tag: {doc["_id"]} - Count: {doc["count"]}");
}

Deserializing Complex Fields

Metadata Deserialization

private MetadataItem[] ToMetadata(BsonValue document)
{
    // Handle null or non-array values
    if ((document?.IsBsonNull ?? true) || !document.IsBsonArray)
        return null;

    var items = new List<MetadataItem>();
    
    foreach (var item in document.AsBsonArray)
    {
        items.Add(new MetadataItem 
        { 
            Application = item["Application"].AsString,
            Values = item["Values"].IsBsonNull 
                ? string.Empty 
                : item["Values"].ToJson() 
        });
    }
    
    return items.ToArray();
}

// Usage
foreach (var doc in people)
{
    var metadata = ToMetadata(doc.GetValue("Metadata", BsonNull.Value));
    if (metadata != null)
    {
        foreach (var meta in metadata)
        {
            Console.WriteLine($"  App: {meta.Application}, Data: {meta.Values}");
        }
    }
}

Card Assignment Deserialization

private List<(string CardNumber, DateTime? ExpiresOn, bool IsDisabled)> ToCardInfo(BsonValue document)
{
    var cards = new List<(string, DateTime?, bool)>();
    
    if ((document?.IsBsonNull ?? true) || !document.IsBsonArray)
        return cards;

    foreach (var card in document.AsBsonArray)
    {
        var cardNumber = card["DisplayCardNumber"]?.AsString ?? "Unknown";
        DateTime? expiresOn = null;
        if (card.AsBsonDocument.Contains("ExpiresOn") && !card["ExpiresOn"].IsBsonNull)
            expiresOn = card["ExpiresOn"].ToUniversalTime();
        var isDisabled = card.AsBsonDocument.Contains("IsDisabled") 
            && card["IsDisabled"].AsBoolean;
        
        cards.Add((cardNumber, expiresOn, isDisabled));
    }
    
    return cards;
}

Pagination for Large Datasets

public async Task<List<BsonDocument>> GetPeoplePagedAsync(
    InstanceInfo instance, 
    int pageNumber, 
    int pageSize = 100)
{
    var skip = pageNumber * pageSize;
    
    var pipeline = new BsonDocument[]
    {
        new BsonDocument { { "$match", new BsonDocument { { "_t", "Person" } } } },
        new BsonDocument { { "$project", new BsonDocument 
            { 
                { "_id", 1 }, 
                { "CommonName", 1 },
                { "GivenName", 1 },
                { "Surname", 1 }
            } 
        }},
        new BsonDocument { { "$sort", new BsonDocument { { "CommonName", 1 } } } },
        new BsonDocument { { "$skip", skip } },
        new BsonDocument { { "$limit", pageSize } }
    };
    
    return await client.AggregateAsync(instance, "KeepObjects", pipeline, 60);
}

// Usage
var page1 = await GetPeoplePagedAsync(currentInstance, 0, 100);  // First 100
var page2 = await GetPeoplePagedAsync(currentInstance, 1, 100);  // Next 100

cURL Example

curl -X POST \
  "https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/aggregate/KeepObjects" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    { "$match": { "_t": "Person" } },
    { "$project": { "_id": 1, "CommonName": 1, "GivenName": 1, "Surname": 1 } },
    { "$sort": { "Surname": 1 } },
    { "$limit": 100 }
  ]'

Best Practices

Practice Recommendation
Project Minimally Only include fields you actually need
Add Indexes Index fields used in $match for better performance
Use Pagination Limit results with $skip and $limit for large datasets
Set Timeouts Always specify reasonable timeout values
Cache Results Cache dropdown/list data to reduce API calls
Handle Nulls Always check for null/BsonNull when deserializing

Troubleshooting

Issue Cause Solution
Empty results Wrong collection name Use “KeepObjects” for people
Timeout errors Large dataset Add pagination or more restrictive match
Missing fields Field doesn’t exist on all documents Use conditional projection
Parse errors Incorrect BSON format Validate pipeline syntax