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:
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?
AnswerAs 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:
Each method uses a SQL++ call to retrieve a single record from its respective collection.
|
||
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.
The AnswerThis 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.