SDN 4 doesn't create relationship with properties - neo4j

I am new to Neo4J. I have built a project that uses spring-data-neo4j (4.0.0.BUILD-SNAPSHOT - version), spring-boot (1.2.3.RELEASE - version) and succeeded to create node entities, add properties to node entities and add relationships. It works fine. Now I want to create properties for the relationships. I have used sdn4 university as a reference, here is the link https://github.com/neo4j-examples/sdn4-university .
I want to create a property called "challengedBy" for relationship PLAY_MATCH (Start node is Match and end node is Player). You can have a look on below class.
#RelationshipEntity(type = "PLAY_MATCH")
public class PlayMatch extends Entity {
//Entity is a class with the id property for the node / relationship
#Property
private String challengedBy;
#StartNode
private Match match;
#EndNode
private Player player1;
}
I have created a controller in the project /api/playmatch to create only the relationship between match and a player. So when I pass the values for an existing match node and a player node, the relationship is not created at all.
Any help will be appreciated..
PlayMatch code is
#RelationshipEntity(type = "PLAY_MATCH")
public class PlayMatch extends Entity{
#Property
private String challengedBy;
#StartNode
private Match match;
#EndNode
private Player player1;
public PlayMatch() {
}
public PlayMatch(String challengedBy, Match match,
Player player1) {
super();
this.challengedBy = challengedBy;
this.match = match;
this.player1 = player1;
}
// after this i have getters & setters and toString method for above fields.
}
Match code is
#NodeEntity(label = "Match")
public class Match extends Entity {
private String createdBy;
private Long createdTime;
private String status;
private int noOfGames;
private int noOfPoints;
private String type;
private Long date;
#Relationship(type="PLAY_MATCH",direction= Relationship.UNDIRECTED)
private PlayMatch playMatch;
public Match() {
}
public Match(String createdBy, Long createdTime, String status,
int noOfGames, int noOfPoints, String type, Long date) {
super();
this.createdBy = createdBy;
this.createdTime = createdTime;
this.status = status;
this.noOfGames = noOfGames;
this.noOfPoints = noOfPoints;
this.type = type;
this.date = date;
}
public PlayMatch getPlayMatch() {
return playMatch;
}
public void setPlayMatch(PlayMatch playMatch) {
this.playMatch = playMatch;
}
// after this i have getters & setters and toString method for above fields.
}
Player code is
#NodeEntity(label = "Player")
public class Player extends Entity {
private String address;
private String preferredSport;
private float height;
private float weight;
private String phone;
private String photo;
#Relationship(type="PLAY_MATCH")
private PlayMatch playMatch;
public PlayMatch getPlayMatch() {
return playMatch;
}
public void setPlayMatch(PlayMatch playMatch) {
this.playMatch = playMatch;
}
public Player() {
}
public Player(String address, String preferredSport, float height,
float weight, String phone, String photo) {
super();
this.address = address;
this.preferredSport = preferredSport;
this.height = height;
this.weight = weight;
this.phone = phone;
this.photo = photo;
}
// after this i have getters & setters and toString method for above fields.
}

I think you have playmatch relationship within the player end node as well. If you comment the following code in the player node. It should work. I have also attached a json sample to pass from the UI in the match URL (/api/match) instead of (/api/playmatch)
#Relationship(type="PLAY_MATCH")
private PlayMatch playMatch;
public PlayMatch getPlayMatch() {
return playMatch;
}
public void setPlayMatch(PlayMatch playMatch) {
this.playMatch = playMatch;
}
Sample JSON
{
"type": "typename",
"status": "statusname",
"createdTime": 1435928223021,
"noOfGames": 5,
"noOfPoints": 19,
"playMatch": {"challengedBy" : "John", "player1" : {"id":732}, "match":{"type": "typename",
"status": "statusname",
"createdTime": 1435928223021,
"noOfGames": 5,
"noOfPoints": 19}}
}
this should create a new match and a new relationship with property challengedBy to an existing player node with id 732.
check it out and let me know if this works.

Related

Creating relationships between nodes using Spring Data Rest and neo4j

I am starting to use Neo4J with Spring Data Rest. I have a node entity and a relationship entity for modelling nodes and edges. I'm able to create new nodes with the following using postman.
POST http://localhost:8080/nodes
{
"name" : "Test"
}
I am unsure of what the JSON format would be to create relationships between the nodes. For example:
Create a new node and relate to an existing node
Create a relationship between two existing nodes.
Any examples on what JSON I need to use would be very much appreciated.
My node entity and relationship entity are as follows:
#NodeEntity
public class Node {
#Id
#GeneratedValue
private Long id;
private String name;
private int count;
#Relationship(type = Edge.TYPE, direction = Relationship.UNDIRECTED)
private Set<Edge> edges = new HashSet<>();
public void addEdge(Node target, int count) {
this.edges.add(new Edge(this, target, count));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<Edge> getEdges() {
return edges;
}
public void setEdges(Set<Edge> edges) {
this.edges = edges;
}
}
#RelationshipEntity(type = Edge.TYPE)
public class Edge {
public static final String TYPE = "LINKED_TO";
#Id
#GeneratedValue
private Long relationshipId;
#StartNode
private Node start;
#EndNode
private Node end;
private int count;
public Edge() {
super();
}
public Edge(Node start, Node end, int count) {
this.start = start;
this.end = end;
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Node getStart() {
return start;
}
public void setStart(Node start) {
this.start = start;
}
public Node getEnd() {
return end;
}
public void setEnd(Node end) {
this.end = end;
}
public Long getRelationshipId() {
return relationshipId;
}
public void setRelationshipId(Long relationshipId) {
this.relationshipId = relationshipId;
}
}
OK I worked it out, you can do this:
PATCH http://localhost:8080/nodes/1
{
"name" : "Test",
"edges": [
{
"start": "http://localhost:8080/nodes/1",
"end": "http://localhost:8080/nodes/2"
}
]
}
This will add the relationships between the nodes.
Hope this helps someone.

Vaadin binding objects

I am trying to bind a textfield to an object. I've done some research and I have found this answer.
public class Person {
String name;
String surname;
Address address;
// assume getters and setters
}
public class Address {
String street;
// assume getter and setters
}
Then, you could bind the street address like this:
Binder<Person> binder = new Binder<>();
TextField streetAddressField = new TextField();
// bind using lambda expressions
binder.bind(streetAddressField,
person -> person.getAddress().getStreet(),
(person, street) -> person.getAddress().setStreet(street));
What value do I instantiate street as (in the last line of code)?
The above was the example I found. My code is as follows - I have a contact class:
#Entity
public class Contact {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
private String phoneNumber;
#ManyToOne (cascade = {CascadeType.ALL})
#JoinColumn(name="phoneType_typeId")
private PhoneType phoneType;
public Contact(){
}
public Contact(String firstName, String lastName, String phoneNumber, PhoneType type) {
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
this.phoneType = type;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public PhoneType getPhoneType() {
return phoneType;
}
public void setPhoneType(PhoneType phoneType) {
this.phoneType = phoneType;
}
#Override
public String toString() {
return String.format("Contact[firstName='%s', lastName='%s', phoneNumber='%s', phoneType = '%s']",
firstName, lastName, phoneNumber, phoneType);
}
}
Then I have a phoneType class:
#Entity
#Table(name="phoneType")
public class PhoneType {
#Id
#GeneratedValue
#Column(name = "typeId")
private Long id;
private String type;
public PhoneType(String type){
this.type = type;
}
public PhoneType(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public String toString() {
return type;
}
}
Then in a Contact Editor I am trying to bind the phoneType to a textfield:
#SpringComponent
#UIScope
public class ContactEditor extends VerticalLayout {
private final ContactRepository repository;
private Contact contact;
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
TextField phoneNumber = new TextField("Phone number");
TextField phoneType = new TextField( "Phone type");
Button save = new Button("Save", VaadinIcons.CHECK);
Button cancel = new Button("Cancel");
Button delete = new Button("Delete", VaadinIcons.TRASH);
CssLayout actions = new CssLayout(save, cancel, delete);
Binder<Contact> binder = new Binder<>(Contact.class);
#Autowired
public ContactEditor(ContactRepository repository, Contact contact) {
this.repository = repository;
this.contact = contact;
String type = contact.getPhoneType().getType();
addComponents(firstName, lastName, phoneNumber, phoneType, actions);
// bind using naming convention
**binder.bind(phoneType, contact.getPhoneType().getType(), contact.getPhoneType().setType(type));**
binder.bindInstanceFields(this);
// Configure and style components
setSpacing(true);
actions.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
save.setStyleName(ValoTheme.BUTTON_PRIMARY);
save.setClickShortcut(ShortcutAction.KeyCode.ENTER);
// wire action buttons to save, delete and reset
save.addClickListener(e -> repository.save(contact));
delete.addClickListener(e -> repository.delete(contact));
cancel.addClickListener(e -> editContact(contact));
setVisible(false);
}
public interface ChangeHandler {
void onChange();
}
public final void editContact(Contact c) {
if (c == null) {
setVisible(false);
return;
}
final boolean persisted = c.getId() != null;
if (persisted) {
// Find fresh entity for editing
contact = repository.findById(c.getId()).get();
}
else {
contact = c;
}
cancel.setVisible(persisted);
// Bind customer properties to similarly named fields
// Could also use annotation or "manual binding" or programmatically
// moving values from fields to entities before saving
binder.setBean(contact);
setVisible(true);
// A hack to ensure the whole form is visible
save.focus();
// Select all text in firstName field automatically
firstName.selectAll();
}
public void setChangeHandler(ChangeHandler h) {
// ChangeHandler is notified when either save or delete
// is clicked
save.addClickListener(e -> h.onChange());
delete.addClickListener(e -> h.onChange());
}
}
The line enclosed in ** in Contact Editor (i.e. binder.bind(phoneType, contact.getPhoneType().getType(), contact.getPhoneType().setType(type))) is giving me an error - "no instance of type variable FIELDVALUE exist so that string conforms to ValueProvider .
The line
binder.bind(phoneType, contact.getPhoneType().getType(), contact.getPhoneType().setType(type));
does not compile because the method arguments do not match to any of the bind methods, and there is an illegal Java expression in the 3rd argument. According to your question, you have simply forgotten to use lambdas. Try:
binder.bind(phoneType, c -> c.getPhoneType().getType(), (c, t) -> c.getPhoneType().setType(t));
Have a look at the method signature:
public <FIELDVALUE> Binder.Binding<BEAN,FIELDVALUE> bind(HasValue<FIELDVALUE> field,
ValueProvider<BEAN,FIELDVALUE> getter,
Setter<BEAN,FIELDVALUE> setter)
It expects ValueProvider and Setter as 2nd and 3rd argument. These interfaces have only one method to be implemented, therefore you can use lambdas to pass them to bind.
I don't know if this is what you'r asking, but what I see as missing is that you haven't binded your binder to any bean.
You have created the binder, and you've told your textfield which property is binded to, but now you need to tell the binder which is his bean.
Something like:
Person yourPerson = new Person(); //or get person from database somehow
yourPerson.setAddress(new Address());
yourPerson.getAddress().setStreet("Road cool code, 404");
binder.setBean(yourPerson);
This should do the trick... if not, please explain better what you need. ;)

Join table issue in Spring Data JPA

I am trying to create a view with datas which combines two tables. I successfully implemented the join and datas are displaying properly by using spring data JPA join. Here my issue is that, when I am calling findAll() method from only one table, which returns all the data including joined table also,
I joined table Users model class like:
#Entity
#Table(name = "users")
public class Users implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "username")
public String username;
#Column(name = "password")
public String password;
#Column(name = "privid")
public Integer privid;
#OneToMany(cascade = CascadeType.ALL,mappedBy="pid")
public Set<Privillages> priviJoin;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getPrivid() {
return privid;
}
public void setPrivid(Integer privid) {
this.privid = privid;
}
public Set<Privillages> getPriviJoin() {
return priviJoin;
}
public void setPriviJoin(Set<Privillages> priviJoin) {
this.priviJoin = priviJoin;
}
public Users() {
}
}
And my second model Privillages like,
#Entity
#Table(name = "Privillages")
public class Privillages implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Integer Id;
#Column(name = "pname")
public String pname;
#ManyToOne(optional = false)
#JoinColumn(name = "pid", referencedColumnName = "privid")
public Users pid;
public Integer getId() {
return Id;
}
public void setId(Integer id) {
Id = id;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Users getPid() {
return pid;
}
public void setPid(Users pid) {
this.pid = pid;
}
public Privillages(){
}
}
And repository containing,
#Query("select u from Users u JOIN FETCH u.priviJoin p")
Set<Users> findByUsername();
These are all my codes, here i added. The thing is that, join is properly working with expected resultset. But when I call findAll() method , the it returns all the structure including both joined table.
I called my findAll function like,
#RequestMapping("/check")
public List<Users> check() {
return (List<Users>) userRepo.findAll();
}
But result is like I previously mentioned.Here I added its screenshot,
In this figure we can see that it returns the both table values instead of users table data.
Why is it happening like this?
You defined your domain type Users to contain a reference so it gets loaded as specified.
If you want something similar to a Users object but without the reference, you have two options:
Change the Users type to not contain a reference.
Use a different type, similar to Users but without the reference. There are multiple ways to do that, but probably the simplest and most helpful in the current situation is to use a projection. See https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

How to specify many relationship types in a #Relationship

I have two nodes user and account and the relationship between them is any one of the 25 relationship.
My Query is
MATCH (u:User)-[r:rel1|rel2|rel3|rel4]->(a:Account) WHERE u.login_id=~('(?i).*'+{fulltextsearch}+'.*') RETURN u as User,r as acronym
My user pojo is
public class User{
#GraphId
private Long id;
String fulltextsearch;
String user_id;
String status;
//#Relationship(type = "rel1", direction= Relationship.OUTGOING)
Acronym acronym;
public Acronym getAcronym() {
return acronym;
}
private Set<Account> accounts;
public User() {
}
public String getUser_id() {
return user_id;
}
public String getStatus() {
return status;
}
public String getFulltextsearch() {
return fulltextsearch;
}
public Set<Account> getAccounts() {
return accounts;
}
public void setAccounts(Set<Account> accounts) {
this.accounts = accounts;
}
}
I am confused in writing my user pojo with multiple relatioships.
can I give #Relatioship for multiple relatioship with OR.
like this #Relationship(type = "rel1 | rel2", direction= Relationship.OUTGOING)
No, you cannot supply multiple relationship types to the #Relationship. You will have to declare them independently in your entity:
#Relationship(type = "rel1", direction= Relationship.OUTGOING)
Acronym acronymRel1;
#Relationship(type = "rel2", direction= Relationship.OUTGOING)
Acronym acronymRel2;
You can write a custom query to fetch all Acronyms based on a set of relationship types.

SDN 4 missing relationships in the graph

When I store my Neo4j nodes to the Neo4j graph, only nodes are stored (no relationships). Does anyone can point me to right direction what i am missing there?
here are my nodes
#NodeEntity public class Game extends AbstractEntity{
public Long timestamp;
public double total_pot_size;
public int flop;
public int turn;
public int river;
#Relationship(type = "ROUND")
public Set<Round> rounds;
#Relationship(type = "OUTCOME")
public Set<Outcome> outcomes;
public Game() {
super();
}
public void add(Outcome outcome) {
if (outcomes == null) {
outcomes = new HashSet<Outcome>();
}
outcomes.add(outcome);
}
public void add(Round round) {
if (rounds == null) {
rounds = new HashSet<Round>();
}
rounds.add(round);
}
}
and
#NodeEntity public class Player extends AbstractEntity {
public String name;
public String platform;
#Relationship(type = "PARTICIPATION", direction = Relationship.OUTGOING)
public Set<Participation> involved_games;
#Relationship(type = "OWNS")
public Set<Statistics> statistics;
public Player() {
super();
}
public void participate(Participation involved_game) {
if (involved_games == null) {
involved_games = new HashSet<Participation>();
}
involved_games.add(involved_game);
}
public void add(Statistics stat) {
if (statistics == null) {
statistics = new HashSet<>();
}
statistics.add(stat);
}
}
here is the code i use to store my data.
// create or get a new game in database
Game game = gameRepo.save( DataUtil.createGame(gi) );
// get the player involved to the game
List<Player> players = gi.getPlayers();
// for each player involved to the game...
for (Player player : players) {
org.quazar.data.domain.Player p = store(player);
// relationship participation
p.participate(DataUtil.createParticipation(p, game, player));
// set the outcome of the players
game.add(DataUtil.createOutcome(game, p, gi, player));
playerRepo.save(p);
}
gameRepo.save( game );
the store(player) method saves or retrieve (if not new) a player, which works properly. DataUtil.createParticipation and DataUtil.createOutcome methods creates new relationships (g.e. new Outcome()) and set the properties.
When running my application i do not get any exceptions, but in the graph i miss my relationships.
I did not listed all the nodes (e.g. Statistics) because right now i do not use them in my model, could that cause the problem?
Here are some relationships:
#RelationshipEntity(type = "PARTICIPATION")
public class Participation extends AbstractEntity {
#StartNode
public Player player;
#EndNode
public Game game;
#Property
public double bankroll;
#Property
public int holecards;
#Property
public byte position;
public Participation () {
super();
}
}
and
#RelationshipEntity(type = "OUTCOME")
public class Outcome extends AbstractEntity {
#StartNode
public Game game;
#EndNode
public Player player;
#Property
public String type;
#Property
public String round;
#Property
public double pot_size;
#Property
public double ante;
#Property
public int holecards;
#Property
public String bestHand;
#Property
public int bestHandrank;
public Outcome() {
super();
}
}

Resources