Navigation

Reading and Writing Data

Overview

The code snippets on this page demonstrate various patterns for working with data in MongoDB.

Data Model

The examples on this page use a collection named store.items that models various items available for purchase in an online store. Each item has a name, an inventory quantity, and an array of customer reviews.

// store.items
{
    _id:      <ObjectID>,
    name:     <string>,
    quantity: <int>,
    reviews:  [ { username: <string>, comment: <string> } ]
}

Snippet Setup

To use a code snippet in a function, you must first instantiate a MongoDB collection handle:

exports = function() {
  const mongodb = context.services.get("mongodb-atlas");
  const itemsCollection = mongodb.db("store").collection("items");
}

To use a code snippet in a JavaScript project, you must first do the following:

1

Import Stitch Dependencies

Import from CDN
<!-- Import the Stitch JS SDK at the top of the file -->
<script src="https://s3.amazonaws.com/stitch-sdks/js/bundles/4.0.0/stitch.js"></script>
<script>
  // Destructure Stitch JS SDK Components
  const { Stitch, RemoteMongoClient } = stitch;
</script>
Import from Module
// Import components of the Stitch JS SDK at the top of the file
import {
  Stitch,
  RemoteMongoClient,
} from "mongodb-stitch-browser-sdk";
2

Instantiate a MongoDB Collection Handle

const stitchApp = Stitch.initializeDefaultAppClient("<Your App ID>");
const mongodb = stitchApp.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
const itemsCollection = mongodb.db("store").collection("items");

To use a code snippet in an Android project, you must first do the following:

1

Set Up Your Project

Follow the steps in the Set up a MongoDB Mobile Project guide.

Note

For more details on setting up your Android app to use Stitch, refer to Build a Mobile App with Sync or Build a Local-Only Mobile App.

2

Import Stitch Dependencies

For CRUD operations on a remote MongoDB collection, you will use one or more of the following import statements:

// Base Stitch Packages
import com.mongodb.stitch.android.core.Stitch;
import com.mongodb.stitch.android.core.StitchAppClient;
// Stitch Authentication Packages
import com.mongodb.stitch.android.core.auth.StitchUser;
import com.mongodb.stitch.core.auth.providers.anonymous.AnonymousCredential;
// MongoDB Service Packages
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
// Utility Packages
import com.mongodb.stitch.core.internal.common.BsonUtils;

To Sync documents between a remote and a local MongoDB instance, you will also need to import the following packages:

// Stitch Sync Packages
import com.mongodb.stitch.core.services.mongodb.remote.sync.ChangeEventListener;
import com.mongodb.stitch.core.services.mongodb.remote.sync.DefaultSyncConflictResolvers;
import com.mongodb.stitch.core.services.mongodb.remote.sync.ErrorListener;
import com.mongodb.stitch.core.services.mongodb.remote.sync.internal.ChangeEvent;

Important

If you use Sync, be sure to add .sync() before every remote database call in the examples below, as in the following:

itemsCollections.sync().find()

To perform CRUD operations only on the local (on-device) database, import the following packages:

// MongoDB Mobile Local Database Packages
import com.mongodb.stitch.android.services.mongodb.local.LocalMongoDbService;
3

Instantiate a MongoDB Collection Handle

Top of Activity File
private StitchAppClient stitchClient;
private RemoteMongoClient mongoClient;
private RemoteMongoCollection itemsCollection;
In Activity.onCreate()
stitchClient = Stitch.getDefaultAppClient();
mongoClient = stitchClient.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
itemsCollection = mongoClient.getDatabase("store").getCollection("items");

To use a code snippet in an iOS project, you must first do the following:

1

Set Up Your Project

Follow the steps in the Set up a MongoDB Mobile Project guide.

2

Import Stitch Dependencies

In scope (e.g. UIViewController)
import StitchCore
import StitchCoreRemoteMongoDBService
import StitchRemoteMongoDBService
3

Initialize the MongoDB Stitch iOS SDK

Application Startup (e.g. AppDelegate.didFinishLaunchingWithOptions())
do {
    let _ = try Stitch.initializeDefaultAppClient(
        withClientAppID: "YOUR-APP-ID"
    )
} catch {
    print("Failed to initialize MongoDB Stitch iOS SDK: \(error)")
}
4

Instantiate a MongoDB Collection Handle

In scope (e.g. UIViewController)
// Variables in scope:
private lazy var stitchClient = Stitch.defaultAppClient!
private var mongoClient: RemoteMongoClient?
private var itemsCollection: RemoteMongoCollection<Document>?

// Set the stitch variables declared above in viewDidLoad()
mongoClient = stitchClient.serviceClient(
    fromFactory: remoteMongoClientFactory,
    withName: "mongodb-atlas"
)
itemsCollection = mongoClient?.db("store").collection("items")

Insert

Insert One Document

Inserts a single item document into the items collection.

const newItem = {
  name: "Plastic Bricks",
  quantity: 10,
  category: "toys",
  reviews: [{ username: "legolover", comment: "These are awesome!" }]
};

itemsCollection.insertOne(doc)
  .then(result => console.log(`Successfully inserted item with _id: ${result.insertedId}`);
  .catch(err => console.error(`Failed to insert item: ${err}`))
const newItem = {
  name: "Plastic Bricks",
  quantity: 10,
  category: "toys",
  reviews: [{ username: "legolover", comment: "These are awesome!" }]
};

itemsCollection.insertOne(doc)
  .then(result => console.log(`Successfully inserted item with _id: ${result.insertedId}`);
  .catch(err => console.error(`Failed to insert item: ${err}`))
Document doc = new Document()
    .append("name", "legos")
    .append("quantity", 10)
    .append("category", "toys")
    .append("reviews", Arrays.asList(
        new Document()
        .append("username", "mongolover")
        .append("comment", "this is great")
    ));


final Task <RemoteInsertOneResult> insertTask = itemsCollection.insertOne(doc);
insertTask.addOnCompleteListener(new OnCompleteListener <RemoteInsertOneResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteInsertOneResult> task) {
        if (task.isSuccessful()) {
            Log.d("app", String.format("successfully inserted item with id %s",
                task.getResult().getInsertedId()));
        } else {
            Log.e("app", "failed to insert document with: ", task.getException());
        }
    }
});
let doc: Document = [
    "name": "Plastic Bricks",
    "quantity": 10,
    "category": "toys",
    "reviews": [
        [
            "username": "legolover",
            "comment": "These are awesome!"
        ] as Document
    ],
];

itemsCollection?.insertOne(doc) { result in
    switch result {
    case .success(let result):
        print("Successfully inserted item with _id: \(result.insertedId))");
    case .failure(let error):
        print("Failed to insert item: \(error)");
    }
}

Insert Multiple Documents

Inserts multiple item documents into the items collection.

const doc1 = { name: "basketball", category: "sports", quantity: 20, reviews: [] };
const doc2 = { name: "football",   category: "sports", quantity: 30, reviews: [] };

return itemsCollection.insertMany([doc1, doc2])
  .then(result => {
    console.log(`Successfully inserted ${result.insertedIds.length} items!`);
    return result
  })
  .catch(err => console.error(`Failed to insert documents: ${err}`))
const doc1 = { name: "basketball", category: "sports", quantity: 20, reviews: [] };
const doc2 = { name: "football",   category: "sports", quantity: 30, reviews: [] };

itemsCollection.insertMany([doc1, doc2])
  .then(result => {
    console.log(`Successfully inserted ${result.insertedIds.length} items!`);
  })
  .catch(err => console.error(`Failed to insert documents: ${err}`))
Document doc1 = new Document()
    .append("name", "basketball")
    .append("category", "sports")
    .append("quantity", 20)
    .append("reviews", Arrays.asList());

Document doc2 = new Document()
    .append("name", "football")
    .append("category", "sports")
    .append("quantity", 30)
    .append("reviews", Arrays.asList());

List<Document> docs = Arrays.asList(doc1, doc2);

final Task <RemoteInsertManyResult> insertTask = itemsCollection.insertMany(docs);
insertTask.addOnCompleteListener(new OnCompleteListener <RemoteInsertManyResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteInsertManyResult> task) {
        if (task.isSuccessful()) {
            Log.d("app",
                String.format("successfully inserted %d items with ids: %s",
                    task.getResult().getInsertedIds().size(),
                    task.getResult().getInsertedIds().toString()));
        } else {
            Log.e("app", "failed to inserts document with: ", task.getException());
        }
    }
});
// Insert two documents into the items collection
let doc1: Document = [
    "name": "basketball",
    "category": "sports",
    "quantity": 20,
    "reviews": []
];

let doc2: Document = [
    "name": "football",
    "category": "sports",
    "quantity": 30,
    "reviews": []
];

itemsCollection?.insertMany([doc1, doc2]) { result in
    switch result {
    case .success(let result):
        print("Successfully inserted items with _ids: \(result.insertedIds))");
    case .failure(let error):
        print("Failed to insert items: \(error)");
    }
}

Find

Find One Document

Finds a single document from the items collection that has a quantity greater than or equal to 25.

const query = { quantity: { $gte: 25 } };
const options = { limit: 1 }

return itemsCollection.findOne(query, options)
  .then(result => {
    if(result) {
      console.log(`Successfully found document: ${result}.`)
    } else {
      console.log("No document matches the provided query.")
    }
    return result
  })
  .catch(err => console.error(`Failed to find document: ${err}`))
const query = { quantity: { $gte: 25 } };
const options = { limit: 1 }

itemsCollection.find(query, options).first()
  .then(result => {
    if(result) {
      console.log(`Successfully found document: ${result}.`)
    } else {
      console.log("No document matches the provided query.")
    }
  })
  .catch(err => console.error(`Failed to find document: ${err}`))
Document filterDoc = new Document()
    .append("quantity", new Document().append("$gte", 25));

final Task <Document> findTask = itemsCollection.find(filterDoc).limit(1).first();
findTask.addOnCompleteListener(new OnCompleteListener <Document> () {
    @Override
    public void onComplete(@NonNull Task <Document> task) {
        if (task.isSuccessful()) {
            if (task.getResult() == null) {
                Log.d("app", "Could not find any matching documents");
            } else {
                Log.d("app", String.format("successfully found document: %s",
                    task.getResult().toString()));
            }
        } else {
            Log.e("app", "failed to find document with: ", task.getException());
        }
    }
});
let query : Document = ["quantity": ["$gte": 25] as Document];
let options = RemoteFindOptions(limit: 1);

itemsCollection?.find(query, options: options).first({ result in
    switch result {
    case .success(let result):
        if result?._id {
            print("Successfully found document: \(result)");
        } else {
            print("No document matches the provided query");
        }
    case .failure(let error):
        print("Failed to find document: \(error)");
    }
})

Find Multiple Documents

Finds all documents in the items collection that have at least one review. Returns them sorted by the item name and omits the _id fields.

const query = { "reviews.0": { $exists: true } };
const options = {
  projection: { _id: 0 },
  sort: { name: 1 }
};

return itemsCollection.find(query, options).toArray()
  .then(items => {
    console.log(`Successfully found ${items.length} documents.`)
    items.forEach(console.log)
    return items
  })
  .catch(err => console.error(`Failed to find documents: ${err}`))
const query = { "reviews.0": { $exists: true } };
const options = {
  projection: { _id: 0 },
  sort: { name: 1 }
};

itemsCollection.find(query, options).toArray()
  .then(items => {
    console.log(`Successfully found ${items.length} documents.`)
    items.forEach(console.log)
    return items
  })
  .catch(err => console.error(`Failed to find documents: ${err}`))
Document filterDoc = new Document()
    .append("reviews.0", new Document().append("$exists", true));

RemoteFindIterable findResults = itemsCollection
    .find(filterDoc)
    .projection(new Document().append("_id", 0))
    .sort(new Document().append("name", 1));

// One way to iterate through
findResults.forEach(item -> {
    Log.d("app", String.format("successfully found:  %s", item.toString()));
});

// Another way to iterate through
Task <List<Document>> itemsTask = findResults.into(new ArrayList<Document>());
itemsTask.addOnCompleteListener(new OnCompleteListener <List<Document>> () {
    @Override
    public void onComplete(@NonNull Task<List<Document>> task) {
        if (task.isSuccessful()) {
            List<Document> items = task.getResult();
            Log.d("app", String.format("successfully found %d documents", items.size()));
            for (Document item: items) {
                Log.d("app", String.format("successfully found:  %s", item.toString()));
            }
        } else {
            Log.e("app", "failed to find documents with: ", task.getException());
        }
    }
});
let query : Document = ["reviews.0": ["$exists": true] as Document];
let options = RemoteFindOptions(
    projection: ["_id": 0],
    sort: ["name": 1]
);

itemsCollection?.find(query, options: options).toArray({ results in
    switch results {
    case .success(let results):
        print("Successfully found \(results.count) documents: ");
        results.forEach({item in
            print(item);
        })
    case .failure(let error):
        print("Failed to find documents: \(error)");
    }
})

Update

Update One Document by Adding / Modifying Fields

Updates the name of a single document in the items collection from legos to blocks and adds a price of 20.99.

const query = { name: "legos" };
const update =
  $set: {
    name: "blocks",
    price: 20.99,
    category: "toys"
  }
};
const options = { upsert: false };

itemsCollection.updateOne(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount } = result;
    if(matchedCount && modifiedCount) {
      console.log(`Successfully updated the item.`)
    }
  })
  .catch(err => console.error(`Failed to update the item: ${err}`))
const query = { name: "legos" };
const update =
  $set: {
    name: "blocks",
    price: 20.99,
    category: "toys"
  }
};
const options = { upsert: false };

itemsCollection.updateOne(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount } = result;
    if(matchedCount && modifiedCount) {
      console.log(`Successfully updated the item.`)
    }
  })
  .catch(err => console.error(`Failed to update the item: ${err}`))
Document filterDoc = new Document().append("name", "legos");
Document updateDoc = new Document().append("$set",
    new Document()
    .append("name", "blocks")
    .append("price", 20.99)
    .append("category", "toys")
);

final Task<RemoteUpdateResult> updateTask =
    itemsCollection.updateOne(filterDoc, updateDoc);
updateTask.addOnCompleteListener(new OnCompleteListener <RemoteUpdateResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteUpdateResult> task) {
        if (task.isSuccessful()) {
            long numMatched = task.getResult().getMatchedCount();
            long numModified = task.getResult().getModifiedCount();
            Log.d("app", String.format("successfully matched %d and modified %d documents",
                    numMatched, numModified));
        } else {
            Log.e("app", "failed to update document with: ", task.getException());
        }
    }
});
let query : Document = ["name": "legos"];
let update : Document = [
    "$set": [
        "name": "blocks",
        "price": 20.99
    ] as Document
];
let options = RemoteUpdateOptions(upsert: false);

itemsCollection?.updateOne(filter: query, update: update) { result in
    switch result {
    case .success(let result):
        if result.matchedCount && result.modifiedCount {
            print("Successfully added a new review.")
        } else {
            print("Could not find a matching item.")
        }
    case .failure(let error):
        print("Failed to update: \(error)");
    }
}

Update One Document by Pushing into Sub-Array

Updates a single document in the items collection that has a name of football by adding a new review to the reviews array.

const query = { name: "football" };
const update = {
  $push: {
    reviews: {
      username: "tombradyfan",
      comment: "I love football!!!"
    }
  }
};
const options = { upsert: false };

itemsCollection.updateOne(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount } = result;
    if(matchedCount && modifiedCount) {
      console.log(`Successfully added a new review.`)
    }
  })
  .catch(err => console.error(`Failed to add review: ${err}`))
const query = { name: "football" };
const update = {
  $push: {
    reviews: {
      username: "stitchfan2018",
      comment: "what a neat product"
    }
  }
};
const options = { upsert: false };

itemsCollection.updateOne(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount } = result;
    if(matchedCount && modifiedCount) {
      console.log(`Successfully added a new review.`)
    }
  })
  .catch(err => console.error(`Failed to add review: ${err}`))
Document filterDoc = new Document().append("name", "football");
Document updateDoc = new Document().append("$push",
    new Document().append("reviews", new Document()
        .append("username", "stitchfan2018")
        .append("comment", "what a neat product")
    )
);

final Task <RemoteUpdateResult> updateTask =
  itemsCollection.updateOne(filterDoc, updateDoc);
updateTask.addOnCompleteListener(new OnCompleteListener <RemoteUpdateResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteUpdateResult> task) {
        if (task.isSuccessful()) {
            long numMatched = task.getResult().getMatchedCount();
            long numModified = task.getResult().getModifiedCount();
            Log.d("app", String.format("successfully matched %d and modified %d documents",
                  numMatched, numModified));
        } else {
            Log.e("app", "failed to update document with: ", task.getException());
        }
    }
});
let query : Document = ["name": "football"];
let update : Document = [
    "$push": [
        "reviews": [
            "username": "stitchfan2018",
            "comment": "what a neat product"
        ] as Document
    ] as Document
];

itemsCollection?.updateOne(filter: query, update: update) { result in
    switch result {
    case .success(let result):
        if result.matchedCount && result.modifiedCount {
            print("Successfully added a new review.")
        } else {
            print("Could not find a matching item.")
        }
    case .failure(let error):
        print("Failed to update: \(error)");
    }
}

Update One Document with Upsert Enabled

The snippet below updates a document in the items collection that has a name of board game by incrementing its quantity by 5. If no document has a name of board game, inserts a new document with the name field set to board game and the quantity value set to 5.

const query = { name: "board games" };
const update = { $inc: { quantity: 5 } };
const options = { upsert: true }

itemsCollection.updateOne(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount, upsertedId } = result;
    if(upsertedId) {
      console.log(`Document not found. Inserted a new document with _id: ${upsertedId}`)
    } else {
      console.log(`Successfully increased ${query.name} quantity by ${update.$inc.quantity}`)
    }
  })
  .catch(err => console.error(`Failed to upsert document: ${err}`))
const query = { name: "board games" };
const update = { $inc: { quantity: 5 } };
const options = { upsert: true }

itemsCollection.updateOne(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount, upsertedId } = result;
    if(upsertedId) {
      console.log(`Document not found. Inserted a new document with _id: ${upsertedId}`)
    } else {
      console.log(`Successfully increased ${query.name} quantity by ${update.$inc.quantity}`)
    }
  })
  .catch(err => console.error(`Failed to upsert document: ${err}`))
Document filterDoc = new Document().append("name", "board game");
Document updateDoc = new Document().append("$inc", new Document().append("quantity", 5));
RemoteUpdateOptions options = new RemoteUpdateOptions().upsert(true);


final Task <RemoteUpdateResult> updateTask =
  itemsCollection.updateOne(filterDoc, updateDoc, options);
updateTask.addOnCompleteListener(new OnCompleteListener <RemoteUpdateResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteUpdateResult> task) {
        if (task.isSuccessful()) {
            if (task.getResult().getUpsertedId() != null) {
                String upsertedId = task.getResult().getUpsertedId().toString();
                Log.d("app", String.format("successfully upserted document with id: %s",
                  upsertedId));
            } else {
                long numMatched = task.getResult().getMatchedCount();
                long numModified = task.getResult().getModifiedCount();
                Log.d("app", String.format("successfully matched %d and modified %d documents",
                      numMatched, numModified));
            }
        } else {
            Log.e("app", "failed to update document with: ", task.getException());
        }
    }
});
let query : Document = ["name": "board game"];
let update : Document = ["$inc": ["quantity": 5] as Document];
let options = RemoteUpdateOptions(upsert: true);

itemsCollection?.updateOne(filter: query, update: update, options: options) { result in
    switch result {
    case .success(let result):
        if let upsertedId = result.upsertedId {
            print("Document not found. Inserted a new document with _id: \(upsertedId).")
        } else {
            print("Successfully increased \(query.name) quantity by \(update["$inc"].quantity)")
        }
    case .failure(let error):
        print("Failed to upsert: \(error)");
    }
}

Update Many Documents

Updates all documents in the items collection by multiplying their quantity values by 10.

const query = {};
const update = { $mul: { quantity: 10 } };
const options = { upsert: false }

return itemsCollection.updateMany(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount } = result;
    console.log(`Successfully matched ${matchedCount} and modified ${modifiedCount} items.`)
    return result
  })
  .catch(err => console.error(`Failed to update items: ${err}`))
const query = {};
const update = { $mul: { quantity: 10 } };
const options = { upsert: false }

itemsCollection.updateMany(query, update, options)
  .then(result => {
    const { matchedCount, modifiedCount } = result;
    console.log(`Successfully matched ${matchedCount} and modified ${modifiedCount} items.`)
  })
  .catch(err => console.error(`Failed to update items: ${err}`))
Document filterDoc = new Document();
Document updateDoc = new Document().append("$mul", new Document().append("quantity", 10));

final Task <RemoteUpdateResult> updateTask =
  itemsCollection.updateMany(filterDoc, updateDoc);
updateTask.addOnCompleteListener(new OnCompleteListener <RemoteUpdateResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteUpdateResult> task) {
        if (task.isSuccessful()) {
            long numMatched = task.getResult().getMatchedCount();
            long numModified = task.getResult().getModifiedCount();
            Log.d("app", String.format("successfully matched %d and modified %d documents",
                  numMatched, numModified));
        } else {
            Log.e("app", "failed to update document with: ", task.getException());
        }
    }
});
let query : Document = [];
let update : Document = ["$mul": ["quantity": 10] as Document];

itemsCollection?.updateMany(filter: query, update: update) { result in
    switch result {
    case .success(let result):
        print("Successfully matched \(result.matchedCount) and modified \(result.modifiedCount) items.");
    case .failure(let error):
        print("Failed to modify items: \(error)");
    }
}

Delete

Delete One Document

Deletes one document in the items collection that has a name value of legos.

const query = { name: "legos" };

itemsCollection.deleteOne(query)
  .then(result => console.log(`Deleted ${result.deletedCount} item.`)
  .catch(err => console.error(`Delete failed with error: ${err}`))
const query = { name: "legos" };

itemsCollection.deleteOne(query)
  .then(result => console.log(`Deleted ${result.deletedCount} item.`)
  .catch(err => console.error(`Delete failed with error: ${err}`))
Document filterDoc = new Document().append("name", "legos");

final Task<RemoteDeleteResult> deleteTask = itemsCollection.deleteOne(filterDoc);
deleteTask.addOnCompleteListener(new OnCompleteListener <RemoteDeleteResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteDeleteResult> task) {
        if (task.isSuccessful()) {
            long numDeleted = task.getResult().getDeletedCount();
            Log.d("app", String.format("successfully deleted %d documents", numDeleted));
        } else {
            Log.e("app", "failed to delete document with: ", task.getException());
        }
    }
});
let query : Document = ["name": "legos"];

itemsCollection?.deleteOne(query) { result in
    switch result {
    case .success(let result):
        print("Deleted \(result.deletedCount) item.");
    case .failure(let error):
        print("Delete failed with error: \(error)");
    }
}

Delete Multiple Documents

Deletes all documents in the items collection that do not have any reviews.

const query = { reviews: { $size: 0 } };

itemsCollection.deleteMany(query)
  .then(result => console.log(`Deleted ${result.deletedCount} item(s).`)
  .catch(err => console.error(`Delete failed with error: ${err}`))
const query = { reviews: { $size: 0 } };

itemsCollection.deleteMany(query)
  .then(result => console.log(`Deleted ${result.deletedCount} item(s).`)
  .catch(err => console.error(`Delete failed with error: ${err}`))
Document filterDoc = new Document().append("reviews", new Document().append("$size", 0));

final Task <RemoteDeleteResult> deleteTask = itemsCollection.deleteMany(filterDoc);
deleteTask.addOnCompleteListener(new OnCompleteListener <RemoteDeleteResult> () {
    @Override
    public void onComplete(@NonNull Task <RemoteDeleteResult> task) {
        if (task.isSuccessful()) {
            long numDeleted = task.getResult().getDeletedCount();
            Log.d("app", String.format("successfully deleted %d documents", numDeleted));
        } else {
            Log.e("app", "failed to delete document with: ", task.getException());
        }
    }
});
let query : Document = ["reviews": ["$size": 0] as Document];

itemsCollection?.deleteMany(query) { result in
    switch result {
    case .success(let result):
        print("Deleted \(result.deletedCount) item(s).");
    case .failure(let error):
        print("Delete failed with error: \(error)");
    }
}

Aggregate

Aggregate Values for Groups of Documents

Groups all documents in the items collection by their category and projects a count of the number of items in each category as well as the sum of their quantity values.

const pipeline = [
  { $group: {
      _id: "$category",
      totalQuantity: { $sum: "$quantity" },
      count: { $sum: 1 }
  } }
]

return itemsCollection.aggregate(pipeline).toArray()
  .then(categories => {
    console.log(`Successfully grouped into ${categories.length} categories.`)
    for(const category of categories) {
      const { _id, totalQuantity, count } = category;
      console.log(`${category._id}:`)
      console.log(`    num items: ${category.count}`)
      console.log(`    inventory: ${category.totalQuantity}`)
    }
    return categories
  })
  .catch(err => console.error(`Failed to group by categories: ${err}`))
const pipeline = [
  { $group: {
      _id: "$category",
      totalQuantity: { $sum: "$quantity" },
      count: { $sum: 1 }
  } }
]

itemsCollection.aggregate(pipeline).toArray()
  .then(categories => {
    console.log(`Successfully grouped into ${categories.length} categories.`)
    for(const category of categories) {
      const { _id, totalQuantity, count } = category;
      console.log(`${category._id}:`)
      console.log(`    num items: ${category.count}`)
      console.log(`    inventory: ${category.totalQuantity}`)
    }
  })
  .catch(err => console.error(`Failed to group by categories: ${err}`))
List<Document> aggregationPipeLine = Arrays.asList(
    new Document().append("$group", new Document()
        .append("_id", "$category")
        .append("totalQuantity", new Document().append("$sum", "$quantity"))
        .append("count", new Document().append("$sum", 1))
    )
);

itemsCollection.aggregate(aggregationPipeLine).forEach(item -> {
    Log.d("app", String.format("aggregation result:  %s", item.toString()));
});

// Another way to iterate through
Task<List<Document>> itemsTask = itemsCollection.aggregate(aggregationPipeLine)
    .into(new ArrayList<Document>());
itemsTask.addOnCompleteListener(new OnCompleteListener <List<Document>> () {
    @Override
    public void onComplete(@NonNull Task <List<Document>> task) {
        if (task.isSuccessful()) {
            List<Document> items = task.getResult();
            Log.d("app", String.format("%d aggregation results", items.size()));
            for (Document item: items) {
                Log.d("app", String.format("aggregation result:  %s", item.toString()));
            }
        } else {
            Log.e("app", "failed to perform aggregation with: ", task.getException());
        }
    }
});
let pipeline : [Document] = [
    ["$group":
        [   "_id": "$category",
            "totalQuantity": ["$sum": "$quantity"] as Document,
            "count": ["$sum": 1] as Document
        ] as Document
    ] as Document
];

itemsCollection?.aggregate(pipeline).toArray({ categories in
    switch categories {
    case .success(let categories):
        print("Successfully grouped into \(categories.count) categories:");
        categories.forEach({category in
            print("\(category._id)");
            print("    num items: \(category.count)");
            print("    inventory: \(category.totalQuantity)");
        })
    case .failure(let error):
        print("Failed to group by categories: \(err)");
    }
})