or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-io.mdauth-policies.mdcluster-session.mdcql-types.mdcqlengine-orm.mdindex.mdmetadata.mdquery-execution.md

metadata.mddocs/

0

# Metadata & Schema

1

2

Cluster metadata access, schema introspection, and topology information with complete keyspace, table, and column metadata. The metadata system provides comprehensive access to cluster topology and schema information.

3

4

## Capabilities

5

6

### Cluster Metadata

7

8

Primary metadata container providing access to cluster topology and schema information.

9

10

```python { .api }

11

class Metadata:

12

def __init__(self):

13

"""Container for cluster metadata including keyspaces, tables, and hosts."""

14

15

def get_keyspace(self, keyspace):

16

"""

17

Get metadata for a specific keyspace.

18

19

Parameters:

20

- keyspace (str): Name of the keyspace

21

22

Returns:

23

KeyspaceMetadata: Keyspace metadata or None if not found

24

"""

25

26

def get_table(self, keyspace, table):

27

"""

28

Get metadata for a specific table.

29

30

Parameters:

31

- keyspace (str): Name of the keyspace

32

- table (str): Name of the table

33

34

Returns:

35

TableMetadata: Table metadata or None if not found

36

"""

37

38

def get_user_type(self, keyspace, user_type):

39

"""

40

Get metadata for a user-defined type.

41

42

Parameters:

43

- keyspace (str): Name of the keyspace

44

- user_type (str): Name of the user-defined type

45

46

Returns:

47

UserType: User-defined type metadata or None if not found

48

"""

49

50

def get_function(self, keyspace, function, signature):

51

"""

52

Get metadata for a user-defined function.

53

54

Parameters:

55

- keyspace (str): Name of the keyspace

56

- function (str): Name of the function

57

- signature (list): Function signature (argument types)

58

59

Returns:

60

Function: Function metadata or None if not found

61

"""

62

63

def get_aggregate(self, keyspace, aggregate, signature):

64

"""

65

Get metadata for a user-defined aggregate.

66

67

Parameters:

68

- keyspace (str): Name of the keyspace

69

- aggregate (str): Name of the aggregate

70

- signature (list): Aggregate signature (argument types)

71

72

Returns:

73

Aggregate: Aggregate metadata or None if not found

74

"""

75

76

def get_host(self, address):

77

"""

78

Get host metadata by address.

79

80

Parameters:

81

- address (str): Host IP address

82

83

Returns:

84

Host: Host metadata or None if not found

85

"""

86

87

def all_hosts(self):

88

"""

89

Get all known hosts in the cluster.

90

91

Returns:

92

list: List of Host objects

93

"""

94

95

def rebuild_schema(self, keyspace=None):

96

"""

97

Rebuild schema metadata from the cluster.

98

99

Parameters:

100

- keyspace (str): Specific keyspace to rebuild, or None for all

101

"""

102

103

@property

104

def keyspaces(self):

105

"""dict: Dictionary mapping keyspace names to KeyspaceMetadata objects"""

106

107

@property

108

def cluster_name(self):

109

"""str: Name of the cluster"""

110

111

@property

112

def partitioner(self):

113

"""str: Partitioner used by the cluster"""

114

115

@property

116

def token_map(self):

117

"""TokenMap: Token mapping for the cluster"""

118

119

@property

120

def hosts(self):

121

"""dict: Dictionary mapping host addresses to Host objects"""

122

```

123

124

### Keyspace Metadata

125

126

Metadata for Cassandra keyspaces including replication configuration and contained objects.

127

128

```python { .api }

129

class KeyspaceMetadata:

130

def __init__(self, name, durable_writes, strategy_class, strategy_options):

131

"""

132

Metadata for a Cassandra keyspace.

133

134

Parameters:

135

- name (str): Keyspace name

136

- durable_writes (bool): Whether durable writes are enabled

137

- strategy_class (str): Replication strategy class

138

- strategy_options (dict): Replication strategy options

139

"""

140

141

@property

142

def name(self):

143

"""str: Name of the keyspace"""

144

145

@property

146

def durable_writes(self):

147

"""bool: Whether durable writes are enabled"""

148

149

@property

150

def replication_strategy(self):

151

"""ReplicationStrategy: Replication strategy instance"""

152

153

@property

154

def tables(self):

155

"""dict: Dictionary mapping table names to TableMetadata objects"""

156

157

@property

158

def user_types(self):

159

"""dict: Dictionary mapping type names to UserType objects"""

160

161

@property

162

def functions(self):

163

"""dict: Dictionary mapping function signatures to Function objects"""

164

165

@property

166

def aggregates(self):

167

"""dict: Dictionary mapping aggregate signatures to Aggregate objects"""

168

169

def export_as_string(self):

170

"""

171

Export keyspace definition as CQL string.

172

173

Returns:

174

str: CQL CREATE KEYSPACE statement

175

"""

176

```

177

178

### Table Metadata

179

180

Comprehensive metadata for Cassandra tables including columns, indexes, and options.

181

182

```python { .api }

183

class TableMetadata:

184

def __init__(self, keyspace, name, columns, partition_key, clustering_key, options, triggers, indexes):

185

"""

186

Metadata for a Cassandra table.

187

188

Parameters:

189

- keyspace (str): Keyspace name

190

- name (str): Table name

191

- columns (list): List of ColumnMetadata objects

192

- partition_key (list): Partition key columns

193

- clustering_key (list): Clustering key columns

194

- options (dict): Table options

195

- triggers (dict): Table triggers

196

- indexes (dict): Secondary indexes

197

"""

198

199

@property

200

def keyspace_name(self):

201

"""str: Name of the keyspace containing this table"""

202

203

@property

204

def name(self):

205

"""str: Name of the table"""

206

207

@property

208

def columns(self):

209

"""dict: Dictionary mapping column names to ColumnMetadata objects"""

210

211

@property

212

def partition_key(self):

213

"""list: List of ColumnMetadata objects forming the partition key"""

214

215

@property

216

def clustering_key(self):

217

"""list: List of ColumnMetadata objects forming the clustering key"""

218

219

@property

220

def primary_key(self):

221

"""list: Complete primary key (partition key + clustering key)"""

222

223

@property

224

def options(self):

225

"""dict: Table options (compaction, compression, etc.)"""

226

227

@property

228

def triggers(self):

229

"""dict: Dictionary mapping trigger names to TriggerMetadata objects"""

230

231

@property

232

def indexes(self):

233

"""dict: Dictionary mapping index names to IndexMetadata objects"""

234

235

def export_as_string(self):

236

"""

237

Export table definition as CQL string.

238

239

Returns:

240

str: CQL CREATE TABLE statement

241

"""

242

243

def is_cql_compatible(self):

244

"""

245

Check if table is compatible with CQL.

246

247

Returns:

248

bool: True if table can be used with CQL

249

"""

250

```

251

252

### Column Metadata

253

254

Metadata for individual table columns including type and constraints.

255

256

```python { .api }

257

class ColumnMetadata:

258

def __init__(self, table, name, cql_type, is_static=False, is_reversed=False):

259

"""

260

Metadata for a table column.

261

262

Parameters:

263

- table (TableMetadata): Parent table

264

- name (str): Column name

265

- cql_type: CQL type of the column

266

- is_static (bool): Whether column is static

267

- is_reversed (bool): Whether column has reversed order

268

"""

269

270

@property

271

def table(self):

272

"""TableMetadata: Parent table metadata"""

273

274

@property

275

def name(self):

276

"""str: Name of the column"""

277

278

@property

279

def cql_type(self):

280

"""_CassandraType: CQL type of the column"""

281

282

@property

283

def is_static(self):

284

"""bool: Whether this is a static column"""

285

286

@property

287

def is_reversed(self):

288

"""bool: Whether this column has reversed clustering order"""

289

290

@property

291

def is_partition_key(self):

292

"""bool: Whether this column is part of the partition key"""

293

294

@property

295

def is_clustering_key(self):

296

"""bool: Whether this column is part of the clustering key"""

297

298

@property

299

def is_primary_key(self):

300

"""bool: Whether this column is part of the primary key"""

301

```

302

303

### Index Metadata

304

305

Metadata for secondary indexes on tables.

306

307

```python { .api }

308

class IndexMetadata:

309

def __init__(self, table, name, kind, options):

310

"""

311

Metadata for a secondary index.

312

313

Parameters:

314

- table (TableMetadata): Parent table

315

- name (str): Index name

316

- kind (str): Index type/kind

317

- options (dict): Index options

318

"""

319

320

@property

321

def table(self):

322

"""TableMetadata: Parent table metadata"""

323

324

@property

325

def name(self):

326

"""str: Name of the index"""

327

328

@property

329

def kind(self):

330

"""str: Type of index (COMPOSITES, KEYS, CUSTOM)"""

331

332

@property

333

def options(self):

334

"""dict: Index configuration options"""

335

336

def export_as_string(self):

337

"""

338

Export index definition as CQL string.

339

340

Returns:

341

str: CQL CREATE INDEX statement

342

"""

343

```

344

345

### User-Defined Types

346

347

Metadata for user-defined composite types.

348

349

```python { .api }

350

class UserType:

351

def __init__(self, keyspace, name, field_names, field_types):

352

"""

353

Metadata for a user-defined type.

354

355

Parameters:

356

- keyspace (str): Keyspace name

357

- name (str): Type name

358

- field_names (list): Field names

359

- field_types (list): Field types

360

"""

361

362

@property

363

def keyspace(self):

364

"""str: Keyspace containing this type"""

365

366

@property

367

def name(self):

368

"""str: Name of the type"""

369

370

@property

371

def field_names(self):

372

"""list: Names of fields in this type"""

373

374

@property

375

def field_types(self):

376

"""list: Types of fields in this type"""

377

378

def export_as_string(self):

379

"""

380

Export type definition as CQL string.

381

382

Returns:

383

str: CQL CREATE TYPE statement

384

"""

385

```

386

387

### User-Defined Functions

388

389

Metadata for user-defined functions and aggregates.

390

391

```python { .api }

392

class Function:

393

def __init__(self, keyspace, name, argument_names, argument_types, body, called_on_null_input, language, return_type):

394

"""

395

Metadata for a user-defined function.

396

397

Parameters:

398

- keyspace (str): Keyspace name

399

- name (str): Function name

400

- argument_names (list): Parameter names

401

- argument_types (list): Parameter types

402

- body (str): Function body code

403

- called_on_null_input (bool): Whether function is called on null input

404

- language (str): Implementation language

405

- return_type: Return type

406

"""

407

408

@property

409

def keyspace_name(self):

410

"""str: Keyspace containing this function"""

411

412

@property

413

def name(self):

414

"""str: Name of the function"""

415

416

@property

417

def argument_names(self):

418

"""list: Names of function parameters"""

419

420

@property

421

def argument_types(self):

422

"""list: Types of function parameters"""

423

424

@property

425

def signature(self):

426

"""str: Function signature string"""

427

428

@property

429

def body(self):

430

"""str: Function implementation code"""

431

432

@property

433

def called_on_null_input(self):

434

"""bool: Whether function is called when input is null"""

435

436

@property

437

def language(self):

438

"""str: Implementation language (java, javascript, etc.)"""

439

440

@property

441

def return_type(self):

442

"""_CassandraType: Return type of the function"""

443

444

class Aggregate:

445

def __init__(self, keyspace, name, argument_types, state_func, state_type, final_func, initial_condition, return_type):

446

"""

447

Metadata for a user-defined aggregate.

448

449

Parameters:

450

- keyspace (str): Keyspace name

451

- name (str): Aggregate name

452

- argument_types (list): Input types

453

- state_func (str): State function name

454

- state_type: State type

455

- final_func (str): Final function name

456

- initial_condition: Initial state value

457

- return_type: Return type

458

"""

459

460

@property

461

def keyspace_name(self):

462

"""str: Keyspace containing this aggregate"""

463

464

@property

465

def name(self):

466

"""str: Name of the aggregate"""

467

468

@property

469

def argument_types(self):

470

"""list: Types of aggregate input"""

471

472

@property

473

def signature(self):

474

"""str: Aggregate signature string"""

475

476

@property

477

def state_func(self):

478

"""str: Name of the state function"""

479

480

@property

481

def state_type(self):

482

"""_CassandraType: Type of the state value"""

483

484

@property

485

def final_func(self):

486

"""str: Name of the final function"""

487

488

@property

489

def initial_condition(self):

490

"""Initial state value"""

491

492

@property

493

def return_type(self):

494

"""_CassandraType: Return type of the aggregate"""

495

```

496

497

### Replication Strategies

498

499

Replication strategy implementations for keyspaces.

500

501

```python { .api }

502

class ReplicationStrategy:

503

"""Base class for replication strategies."""

504

505

@property

506

def name(self):

507

"""str: Name of the replication strategy"""

508

509

@property

510

def options(self):

511

"""dict: Strategy configuration options"""

512

513

class SimpleStrategy(ReplicationStrategy):

514

def __init__(self, replication_factor):

515

"""

516

Simple replication strategy for single-datacenter clusters.

517

518

Parameters:

519

- replication_factor (int): Number of replicas

520

"""

521

522

@property

523

def replication_factor(self):

524

"""int: Number of replicas"""

525

526

class NetworkTopologyStrategy(ReplicationStrategy):

527

def __init__(self, dc_replication_factors):

528

"""

529

Network topology replication strategy for multi-datacenter clusters.

530

531

Parameters:

532

- dc_replication_factors (dict): Replication factors by datacenter

533

"""

534

535

@property

536

def dc_replication_factors(self):

537

"""dict: Replication factors by datacenter name"""

538

539

class LocalStrategy(ReplicationStrategy):

540

def __init__(self):

541

"""Local replication strategy (for system keyspaces)."""

542

```

543

544

### Token Management

545

546

Token ring and routing information for the cluster.

547

548

```python { .api }

549

class TokenMap:

550

def __init__(self, token_to_host_owner, tokens_to_host_owners, ring):

551

"""

552

Token mapping for cluster routing.

553

554

Parameters:

555

- token_to_host_owner (dict): Mapping of tokens to primary hosts

556

- tokens_to_host_owners (dict): Mapping of tokens to replica sets

557

- ring (list): Ordered list of tokens in the ring

558

"""

559

560

def get_replicas(self, keyspace, token):

561

"""

562

Get replica hosts for a token in a keyspace.

563

564

Parameters:

565

- keyspace (str): Keyspace name

566

- token: Token to look up

567

568

Returns:

569

set: Set of Host objects that are replicas for the token

570

"""

571

572

@property

573

def ring(self):

574

"""list: Ordered list of tokens in the cluster ring"""

575

576

class Token:

577

"""Base class for partition tokens."""

578

579

@property

580

def value(self):

581

"""Token value"""

582

583

class Murmur3Token(Token):

584

def __init__(self, value):

585

"""

586

Murmur3 hash token (default partitioner).

587

588

Parameters:

589

- value (int): Token value

590

"""

591

592

class MD5Token(Token):

593

def __init__(self, value):

594

"""

595

MD5 hash token (legacy partitioner).

596

597

Parameters:

598

- value (int): Token value

599

"""

600

601

class BytesToken(Token):

602

def __init__(self, value):

603

"""

604

Bytes-based token (byte order partitioner).

605

606

Parameters:

607

- value (bytes): Token value

608

"""

609

```

610

611

### Metadata Utilities

612

613

Utility functions for working with CQL identifiers and values.

614

615

```python { .api }

616

def protect_names(names):

617

"""

618

Quote CQL identifiers that need protection.

619

620

Parameters:

621

- names (list): List of CQL identifiers

622

623

Returns:

624

list: List of quoted identifiers

625

"""

626

627

def protect_name(name):

628

"""

629

Quote a CQL identifier if it needs protection.

630

631

Parameters:

632

- name (str): CQL identifier

633

634

Returns:

635

str: Quoted identifier if needed, otherwise original name

636

"""

637

638

def protect_value(value):

639

"""

640

Quote a CQL value for safe inclusion in queries.

641

642

Parameters:

643

- value: Value to quote

644

645

Returns:

646

str: Quoted value suitable for CQL

647

"""

648

649

def is_valid_name(name):

650

"""

651

Check if a name is a valid unquoted CQL identifier.

652

653

Parameters:

654

- name (str): Identifier to check

655

656

Returns:

657

bool: True if the name is valid unquoted

658

"""

659

660

def escape_name(name):

661

"""

662

Escape a CQL identifier for use in quoted form.

663

664

Parameters:

665

- name (str): Identifier to escape

666

667

Returns:

668

str: Escaped identifier

669

"""

670

```

671

672

## Usage Examples

673

674

### Exploring Cluster Schema

675

676

```python

677

# Get cluster metadata

678

metadata = cluster.metadata

679

680

print(f"Cluster name: {metadata.cluster_name}")

681

print(f"Partitioner: {metadata.partitioner}")

682

print(f"Total hosts: {len(metadata.all_hosts())}")

683

684

# List all keyspaces

685

print("\nKeyspaces:")

686

for keyspace_name in metadata.keyspaces:

687

keyspace = metadata.keyspaces[keyspace_name]

688

print(f" {keyspace_name}: {keyspace.replication_strategy}")

689

690

# Explore a specific keyspace

691

keyspace = metadata.get_keyspace('my_app')

692

if keyspace:

693

print(f"\nKeyspace '{keyspace.name}':")

694

print(f" Durable writes: {keyspace.durable_writes}")

695

print(f" Tables: {list(keyspace.tables.keys())}")

696

print(f" User types: {list(keyspace.user_types.keys())}")

697

print(f" Functions: {len(keyspace.functions)}")

698

```

699

700

### Examining Table Structure

701

702

```python

703

# Get table metadata

704

table = metadata.get_table('my_app', 'users')

705

if table:

706

print(f"Table: {table.keyspace_name}.{table.name}")

707

708

# Show partition key

709

print(f"Partition key: {[col.name for col in table.partition_key]}")

710

711

# Show clustering key

712

if table.clustering_key:

713

print(f"Clustering key: {[col.name for col in table.clustering_key]}")

714

715

# Show all columns

716

print("\nColumns:")

717

for col_name, column in table.columns.items():

718

key_type = ""

719

if column.is_partition_key:

720

key_type = " (partition key)"

721

elif column.is_clustering_key:

722

key_type = " (clustering key)"

723

elif column.is_static:

724

key_type = " (static)"

725

726

print(f" {col_name}: {column.cql_type}{key_type}")

727

728

# Show indexes

729

if table.indexes:

730

print("\nIndexes:")

731

for index_name, index in table.indexes.items():

732

print(f" {index_name}: {index.kind}")

733

734

# Show table options

735

print(f"\nTable options: {table.options}")

736

737

# Export as CQL

738

print(f"\nCQL Definition:\n{table.export_as_string()}")

739

```

740

741

### Working with User-Defined Types

742

743

```python

744

# Get UDT metadata

745

address_type = metadata.get_user_type('my_app', 'address')

746

if address_type:

747

print(f"User type: {address_type.keyspace}.{address_type.name}")

748

print("Fields:")

749

for field_name, field_type in zip(address_type.field_names, address_type.field_types):

750

print(f" {field_name}: {field_type}")

751

752

print(f"\nCQL Definition:\n{address_type.export_as_string()}")

753

754

# Find tables using this UDT

755

print(f"\nTables using {address_type.name}:")

756

keyspace = metadata.get_keyspace('my_app')

757

for table_name, table in keyspace.tables.items():

758

for col_name, column in table.columns.items():

759

if hasattr(column.cql_type, 'typename') and column.cql_type.typename == 'address':

760

print(f" {table_name}.{col_name}")

761

```

762

763

### Analyzing Cluster Topology

764

765

```python

766

# Examine hosts and datacenters

767

print("Cluster topology:")

768

hosts_by_dc = {}

769

for host in metadata.all_hosts():

770

dc = host.datacenter or 'unknown'

771

if dc not in hosts_by_dc:

772

hosts_by_dc[dc] = []

773

hosts_by_dc[dc].append(host)

774

775

for dc, hosts in hosts_by_dc.items():

776

print(f"\nDatacenter: {dc}")

777

for host in hosts:

778

status = "UP" if host.is_up else "DOWN"

779

print(f" {host.address} ({host.rack}): {status} - {host.release_version}")

780

781

# Examine token distribution

782

token_map = metadata.token_map

783

if token_map:

784

print(f"\nToken ring has {len(token_map.ring)} tokens")

785

786

# Show token ownership for a keyspace

787

keyspace_name = 'my_app'

788

if keyspace_name in metadata.keyspaces:

789

print(f"\nReplica distribution for keyspace '{keyspace_name}':")

790

sample_tokens = token_map.ring[:5] # Sample first 5 tokens

791

for token in sample_tokens:

792

replicas = token_map.get_replicas(keyspace_name, token)

793

replica_addresses = [host.address for host in replicas]

794

print(f" Token {token.value}: {replica_addresses}")

795

```

796

797

### Schema Evolution Tracking

798

799

```python

800

def compare_schemas(old_metadata, new_metadata, keyspace_name):

801

"""Compare two metadata snapshots to detect schema changes."""

802

803

old_ks = old_metadata.get_keyspace(keyspace_name)

804

new_ks = new_metadata.get_keyspace(keyspace_name)

805

806

if not old_ks or not new_ks:

807

print("Keyspace not found in one of the metadata snapshots")

808

return

809

810

# Compare tables

811

old_tables = set(old_ks.tables.keys())

812

new_tables = set(new_ks.tables.keys())

813

814

added_tables = new_tables - old_tables

815

removed_tables = old_tables - new_tables

816

common_tables = old_tables & new_tables

817

818

if added_tables:

819

print(f"Added tables: {added_tables}")

820

if removed_tables:

821

print(f"Removed tables: {removed_tables}")

822

823

# Compare columns in common tables

824

for table_name in common_tables:

825

old_table = old_ks.tables[table_name]

826

new_table = new_ks.tables[table_name]

827

828

old_columns = set(old_table.columns.keys())

829

new_columns = set(new_table.columns.keys())

830

831

added_columns = new_columns - old_columns

832

removed_columns = old_columns - new_columns

833

834

if added_columns:

835

print(f"Table {table_name} - Added columns: {added_columns}")

836

if removed_columns:

837

print(f"Table {table_name} - Removed columns: {removed_columns}")

838

839

# Usage

840

old_metadata = cluster.metadata

841

# ... time passes, schema changes occur ...

842

cluster.metadata.rebuild_schema()

843

new_metadata = cluster.metadata

844

845

compare_schemas(old_metadata, new_metadata, 'my_app')

846

```

847

848

### Dynamic Query Generation

849

850

```python

851

from cassandra.metadata import protect_name, protect_value

852

853

def generate_insert_query(table_metadata, data):

854

"""Generate INSERT query from table metadata and data."""

855

856

table_name = f"{table_metadata.keyspace_name}.{protect_name(table_metadata.name)}"

857

858

# Filter data to only include existing columns

859

valid_columns = []

860

valid_values = []

861

placeholders = []

862

863

for col_name, value in data.items():

864

if col_name in table_metadata.columns:

865

valid_columns.append(protect_name(col_name))

866

valid_values.append(value)

867

placeholders.append('?')

868

869

if not valid_columns:

870

raise ValueError("No valid columns found in data")

871

872

query = f"""

873

INSERT INTO {table_name} ({', '.join(valid_columns)})

874

VALUES ({', '.join(placeholders)})

875

"""

876

877

return query.strip(), valid_values

878

879

# Usage

880

table = metadata.get_table('my_app', 'users')

881

data = {

882

'id': uuid.uuid4(),

883

'name': 'Alice Smith',

884

'email': 'alice@example.com',

885

'invalid_column': 'ignored' # This will be filtered out

886

}

887

888

query, values = generate_insert_query(table, data)

889

print(f"Generated query: {query}")

890

print(f"Values: {values}")

891

892

session.execute(query, values)

893

```

894

895

### Custom Metadata Introspection

896

897

```python

898

def analyze_keyspace_complexity(keyspace_metadata):

899

"""Analyze complexity metrics for a keyspace."""

900

901

metrics = {

902

'total_tables': len(keyspace_metadata.tables),

903

'total_columns': 0,

904

'total_indexes': 0,

905

'tables_with_clustering': 0,

906

'tables_with_static_columns': 0,

907

'user_types': len(keyspace_metadata.user_types),

908

'functions': len(keyspace_metadata.functions),

909

'column_types': set()

910

}

911

912

for table in keyspace_metadata.tables.values():

913

metrics['total_columns'] += len(table.columns)

914

metrics['total_indexes'] += len(table.indexes)

915

916

if table.clustering_key:

917

metrics['tables_with_clustering'] += 1

918

919

has_static = any(col.is_static for col in table.columns.values())

920

if has_static:

921

metrics['tables_with_static_columns'] += 1

922

923

for column in table.columns.values():

924

metrics['column_types'].add(type(column.cql_type).__name__)

925

926

return metrics

927

928

# Usage

929

keyspace = metadata.get_keyspace('my_app')

930

if keyspace:

931

complexity = analyze_keyspace_complexity(keyspace)

932

print(f"Keyspace complexity analysis:")

933

for metric, value in complexity.items():

934

if metric == 'column_types':

935

print(f" {metric}: {sorted(value)}")

936

else:

937

print(f" {metric}: {value}")

938

```