Conditional data insertion in neo4j - neo4j

I have an empty LIST, where I want to insert some CONTACT data. I have write the following query:
MERGE (u:CONTACT { id: 'random-id-01', email: 'some01#email.com', name: 'some-name-01', info: 'some-info-01 } )
WITH u as u
MATCH (pl:LIST { id: {listId} } )
MERGE (pl)-[plcn:LIST_CONTACTS]->(u)
RETURN u
But I also have to check duplicate email. So I have write the following query:
MATCH (pl:LIST)-[plcn:LIST_CONTACTS]->(ux:CONTACT)
WHERE ux.email <> 'some01#email.com'
MERGE (u:CONTACT { id: 'random-id-01', email: 'some01#email.com', name: 'some-name-01', info: 'some-info-01 } )
WITH u as u
MATCH (pl:LIST { id: {listId} } )
MERGE (pl)-[plcn:LIST_CONTACTS]->(u)
RETURN u
This query only works when some CONTACT data are already exist. But when an empty LIST is created and then want to insert CONTACT data into that newly created LIST, then it is not working.
How can I solve this problem?
Thanks in advance.

If you want email to be unique among :CONTACT nodes, then create a unique constraint on :CONTACT(email), that will ensure an error is thrown when an insert attempt violates the uniqueness constraint.
I'm not quite understanding your intent with the MATCH of all contacts with a different email than the one you want to insert. Can you clarify what you're attempting?

Related

How to verify if a node exist and if not exist then a created during an import of .csv to neo4j

I am importing some .cvs files for my database in neo4j, but I have the data of people in three different files, so when I import the data of the person from another file that has more data, I get an error when trying to import people nodes, because I already have other nodes with those dni (constraint) in my database.
So I want to create the new node or, if it exists, retrieve its pointer to create relationships with other nodes that I keep creating while I import.
I have tried several things on the internet but I still can't find the solution
Here my code:
LOAD CSV WITH HEADERS FROM 'file:/D:/ACCOUNT.csv' AS line FIELDTERMINATOR ';'
MERGE (persona :Persona { dni: line.DNI,
nombre: line.NOMBRE,
sexo: line.SEXO,
fechaNacimiento: line.FNACIMIENTO,
direccion: line.DIRECCION
})
I have tried with apoc and "with" but I still can't find the solution.
when this code finds another node with a person label and ID equal to the one entered, it gives me an error
To get this working, you'll have to understand how MERGE works. The statement
MERGE (persona :Persona { dni: line.DNI, nombre: line.NOMBRE, sexo: line.SEXO,
fechaNacimiento: line.FNACIMIENTO,direccion: line.DIRECCION
})
will create a new Persona node for every distinct combination of the above properties. So, for a node with the same dni, but with other values of other properties, this will fail. To fix this, you should try merging the nodes on the basis of their dni, and then set the properties like this:
MERGE (persona :Persona { dni: line.DNI })
ON CREATE
SET persona.nombre = line.NOMBRE,
persona.sexo = line.SEXO,
persona.fechaNacimiento = line.FNACIMIENTO,
persona.direccion = line.DIRECCION
The above query will ignore setting properties if a matching node is found. To set some properties when a match is found, use ON MATCH, like this:
MERGE (persona :Persona { dni: line.DNI })
ON CREATE
SET persona.nombre = line.NOMBRE,
persona.sexo = line.SEXO,
persona.fechaNacimiento = line.FNACIMIENTO,
persona.direccion = line.DIRECCION
ON MATCH
// Matching logic here

How to make Inner Join to work on TypeORM?

I'm trying to build a simple query on TypeORM but I'm not getting the entire data using INNER JOIN. What am I doing wrong?
The SQL query runs perfectly but the typeorm one just returns me the data for the 'watcher' table.
SQL Query
SELECT *
FROM watcher w
INNER JOIN user
ON w.userId = user.id;
TypeORM
async getSystemWideWatchers(): Promise<any[]> {
const query = this.createQueryBuilder('watcher');
const result = await query.innerJoin('user', 'u', 'watcher.userId = u.id').getMany();
console.log(result)
return result;
}
TypeORM has a method called innerJoinAndSelect. You use plain innerJoin. That is why user table is not selected from.
Once you change that part to innerJoinAndSelect, watch table will be selected from. However, getMany and getOne returns objects with your Entity type. Therefore, if your Entity types do not have the relationships between User and Watcher tables, selected columns will not be included in the returned object.
Before I show you how to add these relations, I want to mention that you have the option to use getRawMany function to get all selected columns, but I don't recommend using it, since relationships are much tidier (no raw column names, you get arrays of Entities corresponding to relationships) and in your case, there is no reason not to use relationships.
Now, the way I understand your database design, you have Users, and Users have Watchers. A Watcher watches only one User, and there may be multiple Watchers that watch the same user.
In this case, the relationship of User to Watcher is called "One to Many".
The relationship of Watcher to User is called "Many to One".
You need to specify this information in your Entity. Here is an example. Notice the OneToMany decorator.
#Entity()
export class User {
#PrimaryColumn()
id: number;
#Column()
userName: string;
#OneToMany(type => Watcher, watcher => watcher.user)
watchers: Watcher[];
}
Here is the corresponding Watcher Entity:
#Entity()
export class Watcher {
#PrimaryColumn()
id: number;
#Column()
watcherName: string;
// we can omit this (and the join condition), if userId is a foreign key
#Column()
userId: number;
#ManyToOne(type => User, user => user.watchers)
user: User;
}
Once you have these relationships, you can select a User, along with all their Watchers, or vice versa (select a Watcher with the User they watch).
Here is how you do both:
// Select a user and all their watchers
const query = createQueryBuilder('user', 'u')
.innerJoinAndSelect('u.watchers', 'w'); // 'w.userId = u.id' may be omitted
const result = await query.getMany();
(If you want to include users with no watchers, you use leftJoin instead of innerJoin, as you probably know.)
This is probably what you were initially trying to do:
// Select a watcher and the user they watch
const query = createQueryBuilder('watcher', 'w')
.innerJoinAndSelect('w.user', 'u'); // 'w.userId = u.id' may be omitted
const result = await query.getMany();
Just in case someone runs into another scenario. Adding to what Cem answered.
If there's no relationship defined in the database then you can do the following :
const query = createQueryBuilder('user', 'u')
.innerJoinAndMapMany(
'u.ObjectNameToMapDataOn',
EntityName,// or 'tableName'
'IAmAlias',
'u.columnName= IAmAlias.columnName'
)
similarly , innerJoinAndMapOne can be used. Depends on your case if you expect to have multiple records with joining table or single record.
if there isn't relations between the two tables, you will have to use getRow().
getOne() and getMany() will return object with type of your repository, and so drop the data of the joined table
this example works for me:
this.userRepository
.createQueryBuilder('user')
.select(['user.username as username', 'o.orderTime as orderTime'])
.where('user.id = :id', {id})
.andWhere('o.callerId = :id', {id})
.innerJoin(OrderEntity, 'o')
.getRawMany()

Neo4j cypher query fails with unknown syntax error

I have the following paramObj and dbQuery
paramObj = {
email: newUser.email,
mobilenumber: newUser.telephone,
password: newUser.password,
category: newUser.category,
name: newUser.name,
confirmuid: verificationHash,
confirmexpire: expiryDate.valueOf(),
rewardPoints: 0,
emailconfirmed: 'false',
paramVehicles: makeVehicleArray,
paramVehicleProps: vehiclePropsArray
}
dbQuery = `CREATE (user:Person:Owner {email:$email})
SET user += apoc.map.clean(paramObj,
['email','paramVehicles','paramVehiclesProps'],[])
WITH user, $paramVehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
CREATE UNIQUE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v`;
Then I tried to execute
commons.session
.run(dbQuery, paramObj)
.then(newUser => {
commons.session.close();
if (!newUser.records[0]) {........
I am getting
Error: {"code":"Neo.ClientError.Statement.SyntaxError","name":"Neo4jError"}
which doesn't direct me anywhere. Can anyone tell me what am I doing wrong here?
This is actually the first time I am using the query format .run(dbQuery, paramObj) but this format is critical to my use case. I am using Neo4j 3.4.5 community with apoc plugin installed.
Ok...so I followed #inversFalcon suggestion to test in browser and came up with following parameters and query that closely match the ones above:
:params paramObj:[{ email:"xyz123#abc.com", mobilenumber:"8711231234",password:"password1", category:"Owner",name:"Michaell",vehicles:["Toyota","BMW","Nissan"],vehicleProps: [] }]
and query
PROFILE
CREATE (user:Person:Owner {email:$email})
SET user += apoc.map.clean($paramObj, ["email","vehicles","vehicleProps"],[])
WITH user, $vehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
MERGE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v;
Now I get
Neo.ClientError.Statement.TypeError: Can't coerce `List{Map{name -> String("Michaell"), vehicles -> List{String("Toyota"), String("BMW"), String("Nissan")},.......
I also reverted to neo4j 3.2 (re: an earlier post by Mark Needham) and got the same error.
You should try doing an EXPLAIN of the query using the browser to troubleshoot it.
A few of the things I'm seeing here:
You're referring to paramObj, but it's not a parameter (rather, it's the map of parameters you're passing in, but it itself is not a parameter you can reference in the query). If you need to reference the entire set of parameters being passed in, then you need to use nested maps, and have paramObj be a key in the map that you pass as the parameter map (and when you do use it in the query, you'll need to use $paramObj)
CREATE UNIQUE is deprecated, you should use MERGE instead, though be aware that it does behave in a different manner (see the MERGE documentation as well as our knowledge base article explaining some of the easy-to-miss details of how MERGE works).
I am not sure what caused the coercion error to disappear but it did with the same query and I got a "expected parameter error" this was fixed by using $paramObj.email, etc. so the final query looks like this:
CREATE (user:Person:Owner {email: $paramObj.email})
SET user += apoc.map.clean($queryObj, ["email","vehicles","vehicleProps"],[])
WITH user, $paramObj.vehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
MERGE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v;
which fixed my original problem of how to remove properties from a map when using SET += map.

return deleted node after DETACH DELETE

I have a problem to get node record in result after I deleted the node.
My query is:
MATCH (user:User)-[:CREATED]->(comment:Comment)-[:BELONGS_TO]->(post:Post)
WHERE comment.uuid = {commentUUID} AND post.uuid = {postUUID} AND user.uuid = {userUUID} AND NOT EXISTS(user.deleted)
DETACH DELETE comment
RETURN comment
Here I am deleting comment which belongs to some post (BELONGS_TO). I also check if user who wants to delete the comment is also author of this comment (CREATED).
Query works, the deletion of node and relationship happen successfully, but I am not satisfied with returned value.
It shows me this result instead:
{ metadata: { deleted: true, id: 89 },
paged_traverse: 'http://myDatabaseURI/db/data/node/89/paged/traverse/{returnType}{?pageSize,leaseTime}',
outgoing_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/out',
outgoing_typed_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/out/{-list|&|types}',
labels: 'http://myDatabaseURI/db/data/node/89/labels',
create_relationship: 'http://myDatabaseURI/db/data/node/89/relationships',
traverse: 'http://myDatabaseURI/db/data/node/89/traverse/{returnType}',
all_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/all',
all_typed_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/all/{-list|&|types}',
property: 'http://myDatabaseURI/db/data/node/89/properties/{key}',
self: 'http://myDatabaseURI/db/data/node/89',
incoming_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/in',
properties: 'http://myDatabaseURI/db/data/node/89/properties',
incoming_typed_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/in/{-list|&|types}' }
Thanks for help!
Edit:
I hide my db URI in result and replaced it with myDatabaseURI
As far as I can tell, when you delete a node, you cannot return its properties by returning the node itself.
However, you can get the properties of the node before the deletion, and return that after deleting the node itself:
MATCH (user:User)-[:CREATED]->(comment:Comment)-[:BELONGS_TO]->(post:Post)
WHERE comment.uuid = {commentUUID} AND post.uuid = {postUUID} AND user.uuid = {userUUID} AND NOT EXISTS(user.deleted)
WITH comment, properties(comment) as props
DETACH DELETE comment
RETURN props
This won't get you the node id, though, so if you need to return that, you can use map projection to get all properties plus the node id instead.

How can I check if a node already exist, before linking it to a new node in Neo4j with cypher

I'm novice in Neo4j and cypher and would like some help solving an issue. All help would be appreciated.
This is my problem:
I have created two nodes, one as a user and one as a city, linked to the user.
Graph setup
CREATE (n:User{firstName : "John", lastName : "Doe"});
MATCH (user:User{firstName : "John", lastName : "Doe"})
Return user;
Query:
MATCH (user:User)
WHERE user.firstName = "John"
CREATE (city:City { cityName:"Liverpool", areaCode:"34343" })
CREATE (user)-[:STUDY_IN]->(city);
Now I want to create a new node(user) and link that user to the existing node (city:Liverpool).
I've done that i this way:
MATCH (city:City)
WHERE city.cityName = "Liverpool"
CREATE (user:User { firstName : "Kent", lastName : "Clark" })
CREATE (user)-[:STUDY_IN]->(city);
In real life, I would need to check if the city exist, before creating the relationship and if it doesn't exist, then I would like to create a new node for that city.
You can see my code here:
http://console.neo4j.org/?id=utor92
Use the MERGE command which will match a pattern if it exists or create it if it doesn't.
CREATE (user:User{firstName: "Kent", lastName: "Clark"})
MERGE (city:City{cityName: "Liverpool"})
CREATE (user)-[:STUDY_IN]->(city)
RETURN user, city
You can also use ON CREATE and ON MATCH with MERGE. For example:
MERGE (city:City{cityName: "Manchester"})
ON CREATE SET city.foo = "bar"
ON MATCH SET city.baz = "qux"
In this example if city does not exist it will be created and the property foo is set to "bar". If it exists already the property baz gets set to "qux".
For a more in depth look at MERGE check out the docs.

Resources