/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mongodbapp;

import com.mongodb.*;
import com.mongodb.client.*;
import com.mongodb.client.result.*;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;
import static com.mongodb.client.model.Sorts.*;
import static com.mongodb.client.model.Projections.*;
import java.util.*;
import org.bson.*;

/**
 *
 * @author sc-md
 */
public class MongoDBApp {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Connect to localhost on default port number
        //MongoClient mongoClient = MongoClients.create();

        // You can connect to one or more mongodb servers by IP address and port number
        //MongoClient mongoClient = MongoClients.create("mongodb://hostOne:27017,hostTwo:27018");
        MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");

        // You can connect to a server using a specific ip and port number using settings
//        MongoClient mongoClient = MongoClients.create(
//        MongoClientSettings.builder()
//                .applyToClusterSettings(builder ->
//                        builder.hosts(Arrays.asList(new ServerAddress("localhost", 27018))))
//                .build());
        
        // Create a database OR read it if it already exists
        MongoDatabase database = mongoClient.getDatabase("mydb");

        // Create a collection when first document is added OR select it if it already exists
        MongoCollection<Document> collection = database.getCollection("test");
        
        Document doc;

        // Create a document
        doc = new Document("name", "Fred Bloggs")
                .append("age", 22)
                .append("course", "Software Engineering");

        collection.insertOne(doc);

        // Insert many documents
        List<Document> documents = new ArrayList<Document>();
        for (int i = 0; i < 100; i++) {
            documents.add(new Document("i", i));
        }
        collection.insertMany(documents);

        // Count documents in the collection
        System.out.printf("Collection contains %d documents\n", collection.countDocuments());

        // Read the first document in the collection
        doc = collection.find().first();
        System.out.printf("First document in collection is %s\n", doc.toJson());

        // Read all the documents in the collection
        System.out.println("Reading all the documents");
        DisplayData(collection.find());

        // Find specific documents
        doc = collection.find(eq("i", 55)).first();
        System.out.printf("Finding document with i=55 %s\n", doc.toJson());
        
        doc = collection.find(eq("i", 20000)).first();  // Should fail to find document
        if(doc == null)
        {
            System.out.println("Correctly falied to find document with i==20000");
        }

        // Find a set of documents
        System.out.println("Findling all documents with id > 50");
        DisplayData(collection.find(gt("i", 50)));
        
        System.out.println("Findling all documents with id > 50 and <= 70");
        DisplayData(collection.find(and(gt("i", 50), lte("i", 70))));       
                   
        // Read and sort the documents
        System.out.println("Sort and display the documents that have the top 5 values of i");
        DisplayData(collection.find(exists("i")).sort(descending("i")).limit(5));   
        
        // Projections allow us to determine what fields should be included in the documents
        // that are returned.
        System.out.println("Findling all documents with a 'name' field and exclude the '_id' and 'course' fields in the returned documents");
        DisplayData(collection.find(exists("name")).projection(exclude("_id", "course")));       
              
        // Update a single document.
        String name = "Fred Bloggs";
        doc = collection.find(eq("name", name)).first();
        if(doc == null)
        {
            System.out.printf("No document found for %s\n", name);
        }
        else if(doc.replace("age", 23) != null)
        {
            System.out.printf("[%s] Changed the age of %s to %d in the document\n", doc.get("_id"), doc.get("name"), doc.get("age"));
        }
        else
        {
            // Use doc.put("age", 22) if you want to add a new field and value instead.
            System.out.println("Error: age field did not exist in document");
        }
        
        // Note: A new document is added if it does not already exist
        UpdateResult updated;
        updated = collection.updateOne(eq("_id", doc.get("_id")), new Document("$set", doc));
        if(updated.wasAcknowledged())
        {
            System.out.printf("Matched %d documents for update and changed %d documents\n",
                    updated.getMatchedCount(),
                    updated.getModifiedCount());
            
            doc = collection.find(eq("name", name)).first();
            System.out.printf("Changed document now contains %s\n", doc.toJson());
        }
        
        // Updated multiple documents at the same time
        updated = collection.updateMany(lt("i", 100), inc("i", 100));
        if(updated.wasAcknowledged())
        {
            System.out.printf("Updating many documents, matched %d documents and changed %d documents\n",
                    updated.getMatchedCount(),
                    updated.getModifiedCount());
            
            //DisplayData(collection.find()); // Display all
        }
        
        DeleteResult deleted;
        
        // Delete one document
        deleted = collection.deleteOne(eq("i", 110));
        if(deleted.wasAcknowledged())
        {
            System.out.printf("Deleted %d documents\n",
                deleted.getDeletedCount());   
        }
        
        // Delete many documents
        deleted = collection.deleteMany(gte("i", 112));
        if(deleted.wasAcknowledged())
        {
            System.out.printf("Deleting many documents, deleted %d documents\n",
                deleted.getDeletedCount());   
            
            //DisplayData(collection.find()); // Display all
        }
        
        // Delete all documents in collection
        long countBefore = collection.countDocuments();
        deleted = collection.deleteMany(new Document());        // May as well just drop the collection!!
        if(deleted.wasAcknowledged())
        {
            System.out.printf("Deleting all documents in the collection, deleted %d documents, before:%d after:%d\n",
                deleted.getDeletedCount(),
                countBefore,
                collection.countDocuments());   
            
            DisplayData(collection.find()); // Display all
        }      

        // Remove the collection and all its content
        collection.drop();

        // Remove the database and all its content       
        database.drop();

        mongoClient.close();        // Free all client resources
    }
    
    private static void DisplayData(FindIterable<Document> iterable)
    {
        MongoCursor<Document> cursor = iterable.iterator();
        try {
            Document doc;
            while (cursor.hasNext()) {
                doc = cursor.next();
                System.out.println(doc.toJson());
            }
        } finally {
            cursor.close();
        }        
    }
}
