From d47c41ff14ac5c0229c61b18f79bc3a5c5fea533 Mon Sep 17 00:00:00 2001 From: Daniel Gerhardt <code@dgerhardt.net> Date: Tue, 4 Jul 2017 18:27:09 +0200 Subject: [PATCH] Add CouchDB design docs as JavaScript Functions need to be stringified to create valid JSON which is accepted by CouchDB. --- src/main/resources/couchdb/answer.design.js | 73 ++++++++++++++++++ src/main/resources/couchdb/comment.design.js | 45 +++++++++++ src/main/resources/couchdb/content.design.js | 31 ++++++++ .../couchdb/lerning_progress.design.js | 55 +++++++++++++ .../resources/couchdb/logged_in.design.js | 27 +++++++ src/main/resources/couchdb/motd.design.js | 27 +++++++ src/main/resources/couchdb/motdlist.design.js | 13 ++++ src/main/resources/couchdb/session.design.js | 52 +++++++++++++ .../resources/couchdb/statistics.design.js | 77 +++++++++++++++++++ src/main/resources/couchdb/user.design.js | 20 +++++ 10 files changed, 420 insertions(+) create mode 100644 src/main/resources/couchdb/answer.design.js create mode 100644 src/main/resources/couchdb/comment.design.js create mode 100644 src/main/resources/couchdb/content.design.js create mode 100644 src/main/resources/couchdb/lerning_progress.design.js create mode 100644 src/main/resources/couchdb/logged_in.design.js create mode 100644 src/main/resources/couchdb/motd.design.js create mode 100644 src/main/resources/couchdb/motdlist.design.js create mode 100644 src/main/resources/couchdb/session.design.js create mode 100644 src/main/resources/couchdb/statistics.design.js create mode 100644 src/main/resources/couchdb/user.design.js diff --git a/src/main/resources/couchdb/answer.design.js b/src/main/resources/couchdb/answer.design.js new file mode 100644 index 00000000..a79ac0a4 --- /dev/null +++ b/src/main/resources/couchdb/answer.design.js @@ -0,0 +1,73 @@ +var designDoc = { + "_id": "_design/answer", + "language": "javascript", + "views": { + "doc_by_questionid_user_piround": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.questionId, doc.user, doc.piRound], doc); + } + } + }, + "doc_by_questionid_timestamp": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.questionId, doc.timestamp], doc); + } + } + }, + "doc_by_user_sessionid": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.user, doc.sessionId], doc); + } + } + }, + "by_questionid": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit(doc.questionId, null); + } + } + }, + "by_questionid_piround_text_subject": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.questionId, doc.piRound, doc.abstention, doc.answerText, doc.answerSubject, doc.successfulFreeTextAnswer], null); + } + }, + "reduce": "_count" + }, + "by_sessionid": { + /* Redundant view but kept for now to allow simpler queries. */ + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit(doc.sessionId, null); + } + }, + "reduce": "_count" + }, + "by_sessionid_variant": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.sessionId, doc.questionVariant], null); + } + }, + "reduce": "_count" + }, + "questionid_by_user_sessionid_variant": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.user, doc.sessionId, doc.questionVariant], doc.questionId); + } + } + }, + "questionid_piround_by_user_sessionid_variant": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit([doc.user, doc.sessionId, doc.questionVariant], [doc.questionId, doc.piRound]); + } + } + } + } +}; diff --git a/src/main/resources/couchdb/comment.design.js b/src/main/resources/couchdb/comment.design.js new file mode 100644 index 00000000..23936b94 --- /dev/null +++ b/src/main/resources/couchdb/comment.design.js @@ -0,0 +1,45 @@ +var designDoc = { + "_id": "_design/comment", + "language": "javascript", + "views": { + "doc_by_sessionid_creator_timestamp": { + "map": function (doc) { + if (doc.type === "interposed_question") { + emit([doc.sessionId, doc.creator, doc.timestamp], doc); + } + } + }, + "doc_by_sessionid_timestamp": { + "map": function (doc) { + if (doc.type === "interposed_question") { + emit([doc.sessionId, doc.timestamp], doc); + } + } + }, + "by_sessionid": { + /* Redundant view but kept for now to allow simpler queries. */ + "map": function (doc) { + if (doc.type === "interposed_question") { + emit(doc.sessionId, null); + } + }, + "reduce": "_count" + }, + "by_sessionid_read": { + "map": function (doc) { + if (doc.type === "interposed_question") { + emit([doc.sessionId, doc.read], null); + } + }, + "reduce": "_count" + }, + "by_sessionid_creator_read": { + "map": function (doc) { + if (doc.type === "interposed_question") { + emit([doc.sessionId, doc.creator, doc.read], null); + } + }, + "reduce": "_count" + } + } +}; diff --git a/src/main/resources/couchdb/content.design.js b/src/main/resources/couchdb/content.design.js new file mode 100644 index 00000000..74518a47 --- /dev/null +++ b/src/main/resources/couchdb/content.design.js @@ -0,0 +1,31 @@ +var designDoc = { + "_id": "_design/content", + "language": "javascript", + "views": { + "doc_by_sessionid_variant_active": { + "map": function (doc) { + if (doc.type === "skill_question") { + emit([doc.sessionId, doc.questionVariant, doc.active, doc.subject, doc.text.substr(0, 16)], doc); + } + }, + "reduce": "_count" + }, + "by_sessionid": { + /* Redundant view but kept for now to allow simpler queries. */ + "map": function (doc) { + if (doc.type === "skill_question") { + emit(doc.sessionId, null); + } + }, + "reduce": "_count" + }, + "by_sessionid_variant_active": { + "map": function (doc) { + if (doc.type === "skill_question") { + emit([doc.sessionId, doc.questionVariant, doc.active, doc.subject, doc.text.substr(0, 16)], null); + } + }, + "reduce": "_count" + } + } +}; diff --git a/src/main/resources/couchdb/lerning_progress.design.js b/src/main/resources/couchdb/lerning_progress.design.js new file mode 100644 index 00000000..2b35eec8 --- /dev/null +++ b/src/main/resources/couchdb/lerning_progress.design.js @@ -0,0 +1,55 @@ +var designDoc = { + "_id": "_design/learning_progress", + "language": "javascript", + "views": { + "question_value_achieved_for_user": { + "comment": "This view returns the points users scored for answered questions.", + "map": function (doc) { + if (doc.type === "skill_question_answer" && !doc.abstention) { + /* The 'questionValue' contains the points scored with this answer, + * and this could be negative if a wrong answer was given. + * However, we do not want negative values, so we set the lower bound to 0.*/ + score = Math.max(doc.questionValue || 0, 0); + emit([doc.sessionId, doc.user], { + questionId: doc.questionId, + score: score, piRound: doc.piRound + }); + } + } + }, + "maximum_value_of_question": { + "comment": "This view returns the maximum number that can be achieved when answering this question.", + "map": function (doc) { + /* The question's value is determined by the maximum of all possibleAnswer values. + * We assume that a correct answer is assigned a positive value, + * while a negative value suggests a wrong answer. + * The goal then is to get the highest possible value. + * This leaves us with two cases: + * 1) On any single choice question, the value is the maximum of all possibleAnswer values. + * 2) On a multiple choice question, we add up all positive values. */ + var value = 0, answers = [], positiveAnswers = [], score = 0; + if (doc.type === "skill_question" && ["school", "flashcard"].indexOf(doc.questionType) === -1) { + if ("freetext" === doc.questionType && !doc.fixedAnswer) { return; } + answers = doc.possibleAnswers.map(function(answer) { return answer.value || 0; }); + /* find the maximum value */ + if (doc.fixedAnswer) { value = doc.rating; } + else { value = Math.max.apply(null, [0].concat(answers)); } + /* ignore likert ('vote') questions without any points */ + if (doc.questionType === "vote" && value === 0) { return; } + /* special case for mc and grid questions: add up all positive answers. */ + if (["grid", "mc"].indexOf(doc.questionType) !== -1) { + positiveAnswers = answers.filter(function(val) { return val >= 0; }); + if (positiveAnswers.length > 0) { + value = positiveAnswers.reduce(function(prev, cur) { return prev + cur; }, 0); + } + } + emit([doc.sessionId, doc._id], { + value: value, + questionVariant: doc.questionVariant, + piRound: doc.piRound + }); + } + } + } + } +}; diff --git a/src/main/resources/couchdb/logged_in.design.js b/src/main/resources/couchdb/logged_in.design.js new file mode 100644 index 00000000..1b196b4f --- /dev/null +++ b/src/main/resources/couchdb/logged_in.design.js @@ -0,0 +1,27 @@ +var designDoc = { + "_id": "_design/logged_in", + "language": "javascript", + "views": { + "visited_sessions_by_user": { + "map": function (doc) { + if (doc.type === "logged_in") { + emit(doc.user, doc.visitedSessions); + } + } + }, + "all": { + "map": function (doc) { + if (doc.type === "logged_in"){ + emit(doc.user, doc); + } + } + }, + "by_last_activity_for_guests": { + "map": function (doc) { + if (doc.type === "logged_in" && doc.user.indexOf("Guest") === 0) { + emit(doc.timestamp || 0, {_rev: doc._rev}); + } + } + } + } +}; diff --git a/src/main/resources/couchdb/motd.design.js b/src/main/resources/couchdb/motd.design.js new file mode 100644 index 00000000..33b111ee --- /dev/null +++ b/src/main/resources/couchdb/motd.design.js @@ -0,0 +1,27 @@ +var designDoc = { + "_id": "_design/motd", + "language": "javascript", + "views": { + "doc_by_sessionkey": { + "map": function (doc) { + if (doc.type === "motd" && doc.audience === "session") { + emit(doc.sessionkey, doc); + } + } + }, + "doc_by_audience_for_global": { + "map": function (doc) { + if (doc.type === "motd" && doc.audience !== "session") { + emit(doc.audience, doc); + } + } + }, + "by_motdkey": { + "map": function (doc) { + if (doc.type === "motd") { + emit(doc.motdkey, doc); + } + } + } + } +}; diff --git a/src/main/resources/couchdb/motdlist.design.js b/src/main/resources/couchdb/motdlist.design.js new file mode 100644 index 00000000..37712e4e --- /dev/null +++ b/src/main/resources/couchdb/motdlist.design.js @@ -0,0 +1,13 @@ +var designDoc = { + "_id": "_design/motdlist", + "language": "javascript", + "views": { + "doc_by_username": { + "map": function (doc) { + if (doc.type === "motdlist") { + emit(doc.username, doc); + } + } + } + } +}; diff --git a/src/main/resources/couchdb/session.design.js b/src/main/resources/couchdb/session.design.js new file mode 100644 index 00000000..b8b1346b --- /dev/null +++ b/src/main/resources/couchdb/session.design.js @@ -0,0 +1,52 @@ +var designDoc = { + "_id": "_design/session", + "language": "javascript", + "views": { + "by_courseid": { + "map": function (doc) { + if (doc.type === "session" && doc.courseId && doc.sessionType !== "public_pool") { + emit(doc.courseId, null); + } + } + }, + "by_keyword": { + "map": function (doc) { + if (doc.type === "session") { + emit(doc.keyword, null); + } + } + }, + "partial_by_sessiontype_creator_name": { + "map": function (doc) { + if (doc.type === "session") { + emit([doc.sessionType, doc.creator, doc.name], { + shortName: doc.shortName, + keyword: doc.keyword, + active: doc.active, + courseType: doc.courseType, + creationTime: doc.creationTime + }); + } + } + }, + "partial_by_ppsubject_name_for_publicpool": { + "map": function (doc) { + if (doc.type === "session" && doc.sessionType === "public_pool") { + emit([doc.ppSubject, doc.name], { + ppSubject: doc.ppSubject, + name: doc.name, + keyword: doc.keyword, + ppLevel: doc.ppLevel + }); + } + } + }, + "by_lastactivity_for_guests": { + "map": function (doc) { + if (doc.type === "session" && doc.sessionType !== "public_pool" && doc.creator.indexOf("Guest") === 0) { + emit(doc.lastOwnerActivity || doc.creationTime, {_rev: doc._rev}); + } + } + } + } +}; diff --git a/src/main/resources/couchdb/statistics.design.js b/src/main/resources/couchdb/statistics.design.js new file mode 100644 index 00000000..c9004fd5 --- /dev/null +++ b/src/main/resources/couchdb/statistics.design.js @@ -0,0 +1,77 @@ +var designDoc = { + "_id": "_design/statistics", + "language": "javascript", + "views": { + "active_student_users": { + "map": function (doc) { + if (doc.type === "skill_question_answer") { + emit(doc.user, 1); + } + }, + "reduce": "_count" + }, + "statistics": { + "map": function (doc) { + switch (doc.type) { + case "session": + if (doc.active) { + emit("openSessions", 1); + } else { + emit("closedSessions", 1); + } + break; + case "skill_question": + if (doc.questionType === "flashcard") { + emit("flashcards", 1); + } else { + if (doc.questionVariant === "lecture") { + emit("lectureQuestions", 1); + } else if (doc.questionVariant === "preparation") { + emit("preparationQuestions", 1); + } + if (doc.piRound === 2) { + emit("conceptQuestions", 1); + } + } + break; + case "skill_question_answer": + emit("answers", 1); + break; + case "interposed_question": + emit ("interposedQuestions", 1); + break; + case "log": + if (doc.event === "delete") { + switch (doc.payload.type) { + case "session": + emit("deletedSessions", doc.payload.sessionCount || 1); + break; + case "question": + emit("deletedQuestions", doc.payload.questionCount || 1); + break; + case "answer": + emit("deletedAnswers", doc.payload.answerCount || 1); + break; + case "comment": + emit("deletedComments", doc.payload.commentCount || 1); + break; + case "user": + emit("deletedUsers", 1); + break; + } + } + break; + } + }, + "reduce": "_sum" + }, + "unique_session_creators": { + "map": function (doc) { + if (doc.type === "session") { + emit(doc.creator, 1); + } + }, + "reduce": "_count" + } + } +}; diff --git a/src/main/resources/couchdb/user.design.js b/src/main/resources/couchdb/user.design.js new file mode 100644 index 00000000..9299516f --- /dev/null +++ b/src/main/resources/couchdb/user.design.js @@ -0,0 +1,20 @@ +var designDoc = { + "_id": "_design/user", + "language": "javascript", + "views": { + "doc_by_username": { + "map": function (doc) { + if (doc.type === "userdetails") { + emit(doc.username, doc); + } + } + }, + "by_creation_for_inactive": { + "map": function (doc) { + if (doc.type === "userdetails" && doc.activationKey) { + emit(doc.creation, {_rev: doc._rev}); + } + } + } + } +}; -- GitLab