W3docs

Inserimento in MongoDB

Impara a inserire singoli e multipli documenti in MongoDB con Python e pymongo — con gestione degli errori, ObjectId e opzioni di inserimento bulk.

MongoDB memorizza i dati come documenti — oggetti flessibili simili a JSON che possono contenere campi annidati e array. Questo capitolo mostra come inserire un documento alla volta con insert_one(), inserire più documenti in una singola chiamata con insert_many(), comprendere il campo _id generato automaticamente, gestire gli errori di chiave duplicata e scegliere tra inserimenti bulk ordinati e non ordinati.

Prerequisiti

pip install pymongo

Connessione a MongoDB

Importa MongoClient e apri una connessione prima di eseguire qualsiasi operazione di inserimento. Quando MongoDB è in esecuzione localmente con le impostazioni predefinite, puoi chiamare MongoClient() senza argomenti:

from pymongo import MongoClient

client = MongoClient()          # connects to localhost:27017
db = client["bookstore"]        # database (created on first write)
books = db["books"]             # collection (created on first write)

Per connettersi a un server remoto o ad Atlas, passa un URI di connessione:

client = MongoClient("mongodb://username:password@hostname:27017/")

MongoClient mantiene internamente un pool di connessioni, quindi dovresti creare un solo client per applicazione e riutilizzarlo in tutte le operazioni.

Inserimento di un singolo documento con insert_one()

insert_one() aggiunge un documento a una collection e restituisce un object InsertOneResult. La proprietà più utile di quell'object è inserted_id, che contiene il valore _id assegnato al nuovo documento.

from pymongo import MongoClient

client = MongoClient()
books = client["bookstore"]["books"]

document = {
    "title": "The Pragmatic Programmer",
    "author": "David Thomas",
    "year": 1999,
    "in_stock": True,
}

result = books.insert_one(document)
print("Inserted _id:", result.inserted_id)

Output di esempio:

Inserted _id: 64b3e2c1f0a1234567890abc

Il valore esatto di _id sarà diverso ogni volta — MongoDB genera un ObjectId univoco a meno che tu non ne fornisca uno personale.

Il campo _id

Ogni documento MongoDB deve avere un campo _id. Se non ne includi uno, il driver genera automaticamente un valore bson.ObjectId. ObjectId è un valore di 12 byte che codifica:

  • un timestamp Unix a 4 byte (secondi),
  • un valore casuale a 5 byte univoco per la macchina e il processo,
  • un contatore incrementale a 3 byte.

Ciò significa che i valori ObjectId sono approssimativamente ordinati in ordine cronologico e globalmente unici senza alcun coordinamento tra i server.

Puoi fornire il tuo _id se hai una chiave univoca naturale (ad esempio, un ISBN):

result = books.insert_one({
    "_id": "978-0-13-468599-1",
    "title": "The Pragmatic Programmer",
    "author": "David Thomas",
    "year": 1999,
})
print("Inserted _id:", result.inserted_id)
# Inserted _id: 978-0-13-468599-1

Se inserisci un secondo documento con lo stesso _id, MongoDB genera un DuplicateKeyError (vedi Gestione degli errori di seguito).

Inserimento di più documenti con insert_many()

insert_many() accetta una lista di documenti e li inserisce tutti in un singolo round-trip di rete. Restituisce un InsertManyResult il cui attributo inserted_ids contiene la lista dei valori _id assegnati nell'ordine di inserimento.

from pymongo import MongoClient

client = MongoClient()
books = client["bookstore"]["books"]

new_books = [
    {"title": "Clean Code", "author": "Robert C. Martin", "year": 2008},
    {"title": "Refactoring",  "author": "Martin Fowler",    "year": 1999},
    {"title": "Design Patterns", "author": "Gang of Four",  "year": 1994},
]

result = books.insert_many(new_books)
print("Inserted IDs:", result.inserted_ids)

Output di esempio:

Inserted IDs: [ObjectId('...'), ObjectId('...'), ObjectId('...')]

Inserimenti ordinati e non ordinati

Per impostazione predefinita, insert_many() usa la modalità ordinata: i documenti vengono inseriti uno alla volta nell'ordine della lista e l'elaborazione si interrompe al primo errore.

Passa ordered=False per la modalità non ordinata: MongoDB tenta ogni documento in modo indipendente e raccoglie tutti gli errori prima di generare un'eccezione. Questo è più veloce per batch di grandi dimensioni in cui si prevedono alcuni duplicati e si vogliono saltare i documenti problematici invece di interrompere l'intero batch.

from pymongo import MongoClient
from pymongo.errors import BulkWriteError

client = MongoClient()
books = client["bookstore"]["books"]

# Two documents with duplicate _id values mixed in
docs = [
    {"_id": 1, "title": "Book A"},
    {"_id": 2, "title": "Book B"},
    {"_id": 1, "title": "Duplicate — will fail"},  # duplicate _id
    {"_id": 3, "title": "Book C"},
]

try:
    result = books.insert_many(docs, ordered=False)
    print("Inserted:", result.inserted_ids)
except BulkWriteError as e:
    # inserted_ids still shows the documents that succeeded
    print("Some inserts failed:", e.details["nInserted"], "succeeded")
    for err in e.details["writeErrors"]:
        print("  Error on index", err["index"], "—", err["errmsg"])

Con ordered=False, Book A, Book B e Book C vengono inseriti anche se il duplicato fallisce. Con ordered=True (il valore predefinito), l'elaborazione si fermerebbe al terzo documento e Book C non verrebbe mai inserito.

Gestione degli errori

Errore di chiave duplicata

L'inserimento di un documento il cui _id (o qualsiasi campo coperto da un indice univoco) esiste già genera pymongo.errors.DuplicateKeyError:

from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError

client = MongoClient()
books = client["bookstore"]["books"]

try:
    books.insert_one({"_id": "isbn-001", "title": "First"})
    books.insert_one({"_id": "isbn-001", "title": "Duplicate"})  # raises
except DuplicateKeyError as e:
    print("Duplicate key:", e.details["keyValue"])

Output:

Duplicate key: {'_id': 'isbn-001'}

Errori di connessione

MongoClient() ha successo anche quando MongoDB non è in esecuzione — l'errore emerge solo quando effettui una richiesta reale. Racchiudi le operazioni di inserimento in un blocco try/except per gestire gli errori di connessione in modo elegante:

from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, PyMongoError

client = MongoClient(serverSelectionTimeoutMS=3000)

try:
    result = books.insert_one({"title": "Test"})
    print("Inserted:", result.inserted_id)
except ConnectionFailure:
    print("Could not reach MongoDB server.")
except PyMongoError as e:
    print("MongoDB error:", e)

Verifica del risultato

Sia insert_one() che insert_many() restituiscono object risultato con proprietà utili:

Object risultatoProprietà principali
InsertOneResultinserted_id, acknowledged
InsertManyResultinserted_ids (lista), acknowledged

acknowledged è True quando MongoDB ha confermato la scrittura. Può essere False solo quando si usa una write concern non riconosciuta (w=0), che salta la conferma per ottenere la massima velocità al costo di non sapere se la scrittura è riuscita.

Esempio completo funzionante

Il seguente script autonomo si connette a un server MongoDB locale, inserisce diversi documenti e stampa i risultati:

from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError, BulkWriteError

DB_NAME = "demo_bookstore"
COL_NAME = "books"

def main():
    client = MongoClient(serverSelectionTimeoutMS=3000)

    # Verify connectivity
    client.admin.command("ping")
    print("Connected to MongoDB")

    col = client[DB_NAME][COL_NAME]
    col.drop()  # start fresh for this demo

    # --- insert_one ---
    result = col.insert_one({
        "_id": "isbn-001",
        "title": "The Pragmatic Programmer",
        "author": "David Thomas",
        "year": 1999,
    })
    print("insert_one _id:", result.inserted_id)

    # --- insert_many ---
    result = col.insert_many([
        {"title": "Clean Code",      "author": "Robert C. Martin", "year": 2008},
        {"title": "Refactoring",     "author": "Martin Fowler",    "year": 1999},
        {"title": "Design Patterns", "author": "Gang of Four",     "year": 1994},
    ])
    print("insert_many IDs:", result.inserted_ids)

    # --- duplicate key ---
    try:
        col.insert_one({"_id": "isbn-001", "title": "Duplicate"})
    except DuplicateKeyError:
        print("Caught DuplicateKeyError as expected")

    # --- unordered bulk insert ---
    docs = [
        {"_id": "isbn-002", "title": "Book A"},
        {"_id": "isbn-001", "title": "Dup — will fail"},  # duplicate
        {"_id": "isbn-003", "title": "Book C"},
    ]
    try:
        col.insert_many(docs, ordered=False)
    except BulkWriteError as e:
        print("Bulk insert: succeeded =", e.details["nInserted"],
              ", failed =", len(e.details["writeErrors"]))

    print("Total documents:", col.count_documents({}))

    # Clean up
    client.drop_database(DB_NAME)

if __name__ == "__main__":
    main()

Output atteso:

Connected to MongoDB
insert_one _id: isbn-001
insert_many IDs: [ObjectId('...'), ObjectId('...'), ObjectId('...')]
Caught DuplicateKeyError as expected
Bulk insert: succeeded = 2 , failed = 1
Total documents: 6

Problemi comuni

PyMongo modifica il tuo documento

Quando passi un dict semplice a insert_one(), PyMongo aggiunge una chiave _id al dict originale:

doc = {"title": "My Book"}
col.insert_one(doc)
print(doc)  # {'title': 'My Book', '_id': ObjectId('...')}

Se prevedi di riutilizzare lo stesso dict (ad esempio in un ciclo), passa invece una copia: col.insert_one(doc.copy()).

Inserimenti di grandi dimensioni: usa insert_many() invece di un ciclo

Inserire 10.000 documenti uno alla volta richiede 10.000 round-trip di rete. Usa insert_many() per inviarli tutti in una volta — è di ordini di grandezza più veloce per i caricamenti bulk.

Se la tua lista è molto grande (milioni di documenti), dividila in batch di qualche migliaio per evitare di superare il limite di dimensione del documento BSON di 48 MB per batch.

I campi datetime richiedono object datetime, non stringhe

MongoDB memorizza le date come BSON Date (millisecondi dall'epoch). Usa il tipo datetime.datetime di Python per i campi data in modo che vengano memorizzati e interrogati correttamente:

from datetime import datetime
col.insert_one({"title": "New Book", "published": datetime(2024, 3, 15)})

Prossimi passi

Was this page helpful?