I am trying to make database of Bookstore where customers can buy books. There is lables Book, BookCategory, Customer, Invoice. I am using relation INVOICEITEM between invoice and books. i added property netTotal in invoice to store the sum value of books related in INVOICEITEM with invoice. I tried to get total value of books which is related to invoice.
MATCH (n:Invoice {id:'inv001'})-[:INVOICEITEM]->m RETURN SUM(m.price)
and i tried to change netTotal propery of invoice by using this command.
MATCH (n:Invoice{ id: 'inv001' }) SET n.netTotal = ((n:Invoice {id:'inv001'})-[:INVOICEITEM]-> m RETURN SUM(m.price))
but it returns an error. I am new to neo4j and thank you.
The query to update netTotal on the Invoice node should be:
MATCH (n:Invoice {id: 'inv001'})-[:INVOICEITEM]->(m:Book)
WITH n, SUM(m.price) AS netTotal
SET n.netTotal = netTotal
Calculate the sum of the Book prices using a WITH statement to bring that value along to a SET statement to update the netTotal property on Invoice n. Note that you also need to specify n in the WITH statement to bring that bound variable through the query as well. More info on WITH statement here
Related
I am trying to return the address of a most recent property transaction for every property in my db, along with some details of the transaction. Each property can only have one address but each address may have multiple transactions. So I want the most recent transaction for each property.
This query returns just address of the most recent transaction
MATCH (:Property)<-[:ADDRESS_OF]-(a:Address)<-[:PROPERTY_TRANSACTION]-(p:Transaction_details)
return a.id, a.address_line_1, a.address_line_2, a.address_line_3, a.postcode, max(p.purchase_date)
order by a.id
but if I want to include some transaction details like this
MATCH (:Property)<-[:ADDRESS_OF]-(a:Address)<-[:PROPERTY_TRANSACTION]-(td:Transaction_details)
return a.id, a.address_line_1, a.address_line_2, a.address_line_3, a.postcode, max(td.purchase_date), td.purchase_price, td.lease_type
order by a.id
I get all of the transactions returned for each address.
Is there a way to return the transaction details of the most recent transaction, as well as the address details?
You can get the most recent purchase_date for the given property and then use it as a parameter in the following query:
MATCH (:Poperty{id:"abc123"})<-[:ADDRESS_OF]-(:Address)<-[:PROPERTY_TRANSACTION]-(td:Transaction_details)
// get the most recent purchase_date
WITH max(td.purchase_date) AS purchase_date
MATCH (:Poperty{id:"abc123"})<-[:ADDRESS_OF]-(a:Address)<-[:PROPERTY_TRANSACTION]-(td:Transaction_details {purchase_date : purchase_date})
return a.id, a.address_line_1, a.address_line_2, a.address_line_3, a.postcode, td.purchase_date, td.purchase_price, td.lease_type
order by a.id
This simple query should give you info on the last purchase:
MATCH (:Property{id:"abc123"})<-[:ADDRESS_OF]-(a:Address)<-[:PROPERTY_TRANSACTION]-(p:Transaction_details)
RETURN a.id, a.address_line_1, a.address_line_2, a.address_line_3, a.postcode, p.purchase_date, p.purchase_price, p.lease_type
ORDER BY p.purchase_date DESC
LIMIT 1;
The query orders the results by descending purchase_date and return just one result row (for the latest date).
By the way, I used the label Property instead of Poperty, whihc seems like a typo.
I am working on Neo4j database and I want to replicate the scenario mentioned below,
I have 2 nodes Product and customer. In the customer node I am storing customer id and list of products. and in the product I am storing only productid.
Customer has values {custId:1,products:[1,2,3,4]}
Product has values {productid:1},{productid:2},{productid:3},{productid:4}
Now what I want to do is,
I need to replace all these ids to an autogenerated ids after adding the nodes in the graph database. SOmething like set custId=ID(customer) and productId=ID(product) but what I am stuck at is how to iterate the list of products in customer node and change the product id to auto generated ids.
Any help is appreciated.
The idea of storing the product IDs are automatically generated by database in an array of user property - it is the wrong idea. In all senses.
The graph spirit - is to establish a relationship between the node Customer and its corresponding nodes Product, and then delete the property products from Customer and productid from Product:
MATCH (Customer:Customer)
UNWIND Customer.products as prodID
MATCH (Product:Product {productid: prodID})
MERGE (Customer)-[r:hasProduct]->(Product)
WITH Customer, count(Product) as mergedProduct
REMOVE Customer.products
WITH count(Customer) as totalMerged
MATCH (Product:Product)
REMOVE Product.productid
I am using the IMDB database to find the actor/actress with the highest rating and was in the most movies in a given year. I am trying to join the actors dataset with their ratings. Then filter the year and sort the data based on highest rating and movie count.
joinedActorRating = JOIN ratings by movie, actors BY movie;
actorRating = FOREACH joinedActorRating GENERATE *;
actorsYear = FILTER actorRating BY(year MATCHES '2000');
groupedYear = GROUP actorsYear BY (year,rating,firstName,lastName);
aggregatedYear = FOREACH groupedYear GENERATE group, COUNT (actorsYear) AS movieCount;
unaggregatedYear = FOREACH aggregatedYear GENERATE FLATTEN(group) AS (year,rating,firstName,lastName);
sortRating = ORDER unaggregatedYear BY rating ASC, count ASC;
dump sortRating;
The compiler says that the second line is an "Invalid field projection" but I am not sure how to access the year field after joining the two datasets. Does anyone know how to fix this?
After your join, you need to project the fields you want through to your current relation.
joinedActorRating = JOIN ratings by movie, actors BY movie;
actorRating = FOREACH joinedActorRating GENERATE ratings::movie as movie
, ratings::rank as rank, ratings::year as year, actors::firstName as firstName
, actors::lastName as lastName;
I'm not sure which columns are in which table (other than movie is in both) because you didn't include the two tables, so I just guessed. You can modify the projections as needed.
I have two tables,
Order (ID, Value)
and
OrderType (ID, Name [Quote, Sale, Purchase, etc])
I want to get the total number of orders in each type (count) and the total value of those orders per type (sum)
I can get these individually using
Order.group(:order_type).count(:id)
and
Order.group(:order_type).sum(:value)
I would like to perform these in one query, the equivalent to the following SQL
SELECT
order_types.id, Count(*) as total_count, Sum(orders.value) As total_value
FROM
order
JOIN
order_types ON orders.order_type_id = order_types.ID
GROUP BY
order_types.id
The query should also return the full OrderType object so I can display the name in my view
Since ActiveRecord does not support multiple aggregation functions in the same query, you need to do a bit of raw SQL to achieve this.
grouped_sales = OrderType
.select('order_types.id, order_types.name,
sum(orders.value) as sale, count(*) as purchase')
.join('JOIN orders ON orders.order_type_id = order_types.id')
.group('order_types.id')
The point to note here is that you need to use an existing column in OrderType as the alias for your aggregated columns. Here you will get the OrderType object as well.
To access the result:
id -> grouped_sales.first.id
name -> grouped_sales.first.name
total orders -> grouped_sales.first.sale
order value -> grouped_sales.first.purchase
There is even better solution, just:
.pluck('sum(orders.value), count(*)').first
Nowadays pluck+arel will do the job.
model = Model.arel_table
Model.group(:order_type).pluck(model[:id].count, model[:value].sum)
Also appending with .order(:order_type) may be needed if there applied default ordering by ID.
Let say a book model HABTM categories, for an example book A has categories "CA" & "CB". How can i retrieve book A if I query using "CA" & "CB" only. I know about the .where("category_id in (1,2)") but it uses OR operation. I need something like AND operation.
Edited
And also able to get books from category CA only. And how to include query criteria such as .where("book.p_year = 2012")
ca = Category.find_by_name('CA')
cb = Category.find_by_name('CB')
Book.where(:id => (ca.book_ids & cb.book_ids)) # & returns elements common to both arrays.
Otherwise you'd need to abuse the join table directly in SQL, group the results by book_id, count them, and only return rows where the count is at least equal to the number of categories... something like this (but I'm sure it's wrong so double check the syntax if you go this route. Also not sure it would be any faster than the above):
SELECT book_id, count(*) as c from books_categories where category_id IN (1,2) group by book_id having count(*) >= 2;