diff --git a/src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java index d630547..b4ea6d2 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java @@ -1,388 +1,422 @@ package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.Index; import org.neo4j.ogm.annotation.NodeEntity; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; import org.neo4j.ogm.annotation.Relationship; import org.neo4j.ogm.annotation.typeconversion.DateLong; //import org.springframework.data.annotation.Transient; import java.util.Date; import java.util.HashSet; import java.util.Set; /** * This class handles the data of the Revision of a page. * Created by valsdav on 14/03/17. */ @JsonIgnoreProperties(ignoreUnknown = true) @NodeEntity( label = "Revision" ) public class Revision { @GraphId @JsonIgnore private Long graphId; @Index(unique = true, primary=true) @JsonProperty(access = Access.WRITE_ONLY) private String langRevId; @JsonProperty("revid") private int revId; @JsonProperty(access = Access.WRITE_ONLY) private String lang; @JsonProperty(value = "userid", access = Access.WRITE_ONLY) private int userId; @JsonProperty(value = "parentid", access = Access.WRITE_ONLY) private int parentId; @DateLong @JsonProperty(access = Access.WRITE_ONLY) private Date timestamp; @JsonProperty(value = "size", access = Access.WRITE_ONLY) private long length; @JsonProperty(access = Access.WRITE_ONLY) private double changeCoefficient; private double currentMeanVote; private double currentVotesReliability; private double totalMeanVote; private double totalVotesReliability; private double normalizedNumberOfVotes; private double currentScore; private double totalScore; @Relationship(type="PREVIOUS_REVISION", direction = Relationship.OUTGOING) private Revision previousRevision; @Relationship(type="PREVIOUS_REVISION", direction = Relationship.INCOMING) private Revision nextRevision; @Relationship(type="VOTE", direction = Relationship.INCOMING) private Set votes; //This relationship represents the connection between the revision //and its creator (user). It saves his reliability at the moment of the author. @Relationship(type="AUTHOR", direction = Relationship.INCOMING) private Author author; /** * */ public Revision() {} /** * @param revId * @param userId * @param parentId * @param length * @param lang * @param timestamp */ public Revision(int revId, String lang, int userId, int parentId, long length, Date timestamp) { this.revId = revId; this.langRevId = lang +"_"+revId; this.userId = userId; this.parentId = parentId; this.length = length; this.lang = lang; this.timestamp = timestamp; this.changeCoefficient = 0.0; } /** * @return the revId */ public int getRevId() { return revId; } /** * @param revId the revId to set */ public void setRevId(int revId) { this.revId = revId; } /** * @return the userId */ public int getUserId() { return userId; } /** * @param userId the userId to set */ public void setUserId(int userId) { this.userId = userId; } /** * @return the parentId */ public int getParentId() { return parentId; } /** * @param parentId the parentId to set */ public void setParentId(int parentId) { this.parentId = parentId; } /** * @return the length */ public long getLength() { return length; } /** * @param length the length to set */ public void setLength(long length) { this.length = length; } /** * @return the changeCoefficient */ public double getChangeCoefficient() { return changeCoefficient; } /** * @param changeCoefficient the changeCoefficient to set */ public void setChangeCoefficient(double changeCoefficient) { this.changeCoefficient = changeCoefficient; } /** * @return the currentMeanVote */ public double getCurrentMeanVote() { return currentMeanVote; } /** * @param currentMeanVote the currentMeanVote to set */ public void setCurrentMeanVote(double currentMeanVote) { this.currentMeanVote = currentMeanVote; } /** * @return the currentVotesReliability */ public double getCurrentVotesReliability() { return currentVotesReliability; } /** * @param currentVotesReliability the currentVotesReliability to set */ public void setCurrentVotesReliability(double currentVotesReliability) { this.currentVotesReliability = currentVotesReliability; } /** * @return the totalMeanVote */ public double getTotalMeanVote() { return totalMeanVote; } /** * @param totalMeanVote the totalMeanVote to set */ public void setTotalMeanVote(double totalMeanVote) { this.totalMeanVote = totalMeanVote; } /** * @return the totalVotesReliability */ public double getTotalVotesReliability() { return totalVotesReliability; } /** * @param totalVotesReliability the totalVotesReliability to set */ public void setTotalVotesReliability(double totalVotesReliability) { this.totalVotesReliability = totalVotesReliability; } public double getCurrentScore() { return currentScore; } public void setCurrentScore(double currentScore) { this.currentScore = currentScore; } public double getTotalScore() { return totalScore; } public void setTotalScore(double totalScore) { this.totalScore = totalScore; } /** * @return the lang */ public String getLang() { return lang; } /** * @param lang the lang to set */ public void setLang(String lang) { this.lang = lang; } /** * @return the langRevId */ public String getLangRevId() { return langRevId; } /** * @param langRevId the langRevId to set */ public void setLangRevId(String langRevId) { this.langRevId = langRevId; } /** * @return the graphId */ public Long getGraphId() { return graphId; } /** * @param graphId the graphId to set */ public void setGraphId(Long graphId) { this.graphId = graphId; } /** * @return the timestamp */ public Date getTimestamp() { return timestamp; } /** * @param timestamp the timestamp to set */ public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } /** * @return the previousRevision */ public Revision getPreviousRevision() { return previousRevision; } /** * @param previousRevision the previousRevision to set */ public void setPreviousRevision(Revision previousRevision) { this.previousRevision = previousRevision; } public boolean hasPreviousRevision(){ return this.nextRevision != null; } public Revision getNextRevision() { return nextRevision; } public void setNextRevision(Revision nextRevision) { this.nextRevision = nextRevision; } public boolean hasNextRevision(){ return this.nextRevision != null; } /** * @return the votes */ public Set getVotes() { if (votes == null){ return new HashSet<>(); } return votes; } /** * @param votes the votes to set */ public void setVotes(Set votes) { this.votes = votes; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } public double getNormalizedNumberOfVotes() { return normalizedNumberOfVotes; } public void setNormalizedNumberOfVotes(double normalizedNumberOfVotes) { this.normalizedNumberOfVotes = normalizedNumberOfVotes; } public int getCurrentNumberOfVotes() { if (votes != null) { return this.votes.size(); }else{ return 0; } } /** * * @param vote the vote to set */ public void addVote(Vote vote){ this.votes.add(vote); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { String s= "Revision{" + "langRevId=" + langRevId + ", userId=" + userId + ", timestamp=" + timestamp + ", length=" + length ; if (author!=null){ s+= ", author=" + author.getUser().getUserId() + '}'; }else{ s += '}'; } return s; } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((langRevId == null) ? 0 : langRevId.hashCode()); + return result; + } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Revision)) { + return false; + } + Revision other = (Revision) obj; + if (langRevId == null) { + if (other.langRevId != null) { + return false; + } + } else if (!langRevId.equals(other.langRevId)) { + return false; + } + return true; + } } \ No newline at end of file diff --git a/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java index 3be6742..5cb9bbd 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java @@ -1,90 +1,90 @@ /** * */ package org.wikitolearn.wikirating.repository; import java.util.List; import java.util.Set; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.repository.query.Param; import org.wikitolearn.wikirating.model.graph.Revision; import org.wikitolearn.wikirating.model.graph.queryresult.RevisionResult; /** * @author aletundo * */ public interface RevisionRepository extends GraphRepository { /** * Get a revision given its langRevId * @param langRevId the langRevId of the page * @return the revision */ Revision findByLangRevId(String langRevId); /** * Get all the revisions of the requested domain language * @param lang the language of the revisions * @return a set of revisions */ Set findByLang(String lang); /** * Get all the revisions authored by a specific user * @param userId the user id * @return a set of revisions */ Set findByUserId(int userId); /** * Get all the revisions of a Page (CourseLevelThree). * The direction of the link is important to traverse * only the chain of the page revisions without reaching other nodes. * @param langPageId the page langPageId * @return a set of revisions */ @Query("MATCH (p:CourseLevelThree {langPageId:{langPageId}})-[:LAST_REVISION|PREVIOUS_REVISION*]->(r:Revision) RETURN r") Set findAllRevisionOfPage(@Param("langPageId") String langPageId); /** * Get all the revisions of a Page ordered by revision id * @param langPageId the page langPageId * @return a list of revisions */ @Query("MATCH (p:CourseLevelThree {langPageId:{langPageId}})-[:LAST_REVISION|PREVIOUS_REVISION*]->(r:Revision) RETURN r ORDER BY r.revId") List findAllRevisionOfPageOrdered(@Param("langPageId") String langPageId); /** * Get all the revisions after the LAST_VALIDATED_REVISION. * The revisions are returned ordered by revision id * @param langPageId the page langPageId * @return a revision result with its related nodes and relationships partially hydrated */ @Query("MATCH (page:Page {langPageId:{langPageId}})-[:LAST_VALIDATED_REVISION]->(rev:Revision) " + "WITH rev MATCH (prev:Revision)<-[pr:PREVIOUS_REVISION]-(rev)<-[:PREVIOUS_REVISION*]-(nextr:Revision) " + "WITH rev, prev,pr, nextr " + "MATCH p=(nextr)-[r*0..1]-() " + "RETURN rev as revision,prev,pr, nodes(p) as nodes, relationships(p) as rels ") RevisionResult getNotValidatedRevisionsChain(@Param("langPageId") String langPageId); /** * Get the last validated revision and map its related nodes and relationships too * @param langPageId the page langPageId * @return a revision result with its related nodes and relationships partially hydrated */ @Query("MATCH (Page {langPageId:{langPageId}})-[:LAST_VALIDATED_REVISION]->(rev:Revision) " + "WITH rev MATCH p=(rev)-[a*0..1]-() " + "RETURN rev as revision, nodes(p), relationships(p) ") RevisionResult findLastValidatedRevision(@Param("langPageId") String langPageId); /** * Get the previous revision of a specific revision given its langRevId * @param langRevId the revision langRevId * @return the previous revision */ - @Query("MATCH (r:Revision {langRevId:{langRevId})-[:PREVIOUS_REVISION]->(a:Revision) RETURN a") + @Query("MATCH (r:Revision {langRevId:{langRevId}})-[:PREVIOUS_REVISION]->(a:Revision) RETURN a") Revision findPreviousRevision(@Param("langRevId") String langRevId); } diff --git a/src/test/java/org/wikitolearn/wikirating/repository/RevisionRepositoryTest.java b/src/test/java/org/wikitolearn/wikirating/repository/RevisionRepositoryTest.java new file mode 100644 index 0000000..49cb95c --- /dev/null +++ b/src/test/java/org/wikitolearn/wikirating/repository/RevisionRepositoryTest.java @@ -0,0 +1,129 @@ +/** + * + */ +package org.wikitolearn.wikirating.repository; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.neo4j.ogm.session.Session; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; +import org.wikitolearn.wikirating.model.graph.Author; +import org.wikitolearn.wikirating.model.graph.CourseLevelThree; +import org.wikitolearn.wikirating.model.graph.Revision; +import org.wikitolearn.wikirating.model.graph.User; +import org.wikitolearn.wikirating.model.graph.queryresult.RevisionResult; + +/** + * @author aletundo + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@Transactional +public class RevisionRepositoryTest { + + @Autowired + private Session session; + + @Autowired + private RevisionRepository revisionRepository; + + @Autowired + private CourseLevelThreeRepository courseLevelThreeRepository; + + @Before + public void setup() { + CourseLevelThree page = new CourseLevelThree(1, "Title1", "en", "en_1"); + Revision r1 = new Revision(2, "en", 1, 1, 123456, new Date()); + Revision r2 = new Revision(3, "en", 2, 1, 123456, new Date()); + Revision r3 = new Revision(4, "en", 3, 1, 123456, new Date()); + Revision r4 = new Revision(5, "en", 4, 1, 123456, new Date()); + Revision r5 = new Revision(6, "en", 5, 1, 123456, new Date()); + // Used to check if the revision is hydrated when a RevisionResult is returned + User u1 = new User("User", 1, 0.0, 0.0, 0.0); + Author a = new Author(0.4); + a.setRevision(r1); + a.setUser(u1); + r1.setAuthor(a); + + page.setFirstRevision(r1); + page.setLastRevision(r5); + page.setLastValidatedRevision(r2); + r5.setPreviousRevision(r4); + r4.setPreviousRevision(r3); + r3.setPreviousRevision(r2); + r2.setPreviousRevision(r1); + + // Related nodes are automatically persisted + courseLevelThreeRepository.save(page); + } + + @After + public void teardown() { + session.purgeDatabase(); + } + + @Test + public void findAllRevisionOfPageTest(){ + Set result = revisionRepository.findAllRevisionOfPage("en_1"); + assertEquals(5, result.size()); + } + + @Test + public void findAllRevisionOfPageOrderedTest(){ + List result = revisionRepository.findAllRevisionOfPageOrdered("en_1"); + assertEquals(2, result.get(0).getRevId()); + assertEquals(3, result.get(1).getRevId()); + assertEquals(4, result.get(2).getRevId()); + assertEquals(5, result.get(3).getRevId()); + assertEquals(6, result.get(4).getRevId()); + } + + @Test + public void getNotValidatedRevisionsChainTest(){ + RevisionResult result = revisionRepository.getNotValidatedRevisionsChain("en_1"); + assertTrue("en_3".equals(result.revision.getLangRevId())); + + // Check existing related nodes traversing :PREVIOUS_REVISION forward and backward + assertNotNull(result.revision.getPreviousRevision()); + assertNotNull(result.revision.getPreviousRevision().getAuthor()); + assertEquals(1, result.revision.getPreviousRevision().getAuthor().getUser().getUserId()); + assertTrue("en_2".equals(result.revision.getPreviousRevision().getLangRevId())); + assertNotNull(result.revision.getNextRevision()); + assertTrue("en_4".equals(result.revision.getNextRevision().getLangRevId())); + assertNotNull(result.revision.getNextRevision().getNextRevision()); + assertTrue("en_5".equals(result.revision.getNextRevision().getNextRevision().getLangRevId())); + assertNotNull(result.revision.getNextRevision().getNextRevision().getNextRevision()); + assertTrue("en_6".equals(result.revision.getNextRevision().getNextRevision().getNextRevision().getLangRevId())); + } + + @Test + public void findLastValidatedRevisionTest(){ + RevisionResult result = revisionRepository.findLastValidatedRevision("en_1"); + assertTrue("en_3".equals(result.revision.getLangRevId())); + // Check existing related nodes + assertNotNull(result.revision.getPreviousRevision()); + assertNotNull(result.revision.getPreviousRevision().getAuthor()); + assertEquals(1, result.revision.getPreviousRevision().getAuthor().getUser().getUserId()); + assertTrue("en_2".equals(result.revision.getPreviousRevision().getLangRevId())); + } + + @Test + public void findPreviousRevision(){ + Revision result = revisionRepository.findPreviousRevision("en_4"); + assertTrue("en_3".equals(result.getLangRevId())); + } +}