Spring Data REST Neo4j create a relationship - neo4j

I'm building a little test app as a way to learn Angular and refresh myself on a lot of the Spring stack. I have some minor experience with Neo4J, but the app idea has ground with a graph db like Neo4j.
The idea is pretty simple, a ui to create characters and stories, and relate the characters to the stories and each other, map their individual versions of a story and create some graphs that show the character interactions to help write the overall narrative.
I've got nodes for the characters and stories easily enough and the Spring stack is great for giving me rest easy to use rest endpoints for the nodes themselves. But I can't find any concrete examples of creating and maintaining the relationships between those nodes.
For instance, in Cypher, I can relate a character to a story and tell that being's involvement to the story as a relationship property with:
match(p:Being ),(s:Story ) where id(p) = 7 and id(s) = 16
create (p)-[r:TOOK_PART_IN{perspective:"I did not know Mr. Grey better than an acquaintance, though I knew others whom did. They were not made better because of their relationship with him."}]->(s) return r
Then with the mapping in Spring, the data I get back from the REST endpoint gives me my character and I can follow a link to get the stories that character is a part of. I don't see a way though to post or put to add or remove the character from stories.
I'm also only finding concrete examples in docs from Spring regarding nodes, not really with edges/relationships. Can anyone supply anything like that?
I am fully aware that Neo4J has it's own REST interface, and that is basically what Spring is consuming as well. The main purpose of this exercise is learning some new technology (Angular2/typescript) and refreshing my knowledge of the Spring stack
Thanks!

I'm not sure if anyone else has ever found a good or better answer to this, but here is what I had found to work. I have a spring boot project running, I'll post some of the most pertinent code and examples in this answer, but to see the whole REST service project, check https://github.com/jrspriggs/Chronicler
So, the purpose atm for the small app is to create characters/beings that take part in stories, create stories (featuring a title and slug line) and create a relationship between a being and a story with the being's perspective of the story attached to that relationship. This way it collects the various versions of the story from each character.
The neo4j instance is just a basic neo4j instance in Docker/Kitematic on my Windows laptop. Here are the models:
Being.java:
package com.chronicler.model;
import java.util.Iterator;
import java.util.Set;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.annotation.RelatedToVia;
#NodeEntity
public class Being {
public Long getId() {
return id;
}
#GraphId private Long id;
private String firstName;
private String lastName;
private boolean hero;
private boolean villain;
#RelatedToVia(type="TOOK_PART_IN")
#Fetch private Set<Involvement> involvements;
public Set<Involvement> getInvolvements() {
return involvements;
}
public void setInvolvements(Set<Involvement> involvements) {
this.involvements = involvements;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public boolean isHero() {
return hero;
}
public void setHero(boolean hero) {
this.hero = hero;
}
public boolean isVillain() {
return villain;
}
public void setVillain(boolean villain) {
this.villain = villain;
}
}
Story.java
package com.chronicler.model;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
#NodeEntity
public class Story {
public Long getId() {
return id;
}
#GraphId private Long id;
private String title;
private String slug;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
}
Involvement.java (relationship between being to story)
package com.chronicler.model;
import org.springframework.data.neo4j.annotation.EndNode;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.annotation.StartNode;
#RelationshipEntity(type="TOOK_PART_IN")
public class Involvement {
#GraphId private Long relationshipId;
#Fetch #StartNode private Being being;
#Fetch #EndNode private Story story;
private String perspective;
public Long getRelationshipId() {
return relationshipId;
}
public void setRelationshipId(Long relationshipId) {
this.relationshipId = relationshipId;
}
public Being getBeing() {
return being;
}
public void setBeing(Being being) {
this.being = being;
}
public Story getStory() {
return story;
}
public void setStory(Story story) {
this.story = story;
}
public String getPerspective() {
return perspective;
}
public void setPerspective(String perspective) {
this.perspective = perspective;
}
}
From there I have basically the base kind of repository rest resource classes set up for the spring data services. Those take care of the entities, but they fail to really address the relationship for me. What does is to implement a separate rest route to save it
BeingController.java:
package com.chronicler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.chronicler.model.Involvement;
import com.chronicler.repo.InvolvementRepository;
#RestController
public class BeingController {
#Autowired
InvolvementRepository involvementRepository;
#RequestMapping(value="/beingStory", method=RequestMethod.POST)
public Involvement createBeingStoryRelationship(#RequestBody Involvement involvement) {
involvementRepository.save(involvement);
return involvement;
}
}
From there, just posting to localhost:3000/beingStory with the following kind of json body will accurately create the relationship:
{
"character": {
"id": 17,
"firstName": "Dr. Victor",
"lastName": "Frankenstein",
"hero": true,
"villain": true
},
"story": {
"id": 15,
"title": "Frankenstein",
"slug": "A doctor plays god"
},
"relationshipId": 10,
"perspective": "I did a monstrous thing. I wanted to create life, but I created horrors... such unimaginable horrors, such that mankind has not ever imagined."
}
From that point then, you can walk the relationships from the person to the story. I'll have to add more in the future as I work through this sample app some more to implement the reverse relationship for seeing character involvements from a selected story, and I intend to add relationships between the characters.

Related

How to return multiple fields with Spring Data Neo4j RX?

I am using Spring Data Neo4j RX. And I have a query like this:
#Query("MATCH (a:Repo)-[:REPO_DEPEND_ON]->(b:Repo) WHERE a.name= $name RETURN a.name, b.name")
String[] getSingleRepoDependencyTo(String name);
I know the return type is wrong here, as it cannot be a String array. But how can I get the result properly, which contains two fields?
I have searched online for a long time but cannot find an answer. The "#QueryResult" annotation is not supported in this RX version yet.
Thanks for your help.
Assuming that you have a mapped #Node Repo with its relationships like
#Node
public class Repo {
// other things
String name;
#Relationship("REPO_DEPEND_ON") Repo repo;
}
and defining this method in a ...extends Neo4jRepository<Repo,...>,
you could use Projections.
public interface RepoProjection {
String getName();
DependingRepo getRepo();
/**
* nested projection
*/
interface DependingRepo {
String getName();
}
}
Important to keep in mind that the returned values should be the nodes and relationship to make it work this way.
You could also remove the custom query and do something like:
RepoProjection findByName(String name)
if you do not have the need for a findByName in this repository for the entity itself.
Take a look here: https://neo4j.github.io/sdn-rx/current/#projections.interfaces
It seems to list exactly what you want. From those docs:
interface NamesOnly {
String getFirstName();
String getLastName();
}
interface PersonRepository extends Neo4jRepository<Person, Long> {
List<NamesOnly> findByFirstName(String firstName);
}
There are some other variations too.
You can use annotation #QueryResult on your expected model. For instance you can do that in this way.
DTO:
import org.springframework.data.neo4j.annotation.QueryResult;
#QueryResult
public class SomeDto {
private int someInt;
private SomeObject sobj;
private double sdouble;
private AnotherObject anObj;
//getters setters
}
Neo4jRepository:
public interface DomainObjectRepository extends Neo4jRepository<DomainObject, Long> {
#Query("MATCH(n:SomeTable) RETURN someInt, sobj, sdouble, anObj") //Return a few columns
Optional<SomeDto> getSomeDto();
}

Play 2.6 - Java - Transaction managment

I try to migrate a Play1 application in Play2 (version 2.6.15 at the moment).
With Play1, transactions were automatically managed when a request arrived. So I didn’t have to worry about consistency of my database updates. Database updates were done all over the service layer.
I have to keep that logic in Play2 and I’m not sure how to do that. I saw some threads with Scala but the answers don't help me. So I'll take my chances :)
To understand how it works, I worked on a snippet with 2 twos models : Person and Address. I want to save a person and the list of their addresses and rollback in case an error occurs on one of the requests.
To start my migration, I was inspired by play-java-jpa-example : I created some repositories that contain all access to database, based on JPAApi and executed on DatabaseExecutionContext
public class PersonRepository implements IPersonRepository {
private final JPAApi jpaApi;
private final DatabaseExecutionContext executionContext;
#Inject
public PersonRepository(JPAApi jpaApi, DatabaseExecutionContext executionContext) {
this.jpaApi = jpaApi;
this.executionContext = executionContext;
}
...
#Override
public CompletionStage<Person> add(Person person) {
return supplyAsync(() -> jpaApi.withTransaction(em -> {
em.persist(person);
return person;
}), executionContext);
}
}
public class AddressRepository implements IAddressRepository {
...
#Override
public CompletionStage<Address> add(Address address) {
return supplyAsync(() -> jpaApi.withTransaction(em -> {
em.persist(address);
return address;
}), executionContext);
}
}
I have a service to save Address
public class AddressService implements IAddressService {
....
#Override
public CompletionStage<Address> add(Address address) {
//do some stuff here
return addressRepository.add(address);
}
}
And a service to save a Person, with
public class PersonService implements IPersonService {
...
#Override
public CompletionStage<Person> add(Person person, List<Address> address) {
return add(person).thenApplyAsync(p -> {
for (Address a : address) {
a.person=p;
addressServiceAsync.add(a);
}
return p;
}, ec.current());
}
}
With this implementation of add(Person person, List address), if saving the second address fails, the person and the first address will have been persisted in the database which is not good enough for me.
I tried to remove transaction management in repositories and put it in my PersonService.add function. By passing the entity manager to the functions of my services and repositories it works (I only tested with synchronous calls). Something like that :
public class PersonService implements IPersonService {
#Override
public CompletionStage<Person> add(Person person, List<Address> address) {
return supplyAsync(() -> jpaApi.withTransaction(em -> {
Person person1 = personRepository.insert(em, person);
for (Address a : address) {
a.person = person1;
addressService.add(em, a);
}
return person1;
})).exceptionally(throwable -> {
Logger.error("pan", throwable);
return null;
});
}
}
I don’t like the approach (giving em to all functions) and wonder about asynchronous calls.
What is planned in Play to handle this kind of rollback problem with JPAApi and DatabaseExecutionContext ?
I didn’t see any explicit thread evoking this point, maybe I missed something. What would be the best practice to solve this problem?
Thanks for your help.

android MVP & DI

I am creating an android app and I write following classes and interface and codes .Because I try to use MVP pattern , But now I am not sure that my codes are standard or not ?
should I use Dagger2 for Di or should not?
model , presenter class are below:
public class ChangePasswordModel{
public void saveChange(final String oldPass, final String newPass, final ChangePasswordCallback callback) {
/*in real world it change password with API*/
callback.onDo();
} }
my presenter is :
public class ChangePasswordPresenter{
private ChangePasswordContract.View mView;//this is an interface to interact with Activity
public ChangePasswordPresenter(ChangePasswordContract.View mView) {
this.mView = mView;
}
public void pSaveChange(String oldPass, String newPass) {
ChangePasswordModel model = new ChangePasswordModel();
model.saveChange(oldPass, newPass, new ChangePasswordCallback() {
#Override
public void onDo() {
mView.showMessage("password Changed");
}
});
}
}
Do I implemented MVP correctly?
Should I use Dagger2 for DI? why?
The sample codes that you mentioned in your question is a correct implementation of MVP which can get improved by some changes.
The best practice is a mixture of Dagger, MVP, RxJava, Retrofit and Mock tests which improve the quality and readability of your project. MVP helps you with a clean and separate layered code and RxJava will help with wiring different layers up together and Dagger2 can really helps you with managing dependencies and also make your Mocking in test easy.
You can take a look at this sample project repo on my Github which has been developed using Dagger, MVP and there are also some test available:
http://github.com/mmirhoseini/fyber_mobile_offers
It needs to be improved in my opinion. You Model class must be a concrete object class however your ChangePasswordModel contains saveChange() method that have no idea why it's calling a callback. This logic must be implemented in Presenter class.
Basically process of following MVP design pattern is as follows:
create a package based on your screen. Let's say you have a Signup screen. Then create a package, <your.main.package>.signupscreen.
Create three classes of SignupActivity, SignupView(this is interface), SignupPresenter.
SignupView interface:
interface SignUpView {
String getFirstName();
String getLastName();
String getEmailAddress();
String getPassword();
}
SignupActivity implements SignUpView and holds a reference of SignupPresenter.
public class SignUpActivity implements SignUpView {
#Inject
public Service mService;
SignUpPresenter mSignUpPresenter;
#Override
protected void onStart() {
super.onStart();
mSignUpPresenter = new SignUpPresenter(mService, this);
}
#Override
protected void onStop() {
super.onStop();
if (mSignUpPresenter != null) {
mSignUpPresenter.onStop();
}
}
#Override
public String getFirstName() {
return etFirstName.getText().toString().trim();
}
#Override
public String getLastName() {
return etLastName.getText().toString().trim();
}
#Override
public String getEmailAddress() {
return etEmail.getText().toString().trim();
}
#Override
public String getPassword() {
return etPassword.getText().toString().trim();
}
}
And finally SignupPresenter class:
class SignUpPresenter implements SignUpCallback {
private final Service service;
private final SignUpView view;
private CompositeSubscription subscriptions;
SignUpPresenter(Service service, SignUpView view) {
this.service = service;
this.view = view;
this.subscriptions = new CompositeSubscription();
}
void onStop() {
subscriptions.unsubscribe();
}
}
This is very basic implementation of Activity based on MVP. I do recommend to have a look at this awesome doc regard MVP, Retrofit and Dagger 2, A Simple Android Apps with MVP, Dagger, RxJava, and Retrofit

Neo4J SDN- 3.0.0 ExecutingRestAPI.getNodeById is invoced repeatedly

I'm using Neo4J-2.0.1 and SDN - 3.0.0.RELEASE.
I have a NodeEntity as follow:
#NodeEntity
public class Group {
#GraphId
Long id;
#Indexed(unique = true, indexType = IndexType.SIMPLE)
public String name;
public String property1;
public String property2;
public Group() {
}
public Group(String str) {
name = str;
}
}
I have a groups repository :
public interface GroupsRepository extends GraphRepository<Group> {
Group getGroupByName(String name);
}
After the getGroupByName(...) method is invoked, the
ExecutingRestAPI.getNodeById(...)
method is invoked as the number of the properties that the Group has.
How can I avoid this kind of behaviour?
Are there any additional queries being executed under the hood?
You cannot avoid this behavior at the moment, it loads the dependent entities individually. The REST integration is currently not at all optimized.

SpringData neo4j node property not populated

I have a simple relationship (Account)-[IdentifiedBy]->(Identity), defined like this
#RelatedTo(type = "IDENTIFIED_BY", direction = Direction.OUTGOING)
private Set<Identity> identities = new HashSet<Identity>();
When I load the Account and access its identities, all the identities are loaded, but all their properties except for ID are null. However, if I annotate the property with #Fetch, then the identities are loaded correctly, with all properties. Is this by design or am I missing something?
#NodeEntity
public class Account {
#GraphId Long nodeId;
#RelatedTo(type = "IDENTIFIED_BY", direction = Direction.OUTGOING)
//#Fetch
private Set<Identity> identities = new HashSet<Identity>();
public Set<Identity> getIdentities() {
return identities;
}
public void setIdentities(Set<Identity> identities) {
this.identities = identities;
}
}
#NodeEntity
public class Identity {
#GraphId Long nodeId;
private String identifier;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
}
public interface AccountRepository extends GraphRepository<Account> { }
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/test-context.xml"})
#Transactional
public class AccountTests {
#Autowired
protected AccountRepository accountRepository;
#Test
public void accountMustLoadItsIdentities() {
Account acct = accountRepository.save(new Account());
Identity id = new Identity();
id.setIdentifier("merlin");
acct.getIdentities().add(id);
accountRepository.save(acct);
acct = accountRepository.findOne(acct.nodeId);
id = acct.getIdentities().iterator().next();
assertEquals("merlin", id.getIdentifier());
}
}
The unit test fails on the last assertion, but succeeds if #Fetch on Account is uncommented.
Instead of using
account.getIdentities()
you should do something like the following:
this.neo4jTemplate.fetch(account.getIdentities())
Not using the #Fetch keyword does not automatically enable lazy loading. To lazy load your properties use the Neo4jTemplate as plotted above.
This is by design
We try to avoid loading the whole graph into memory by not following relationships eagerly. A dedicated #Fetch annotation controls instead if related entities are loaded or not. Whenever an entity is not fully loaded, then only its id is stored. Those entities or collections of entities can then later be loaded explicitly using the template.fetch() operation.
http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#reference:simple-mapping

Resources