Neo4J two way relationship query with cypher - neo4j

I have a two-way [:FRIENDSHIP] relationship between User nodes:
(UserA)<-[:FRIENDSHIP {approved:true}]->(UserB)
Here's a little test function in Java to setup the relationship:
public void approveFriendship(User requestor, User requestee) {
Friendship friendship = new Friendship(requestor, requestee);
friendship.setApproved(true);
Friendship invertedFriendship = new Friendship(requestee, requestor);
invertedFriendship.setApproved(true);
requestor.getFriendships().add(friendship);
requestee.getFriendships().add(invertedFriendship);
userRepository.save(requestor);
userRepository.save(requestee);
}
This cypher query returns the friends of UserA, and works fine:
start user=node({0})
match (user)-[r?:FRIENDSHIP]->(friends)
where r.approved = true
return friends
This cypher query returns the posts of a friend, and does not work (returns empty result):
start n=node({0})
match (n)<-[r?:FRIENDSHIP]->(friend)<-[:AUTHOR]-(friendposts)
where r.approved = true
return friendposts order by friendposts.createdAt
When omitting the where r.approved = true line or changing it to where r.approved = false it returns posts of friends without the approved status in both cases.
Can anybody spot if I'm doing something wrong here? Much obliged.

Solved it.
Dunno why, but in a one-to-many type relationship (eg. User to Post) Neo4J prefers outgoing relationships on the Collection, and the incoming on the single entity. I had it the other way round...
Now my classes look like this:
public class User {
#RelatedTo(type = "AUTHOR")
private Set<Post> posts;
}
public class POST {
#RelatedTo(type = "AUTHOR", direction = Direction.INCOMING)
private User author;
}
And of course the cypher query has to change to reflect the new relationship direction (note arrow behind author):
start n=node({0})
match (n)<-[r?:FRIENDSHIP]->(friend)-[:AUTHOR]->(friendposts)
where r.approved = true
return friendposts order by friendposts.createdAt

Related

Grails distinct projection get the result count of distinct items

I am using grails-2.5.6 version. I am using spring-security-core plugin. I have a criteria query on UserRole table. Where I want to find all distinct users by a role. It is working properly.
But the problem is the pagination effect. When I am counting on the list it is counting on UserRole list object. But I need the count on distinct projection items. Here is my attempt below:
def list(Integer max) {
def userInstanceList = UserRole.createCriteria().list(params) {
createAlias('user', 'au')
createAlias('role', 'ar')
projections {
distinct("user")
}
if (params.roleId) {
eq('ar.id', params.getLong("roleId"))
}
}
def totalCount = userInstanceList.totalCount
[userInstanceList: userInstanceList, totalCount: totalCount]
}
Here, totalCount is the number of UserRole list. But I want the distinct projection count.
I would tackle this slightly differently, you want to analyse the users, not the userroles.
So I'd do something like:
List<User> usersWithRole = UserRole.createCriteria().list(params) {
role {
eq('id', params.getLong("roleId"))
}
}*.user
int count = usersWithRole.size()
Unless of course there's hundreds or thousands of users, in which case I wouldn't want to load all of them each time and would revert to SQL.
Is this a custom version of spring security you're using? I've never seen Roles with a 'long' based ID, usually, the key is a String representing the Authority name.
Usually the DBAs see the use of distinct keyword as a code-smell.
In your case I would rather use the User as the main domain object to run the query against and a group by clause:
long id = params.getLong "roleId"
def list = User.createCriteria().list( params ) {
projections{
groupProperty 'userRole.role.id'
}
if( id )
userRole{
role{
eq 'id', id
}
}
}

How do I represent Neo4j relationship properties in my Graphql Schema?

I have a Neo4j DB with relationships that have properties such as [:FRIENDS {since: "11/2015"}]. I need to represent the "since" property in the GraphQl Schema. RELAY has something call "edges" an apparently this is how they implement this feature but I am not using RELAY.....I didn't see anything in Apollo (maybe I missed it). Can someone show me how to do this?
Ok...so in order to get what I wanted which was to present both the node and the relationship (edge) to graphql I did what I would call a work-around by returning object.assign(node,relationship) to graphql....the downside is that I have to define a type nodeRel {} to receive the combined objects but it works. Also, the node and relationship objects can't have similar named properties. I can now answer the question how long John and Mary are friends or what groups John belongs to and how long he has been a member....Schema snippet:
... memberOf : [Group]
groupStatus : [MemberProfile]
attended : [Meeting]
submittedReport : [Report]
post : [Post]
}
type MemberProfile {
name : String
location : String
created : String
since : String
role : String
financial : Boolean
active : Boolean
}
Resolver:
groupStatus(voter) {
let session = driver.session(),
params = { voterid: voter.voterid },
query = `
MATCH (v:Voter)-[r:MEMBER_OF]->(g:Group)
WHERE v.voterid = $voterid
RETURN g AS group,r AS rel;
`
return session
.run(query, params)
.then(result => {
return result.records.map(record => {
return Object.assign(record.get("group").properties, record.get("rel").properties)
})
})
},
I hope this help someone else....

Criteria in relation between entities - Grails

I have an app with the following entities:
Topic:
class Topic {
UUID id
String description
String name
boolean visibility = true
// Relation
static hasMany = [tests:Test]
...
}
Test:
class Test {
UUID id
boolean active = true
String description
...
static hasMany = [evaluationsTest: Evaluation]
static belongsTo = [topic: Topic, catalog: Catalog]
}
When I show all visible topics to the user I request the query:
def visibleTopics = Topic.findAllByVisibility(true, [sort:"name", order:"asc"])
This query returns me for example: [['English'], ['Spanish']]. Then, I can show the full information about each topic to the user.
But I also want to indicate to the user the number of active test in each visible topic.
For example:
English topic has 2 active test.
Spanish topic has a total of 2 test. One is active and the other is not.
German topic has not any active test.
Then I need a query whose result is: def activeTotalEachTopic = [[2],[1],[0]] and I can pass the activeTotalEachTopic variable to the view (.gsp).
Solution:
From the first query where I can obtain all visible topics, I get the number of active test.
def visibleTopics = Topic.findAllByVisibility(true, [sort:"name", order:"asc"])
def numberActiveTest = []
activeTopics.each { topic ->
def result = Test.findAllByTopicAndActive(topic, true).size()
numberActiveTest.push(result)
}
And I pass to the view both variables.
render view: 'home', model: [activeTopics: activeTopics, numberActiveTest: numberActiveTest]
What you are missing is grouping so that you get the count per group, rather than a total count.
You also need to change the join type from the default inner join to an outer join in order for topics without an active test to return 0. However, a side-effect of this is that it changes how association properties are referenced due to the alias that's created by the join. Something like this:
import static org.hibernate.sql.JoinType.*
def activeTotalEachTopic = Topic.createCriteria().list() {
createAlias('tests', 't', LEFT_OUTER_JOIN)
eq 'visibility', true
or {
eq 't.active', true
isNull 't.id'
}
projections {
groupProperty 'name'
count()
}
order ("name", "asc")
}
Now, another issue to address is that the output would be something like this due to the grouping: [['English', 2],['Spanish', 1],['German', 0]]. So what you can do is collect the second item in each sub-list:
activeTotalEachTopic*.getAt(1)
// Which is the equivalent of...
activeTotalEachTopic.collect { it[1] }

Grails 1:m get most relations

I'm relatively new to Grails.
I have the following
class House {
Integer number
Integer maxResidents
static belongsTo = [town: Town]
}
class Town {
String name
static hasMany = [houses: House]
}
I want to get five towns with most Houses. I have seen the possibility to create a criteria but I can't deal with it now. Can someone support?
Thank you!
As you have a bidirectional association you can do this with a query on House:
def result = House.withCriteria {
projections {
groupProperty("town", "town")
rowCount("numHouses")
}
order("numHouses", "desc")
maxResults(5)
}
This would return you a list of results where each result res has the town as res[0] and the number of houses as res[1]. If you'd prefer each result to be a map giving access to res.town and res.numHouses then you should add
resultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
after the maxResults line (along with the appropriate import at the top of your file).

How to join multiple tables using LINQ-to-SQL?

I'm quite new to linq, so please bear with me.
I'm working on a asp.net webpage and I want to add a "search function" (textbox where user inputs name or surname or both or just parts of it and gets back all related information). I have two tables ("Person" and "Application") and I want to display some columns from Person (name and surname) and some from Application (score, position,...). I know how I could do it using sql, but I want to learn more about linq and thus I want to do it using linq.
For now I got two main ideas:
1.)
var person = dataContext.GetTable<Person>();
var application = dataContext.GetTable<Application>();
var p1 = from p in Person
where(p.Name.Contains(tokens[0]) || p.Surname.Contains(tokens[1]))
select new {Id = p.Id, Name = p.Name, Surname = p.Surname}; //or maybe without this line
//I don't know how to do the following properly
var result = from a in Application
where a.FK_Application.Equals(index) //just to get the "right" type of application
//this is not right, but I don't know how to do it better
join p1
on p1.Id == a.FK_Person
2.) The other idea is just to go through "Application" and instead of "join p1 ..." to use
var result = from a in Application
where a.FK_Application.Equals(index) //just to get the "right" type of application
join p from Person
on p.Id == a.FK_Person
where p.Name.Contains(tokens[0]) || p.Surname.Contains(tokens[1])
I think that first idea is better for queries without the first "where" condition, which I also intended to use. Regardless of what is better (faster), I still don't know how to do it using linq. Also in the end I wanted to display / select just some parts (columns) of the result (joined tables + filtering conditions).
I really want to know how to do such things using linq as I'll be dealing also with some similar problems with local data, where I can use only linq.
Could somebody please explain me how to do it, I spent days trying to figure it out and searching on the Internet for answers.
var result = from a in dataContext.Applications
join p in dataContext.Persons
on p.Id equals a.FK_Person
where (p.Name.Contains("blah") || p.Surname.Contains("foo")) && a.FK_Application == index
select new { Id = p.Id, Name = p.Name, Surname = p.Surname, a.Score, a.Position };
Well as Odrahn pointed out, this will give you flat results, with possibly many rows for a single person, since a person could join on multiple applications that all have the same FK. Here's a way to search all the right people, and then add on the relevant application to the results:
var p1 = from p in dataContext.Persons
where(p.Name.Contains(tokens[0]) || p.Surname.Contains(tokens[1]))
select new {
Id = p.Id, Name = p.Name, Surname = p.Surname,
BestApplication = dataContext.Applications.FirstOrDefault(a => a.FK_Application == index /* && ???? */);
};
Sorry - it looks like this second query will result in a roundtrip per person, so it clearly won't be scalable. I assumed L2S would handle it better.
In order to answer this properly, I need to know if Application and Person are directly related (i.e. does Person have many Applications)? From reading your post, I'm assuming that they are because Application seems to have a foreign key to person.
If so, then you could create a custom PersonModel which will be populated by the fields you need from the different entities like this:
class PersonModel
{
string Name { get; set; }
string Surname { get; set; }
List<int> Scores { get; set; }
List<int> Positions { get; set; }
}
Then to populate it, you'd do the following:
// Select the correct person based on Name and Surname inputs
var person = dataContext.Persons.Where(p => p.Name.Contains("firstname") || p.Name.Contains("surname")).FirstOrDefault();
// Get the first person we find (note, there may be many - do you need to account for this?)
if (person != null)
{
var scores = new List<int>();
var positions = new List<int>();
scores.AddRange(person.Applications.Select(i => i.Score);
positions.AddRange(person.Applications.Select(i => i.Position);
var personModel = new PersonModel
{
Name = person.Name,
Surname = person.Surname,
Scores = scores,
Positions = positions
};
}
Because of your relationship between Person and Application, where a person can have many applications, I've had to account for the possibility of there being many scores and positions (hence the List).
Also note that I've used lambda expressions instead of plain linqToSql for simple selecting so that you can visualise easily what's going on.

Resources