Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c443240

Browse files
authored
PHPORM-215 Implement Schema::getColumns and getIndexes (#3045)
1 parent 256a830 commit c443240

File tree

3 files changed

+151
-1
lines changed

3 files changed

+151
-1
lines changed

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file.
44
## [4.9.0] - coming soon
55

66
* Add `Connection::getServerVersion()` by @GromNaN in [#3043](https://github.com/mongodb/laravel-mongodb/pull/3043)
7-
* Add `Schema\Builder::getTables()` and `getTableListing` by @GromNaN in [#3044](https://github.com/mongodb/laravel-mongodb/pull/3044)
7+
* Add `Schema\Builder::getTables()` and `getTableListing()` by @GromNaN in [#3044](https://github.com/mongodb/laravel-mongodb/pull/3044)
8+
* Add `Schema\Builder::getColumns()` and `getIndexes()` by @GromNaN in [#3045](https://github.com/mongodb/laravel-mongodb/pull/3045)
89

910
## [4.6.0] - 2024年07月09日
1011

‎src/Schema/Builder.php‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66

77
use Closure;
88
use MongoDB\Model\CollectionInfo;
9+
use MongoDB\Model\IndexInfo;
910

11+
use function array_keys;
12+
use function assert;
1013
use function count;
1114
use function current;
15+
use function implode;
1216
use function iterator_to_array;
1317
use function sort;
18+
use function sprintf;
1419
use function usort;
1520

1621
class Builder extends \Illuminate\Database\Schema\Builder
@@ -146,6 +151,83 @@ public function getTableListing()
146151
return $collections;
147152
}
148153

154+
public function getColumns($table)
155+
{
156+
$stats = $this->connection->getMongoDB()->selectCollection($table)->aggregate([
157+
// Sample 1,000 documents to get a representative sample of the collection
158+
['$sample' => ['size' => 1_000]],
159+
// Convert each document to an array of fields
160+
['$project' => ['fields' => ['$objectToArray' => '$$ROOT']]],
161+
// Unwind to get one document per field
162+
['$unwind' => '$fields'],
163+
// Group by field name, count the number of occurrences and get the types
164+
[
165+
'$group' => [
166+
'_id' => '$fields.k',
167+
'total' => ['$sum' => 1],
168+
'types' => ['$addToSet' => ['$type' => '$fields.v']],
169+
],
170+
],
171+
// Get the most seen field names
172+
['$sort' => ['total' => -1]],
173+
// Limit to 1,000 fields
174+
['$limit' => 1_000],
175+
// Sort by field name
176+
['$sort' => ['_id' => 1]],
177+
], [
178+
'typeMap' => ['array' => 'array'],
179+
'allowDiskUse' => true,
180+
])->toArray();
181+
182+
$columns = [];
183+
foreach ($stats as $stat) {
184+
sort($stat->types);
185+
$type = implode(', ', $stat->types);
186+
$columns[] = [
187+
'name' => $stat->_id,
188+
'type_name' => $type,
189+
'type' => $type,
190+
'collation' => null,
191+
'nullable' => $stat->_id !== '_id',
192+
'default' => null,
193+
'auto_increment' => false,
194+
'comment' => sprintf('%d occurrences', $stat->total),
195+
'generation' => $stat->_id === '_id' ? ['type' => 'objectId', 'expression' => null] : null,
196+
];
197+
}
198+
199+
return $columns;
200+
}
201+
202+
public function getIndexes($table)
203+
{
204+
$indexes = $this->connection->getMongoDB()->selectCollection($table)->listIndexes();
205+
206+
$indexList = [];
207+
foreach ($indexes as $index) {
208+
assert($index instanceof IndexInfo);
209+
$indexList[] = [
210+
'name' => $index->getName(),
211+
'columns' => array_keys($index->getKey()),
212+
'primary' => $index->getKey() === ['_id' => 1],
213+
'type' => match (true) {
214+
$index->isText() => 'text',
215+
$index->is2dSphere() => '2dsphere',
216+
$index->isTtl() => 'ttl',
217+
default => 'default',
218+
},
219+
'unique' => $index->isUnique(),
220+
];
221+
}
222+
223+
return $indexList;
224+
}
225+
226+
public function getForeignKeys($table)
227+
{
228+
return [];
229+
}
230+
149231
/** @inheritdoc */
150232
protected function createBlueprint($table, ?Closure $callback = null)
151233
{

‎tests/SchemaTest.php‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66

77
use Illuminate\Support\Facades\DB;
88
use Illuminate\Support\Facades\Schema;
9+
use MongoDB\BSON\Binary;
10+
use MongoDB\BSON\UTCDateTime;
911
use MongoDB\Laravel\Schema\Blueprint;
1012

13+
use function collect;
1114
use function count;
1215

1316
class SchemaTest extends TestCase
@@ -416,6 +419,70 @@ public function testGetTableListing()
416419
$this->assertContains('newcollection_two', $tables);
417420
}
418421

422+
public function testGetColumns()
423+
{
424+
$collection = DB::connection('mongodb')->collection('newcollection');
425+
$collection->insert(['text' => 'value', 'mixed' => ['key' => 'value']]);
426+
$collection->insert(['date' => new UTCDateTime(), 'binary' => new Binary('binary'), 'mixed' => true]);
427+
428+
$columns = Schema::getColumns('newcollection');
429+
$this->assertIsArray($columns);
430+
$this->assertCount(5, $columns);
431+
432+
$columns = collect($columns)->keyBy('name');
433+
434+
$columns->each(function ($column) {
435+
$this->assertIsString($column['name']);
436+
$this->assertEquals($column['type'], $column['type_name']);
437+
$this->assertNull($column['collation']);
438+
$this->assertIsBool($column['nullable']);
439+
$this->assertNull($column['default']);
440+
$this->assertFalse($column['auto_increment']);
441+
$this->assertIsString($column['comment']);
442+
});
443+
444+
$this->assertEquals('objectId', $columns->get('_id')['type']);
445+
$this->assertEquals('objectId', $columns->get('_id')['generation']['type']);
446+
$this->assertNull($columns->get('text')['generation']);
447+
$this->assertEquals('string', $columns->get('text')['type']);
448+
$this->assertEquals('date', $columns->get('date')['type']);
449+
$this->assertEquals('binData', $columns->get('binary')['type']);
450+
$this->assertEquals('bool, object', $columns->get('mixed')['type']);
451+
$this->assertEquals('2 occurrences', $columns->get('mixed')['comment']);
452+
453+
// Non-existent collection
454+
$columns = Schema::getColumns('missing');
455+
$this->assertSame([], $columns);
456+
}
457+
458+
public function testGetIndexes()
459+
{
460+
Schema::create('newcollection', function (Blueprint $collection) {
461+
$collection->index('mykey1');
462+
$collection->string('mykey2')->unique('unique_index');
463+
$collection->string('mykey3')->index();
464+
});
465+
$indexes = Schema::getIndexes('newcollection');
466+
$this->assertIsArray($indexes);
467+
$this->assertCount(4, $indexes);
468+
469+
$indexes = collect($indexes)->keyBy('name');
470+
471+
$indexes->each(function ($index) {
472+
$this->assertIsString($index['name']);
473+
$this->assertIsString($index['type']);
474+
$this->assertIsArray($index['columns']);
475+
$this->assertIsBool($index['unique']);
476+
$this->assertIsBool($index['primary']);
477+
});
478+
$this->assertTrue($indexes->get('_id_')['primary']);
479+
$this->assertTrue($indexes->get('unique_index_1')['unique']);
480+
481+
// Non-existent collection
482+
$indexes = Schema::getIndexes('missing');
483+
$this->assertSame([], $indexes);
484+
}
485+
419486
protected function getIndex(string $collection, string $name)
420487
{
421488
$collection = DB::getCollection($collection);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /