How do I delete a relationship using Neo4jClient - neo4j

Am trying to get the basics down before moving forward with neo4j. Love the querying aspect but now trying to delete using neo4jclient and stuck.
Simple setup
root-[:has_user]->user
and
user-[:friends_with]->friend`
For a user with the Id of 1 I would like to remove the specified from Id == 2. User 1 is no longer friends with User 2 :(
Anyway, using neo4jclient I first check to make sure that the users are friends in the first place with this:
if (client.Cypher.Start("root", client.RootNode)
.Match("root-[:HAS_USER]->user-[:FRIEND]->friend")
.Where((UserNode user, UserNode friend) => user.Id == 1 && friend.Id == id)
.Return<Node<UserNode>>("user")
.Results
.Count() == 1)
{
now I'm trying to delete:
client.Cypher.Start("root", client.RootNode)
.Match("root-[:HAS_USER]->user-[r]->friend")
.Where("user.Id = 1")
.And()
.Where("friend.Id = " + id)
.And()
.Where(string.Format("type(r) = 'FRIEND'"))
.Delete("r");
}
No errors, but the relationship is still there. Any ideas?
Update 11/12/2012
Got it working. I first updated by Neo4J instance with the stable 1.8. I think something with the latest neo4jclient and neo4j server were not working together. I first got the user's node based on the id, then from that node tested if the node had a relationship, then was able to remove it. Code below:
var currentUserNode = client.Cypher.Start("root", client.RootNode)
.Match("root-[:HAS_USER]->user")
.Where((UserNode user) => user.Id == 1)
.Return<Node<UserNode>>("user")
.Results.Single();
if (currentUserNode.StartCypher("user")
.Match("user-[r]->friend")
.Where("friend.Id = " + id).And()
.Where("type(r) = 'FRIEND'")
.Return<Node<UserNode>>("user")
.Results
.Count() == 1)
{
currentUserNode.StartCypher("user")
.Match("user-[r]->friend")
.Where("friend.Id = " + id).And()
.Where("type(r) = 'FRIEND'")
.Delete("r").ExecuteWithoutResults();
}

One way is to switch to use a CypherFluentQuery instead:
new CypherFluentQuery(client)
.Start("root", client.RootNode)
.Match("root-[:HAS_USER]->user-[r]->friend")
.Where("user.Val = 1").And()
.Where("friend.Val = " + 2).And()
.Where("type(r) = 'FRIEND'")
.Delete("r").ExecuteWithoutResults();
which will do what you want.
I believe this all stems from a bug: https://bitbucket.org/Readify/neo4jclient/issue/40/should-be-able-to-add-cypher-delete-clause
As to why client.Cypher.Start isn't working as it should, I'm not sure, the bug is fixed, and should be working from version 1.0.0.479 (current at time of writing is 1.0.0.496)

Use any Neo4jClient build after 1.0.0.500. See issue 45 if you're interested in why.
Don't forget to call ExecuteWithoutResults. The first code sample in your question is missing this. This is documented on https://bitbucket.org/Readify/neo4jclient/wiki/cypher

Related

Grails GORM behaving strangely

I am experiencing the following very strange behavior with Grails GORM queries...
NOTE: I have 20 instances of MyDomain created. I.e 20 records
CASE 1
List<MyDomain> case1Results = MyDomain.where {
(isTypeA == true) && (relation1.relation2.relation3.organization == org)
}.list()
In this case, case1Results is an empty list.
CASE 2
List<MyDomain> case2Results = MyDomain.where {
(isTypeA == true)
}.list()
In this case, case2Results contains the 20 records I have stored.
This all seems to find up to this point. This shows that the 20 records are not associated with organization. However...
THE PROBLEM:
when I take case2Results and do the following...
println(case2Results.every {
it.relation1.relation2.relation3.organization == org
}) //true
I get true
OR:
case2Results = case2Results.findAll{
it.relation1.relation2.relation3.organization == org
}
Here, case2Results still contains 20 records as in CASE 2 above, not 0 as in CASE 1
I can't wrap my head around this behavior and I don't even know how to explain this if not with these examples. Does anyone know why this is happening and how to fix it?
PS: Each of my domain classes has a belongsTo and hasMany static maps, creating relationships between the domain classes.
PS: This behavior is being experienced when I run unit tests but works well in the actual feature being tested.

Efficient way to get a huge number of records sorted via Linq. Also some help regarding editing an existing DB entry

The fist part of my question is
suppose I have a poco class
public class shop{
public virtual string fruitName {get;set;}
public virtual double numberOfFruitsLeftToConsume {get;set;}
public virtual double numberOfFruitsLeftForStorage {get;set;}
public virtual List<Locations> shopLocations {get;set;}
}
I add new fruits in the db by creating a new object of shop and then add it via my context then save it.
Now to retrieve the data
will it be more efficient for me to first filter by fruit name get a List then in that collection should I run my query to sort by the number of fruits to consume , or should I just put it all into one query. Supposing that the site has more than 1000 hits/sec and a massive DB, which method will be efficient.
List<shop> sh = context.shopDB.Where(p => p.fruitName == "mango" &&
p.fruitName == "apple").ToList();
List<shop> sh = sh.Where(f => f.numberOfFruitsLeftToConsume >= 100 &&
f.numberOfFruitsLeftForStorage <= 100).ToList();
The example has no meaning , I just wanted to show the type of query I am using.
The second part of my question is, when I initialize the class shop I do not initialize the List within it. Later on when I try to add it it does not get saved, the shop is connected to the user.
ApplicationUser user = await usemanager.FindByEmailAsync("email");
if(user.shops.shopLocations == null){
user.shops.shopLocation = new List<Location>();
uset.shops.shopLocation.Add(someLocation);
await context.shopDB.SaveChangesAsync();
}
////already tried
//List<Location> loc = new List<Location>();
//loc.Add(someLocation);
//user.shops.shopLocation = loc;
//await context.shopDB.SaveChangesAsync();
I tried both the methods in a try catch block and no exception is thrown.
If you need any more details or if something is not clear to you please ask.
Thank you.
If I add Location and LocationId properties to shop, and then save, I can only view the LocationId , but the Location property still remains null.
To clear any question , If I save a location Individually it saves. So I don't think I'm providing wrong data.
Now to retrieve the data will it be more efficient for me to first filter by fruit name get a List then in that collection should I run my query to sort by the number of fruits to consume , or should I just put it all into one query. Supposing that the site has more than 1000 hits/sec and a massive DB, which method will be efficient.
You are the only one who can answer that question by measuring the query performance. Only as a general rule I can say that putting all into one query and let the database do the most of the job (eventually tuning it by creating appropriate indexes) is usually preferable.
What about the second part of the question (which basically is a different question), this block
if (user.shops.shopLocations == null)
{
user.shops.shopLocation = new List<Location>();
user.shops.shopLocation.Add(someLocation);
await context.shopDB.SaveChangesAsync();
}
looks suspicious. Your shopLocations member is declared as virtual, which means it's intended to use lazy loading, hence most probably will never be null. And even if it is null, you need to keep only the new part inside the if and do the rest outside, like this
if (user.shops.shopLocations == null)
user.shops.shopLocation = new List<Location>();
user.shops.shopLocation.Add(someLocation);
await context.shopDB.SaveChangesAsync();
1st Question
Because you are calling .ToList() at the end of your queries it will have to fetch all the rows from the db each time, so it will be much faster to do all your filtering in one LINQ .Where() call like this:
List<shop> sh = context.shopDB.Where(p => p.fruitName == "mango" && p.fruitName == "apple" && f.numberOfFruitsLeftToConsume >= 100 && f.numberOfFruitsLeftForStorage <= 100).ToList();
but if you don't call .ToList() at the end of first Linq query, spliting your query into two calls will be tottally fine and will yield the same performance as the previous approach like this:
var sh = context.shopDB.Where(p => p.fruitName == "mango" &&
p.fruitName == "apple");
List<shop> shList = sh.Where(f => f.numberOfFruitsLeftToConsume >= 100 &&
f.numberOfFruitsLeftForStorage <= 100).ToList();
2nd Question
when you initialize the Location for the shop, you must set the shopId property and then it should work, if not the problem might be with your database relationships.

Is there a 'does not contains' functionality on a collection property of a domain object for createCriteria?

I have a problem similar to this. But I want a does not contain functionality.
Like I have a Post domain. A Post hasMany User.
What I'd like to do, using createCriteria, is something like this:
def c = Post.createCriteria()
def l = c.list (max: maxVar) {
notContains("users", thisUser)
}
I tried using ne But no luck.
def l = c.list (max: maxVar) {
users {
ne('id', thisUser.id)
}
}
To be clear, how can I get list of all the Post whose users field which is a collection does not contain thisUser ?
You can use HQL for this
List<Post> posts = Post.executeQuery("select distinct p from Post p where :myUser not member of p.users", [myUser: user, max: maxVar])
c.list (max: maxVar) { not { 'in'("users", thisUser) }}
Will return a list without thisUser. This works for grails version 2.*. You may need to override .equals for your User class.
There are also many other ways to this such as using .find. Please see this link http://groovy.codehaus.org/JN1015-Collections
I was not able to get a conventional solution to it, but I solved this problem using two separate queries.
First I got all the Posts whose users contain thisUser using
users {
eq('id', thisUser.id)
}
Then I got max number of Post whose id does not match any of the id from above.
Post.findAllByIdNotInList(usersList.id, [max:15])
where usersList is result from first query
P.S. : Please answer if anyone has a better solution. Thanks

Add multiple relationships between 2 nodes with Neo4jClient

I am building a social graph application using Neo4jClient to store data and I am trying to come up with the best strategy to model where a user has worked and currently works. So my User is connected with the relationship "USER_WORK" to several Work nodes which has StartDate/EndDate properties. If EndDate is not set I want to add another relationship "CURRENT" between the user/work node to be able to fetch only current work places in an efficient way.
For some reason Neo4jClient does not let me do this? Below query executes without exceptions and the work node and all relationships except "CURRENT" is added (and yes I have checked that there is no problem with the EndDate logic:) I have also tried using Create instead of CreateUnique but that doesn't solve the problem :(
var query = graphClient.Cypher
.Match("(city:City)", "(profession:Profession)", "(company:Company)", "(user:User)")
.Where((CityEntity city) => city.Id == model.CityId)
.AndWhere((ProfessionEntity profession) => profession.Id == model.ProfessionId)
.AndWhere((CompanyEntity company) => company.Id == model.CompanyId)
.AndWhere((UserEntity user) => user.Id == model.UserId)
.Merge("(work:Work { Id: {Id} })")
.OnCreate()
.Set("work = {entity}")
.WithParams(new
{
Id = entity.Id,
entity
})
.CreateUnique("work-[:WORK_AS_PROFESSION]->profession")
.CreateUnique("work-[:WORK_AT_COMPANY]->company")
.CreateUnique("work-[:WORK_IN_CITY]->city")
.CreateUnique("user-[:USER_WORK]->work");
if (model.EndDate == DateTime.MinValue)
{
query.CreateUnique("user-[:CURRENT]->work");
}
query.ExecuteWithoutResults();
When you call CreateUnique to create the user-[:CURRENT]->work relationship, it's not actually being appended to the query. What you need to change that line to is:
query = query.CreateUnique("user-[:CURRENT]->work");
Which is what is happening for all the fluent methods chained in the first query you write out. The easiest way to spot these things is stick a breakpoint on the query.ExecuteWithoutResults(); method and when VS breaks there, hover over query and see if the text matches what you think it should.

Query - Does Not Contain

I have a search query to lookup Customers.
I would like to use the Sounds Like function to return additional possible results, however this is returning some of the same results in my main search query.
I would like to only show the additional results in a partial view.
I basically need a DoesNotContain.
Here is what I have so far for my main query:
customer = customer.Where(c => SqlFunctions.StringConvert((double)c.CustomerID).Trim().Equals(searchString)
|| c.CustomerName.ToUpper().Contains(searchString.ToUpper()));
And for the additional results:
customeradditional = customeradditional.Where(c => SqlFunctions.SoundCode(c.CustomerName.ToUpper()) == SqlFunctions.SoundCode(searchString.ToUpper()));
The only possible solution I can see at the minute is to do a Contains Query, loop through each item and get the IDs, then do another query for CustomerID != 1 or CustomerID != 2 or CustomerID != 3, etc.
Try using Except:
customeradditional = customeradditional
.Where(c => SqlFunctions.SoundCode(c.CustomerName.ToUpper()) == SqlFunctions.SoundCode(searchString.ToUpper()))
.Except(customer);
I am not sure if I understood you correct:
From what you have now, the customeraddtional query does return some of the customers already returned in the customer query. And you only want the results, which are not already contained in the customer query.
Then the solution would be:
customeradditional = customeradditional.Where(c =>
SqlFunctions.SoundCode(c.CustomerName.ToUpper()) ==
SqlFunctions.SoundCode(searchString.ToUpper()))
.Except(customer);
This way your are explicitly excluding every item, which is present in the customer object.

Resources