Join two matches in Neo4J - group-by

Im new in neo4j and I need help I have the following problem:
I have a little database that describes the reviews of a movie.
Basically, the nodes are set like this:
(Critic)-[review]->(Movie)
The review relation has an attribute called stars which describe the stars given by the critic to the movie.
An example is this.
(Jon)-[review{stars:4}]->(Titanic)
(Jon)-[review{stars:3}]->(Avatar)
(Alf)-[review{stars:4}]->(Avatar)
I have an excersice that Ive been asked to get all the critics name that have given 4 star in their reviews but never gave an 3.
The answer in this case must be Alf
First I have a match for the critic that gave 4 stars (Jon,Alf) and then I get a match for the critics that gave 3 (Jon). Now I want to inner join them to only get Alf. How I can do this? This is what ive got so far:
MATCH (N:Critic)-[r:REVIEWS]->()
WHERE r.Stars=4 with distinct n.Name as names4
MATCH (m:Critic)-[s:REVIEWS]->()
WHERE s.Stars=3 with distinct m.Name as names3, names4 as names4
RETURN distinct [n IN names4 WHERE NOT n IN names3 ] as listC
As you se I have the two matches but I dont know how to make the inner join

You can simplify your query a lot. Cypher is quite cool and expressive :)
MATCH (c:Critic)
WHERE (c)-[:REVIEWS{Stars:4}]->() AND
NOT (c)-[:REVIEWS{Stars:3}]->()
RETURN c.name as critic_name
The way you have tried would also work, you have to change the query to:
// Get all the critics who given a score 3
MATCH (c:Critic)-[r:REVIEWS]->()
WHERE r.Score = 3
WITH collect(distinct c) as score_3_critics
// get all critics who gave a score 4, but filter out
// critics who gave it 3
MATCH (c1:Critic)-[r1:REVIEWS]->()
WHERE r1.Score = 4 AND NOT c1 in score_3_critics
RETURN distinct c1.name as critic
I always preferred filtering out results as in the first example. When you have multiple ways of defining a query to get the same results, it is best to check the execution plan with the PROFILE statement to see which query is faster

Related

Postgresql Query - Return all matching search terms for each result row when using an ANY query and LIKE

Essentially what I'm trying to figure out is if there is a way to return all matching search terms in addition to the matched row when running a query that looks up a list of items using ANY or IN. In most cases the search term will exactly match the returned column value but in cases such as text search or with certain extensions like IP4r this is not always the case. In addition, you can have multiple search terms match on a single row.
To make this concrete suppose this is my query:
SELECT id, item_name, description FROM items WHERE description LIKE ANY('{%gaming%, %computer%, %socks%, %men%}');
and it returns the following two rows:
id, item_name, description
1, 'computer', 'super fast gaming computer that will help you win'
5, 'socks', 'These socks are sure to please the men in your family'
What I'd like to know is which original search terms map to the result row that was returned. In other words, I'd like the returned rows to look like this:
id, search_terms, item_name, description
1, '{%gaming%, %computer%}', 'computer', 'super fast gaming computer that will help you win'
5, '{%socks%, %men%}', 'socks', 'These socks are sure to please the men in your family'
Is there a way to efficiently do this in PostgreSQL? In the example above we're using LIKE with strings but in my real-world scenario I'm using the IP4r extension to do IP lookups against CIDR ranges where you can have multiple IP addresses in the same returned CIDR range.
I previously asked this question: PostgreSQL 9.5: Return matching search terms in each result row when using LIKE which used a CASE statement to almost solve the problem I'm describing here.
The added complexity in the scenario above is that you can have multiple search terms match a single row (e.f., gaming and computer are both matches for the description super fast gaming computer that will help you win). If you use a CASE statement then only the first match in the CASE statement gets set as the search term and you miss any other matching search terms.
Thank you for your help!
This would be a way using VALUES:
SELECT i.id, i.item_name, i.description, m.pat
FROM items AS i
JOIN (VALUES ('%gaming%'), ('%computer%'), ('%socks%'), ('%men%')) AS m(pat)
ON i.description LIKE m.pat;

pattern match with SQL

I am still new to writing SQL queries.
I am trying to find all rows that have a specific pattern match that is a combination of a constant_string_1, followed by any combination of integers, followed by another constant_string_2.
So for ex I am trying to find all rows that match:
Roger_117788_Maryland
Roger_188_Maryland
Roger_211_Maryland
I have tried:
select * from cust where cust_string ilike 'Roger_[0-9]_Maryland'
However that query doesn't generates 0 results. I have also tried using [:digit:] and \d but neither has worked. Not sure what I am doing wrong. Any help is appreciated.
Thanks
You can work this with just "%". Ilike may cause issues, so you can use "like" after transforming your column to lower case to match all cases.
select * from cust where lower(cust_string) like 'roger%maryland';
The following worked. I ended up using regexp_instr, but it was the multiple-digit match was where my query was not giving the results.
select * from cust where regexp_instr(cust_string, '^(Roger_)[0-9]{1,10}__Maryland$') > 0'
This did two things. One it achieved the result set above, i.e. Roger_188_Maryland, etc. But it also removed any multiple occurrences of the patterns, so for ex it eliminated a multi-match like Roger_188_Roger_211_Maryland. Thanks everyone for your help.

Recursive CTE or self joins with circular use case

I have two tables:
CompanyCases is a table of companies and case numbers like this:
CompanyId, CaseNumber
CaseRelations is a table of cases and their related cases like this:
CaseNumber, RelatedCase
There can be several companies related to one case, and one case can be related to several companies.
I need a query that will give me all related cases for a company id. The trick is that when I find the related case, that can also have a related case, which can have a related case etc.
My first assumption was that it would not be that deep, so I could just do self joins like:
Select
cc.CompanyId,
cc.CaseNumber,
CR1.CaseNumber,
CR1.RelatedCase,
CR2.CaseNumber,
CR2.RelatedCase
FROM CompanyCases cc
LEFT JOIN CaseRelations CR1 ON CR1.CaseNumber = cc.CaseNumber
LEFT JOIN CaseRelations CR2 ON CR2.CaseNumber = CR1.RelatedCase
And then keep joining as many levels as is needed. The problem is that the cases loop. So it can go like this:
CaseNumber RelatedCase
1 2
2 3
3 1
So I can keep joining forever without reaching a full column of nulls. Also it is at least 5 levels deep so this is not a great solution. I don't mind using recursive CTEs either but I think I will get the same problem with the circular cases.
I hope I described it well enough - Does anyone know how to solve this?
Thanks in advance :)

Oracle 10g, how to query numerical value, (years) with specific limits on results

So the question I am posed with is to take the years produced of all of the movies in two genre's, (SH and CH) an then print out a list of all the movies, (title and year), that were produced before any of the movies in my specific genre were produced. I have this:
SELECT x.title "Title", x.yr "Year"
FROM movies x
WHERE EXISTS (SELECT x FROM movies y
WHERE y.genre IN ('SH', 'CH') AND y.yr < x.yr)
ORDER BY yr;
but it's producing all sorts of titles that were produced during and after the two genres had any of their movies produced. I would think that the less than would limit the results to anything under 1965, (the oldest move in either genre), but it doesn't, but if I use the greater than operator it does, (although it still pumps out newer results as well, so that doesn't work either)
Does anybody see what it is I am missing here? Thanks for any help.
Got it!
I just needed to use the operator differently
WHERE yr <
(SELECT insert stuff here); // this is homework so I can't post the full code
Seems I was just over complicating things.

How to get a number of rows for given query?

I would like to get a number (like 5, 1000, etc.) of rows for given query. However method "count" for query gives me ColumnOps.CountAll -- and I don't know how to get the number.
See SQ wiki for example:
https://github.com/szeiger/scala-query/wiki/Counts
(for(...) yield ...).count
Obviously one step is missing, and the question is -- what is that step?
I use explicit query route because the count is for join.
You can get a number to do like this:
val q = (for(...) yield ...).count
q.first
I was confused by ScalaQuery document too.
I recommend that you check SQ's tests to know how it use.