here'ActiveRecord - Optional matching - ruby-on-rails

If I have a Case model, which has multiple relations, how can I create a query that matches one or more relation.
Example: Case has Company which has a name. Case also has many Sessions which have a response. I want to create a query where I can search for one or more of those matching (Company name AND/OR Session response). If there's no company, but a Session response matches, it should return and visa versa.
What would be the best way to achieve this?

You can do the following:
sql_cond = 'companies.id IS NOT NULL OR sessions.id IS NOT NULL')
Case.includes(:company, :sessions).references(:company, :session).where(sql_cond)
It will return the Case records having at least 1 Company OR Session record associated.

Related

Handling multiple search fields in cypher or py2neo

Just getting started with cypher. Trying to understand best way to handle this case. I need to query database based on several search fields(not always present). Wondering what's the best way to handle it. For e.g I collect data from html post request
storeName = request.form['storeName']
storeCountry = request.form['storeCountry']
storeState = request.form['storeState']
storeCity = request.form['storeState']
I'm using flask framework, so defined this function with py2neo to handle search fields. Not sure if I got it right but if all fields are set correctly, hopefully, something like this should work fine
def get_search_results(skipNumber,limitNumber, storeName, storeCountry,storeState, storeCity):
query = """
MATCH (store:store) WHERE store.name = {storeName}
MATCH (store:store)-[:IS_IN_COUNTRY]->(c:Country) WHERE c.name = {storeCountry}
MATCH (store:store)-[:IS_IN_STATE]->(s:State) WHERE s.name = {storeState}
MATCH (store:store)-[:IS_IN_CITY]->(ct:City) WHERE ct.name = {storeCity}
RETURN user SKIP {skip} LIMIT {limit}
"""
return graph.cypher.execute(query, skip=skipNumber, limit=limitNumber, storeName=storeName, storeCountry=storeCountry, storeState=storeState, storeCity=storeCity)
I would like to handle cases where users only submit one or two fields.
For e.g
1. if users only submit store name, then I should be able to return all the nodes with that store name.
2. If users only submit store name and country. Return all the stores nodes in that country
3. If users only submit store name, country and state. Return all the stores nodes in that country and state
Is it possible to write a generic cypher query to handle such scenarios i.e ignore cases where field is not set or is None or I need to write different queries to handle each case ?

In- and excluding nodes in a cypher query

Good morning,
I want to build a structure in Neo4J where I can handle my users and groups (kind of ACL). The idea is to have for each user and for each group a node with all the details. The groups shall become a graph where a root group will have sub-groups that can have also sub-groups without limit. The relation will be -[:IS_SUBGROUP_OF]- - so far nothing exciting. Every user will be related to a group with -[:IS_MEMBER_OF]- to have a clear assignment. Of course a user can be a member of 1 or more groups. Some users will have a different relation like -[:IS_LEADER_OF]- to identify teamlead of the groups.
My tasks:
Assignment: I can query each member of a group with a simple query, I can even query members of the subgroups using the current logged in and asking user:
MATCH (d1:Group:Local) -- (c:User)
MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1)
WHERE c.login = userLogin
RETURN DISTINCT d.lastname, d.firstname
I get every related user to every group of the current user and below (subgroups). Maybe you have a hint how I cna improve the query or the model.
Approval
Here I am stucked as I want to have all users of the current group from the querying user and all members of all subgroups - except the leader of the current group. The reason behind is that a teamlead shall not be able to approve actions for himself but though for every other member of his group and all members of subgroups including their teamleads.
I tried to use the relations -[:IS_LEADER_OF]- to exclude them but than I loose also the teamleads of the subgroups. Does anyone has an idea how I would either change the model or how I can query the graph to get all users except the teamlead of the current group?
Thanks for your time,
Balael
* EDIT *
I think I am getting close, I just need to understand the results of those both queries:
MATCH (d:User) -- (g:Group) WHERE g.uuid = "xx"
RETURN d.lastname, d.firstname
Returns all user in this group no matter what relationship (leader / member)
MATCH (d:User) -- (g:Group), (g)--(c:User{uuid:"yy"})
RETURN d.lastname, d.firstname
Returns all user of that group except the user c. I would have expected to get c as well in the list with d-users as c is part of that group and should be found with (d:User).
I do not understand the difference between both queries, maybe someone has a hint for me?
You can simplify your query slightly (however this should not have an impact on performance):
MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1:Group:Local)--(c:User{login:"userlogin"})
RETURN DISTINCT d.lastname, d.firstname
Don't completely understand your question, but I assume you want to make sure that d1 and c are not connected by a IS_LEADER_OF relationship. If so, try:
MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1:Group:Local)-[r]-(c:User{login:"userlogin"})
WHERE type(r)<>'IS_LEADER_OF'
RETURN DISTINCT d.lastname, d.firstname
following up on * EDIT * in the question
In a MATCH you specify a path. By definition a path does not use the same relationship twice. Otherwise there is a danger to run into infinite recursion. Looking at the second query in the "EDIT" section above: the right part matches yy's relationship to the group whereas the left part matches all user related to this group. To prevent multiple usage of the same relationship the left part does not hit use yy

Returning like count and whether current user liked a post in Cypher

I have a possibly bone-headed question, but I'm just starting out with Neo4j, and I hope someone can help me out with learning Cypher syntax, which I've just started learning and evaluating.
I have two User nodes, and a single NewsPost node. Both users LIKE the NewsPost. I'm able to construct a Cypher query to count the likes for the post, but I'm wondering if it's also possible to check if the current user has liked the post in the same query.
What I have so far for a Cypher query is
match (p:NewsPost)<-[r:LIKES]-(u:User)
where id(p) = 1
return p, count(*)
Which returns the post and like count, but I can't figure out the other part of "has the current user liked this post". I know you're not supposed to filter on <id>, but I learned that after the fact and I'll go back and fix it later.
So first, is it possible to answer the "has the current user liked this post" question in the same query? And if so, how do I modify my query to do that?
The smallest change to your query that adds a true/false test for a particular user liking the news post would be
MATCH (p:NewsPost)<-[r:LIKES]-(u:User)
WHERE ID(p) = 1
RETURN p, count(r), 0 < size(p<-[:LIKES]-(:User {email:"michael#nero.com"}))
This returns, in addition to your query, the comparison of 0 being less than the size of the path from the news post node via an incoming likes relationship to a user node with email address michael#nero.com. If there is no such path you get false, if there is one or more such paths you get true.
If that does what you want you can go ahead and change the query a little, for instance use RETURN ... AS ... to get nicer result identifiers, and so on.
What you are looking for is Case.
In your database you should have something unique for each user (id property, email or maybe login, I don't know), so you have to match this user, and then match the relation to the post you want, using case you can return a boolean.
Example:
Optional Match (u:User{login:"Michael"})-[r:LIKES]-(p:newPost{id:1})
return CASE WHEN r IS NULL THEN false ELSE true END as userLikesTopic
If you want to get the relation directly (to get a property in it as example) you can remove the CASE part and directly return r, if it does not exist, null will be returned from the query.

In Neo4J, how to retrieve nodes, along with additional information about if they are connected or not to a specific node

I have nodes with Product Information (P). Every time a user likes the product, relationship [L] is created connecting user (U)->[L]->[P]
Now I need to retrieve a set of Product nodes based on specific condition, but also need to return additional information that whether they are liked by a specific user or not.
So if the Product Structure is like
{ Product Name, Price }
If lets say, 1 out of the 3 products in question is liked by user X, the result set I need may look something like this
[{Product1, 29.00, true}, {Product2, 39.00, false}, {Product3, 25.00, false}]
Here true refers to the fact that user has liked Product1 and has not liked Product2.
I am not sure how to write such a query that includes this additional information of whether the returned nodes are liked or not
I think something like this will suit your needs.
Match all the products. You will want to scope this match down in some way.
Optionally match user likes per product.
Return collection of maps that contain name, price and like status.
match (p:Product)
optional match (u:User)-[:LIKES]->p
with {product:p.name, price:p.price, like: case when u is null then false else true end} as Product_Detail
return collect(Product_Detail)

How can i show only the results that have an association in rails 3?

I am trying do a where statement that only brings up results that have that particular association.
For example:
A company has many statuses through company statuses. They can have multiple statuses that can be gold, silver, and/or bronze, or none at all. I am trying to have my results only return the companies that have a status (gold, silver, and/or bronze) and not the ones have no statuses.
Your use case isn't very well articulated, but I think what you're wanting is to find Companies that have a status that matches a specified one:
Company.includes(:statuses).where('status.name = ?', params[:status_name])
That should give you the right query, assuming a Company has_many :statuses.
From the Ruby on Rails guide on Active Record Associations:
4.2.3 How To Know Whether There’s an Associated Object?
To know whether there’s and associated object just check
association.nil?:
if #supplier.account.nil?
#msg = "No account found for this supplier"
end
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
Company.joins(:statuses).select("DISTINCT(companies.id), companies.*, statuses.*")
If you have an associative table called companies_statuses:
to retrieve all companies with at least one status
Company.where("EXISTS (select 1 from companies_statuses where companies_statuses.company_id = companies.id)")
to retrieve all companies with no status
Company.where("NOT EXISTS (select 1 from companies_statuses where companies_statuses.company_id = companies.id)")

Resources