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);
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
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;
}
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 |