How to read properties of node in neo4j? - neo4j

I am quite new to neo4j, and constructing db which consists of >10M nodes. During query operations I want to find a node by using two properties of it. For example: node - name: xxx surname: yyy id:1 during query operation I need to get node id which name: xxx, surname: yyy. How is it possible with java query (not cypher)? And there will be more than one entry with given properties.

Here is an example how to find ids:
GraphDatabaseService database;
Label label = DynamicLabel.label("your_label_name");
String propertyId = "id";
String propertyName = "name";
String propertySurname = "surname";
public Set<Node> getIdsForPeople(Set<Person> people) {
Set<String> ids = new HashSet<>();
try(Transaction tx = database.beginTx()) {
for (Person person in people) {
Node node = database.findNode(label, propertyName, person.getName());
if (node.hasProperty(propertySurname)) {
if (node.getProperty(propertySurname) == person.getSurname()) {
String id = node.getProperty(propertyId).toString();
ids.add(id);
}
}
}
tx.success();
}
return ids;
}
Person holder
public class Person {
private final String name;
private final String surname;
public Person(String name, String surname) {
this.name = name;
this.surname = surname;
}
public String getName() { return name; }
public String getSurname() { return surname; }
}
example
Set<Person> people = new HashSet<Person>(){{
add(new Person("xxx1", "yyy1"));
add(new Person("xxx2", "yyy2"));
add(new Person("xxx3", "yyy3");
add(new Person("xxx4", "yyy4");
}};
Set<String> ids = getIdsForPeople(people);

Related

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. ;)

Neo4j remove existing relationships itself during adding new relationships

I have a code responsible for creating new relationship between two nodes.
At first It get a two nodes by property (pk), check if such relationship between those nodes already exists and if It doesn't not, create one.
public void createNeo4jGraphLink(#Nonnull final String childPk, #Nonnull final String parentPk) {
final UUID measureId = measureService.startMeasure(MeasureService.MeasureEvent.NEO_PERSIST);
Node child = neoRepository.findOneByPk(childPk);
Node parent = neoRepository.findOneByPk(parentPk);
checkIfLinkingAllowed(child, parent);
if (child.getPartOf() == null || !child.getPartOf().contains(parent)) {
if (child.getPartOf() == null) {
child.setPartOf(new HashSet<>());
}
child.getPartOf().add(parent);
neoRepository.save(child);
}
measureService.fixMeasure(MeasureService.MeasureEvent.NEO_PERSIST, measureId);
}
I can see that number of relationships rises in db, but one moment it sharply decrease and so on.. What reason can lead to this behavior? I've tried to use #Transactional annotation, experimented with propagation and isolation of transaction and tried without transactions at all, but it all does not work. Btw I use Spring Data Neo4j repositories. Spring Boot 1.4.4.RELEASE.
Child and parent entity' type is "Node" which is defined as a follows
#NodeEntity(label = "node")
public class Node {
private String pk = UUID.randomUUID().toString();
#JsonIgnore
private Long id;
private String name;
private Set<Node> partOf;
private String type;
public Node() {
}
#Property(name = "pk")
public String getPk() {
return pk;
}
public void setPk(String pk) {
this.pk = pk;
}
#GraphId(name = "id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Property(name = "type")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Property(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Relationship(type = "PART_OF", direction = Relationship.OUTGOING)
public Set<Node> getPartOf() {
return partOf;
}
public void setPartOf(Set<Node> partOf) {
this.partOf = partOf;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
if (!pk.equals(node.pk)) return false;
return true;
}
#Override
public int hashCode() {
return pk.hashCode();
}
}
I perform adding the relationship 1800 times, but as result have 62 relationships. Code does not throw any errors. While processing those 1800 requests, I can see in database there is any number of relationships (less then 1800), but as result only 62.
Thank you for any information

Can relate to only one element

I am using Neo4j OGM 2.0.4 driver with Java. I have trouble with adding more than one relationship to element.
I do something like this:
Site site1 = new Site();
site1.setTitle("Site 1");
site1.setHtmlCode("Content of site 1");
Site site2 = new Site();
Site subsite1 = new Site();
subsite1.setTitle("Subsite 1");
subsite1.setHtmlCode("Content of subsite 1");
subsite1.setParent(site1);
Site subsite2 = new Site();
subsite2.setTitle("Subsite 2");
subsite2.setHtmlCode("Content of subsite 2");
subsite2.setParent(site1);
session.deleteAll(Site.class);
session.save(site1);
session.save(subsite1);
session.save(subsite2);
When I want to show all Site nodes (on localhost:7474) then "Subsite 1" has no relationship.
#NodeEntity
public class Site extends Entity
{
private String _title;
private String _htmlCode;
#Relationship(type = "SITE_CREATED_BY")
Author _author;
#Relationship(type = "IS_CHILD")
Set<Site> _parentSite;
#Relationship(type = "IS_CHILD", direction = Relationship.INCOMING)
Set<Site> _childSites;
public Site()
{
_parentSite = new HashSet();
_childSites = new HashSet();
}
public void setTitle(String title)
{
_title = title;
}
public String getTitle()
{
return _title;
}
public void setHtmlCode(String htmlCode)
{
_htmlCode = htmlCode;
}
public String getHtmlCode()
{
return _htmlCode;
}
public void setAuthor(Author author)
{
_author = author;
}
public void setParent(Site site)
{
_parentSite.add(site);
}
}
Entity:
public abstract class Entity
{
private Long id;
private final ZonedDateTime _dateOfCreation;
Entity()
{
_dateOfCreation = ZonedDateTime.now();
}
public Long getId()
{
return id;
}
public ZonedDateTime getDateOfCreation()
{
return _dateOfCreation;
}
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || id == null || getClass() != o.getClass()) return false;
Entity entity = (Entity) o;
return id.equals(entity.id);
}
#Override
public int hashCode()
{
return (id == null) ? -1 : id.hashCode();
}
}
What am I doing wrong?
In this case where you have two relationships in different directions between the same type of node, first, make sure that you annotate both the fields as well as setter/accessor methods with the #Relationship,specifying the direction.
Site in your object model has references to both the parent and children, but when you create sites, they do not seem consistent with the model. Subsite1 and Subsite2 both set their parents to site1 but site has no record of its children (should be both subsites). Should work if your object and graph models are consistent.

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 doesn't create relationship with properties

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.

Resources