List Collections in MongoDB: A Simple Tutorial

Okay, here’s a comprehensive article on listing collections in MongoDB, designed to be approximately 5000 words and cover various aspects of the topic, from basic usage to advanced techniques and considerations.

List Collections in MongoDB: A Simple Tutorial (and Beyond)

This article provides a deep dive into the seemingly simple task of listing collections in MongoDB. While the basic commands are straightforward, understanding the nuances, available options, and underlying mechanisms will empower you to work with MongoDB collections more effectively. We’ll cover everything from simple shell commands to programmatic access in various languages, filtering options, performance considerations, and handling large numbers of collections.

1. Introduction: Why List Collections?

Before diving into the “how,” let’s briefly touch on the “why.” Listing collections is a fundamental operation in database management, necessary for a variety of tasks, including:

  • Database Inspection: Understanding the structure of your database, seeing what collections exist, and verifying the presence of expected collections.
  • Schema Discovery: In the flexible schema world of MongoDB, listing collections can be a starting point for understanding the data model used in an application.
  • Data Migration and Backup: Before performing operations like data migration or backups, you’ll often want to list the collections to be processed.
  • Automated Scripts and Tools: Many database management scripts and tools rely on the ability to list collections programmatically.
  • Monitoring and Administration: Checking for unexpected or orphaned collections can be part of a regular database monitoring routine.
  • Application Logic: Some applications might dynamically discover and work with collections based on their names or other properties.

2. Basic Collection Listing in the MongoDB Shell

The most direct way to list collections is through the MongoDB shell (mongo or mongosh). Here are the core commands:

2.1. show collections

This is the simplest and most commonly used command. It lists the names of all collections in the currently selected database.

javascript
show collections

Example:

“`

use mydatabase
switched to db mydatabase
show collections
customers
orders
products
“`

2.2. show tables

This command is an alias for show collections. It provides identical functionality. The “tables” terminology is a historical remnant and reflects the similarity between collections and tables in relational databases.

javascript
show tables

Example:

“`

use anotherdatabase
switched to db anotherdatabase
show tables
users
logs
settings
“`

2.3. db.getCollectionNames()

This is a more programmatic approach within the shell. It returns an array of strings, each representing a collection name. This is particularly useful when you need to process the collection names within a script.

javascript
db.getCollectionNames()

Example:

“`

use mydatabase
switched to db mydatabase
db.getCollectionNames()
[ “customers”, “orders”, “products” ]
“`

2.4 Switching Databases (use)

Remember that show collections, show tables, and db.getCollectionNames() operate on the currently selected database. To list collections in a different database, you need to switch to it using the use command:

javascript
use <database_name>
show collections

Example:

“`

use admin
switched to db admin
show collections // Lists collections in the ‘admin’ database
system.version
use mydatabase
switched to db mydatabase
show collections // Lists collections in the ‘mydatabase’ database
customers
orders
products
“`

3. Filtering Collection Listings

Often, you won’t want to see all collections. You might need to filter the list based on collection names or other criteria. MongoDB provides several ways to do this.

3.1. Filtering with db.getCollectionNames() and JavaScript

Since db.getCollectionNames() returns a JavaScript array, you can use standard JavaScript array methods like filter() to refine the results.

javascript
db.getCollectionNames().filter(function(collectionName) {
return collectionName.startsWith("user");
});

Example:

“`javascript

use mydatabase
switched to db mydatabase
db.createCollection(“users”)
{ ok: 1 }
db.createCollection(“user_logs”)
{ ok: 1 }
db.createCollection(“products”)
{ ok: 1 }
db.getCollectionNames().filter(function(collectionName) {
… return collectionName.startsWith(“user”);
… });
[ ‘users’, ‘user_logs’ ]
“`

This example filters the collection names to include only those that start with “user”. You can use any valid JavaScript expression within the filter() function to create complex filtering logic. For instance, you could use regular expressions:

javascript
db.getCollectionNames().filter(function(collectionName) {
return /log$/.test(collectionName); // Collections ending with "log"
});

3.2. Using listCollections Command with Filters

The listCollections command (available through db.runCommand()) provides more advanced filtering capabilities directly within the MongoDB command itself. It allows you to filter based on collection name, type, and other options.

javascript
db.runCommand({ listCollections: 1, filter: { name: "customers" } })

Example:
“`javascript

use mydatabase
switched to db mydatabase
db.runCommand({ listCollections: 1, filter: { name: “customers” } })
{
cursor: {
id: Long(“0”),
ns: ‘mydatabase.$cmd.listCollections’,
firstBatch: [
{
name: ‘customers’,
type: ‘collection’,
options: {},
info: { readOnly: false, uuid: UUID(“…”) },
idIndex: { v: 2, key: { id: 1 }, name: ‘_id‘ }
}
]
},
ok: 1
}
``
This command retrieves information only about the "customers" collection. Let's break down the
listCollections` command:

  • listCollections: 1: This specifies that we want to execute the listCollections command. The 1 is a placeholder value; any truthy value would work.
  • filter: { name: "customers" }: This is the crucial part for filtering. The filter option accepts a document that specifies the criteria for selecting collections. In this case, we’re filtering by the name field, looking for a collection named “customers”.
  • Returned Document: The result is a document containing a cursor field. This cursor holds the information about the matching collections. The firstBatch array within the cursor contains the details of the collections that matched the filter.

3.2.1 Filtering by Collection Type
You can also filter based on the type of the collection. The common types are:
* collection: A regular collection.
* view: A read-only view based on an aggregation pipeline.
* timeseries: A time-series collection (introduced in MongoDB 5.0).

javascript
db.runCommand({ listCollections: 1, filter: { type: "view" } })

This command will list all views in current database.

3.2.2. Using Regular Expressions in Filters

The filter option also supports regular expressions for more flexible name matching. You use the $regex operator within the filter document.

javascript
db.runCommand({ listCollections: 1, filter: { name: { $regex: "^user" } } }) // Starts with "user"

Example:

“`javascript

use mydatabase
switched to db mydatabase
db.runCommand({ listCollections: 1, filter: { name: { $regex: “^user” } } })
{
cursor: {
//… (cursor details)
firstBatch: [
{ name: ‘users’, // },
{ name: ‘user_logs’, // }
]
},
ok: 1
}
``
This is equivalent to the JavaScript
startsWith(“user”)` example earlier, but it’s done entirely within the MongoDB command. You can use any valid MongoDB regular expression syntax here. For instance, to find collections containing “log” anywhere in the name:

javascript
db.runCommand({ listCollections: 1, filter: { name: { $regex: "log" } } })

To find collections ending with “log”:
javascript
db.runCommand({ listCollections: 1, filter: { name: { $regex: "log$" } } })

3.2.3. authorizedCollections

When running listCollections with authentication enabled, you might only want to see collections that the current user has privileges to access. The authorizedCollections option can be used for this:

javascript
db.runCommand({ listCollections: 1, authorizedCollections: true })

This ensures that you only receive information about collections that your user account is permitted to see, enhancing security and preventing unauthorized access to collection metadata.
3.3. Name Only

By default the db.runCommand({listCollections}) will output lots of details about each collection, but you can limit the output to just the names.
javascript
db.runCommand( { listCollections: 1.0, filter: {}, nameOnly: true } ).cursor.firstBatch.map(c => c.name)

4. Listing Collections Programmatically (Various Languages)

While the shell is convenient for interactive use, most real-world applications need to list collections programmatically. Here’s how to do it in several popular programming languages, using the official MongoDB drivers.

4.1. Python (PyMongo)

“`python
from pymongo import MongoClient

Connect to MongoDB (replace with your connection string)

client = MongoClient(‘mongodb://localhost:27017/’)

Select the database

db = client[‘mydatabase’]

List collections (method 1: collection_names())

collection_names = db.list_collection_names()
print(f”Collections (method 1): {collection_names}”)

List collections (method 2: list_collections())

for collection_info in db.list_collections():
print(f”Collection Name: {collection_info[‘name’]}, Type: {collection_info[‘type’]}”)

Filtering with list_collections()

for collection_info in db.list_collections(filter={“name”: {“$regex”: “^user”}}):
print(f”Filtered Collection Name: {collection_info[‘name’]}”)

Close the connection

client.close()
“`

Explanation:

  • MongoClient: Establishes a connection to the MongoDB server.
  • db = client['mydatabase']: Selects the database you want to work with.
  • db.list_collection_names(): This is the most direct equivalent of db.getCollectionNames() in the shell. It returns a list of collection names as strings.
  • db.list_collections(): This is the equivalent of the listCollections command. It returns a cursor, which you can iterate over to get detailed information about each collection (including name, type, and options). You can also pass a filter argument to this method, similar to the shell command.
  • Filtering We showed filtering by regex. You can adapt the filter to any valid MongoDB query.

4.2. Node.js (MongoDB Node.js Driver)

“`javascript
const { MongoClient } = require(‘mongodb’);

// Connection URI (replace with your connection string)
const uri = ‘mongodb://localhost:27017/’;

// Database Name
const dbName = ‘mydatabase’;

async function listCollections() {
const client = new MongoClient(uri);

try {
await client.connect();
const db = client.db(dbName);

// Method 1: listCollections() with toArray()
const collections = await db.listCollections().toArray();
console.log("Collections (method 1):");
collections.forEach(collection => {
  console.log(`  Name: ${collection.name}, Type: ${collection.type}`);
});

// Method 2: Using collectionNames()
const collectionNames = await db.collectionNames(); // Deprecated in newer driver versions
console.log("\nCollection Names (method 2):", collectionNames);


// Filtering
const filteredCollections = await db.listCollections({ name: { $regex: '^user' } }).toArray();
console.log("\nFiltered Collections:");
filteredCollections.forEach(collection => {
  console.log(`  Name: ${collection.name}`);
});

} finally {
await client.close();
}
}

listCollections().catch(console.error);
“`

Explanation:

  • MongoClient: Used to connect to the MongoDB server.
  • client.connect(): Establishes the connection (asynchronous operation).
  • db.listCollections(): Returns a ListCollectionsCursor. The .toArray() method converts the cursor to an array of collection information documents.
  • db.collectionNames(): This method is deprecated, and using db.listCollections is preferred.
  • Filtering: The listCollections() method accepts a filter object, just like in the shell and Python examples.
  • async/await: The code uses async/await to handle the asynchronous nature of MongoDB operations.

4.3. Java (MongoDB Java Driver)

“`java
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import org.bson.Document;

public class ListCollectionsExample {

public static void main(String[] args) {

    // Connection URI (replace with your connection string)
    String uri = "mongodb://localhost:27017/";

    // Database Name
    String dbName = "mydatabase";

    try (MongoClient mongoClient = MongoClients.create(uri)) {
        MongoDatabase database = mongoClient.getDatabase(dbName);

        // Method 1: listCollectionNames()
        MongoIterable<String> collectionNames = database.listCollectionNames();
        System.out.println("Collection Names (method 1):");
        for (String name : collectionNames) {
            System.out.println("  " + name);
        }

        // Method 2: listCollections()
        MongoIterable<Document> collections = database.listCollections();
        System.out.println("\nCollections (method 2):");
        for (Document collection : collections) {
            System.out.println("  Name: " + collection.getString("name") + ", Type: " + collection.getString("type"));
        }

        // Filtering with listCollections()
        Document filter = new Document("name", new Document("$regex", "^user"));
        MongoIterable<Document> filteredCollections = database.listCollections(filter);
        System.out.println("\nFiltered Collections:");
        for (Document collection : filteredCollections) {
            System.out.println("  Name: " + collection.getString("name"));
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
}

}
“`

Explanation:

  • MongoClients.create(uri): Creates a MongoClient instance to connect to the server.
  • mongoClient.getDatabase(dbName): Gets a MongoDatabase object representing the specified database.
  • database.listCollectionNames(): Returns a MongoIterable<String> containing the collection names. You can iterate over this iterable to get each name.
  • database.listCollections(): Returns a MongoIterable<Document>. Each Document in the iterable represents a collection and contains detailed information.
  • Filtering: The listCollections() method can accept a Document as a filter, similar to the other examples. We create a Document representing the filter criteria ($regex for a regular expression).
  • Try-with-resources: The try (MongoClient ...) block ensures that the MongoClient is closed automatically, even if exceptions occur.

4.4 C# (MongoDB .NET Driver)

“`csharp
using MongoDB.Driver;
using MongoDB.Bson;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

public class ListCollectionsExample
{
public static async Task Main(string[] args)
{
// Connection URI (replace with your connection string)
string uri = “mongodb://localhost:27017/”;

    // Database Name
    string dbName = "mydatabase";

    var client = new MongoClient(uri);
    var database = client.GetDatabase(dbName);

    // Method 1: ListCollectionNames()
    List<string> collectionNames = await database.ListCollectionNames().ToListAsync();
    Console.WriteLine("Collection Names (method 1):");
    foreach (string name in collectionNames)
    {
        Console.WriteLine("  " + name);
    }

    // Method 2: ListCollections()
    IAsyncCursor<BsonDocument> collectionsCursor = await database.ListCollectionsAsync();
    List<BsonDocument> collections = await collectionsCursor.ToListAsync();
    Console.WriteLine("\nCollections (method 2):");
    foreach (BsonDocument collection in collections)
    {
        Console.WriteLine("  Name: " + collection["name"] + ", Type: " + collection["type"]);
    }

    // Filtering with ListCollections()
    var filter = Builders<BsonDocument>.Filter.Regex("name", new BsonRegularExpression("^user"));
    IAsyncCursor<BsonDocument> filteredCollectionsCursor = await database.ListCollectionsAsync(new ListCollectionsOptions { Filter = filter });
    List<BsonDocument> filteredCollections = await filteredCollectionsCursor.ToListAsync();
    Console.WriteLine("\nFiltered Collections:");
    foreach (BsonDocument collection in filteredCollections)
    {
        Console.WriteLine("  Name: " + collection["name"]);
    }
}

}
“`

Explanation:

  • MongoClient(uri): Creates a new MongoClient instance.
  • client.GetDatabase(dbName): Retrieves a IMongoDatabase instance.
  • database.ListCollectionNames().ToListAsync(): Gets a list of collection names. The ToListAsync() method is used because the operation is asynchronous.
  • database.ListCollectionsAsync(): This method returns an asynchronous cursor (IAsyncCursor<BsonDocument>) that you can iterate to retrieve information about each collection. The ToListAsync() method is used to get all results into a list.
  • Filtering: The ListCollectionsAsync() method can accept a ListCollectionsOptions object, which includes a Filter property. You use the Builders<BsonDocument>.Filter class to create filter definitions. Here, we use Regex to filter by collection name using a regular expression.
  • async/await The C# driver uses async/await extensively for asynchronous operations.

4.5. Go (MongoDB Go Driver)

“`go
package main

import (
“context”
“fmt”
“log”
“go.mongodb.org/mongo-driver/bson”
“go.mongodb.org/mongo-driver/mongo”
“go.mongodb.org/mongo-driver/mongo/options”
)

func main() {
// Connection URI (replace with your connection string)
uri := “mongodb://localhost:27017/”

// Database Name
dbName := "mydatabase"

// Set client options
clientOptions := options.Client().ApplyURI(uri)

// Connect to MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
    log.Fatal(err)
}

// Check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
    log.Fatal(err)
}

fmt.Println("Connected to MongoDB!")

db := client.Database(dbName)

// Method 1: ListCollectionNames()
collectionNames, err := db.ListCollectionNames(context.TODO(), bson.D{})
if err != nil {
    log.Fatal(err)
}
fmt.Println("Collection Names (method 1):")
for _, name := range collectionNames {
    fmt.Println("  ", name)
}

// Method 2: ListCollections()
cursor, err := db.ListCollections(context.TODO(), bson.D{})
if err != nil {
    log.Fatal(err)
}
defer cursor.Close(context.TODO())

fmt.Println("\nCollections (method 2):")
for cursor.Next(context.TODO()) {
    var collectionInfo bson.M
    if err := cursor.Decode(&collectionInfo); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("  Name: %v, Type: %v\n", collectionInfo["name"], collectionInfo["type"])
}

// Filtering with ListCollections()
filter := bson.D{{Key: "name", Value: bson.D{{Key: "$regex", Value: "^user"}}}}
filteredCursor, err := db.ListCollections(context.TODO(), filter)
if err != nil {
    log.Fatal(err)
}
defer filteredCursor.Close(context.TODO())

fmt.Println("\nFiltered Collections:")
for filteredCursor.Next(context.TODO()) {
    var collectionInfo bson.M
    if err := filteredCursor.Decode(&collectionInfo); err != nil {
        log.Fatal(err)
    }
    fmt.Println("  Name:", collectionInfo["name"])
}

if err := client.Disconnect(context.TODO()); err != nil {
    log.Fatal(err)
}

}
“`

Explanation:

  • mongo.Connect(): Connects to the MongoDB server.
  • client.Database(dbName): Gets a Database object.
  • db.ListCollectionNames(): Retrieves a slice of strings containing the collection names. The bson.D{} represents an empty filter (retrieve all).
  • db.ListCollections(): Returns a Cursor, which you iterate over using cursor.Next() and cursor.Decode() to get the collection information.
  • Filtering: Filters are defined using bson.D (ordered document) or bson.M (unordered document) structures. We use bson.D here for clarity. The filter is passed to ListCollections().
  • Context: The context.TODO() is used for context management, which is important for managing timeouts and cancellations in Go.
  • Error Handling: The code includes error handling for each operation, which is crucial for robust Go applications.
  • Defer: defer cursor.Close(context.TODO()) is used to ensure the cursor is closed after it is used, preventing resource leaks.

5. Performance Considerations and Handling Large Numbers of Collections

When dealing with databases that contain a very large number of collections (hundreds or thousands), listing all collections at once can become a performance bottleneck. Here are some strategies to mitigate this:

5.1. Use listCollections with a Cursor and Batching (Shell and Drivers)

The listCollections command (and its driver equivalents) uses a cursor internally. This means that MongoDB doesn’t retrieve all the collection information in one go. Instead, it retrieves the data in batches. This is generally more efficient than retrieving everything at once.

In the shell, you can see this batching behavior if you have a very large number of collections. The initial output might show only a subset, and you’d need to type it (iterate) to see more.

In the driver code examples (Python, Node.js, Java, C#, Go), we’ve already shown how to work with the cursor (or iterable/async cursor). Make sure you’re processing the results in a loop, rather than trying to load the entire list into memory at once, especially if you’re dealing with a potentially huge number of collections.

5.2. Filtering is Key

The most effective way to improve performance is to avoid listing all collections in the first place. If you only need information about a specific subset of collections, use the filtering options described earlier (filter in listCollections, or JavaScript filtering with db.getCollectionNames()). This reduces the amount of data that MongoDB needs to process and return.

5.3. Limit and Skip (Less Common for Collection Listing)

While limit() and skip() are commonly used with find() operations to paginate query results, they are less frequently used with listCollections. However, they can be applied if you absolutely need to process collections in fixed-size chunks and you have a predictable ordering (e.g., alphabetical). This is generally not recommended, as filtering is almost always a better approach. It’s mentioned here for completeness. The listCollections command does not directly support limit and skip. You’d have to retrieve the full list and then apply limiting and skipping in your application code, which defeats the purpose of optimizing the database operation.

5.4. Index on name (If Filtering by Name Frequently)

If you frequently filter collection listings by name using regular expressions that are not anchored to the beginning of the string (e.g., {$regex: "log"} instead of {$regex: "^log"}), creating an index on the name field within the internal collections that store collection metadata might improve performance in some cases. However, this is a very low-level optimization and should only be considered after careful profiling and benchmarking. It’s generally not necessary. The internal structures used by MongoDB to manage collections are already highly optimized.

5.5. Consider Database Design

If you find yourself constantly needing to list and process a massive number of collections, it might be worth revisiting your database design. A very large number of collections could indicate a design issue, such as:

  • Overly Granular Sharding: If you’re using sharding and have a very large number of shards, each with its own set of collections, this could contribute to the problem.
  • Time-Based Collections: Creating a new collection for each day, week, or month can lead to a large number of collections over time. Consider using time-series collections (MongoDB 5.0+) or a different data modeling approach.
  • One Collection Per User: Creating a separate collection for each user is generally not recommended. It’s usually better to have a single “users” collection with appropriate indexing and filtering.

5.6. Caching (Application-Level)

If your application frequently needs to list collections, and the list of collections doesn’t change very often, you could consider caching the list of collection names (or the filtered list) in your application’s memory or in a separate caching layer (like Redis). This can significantly reduce the load on the MongoDB server. Be sure to implement appropriate cache invalidation logic to ensure the cached data remains up-to-date.

6. system.namespaces and system.indexes (Advanced)

For those who want to delve deeper into the internals of MongoDB, there are two special collections within each database that provide information about collections and indexes:

  • system.namespaces: This collection stores information about all namespaces in the database, including collections and views. Do not modify this collection directly.
  • system.indexes: This collection stores information about all indexes in the database. Do not modify this collection directly.

You can query these collections like any other collection, but be aware that they contain internal MongoDB metadata. Directly modifying these collections can corrupt your database. They are primarily useful for advanced diagnostics and understanding how MongoDB stores collection and index information. It’s strongly recommended to use the standard listCollections command and driver methods instead of directly querying these collections unless you have a very specific and well-understood reason to do so.

Example (Caution Advised):

“`javascript
// Accessing system.namespaces (for informational purposes only)
db.getSiblingDB(“mydatabase”).system.namespaces.find()

// Accessing system.indexes (for informational purposes only)
db.getSiblingDB(“mydatabase”).system.indexes.find()
“`

7. Working with Views

Views are read-only collections created from aggregation pipelines. They appear in the list of collections, but have the type property set to “view”.

“`javascript
// Create a view
db.createView(“customerOrders”, “customers”, [
{ $lookup: { from: “orders”, localField: “_id”, foreignField: “customerId”, as: “orders” } }
]);

// List collections (including the view)
show collections;

// Filter for views
db.runCommand({ listCollections: 1, filter: { type: “view” } });
“`

The createView command creates a view named customerOrders based on the customers collection. The aggregation pipeline joins customers with orders. When you list collections, the view will be included. The listCollections command with the filter: { type: "view" } will specifically list only the views.

8. Working with Time-Series Collections

Time-series collections, introduced in MongoDB 5.0, are optimized for storing and querying time-series data. They also appear in collection listings, with the type property set to “timeseries”.

“`javascript
// Create a time-series collection
db.createCollection(“sensorData”, { timeseries: { timeField: “timestamp”, metaField: “sensorId” } });

// List collections (including the time-series collection)
show collections;

// Filter for time-series collections
db.runCommand({ listCollections: 1, filter: { type: “timeseries” } });
“`

The createCollection command with the timeseries option creates a time-series collection. The timeField specifies the field containing the timestamp, and the metaField (optional) specifies a field for metadata that distinguishes different time series within the collection. The listCollections filter can be used to specifically list time-series collections.

9. Permissions and Security

When working with MongoDB in a secure environment (with authentication enabled), the ability to list collections is controlled by user roles and privileges.

  • listCollections Privilege: The listCollections privilege on a database allows a user to list the collections within that database.
  • find Privilege on system.namespaces: Technically, listing collections involves reading from the system.namespaces collection. Therefore, the find privilege on system.namespaces is also required. However, in practice, granting listCollections is usually sufficient.
  • Roles: Built-in roles like read and readWrite on a database typically include the listCollections privilege. You can also create custom roles with specific privileges.
  • authorizedCollections: As mentioned in Section 3.2.3, this field in the db.runCommand function allows users to only view collections that they have privileges for.

Example (Creating a Custom Role):

javascript
db.createRole({
role: "collectionLister",
privileges: [
{ resource: { db: "mydatabase", collection: "" }, actions: ["listCollections"] }
],
roles: []
});

This creates a custom role named collectionLister that grants the listCollections privilege on the mydatabase database. This role can then be assigned to users who need to list collections but should not have other privileges.

10. Conclusion

Listing collections in MongoDB is a fundamental operation with various approaches, from simple shell commands to programmatic access using drivers. Understanding the different methods, filtering options, performance considerations, and security implications is crucial for effective database management and application development. This comprehensive guide has covered the basics, advanced techniques, and best practices for listing collections, empowering you to work confidently with MongoDB collections of any size and complexity. Remember to always prioritize filtering to improve performance, and be mindful of security considerations when working with authentication-enabled deployments.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top