Additional Features
In this section we ll continue the Stack Overflow example and implement up down votes on Questions We ll explore enumerations parameterized properties parameterized projections and aggregations Enumerations We ll start with an enumeration to indicate the direction of a vote The upper case literal names are used within the model The quoted strings are pretty names that will appear in service request and response bodies Partial inference composite keys Next we ll define the vote class Stack Overflow restricts voting based on how many times you ve voted and whether the post was edited since the last vote So votes must be Votes must be to keep track of who cast the vote A user can only cast one vote so the key should be questionId createdById There is no auto generated property has a natural composite key Normally we d allow the property to be inferred based on the keyword but then it would be inferred without the modifier A user cannot upvote and downvote the same question so is not part of the key Associations A vote is like many to many mapping from Question to User Services We ll create an upsert service for creating and editing votes No create service is necessary because there s no auto incrementing id The main criteria uses the global which is the name of the currently logged in principal The validation criteria uses the global less 5 minutes in milliseconds to check whether the vote was created within the last 5 minutes The DELETE service is very similar It just doesn t need to validate that the vote exists and it doesn t need to validate that the voter and the question author are different users Parameterized properties When viewing a question on Stack Overflow you can see your own vote if you ve cast one We add as a parameterized property though there are no parameters in this example This criteria is another example using the global Once the parameterized property is defined it can be included in a projection using the same syntax as an association end In fact a parameterized property without any parameters can be thought of as a uni directional association Parameterized projections The definition of above is fine and fairly similar to the and properties in the real StackOverflow api However let s add some perhaps unrealistic variants that take parameters just to see what parameterized properties can do Parameterized properties can be included in read projections There are two ways to provide the parameter as a constant or as a projection parameter Here we define a projection that parameterizes twice once for each Here we define a parameterized projection that passes its parameter through to Parameterized service When using a parameterized projection the service must also be parameterized Parameterized responses When a json response includes parameterized properties the parameters become part of the field names Aggregations StackOverflow s documentation states Voting scores as displayed are the sum of the up and down votes on a post Vote counts are the individual up and down votes that make up the score You can view the vote counts by clicking on the score of a post This will break the score into upvotes and downvotes We add two aggregation properties to Question and Associations and parameterized properties are defined using criteria Aggregation properties are defined using a fuller expression language These aggregations use the function Other available functions include and Once these aggregation properties are defined in a class they can be included in read projections like normal properties They cannot be included in write projections enumeration VoteDirection UP up DOWN down class QuestionVote systemTemporal audited questionId Long key createdById String key createdBy direction VoteDirection systemTemporal audited id QuestionVote createdById audited key direction association QuestionHasVotes question Question 1 1 final votes QuestionVote 0 * relationship this id QuestionVote questionId association UserHasQuestionVotes user User 1 1 final votes QuestionVote 0 * relationship this userId QuestionVote createdById service QuestionVoteResource upsert upsertQuestionVote questionId Long 1 1 path user User 1 1 user questionVote QuestionVote 1 body service http url question vote questionId Long 1 1 criteria this question id questionId && this createdById user userId after this question createdById user userId && this createdOn > now 5 * 60 * 1000 || this systemFrom < this question systemFrom upsert upsertQuestionVote questionVote QuestionVote 1 body user User 1 1 user service rpc url question vote criteria this question id questionVote question id && this createdById user userId after this question createdById user userId && this createdOn > now 5 * 60 * 1000 || this systemFrom < this question systemFrom question vote questionId Long 1 1 PUT multiplicity one criteria this questionId questionId && this createdById userId validate this question createdById userId && this null || this createdOn > now 5 * 60 * 1000 || this systemFrom < this question systemFrom projection QuestionVoteWriteProjection userId now service QuestionVote question vote questionId Long 1 1 PUT DELETE multiplicity one criteria this questionId questionId && this createdById userId validate this createdOn > now 5 * 60 * 1000 || this systemFrom < this question systemFrom projection QuestionVoteWriteProjection class Question systemTemporal versioned audited myVote QuestionVote 0 1 this votes createdById userId myVote userId myVote upvoted downvoted class Question systemTemporal versioned audited votesByDirection direction VoteDirection 1 1 QuestionVote 0 * this votes direction direction voteByUser userId String 1 1 userId QuestionVote 0 1 userId refers to the parameter not the global this votes createdById userId votesByDirection VoteDirection projection ProjectionWithConstant on Question id Question id title Question title body Question body votesByDirection up This would work but the information is redundant direction Upvote direction user userId Upvote user id votesByDirection down This would work but the information is redundant direction Upvote direction user userId Upvote user id voteByUser projection ProjectionWithParameter userId String 1 1 userId on Question id Question id title Question title body Question body voteByUser userId direction Vote direction This would work but the information is redundant * user userId Vote user id * service Question question id Long 1 1 userId String 1 1 userId GET format json multiplicity one criteria this id id projection ProjectionWithParameter userId upvotes downvotes class Question systemTemporal versioned audited upvotes Integer count this votes direction VoteDirection UP downvotes Integer count this votes direction VoteDirection DOWN count sum min max average