Skip to content

Commit 18c0ca4

Browse files
abnegateclaude
andcommitted
perf(database): skip casting for already-canonical types
casting() was looping over every attribute, building a single-element array, running a match that fell through to default for strings, datetimes, JSON, and so on, and then re-storing the value with setAttribute. That whole round trip is dead work for any attribute whose type doesn't actually need a PHP-side cast. Skip the load + setAttribute pair when the type isn't one of id, boolean, integer, or double (and isn't an array). Per find with N docs × M attributes this used to allocate N×M tiny arrays and call setAttribute that many times; the typical schema is mostly strings, so the win compounds quickly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 20c9041 commit 18c0ca4

1 file changed

Lines changed: 28 additions & 5 deletions

File tree

src/Database/Database.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,24 +1683,47 @@ public function casting(Document $collection, Document $document): Document
16831683
$attributes[] = $attribute;
16841684
}
16851685

1686+
// Cache the set of types that actually require a cast. Strings,
1687+
// datetimes, JSON, etc. all fall through to default in the match
1688+
// below, so skipping them entirely avoids the per-attribute
1689+
// foreach + setAttribute pair on every document.
1690+
$idType = ColumnType::Id->value;
1691+
$boolType = ColumnType::Boolean->value;
1692+
$intType = ColumnType::Integer->value;
1693+
$doubleType = ColumnType::Double->value;
1694+
16861695
foreach ($attributes as $attribute) {
16871696
/** @var string $key */
16881697
$key = $attribute['$id'] ?? '';
1698+
if ($key === '$permissions') {
1699+
continue;
1700+
}
1701+
16891702
$type = $attribute['type'] ?? '';
16901703
$array = $attribute['array'] ?? false;
1691-
$value = $document->getAttribute($key, null);
1692-
if (is_null($value)) {
1704+
$typeKey = $type instanceof ColumnType ? $type->value : (\is_string($type) ? $type : '');
1705+
1706+
$needsCast = $array
1707+
|| $typeKey === $idType
1708+
|| $typeKey === $boolType
1709+
|| $typeKey === $intType
1710+
|| $typeKey === $doubleType;
1711+
if (! $needsCast) {
1712+
// String/datetime/JSON/etc — already in their canonical
1713+
// PHP type after PDO fetch. Skip the load/setAttribute
1714+
// round trip entirely.
16931715
continue;
16941716
}
16951717

1696-
if ($key === '$permissions') {
1718+
$value = $document->getAttribute($key, null);
1719+
if (\is_null($value)) {
16971720
continue;
16981721
}
16991722

17001723
if ($array) {
1701-
$value = ! is_string($value)
1724+
$value = ! \is_string($value)
17021725
? $value
1703-
: json_decode($value, true);
1726+
: \json_decode($value, true);
17041727
} else {
17051728
$value = [$value];
17061729
}

0 commit comments

Comments
 (0)