There are many 'User' nodes any user can send money to other user.
(:User)-[r:SENT_MONEY]->(:User)
here r has properties
created_at = timestamp()
money_transferred = amount of money transferred
How can I find one users last sent money to other user or last received money from other user in single query.
I have tried this query
MATCH (from:User)-[r:SENT_MONEY]->(to:User)
where (id(from)=1234 OR id(to)=1234)
return max(r.created_at) as sent_at,
r.money_transferred as amount, from.username,to.username
order by last_amount_sent_at DESC
Results are like:
sent_at | amount | from.username | to.username
1408961056 | 20 | user1 | user2
1408961041 | 30 | user2 | user1
1408961028 | 50 | user1 | user3
1408951163 | 20 | user4 | user1
1408951140 | 10 | user1 | user4
By this query user "user1" records with "user2" and "user4" comes twice. It should come single with last transaction between those users like
sent_at | amount | from.username | to.username
1408961056 | 20 | user1 | user2
1408961028 | 50 | user1 | user3
1408951163 | 20 | user4 | user1
Satish,
Here's a query that I think will do what you need.
MATCH (m:User {username : 'user1'})-[r:SENT_MONEY]-(n:User)
WITH m, n, collect(r) AS rs, max(r.created_at) AS p
WITH m, n, filter(x IN rs WHERE x.created_at = p) AS l
RETURN STARTNODE(l[0]), ENDNODE(l[0]), l[0]
For each pair of users you collect the transactions and find the last one, then return the start and end node of that last transaction.
Grace and peace,
Jim
In answer to your further question, you can modify the query like this (for example) to avoid the collection vs element error:
MATCH (m:User {username : 'user1'})-[r:SENT_MONEY]-(n:User)
WITH m, n, collect(r) AS rs, max(r.created_at) AS p
WITH m, n, filter(x IN rs WHERE x.created_at = p) AS o
RETURN m, n, o[0], (o[0]).money_transferred
Related
I have two tables, user and car with below mentioned rows and columns.
Table 1: user
id | name
---------
1 | ABC
2 | PQR
3 | XYZ
Table 2: car
id | user_id | is_serviced
--------------------------
1 | 1 | 0
2 | 1 | 1
3 | 2 | 0
4 | 2 | 0
User ABC has two cars - only one car has been serviced.
User PQR has two cars - none of the cars has been serviced.
User XYZ has no cars yet.
I want to fetch records as per below output where I want to display all users who have at least one serviced car.
The query I have (I do not understand what clause to use for my query to get the expected output):
SELECT u.user_name,
CASE WHEN c.is_serviced = true THEN 'YES' ELSE 'NO' END AS has_serviced_car
FROM "user" u
LEFT JOIN car c ON c.user_id = u.id;
The output of the above query:
user_name | has_serviced_car
-----------------------
ABC | No
ABC | Yes
PQR | No
PQR | No
XYZ | No
Expected output:
user_name | has_serviced_car
-----------------------
ABC | Yes
PQR | No
XYZ | No
Please note that user XYZ has no cars still I need it to be displayed.
Got it!
SELECT u.name AS user_name,
CASE
WHEN u.id IN (SELECT u.id
FROM "user" u
LEFT JOIN car c ON u.id = c.user_id
WHERE c.is_serviced = true) THEN 'Yes'
ELSE 'No' END AS has_serviced_car
FROM "user" u
LEFT JOIN car c ON u.id = c.user_id
GROUP BY user_name, u.id
ORDER BY u.id;
I have one table with 3 columns are below
+---------------------------------------+
| id | name | parent_id |
+---------------------------------------+
| -1 | / | |
| 1 | Organization | -1 |
| 2 | United States | 1 |
| 3 | Business Analyst | 1 |
| 4 | Human Resources | 1 |
| 5 | Benefits Manager | 4 |
| 6 | Metropolitan Plant | 2 |
| 7 | Administration | 6 |
+---------------------------------------+
And my query is like this
SELECT CONCAT(parent.name, '/', child.name) AS path
FROM table_name AS child INNER JOIN table_name AS parent
ON child.id = parent.parent_id
I am expecting output as below.
/Organization
/Organization/United States
/Organization/Business Analyst
/Organization/Human Resources
/Organization/Human Resources/Benefits Manager
/Organization/United States/Metropolitan Plant
/Organization/United States/Metropolitan Plant/Administration
Ok...there might be a more elegant way to do this...especially with using do loops...but with what immediately comes to mind, you may need to do several joins. Is the maximum level low? I hope so. Here's an idea, but it's messy and may require a lot of spool depending on your data size:
SELECT CONCAT(path2, '/', D.name) AS path3
FROM
(SELECT CONCAT(path1, '/', B.name) AS path2
FROM
(SELECT CONCAT(parent.name, '/', child.name) AS path1
FROM table_name AS parent LEFT JOIN table_name AS child
ON child.id = parent.parent_id) AS A
LEFT JOIN TABLE_NAME AS B
ON A.id = B.parent_id) AS C
LEFT JOIN TABLE_NAME AS D
ON C.id = D.parent_id
The above code would only take it up to 3 levels. If something better comes to mind, I'll post it.
Suspect you're expected to use a hierarchical query here
WITH foo (id, parent_id, name, fullpath)
AS (SELECT id,
parent_id,
name,
'/' AS fullpath
FROM table_name
WHERE parent_id IS NULL
UNION ALL
SELECT m.id,
m.parent_id,
m.name,
f.fullpath || m.name || '/' AS fullpath
FROM foo f JOIN table_name m ON (m.parent_id = f.id))
SELECT fullpath FROM foo
WHERE id > 0
That'll be pretty close.
I have two queries that are similar:
StoreQuery.group(:location).count(:name)
vs
StoreQuery.group(:location).select('DISTINCT COUNT(name)')
I was expecting the results to be exactly the same but they're not. What is the difference between the two?
The difference is that the first query counts all names, and the second query counts unique names, ignoring duplicates. They will return different numbers if you have some names listed more than once.
With this sample data
id | name | location |
---+------+----------+
1 | NULL | US
2 | A | UK
3 | A | UK
4 | B | AUS
Let check the generated queries the results
1st query
StoreQuery.group(:location).count(:name)
Generated query:
SELECT location, COUNT(name) AS count FROM store_queries GROUP BY location
Result:
{US => 0, UK => 2, AUS => 1}
2nd query
StoreQuery.group(:location).select('DISTINCT COUNT(name)')
Generated query:
SELECT DISTINCT COUNT(name) FROM store_queries GROUP BY location
Result:
ActiveRecord::Relation [StoreQuery count: 0, StoreQuery count: 1, StoreQuery count: 1]
# Mean {US => 0, UK => 1, AUS => 1}
So the differences will be:
|1st query | 2nd query |
|----------+-----------+
# returned fields| 2 | 1 |
distinction | no | yes |
Btw, rails supports this:
StoreQuery.group(:location).count(:name, distinct: true)
if u want to query for a specific node type and related nodes solution is simple, by using collect function we can achieve this goal like return country, collect(city) as c
but what we should do if we need to retrieve a data tree like bloodline or user->post->comment->like
is there any solution to handle this kind of data in cypher output?
Given the following graph:
CREATE (user:User { id: 0 })
CREATE (post:Post)
CREATE (comment:Comment)
CREATE (user)-[:POSTED]->(post)<-[:ON]-(comment)<-[:COMMENTED]-(user)
CREATE (user)-[:LIKES]->(comment)
Retrieve the variable length paths using the following query:
MATCH (user:User { id: 0 })
MATCH p=(user)-[*]->(post)
RETURN p
ORDER BY length(p) DESC
Which results in the following output:
+----------------------------------------------------------------+
| p |
+----------------------------------------------------------------+
| [Node[6]{id:0},:COMMENTED[8] {},Node[8]{},:ON[7] {},Node[7]{}] |
| [Node[6]{id:0},:LIKES[9] {},Node[8]{},:ON[7] {},Node[7]{}] |
| [Node[6]{id:0},:POSTED[6] {},Node[7]{}] |
| [Node[6]{id:0},:COMMENTED[8] {},Node[8]{}] |
| [Node[6]{id:0},:LIKES[9] {},Node[8]{}] |
+----------------------------------------------------------------+
5 rows
19 ms
To see what is related and how, run the following query:
// What is related, and how
MATCH (a)-[r]->(b)
WHERE labels(a) <> [] AND labels(b) <> []
RETURN DISTINCT head(labels(a)) AS This, type(r) as To, head(labels(b)) AS That
LIMIT 10
Which has the results:
+-------------------------------------+
| This | To | That |
+-------------------------------------+
| "User" | "POSTED" | "Post" |
| "User" | "COMMENTED" | "Comment" |
| "User" | "LIKES" | "Comment" |
| "Comment" | "ON" | "Post" |
+-------------------------------------+
4 rows
139 ms
I'm trying to create an inbox for messaging between users.
Here are the following tables:
Messsages
Id | Message_from | message_to | message
1 | 2 | 1 | Hi
2 | 2 | 1 | How are you
3 | 1 | 3 | Hola
4 | 4 | 1 | Whats up
5 | 1 | 4 | Just Chilling
6 | 5 | 1 | Bonjour
Users
Id | Name
1 | Paul
2 | John
3 | Tim
4 | Rob
5 | Sarah
6 | Jeff
I'd like to display an inbox showing the list of users that the person has communicated and the last_message from either users
Paul's Inbox:
Name | user_id | last_message
Sarah| 5 | bonjour
Rob | 4 | Just Chilling
Tim | 3 | Hola
John | 2 | How are you
How do I do this with Active Records?
This should be rather efficient:
SELECT u.name, sub.*
FROM (
SELECT DISTINCT ON (1)
m.message_from AS user_id
, m.message AS last_message
FROM users u
JOIN messages m ON m.message_to = u.id
WHERE u.name = 'Paul' -- must be unique
ORDER BY 1, m.id DESC
) sub
JOIN users u ON sub.user_id = u.id;
Compute all users with the latest message in the subquery sub using DISTINCT ON. Then join to
table users a second time to resolve the name.
Details for DISTINCT ON:
Select first row in each GROUP BY group?
Aside: Using "id" and "name" as column names is not a very helpful naming convention.
How about this:
#received_messages = current_user.messages_to.order(created_at: :desc).uniq
If you want to include messages from the user as well, you might have to do a union query, or two queries, then merge and join them. I'm just guessing with some pseudocode, here, but this should set you on your way.
received_messages = current_user.messages_to
sent_messages = current_user.messages_from
(received_messages + sent_messages).sort_by { |message| message[:created_at] }.reverse
This type of logic is belongs to a model, not the controller, so perhaps you can add this to the message model.
scope :ids_of_latest_per_user, -> { pluck('MAX(id)').group(:user_id) }
scope :latest_per_user, -> { where(:id => Message.latest_by_user) }
Message.latest_per_user