Adding Course Enrollments

      +
      In this section, you’re going to add enrollment details to the student records using the Couchbase SDK

      Introduction

      A quick recap: here’s the structure of our document store:

      student-document-database-design

      At this point, you should have a student records for Hilary Smith and Ashley Jones, along with records for three courses. You’re now going to write a short program that will add enrollment details to the student records.

      You’re going to create another program in the same working directory that will bring together all the concepts you’ve learned so far.

      At the end of the exercise, you should have changed Hilary’s student record to store the enrollment details in an array:

      {
        "name": "Hilary Smith",
        "enrollments": [
          {
            "course-id": "GRAPHIC-DESIGN-000003",
            "date-enrolled": "2021-10-14"
          },
          {
            "course-id": "ART-HISTORY-000001",
            "date-enrolled": "2021-10-14"
          }
        ],
        "id": "000001",
        "date-of-birth": "1980-12-21"
      }

      So, let’s begin with the Java program that will change Hilary’s record.

      import com.couchbase.client.core.error.CouchbaseException;
      import com.couchbase.client.java.Bucket;
      import com.couchbase.client.java.Cluster;
      import com.couchbase.client.java.Collection;
      import com.couchbase.client.java.Scope;
      import com.couchbase.client.java.json.JsonArray;
      import com.couchbase.client.java.json.JsonObject;
      import com.couchbase.client.java.query.QueryOptions;
      import com.couchbase.client.java.query.QueryResult;
      import com.couchbase.client.java.query.QueryScanConsistency;
      
      import java.time.Duration;
      import java.time.LocalDate;
      import java.time.format.DateTimeFormatter;
      
      public class AddEnrollments {
      
          public static void main(String[] args) {
      
              Cluster cluster = Cluster.connect("localhost",
                      "Administrator", "password");    (1)
      
              Bucket bucket = cluster.bucket("student-bucket");    (2)
              bucket.waitUntilReady(Duration.ofSeconds(10));    (3)
      
              Scope scope = bucket.scope("art-school-scope");
              Collection student_records = scope.collection("student-record-collection");    (4)
      
              // Retrieve the records
              JsonObject hilary = retrieveStudent(cluster,"Hilary Smith");    (5)
              JsonObject graphic_design = retrieveCourse(cluster, "graphic design");    (5)
              JsonObject art_history = retrieveCourse(cluster, "art history");    (5)
      
              String currentDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE);     (6)
      
              // Create Hilary's enrollments
      
              JsonArray enrollments = JsonArray.create();     (7)
      
              enrollments.add(JsonObject.create()
                      .put("course-id", graphic_design.getString("id"))
                      .put("date-enrolled", currentDate));    (8)
      
              enrollments.add(JsonObject.create()
                      .put("course-id", art_history.getString("id"))
                      .put("date-enrolled", currentDate));    (8)
      
              hilary.put("enrollments", enrollments);     (9)
      
              student_records.upsert(hilary.getString("id"), hilary);    (10)
      
              cluster.disconnect();
      
          }
      
          private static JsonObject retrieveStudent(Cluster cluster, String name) throws CouchbaseException {
      
              QueryOptions studentQueryOptions = QueryOptions.queryOptions();
              studentQueryOptions.parameters(JsonObject.create().put("name", name));
              studentQueryOptions.scanConsistency(QueryScanConsistency.REQUEST_PLUS);
      
              final QueryResult result = cluster.query("select META().id, src.* " +
                              "from `student-bucket`.`art-school-scope`.`student-record-collection` src " +
                              "where src.`name` = $name", studentQueryOptions);
      
              return result.rowsAsObject().get(0);
      
          }
      
          private static JsonObject retrieveCourse(Cluster cluster, String course) throws CouchbaseException {
      
              QueryOptions courseQueryOptions = QueryOptions.queryOptions();
              courseQueryOptions.parameters(JsonObject.create().put("courseName", course));
              courseQueryOptions.scanConsistency(QueryScanConsistency.REQUEST_PLUS);
      
              final QueryResult result = cluster.query("select META().id, crc.* " +
                              "from `student-bucket`.`art-school-scope`.`course-record-collection` crc " +
                              "where crc.`course-name` = $courseName", courseQueryOptions);
      
              return result.rowsAsObject().get(0);
      
          }
      
      }
      1 As you’ll remember from our first example, the application first has to connect to the Couchbase cluster.
      2 Then it picks up the correct Bucket from the cluster.
      3 Do you remember why we need this waitUntilReady function is needed when connecting to the server?

      Answer

      As discussed earlier in Couchbase Tutorial: A Student Record System, Couchbase has been designed from the ground up to be scalable and performant, and an important part of this design is the architecture behind the APIs. Most of the APIs are non-blocking, which means that once you call them, your program is free to carry on and do other things while the called function executes.

      4 Here again, we pick up the student collection from the art-school-scope; you’ll need it later to change Hilary’s record and write it back to the collection.
      5 There are three method calls here:
      1. Retrieve Hilary’s student record.

      2. Retrieve the graphic design course record.

      3. Retrieve the art history course record.

      Each method uses a SQL++ call to retrieve a single record from its respective collection.

      This is just a demonstration, so there is no error checking to ensure that an item from the collection has been returned. In a live system, checks would have to be made to prevent possible errors while the program is running.

      6 Remember that Couchbase doesn’t have a native date type, so it’s common practice to store the dates as strings.
      7 The enrollments inside the student record are stored as an array; since JSON is the native data format for Couchbase, it’s not surprising that each SDK has a host of functions for processing data in JSON. In this case, the call JsonArray.create() will create an empty list structure.
      8 Now JSON elements are added to the enrollments array. Looking back at the data structure for enrollments, we need store just two items: the course that the enrollment relates to, and the date the student enrolled.
      enrollments.add(JsonObject.create().put("course-id", graphic_design.getString("id"))
          .put("date-enrolled", currentDate));

      The course-id uses the id of the course record retrieved from the database. We could, of course, store the whole record in this field. Why wouldn’t we want to do that?

      Answer

      This goes back to our relational model, and the idea of normalising the data model so that we don’t have repeating data items all over the place. Here, you’re just storing a reference to the course, not the course record itself. So if the course details change (such as the number of credit points assigned to it), then you don’t have to search through every single record that includes its own version.

      9 The array of enrollments is built, so now you add them to Hilary’s record.

      This is where the document database model shines; there is no need to change a database schema and rebuild the database. Just add the data and you’re done.

      10 Finally, the changes need to be committed to the collection. For this, use the upsert function, which takes the key of the record you wish to insert or update and the record itself as parameters. If they key exists in the collection then the record is updated. If the key does not exist then it’s a fresh document, so the item is inserted.

      As always, use maven to run the program.

      mvn exec:java -Dexec.mainClass="AddEnrollments" -Dexec.cleanupDaemonThreads=false

      And check your results in administrator console.

      Great Job!

      You’ve reached the end of the beginners' tutorial. You can explore the documentation site to learn more about Couchbase.