BAEL-203: Querying Couchbase with MapReduce Views

This commit is contained in:
Kevin Gilmore
2017-01-30 21:48:28 -06:00
parent 99e75746f3
commit e371bb9093
9 changed files with 492 additions and 0 deletions
@@ -0,0 +1,6 @@
package com.baeldung.couchbase.mapreduce;
public interface CouchbaseKeyGenerator<T> {
String generateKey(T t);
}
@@ -0,0 +1,10 @@
package com.baeldung.couchbase.mapreduce;
@SuppressWarnings("serial")
public class DuplicateKeyException extends Exception {
public DuplicateKeyException(String s) {
super(s);
}
}
@@ -0,0 +1,11 @@
package com.baeldung.couchbase.mapreduce;
import java.util.UUID;
public class RandomUUIDGenerator<T> implements CouchbaseKeyGenerator<T> {
@Override
public String generateKey(T t) {
return UUID.randomUUID().toString();
}
}
@@ -0,0 +1,50 @@
package com.baeldung.couchbase.mapreduce;
public class StudentGrade {
private String name;
private String course;
private Integer grade;
private Integer hours;
public StudentGrade() { }
public StudentGrade(String name, String course, Integer grade, Integer hours) {
this.name = name;
this.course = course;
this.grade = grade;
this.hours = hours;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public Integer getGrade() {
return grade;
}
public void setGrade(Integer grade) {
this.grade = grade;
}
public Integer getHours() {
return hours;
}
public void setHours(Integer hours) {
this.hours = hours;
}
}
@@ -0,0 +1,9 @@
package com.baeldung.couchbase.mapreduce;
public class StudentGradeKeyGenerator implements CouchbaseKeyGenerator<StudentGrade> {
@Override
public String generateKey(StudentGrade g) {
return g.getName() + ":" + g.getCourse();
}
}
@@ -0,0 +1,70 @@
package com.baeldung.couchbase.mapreduce;
import com.couchbase.client.deps.com.fasterxml.jackson.databind.ObjectMapper;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.view.ViewQuery;
public class StudentGradeQueryBuilder {
final ObjectMapper om = new ObjectMapper();
public ViewQuery findAll() {
return ViewQuery.from("studentGrades", "findByCourse");
}
public ViewQuery findByCourse(String course) {
return ViewQuery.from("studentGrades", "findByCourse")
.key(course);
}
public ViewQuery findByCourses(String... courses) {
return ViewQuery.from("studentGrades", "findByCourse")
.keys(JsonArray.from(courses));
}
public ViewQuery findByGradeInRange(int lower, int upper, boolean inclusiveEnd) {
return ViewQuery.from("studentGrades", "findByGrade")
.startKey(lower)
.endKey(upper)
.inclusiveEnd(inclusiveEnd);
}
public ViewQuery findByGradeLessThan(int upper) {
return ViewQuery.from("studentGrades", "findByGrade")
.endKey(upper)
.inclusiveEnd(false);
}
public ViewQuery findByGradeGreaterThan(int lower) {
return ViewQuery.from("studentGrades", "findByGrade")
.startKey(lower);
}
public ViewQuery findByCourseAndGradeInRange(String course, int minGrade, int maxGrade, boolean inclusiveEnd) {
return ViewQuery.from("studentGrades", "findByCourseAndGrade")
.startKey(JsonArray.from(course, minGrade))
.endKey(JsonArray.from(course, maxGrade))
.inclusiveEnd(inclusiveEnd);
}
public ViewQuery findTopGradesByCourse(String course, int limit) {
return ViewQuery.from("studentGrades", "findByCourseAndGrade")
.startKey(JsonArray.from(course, 100))
.endKey(JsonArray.from(course, 0))
.inclusiveEnd(true)
.descending()
.limit(limit);
}
public ViewQuery countStudentsByCourse() {
return ViewQuery.from("studentGrades", "countStudentsByCourse")
.reduce()
.groupLevel(1);
}
public ViewQuery sumCreditsByStudent() {
return ViewQuery.from("studentGrades", "sumCreditsByStudent")
.reduce()
.groupLevel(1);
}
}
@@ -0,0 +1,169 @@
package com.baeldung.couchbase.mapreduce;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.couchbase.client.deps.com.fasterxml.jackson.databind.ObjectMapper;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import com.couchbase.client.java.view.ViewRow;
public class StudentGradeService {
final CouchbaseKeyGenerator<StudentGrade> keyGenerator;
final CouchbaseCluster cluster;
final Bucket bucket;
final ObjectMapper om = new ObjectMapper();
final StudentGradeQueryBuilder queryBuilder;
public StudentGradeService(CouchbaseKeyGenerator<StudentGrade> keyGenerator) {
this.keyGenerator = keyGenerator;
this.queryBuilder = new StudentGradeQueryBuilder();
cluster = CouchbaseCluster.create("127.0.0.1");
bucket = cluster.openBucket("baeldung-tutorial");
}
public String insert(StudentGrade studentGrade) throws DuplicateKeyException {
String id = keyGenerator.generateKey(studentGrade);
if(bucket.exists(id)) {
throw new DuplicateKeyException("document already exists with key " + id);
}
JsonObject content = JsonObject.empty()
.put("type", "StudentGrade")
.put("name", studentGrade.getName())
.put("course", studentGrade.getCourse())
.put("grade", studentGrade.getGrade())
.put("hours", studentGrade.getHours());
JsonDocument doc = JsonDocument.create(id, content);
bucket.insert(doc);
return id;
}
public List<JsonDocument> findAll() {
ViewQuery query = queryBuilder.findAll();
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
private List<JsonDocument> extractDocuments(ViewResult result) {
List<JsonDocument> docs = new ArrayList<>();
for(ViewRow row : result.allRows()) {
JsonDocument doc = row.document();
docs.add(doc);
}
return docs;
}
public List<JsonDocument> findByCourse(String course) {
ViewQuery query = queryBuilder.findByCourse(course);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public List<JsonDocument> findByCourses(String... courses) {
ViewQuery query = queryBuilder.findByCourses(courses);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public List<JsonDocument> findByGradeInRange(int lower, int upper, boolean inclusiveEnd) {
ViewQuery query = queryBuilder.findByGradeInRange(lower, upper, inclusiveEnd);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public List<JsonDocument> findByGradeLessThan(int upper) {
ViewQuery query = queryBuilder.findByGradeLessThan(upper);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public List<JsonDocument> findByGradeGreaterThan(int lower) {
ViewQuery query = queryBuilder.findByGradeGreaterThan(lower);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public List<JsonDocument> findByCourseAndGradeInRange(String course, int minGrade, int maxGrade, boolean inclusiveEnd) {
ViewQuery query = queryBuilder.findByCourseAndGradeInRange(course, minGrade, maxGrade, inclusiveEnd);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public List<JsonDocument> findTopGradesByCourse(String course, int limit) {
ViewQuery query = queryBuilder.findTopGradesByCourse(course, limit);
ViewResult result = bucket.query(query);
return extractDocuments(result);
}
public Map<String, Long> countStudentsByCourse() {
ViewQuery query = ViewQuery.from("studentGrades", "countStudentsByCourse")
.reduce()
.groupLevel(1);
ViewResult result = bucket.query(query);
Map<String, Long> numStudentsByCourse = new HashMap<>();
for(ViewRow row : result.allRows()) {
JsonArray keyArray = (JsonArray) row.key();
String course = keyArray.getString(0);
long count = Long.valueOf(row.value().toString());
numStudentsByCourse.put(course, count);
}
return numStudentsByCourse;
}
public Map<String, Long> sumCreditHoursByStudent() {
ViewQuery query = ViewQuery.from("studentGrades", "sumHoursByStudent")
.reduce()
.groupLevel(1);
ViewResult result = bucket.query(query);
Map<String, Long> creditHoursByStudent = new HashMap<>();
for(ViewRow row : result.allRows()) {
String course = (String) row.key();
long sum = Long.valueOf(row.value().toString());
creditHoursByStudent.put(course, sum);
}
return creditHoursByStudent;
}
public Map<String, Long> sumGradePointsByStudent() {
ViewQuery query = ViewQuery.from("studentGrades", "sumGradePointsByStudent")
.reduce()
.groupLevel(1);
ViewResult result = bucket.query(query);
Map<String, Long> gradePointsByStudent = new HashMap<>();
for(ViewRow row : result.allRows()) {
String course = (String) row.key();
long sum = Long.valueOf(row.value().toString());
gradePointsByStudent.put(course, sum);
}
return gradePointsByStudent;
}
public Map<String, Float> calculateGpaByStudent() {
Map<String, Long> creditHoursByStudent = sumCreditHoursByStudent();
Map<String, Long> gradePointsByStudent = sumGradePointsByStudent();
Map<String, Float> result = new HashMap<>();
for(Entry<String, Long> creditHoursEntry : creditHoursByStudent.entrySet()) {
String name = creditHoursEntry.getKey();
long totalHours = creditHoursEntry.getValue();
long totalGradePoints = gradePointsByStudent.get(name);
result.put(name, ((float) totalGradePoints / totalHours));
}
return result;
}
}