MATCH blocks for Neo4j, to prevent "null property value"-error - json

I'm creating Person labelled nodes from a JSON, in Neo4j, with something like this (JSON is blank because of privacy/to much data):
WITH [] AS contacts
UNWIND contacts AS contact
But sometimes the person data doesn't have a first name and or last name,
so this query fails:
MERGE(personBind:Person {first_name: contact.first_name, last_name: contact.last_name})
It gives this error:
Cannot merge node using null property value
Is it possible to have a MATCH, with something like these blocks in it:
NOT MATCH
CANT CREATE (Don't do something with person, but make other stuff)
CAN CREATE (Make person and relations to person, and make other stuff)
ON MATCH (Make relations to person, and make other stuff)
This is the other code, that I need to use,
but is to "attached" to person (Sometimes will fail):
FOREACH (addr IN contact.addrs |
MERGE addrPath=((zipBind:ZipCode {zipcode: addr.zipcode})-[nz:NUMBER_IN_ZIPCODE]->(houseBind:House {number: addr.housenumber})<-[ns:NUMBER_IN_STREET]-(streetBind:Street))
MERGE(personBind)-[:WORKS_AT]->(houseBind)
)
FOREACH( phoneValue IN contact.phone | MERGE(phoneBind:Phone {number: phoneValue.value}) MERGE(personBind)-[:REACHABLE_BY]->(phoneBind) )
FOREACH( emailValue IN contact.email | MERGE(emailBind:Email {email: emailValue.value}) MERGE(personBind)-[:REACHABLE_BY]->(emailBind) )

Break your query up into two parts: first, address the universal parts (the contact data), then filter your query for people who have a first and last name and merge the other components.
UNWIND [] AS contact
WITH contact
UNWIND contact.addrs AS addr
MERGE (zipBind:ZipCode {zipcode: addr.zipcode})
MERGE (streetBind:Street) # does this need properties, like a name?
MERGE (streetBind) - [:NUMBER_IN_STREET] ->(houseBind:House {number: addr.housenumber})
MERGE (zipBind) -[:NUMBER_IN_ZIPCODE]-> (houseBind)
WITH contact, COLLECT(houseBind) AS houseBinds
UNWIND contact.phone AS phoneValue
MERGE(phoneBind:Phone {number: phoneValue.value})
WITH contact, houseBinds, COLLECT(phoneBind) AS phoneBinds
UNWIND contact.email AS emailValue
MERGE(emailBind:Email {email: emailValue.value})
WITH contact, houseBinds, phoneBinds, COLLECT(emailBind) AS emailBinds
<NOW FILTER FOR CONTACTS WITH PERSON DATA>
WHERE exists(contact.first_name) AND EXISTS(contact.last_name)
MERGE(personBind:Person {first_name: contact.first_name, last_name: contact.last_name})
FOREACH( housebind IN housebinds|MERGE(personBind)-[:WORKS_AT]->(houseBind))
FOREACH( phoneBind IN phoneBinds | MERGE(personBind)-[:REACHABLE_BY]->(phoneBind) )
FOREACH( emailBind IN emailBinds | ) MERGE(personBind)-[:REACHABLE_BY]->(emailBind) )
If you need rows that don't have Person names later in the query, you can change the filter step to create two lists, one that has names, one that doesn't, but that's more complicated than your question requires.
EDITED: Your Street nodes have no properties, so this query is going to find the same street for every row. Is there something you want to assign there?

Use a combination of foreach and case:
UNWIND [ {lastName: 'Fam1', firstName: 'Nam1'}, {lastName: 'Fam2'} ]
as contact
FOREACH( x in CASE WHEN ( NOT contact.lastName is NULL AND
NOT contact.firstName is NULL
) THEN [1] ELSE [] END |
MERGE (personBind:TMP:Person {first_name: contact.firstName,
last_name: contact.lastName}
)
)
http://www.markhneedham.com/blog/2014/06/17/neo4j-load-csv-handling-conditionals/

Related

Finding UnMatched Users from a Table

I have an array of usernames as users:[Test1,Test2].I have to loop through this array and should find the unmatched usernames from table b.I have written the query as below:
def usersArray = []
def find
params.users.each{
find= sql.rows("select distinct name from table a,table b where a.id=b.id and b.name!=:n",[n:it])
if(find.size >0)
{
def usList = ["nm":find]
usersArray.push(usList);
}
}
From the above solution in my result i see both Test 1 and Test 2 even though they match.How should i change the query to display only the unmatched users?
Another way - count the existing rows which match the parameter name, then push those that have zero (forgive the bad syntax):
....
numberFound = sql.rows("select count(*)from table a where a.name=:n",[n:it])
if(numberFound = 0)
{
def usList = ["nm":find]
usersArray.push(usList);
}
...
Here is an example of how you might go about solving this problem. This assumes you have a domain class called User with a property called name which you want to match on.
// given a list of user names
​List users = ['Test1', 'Test2', 'Test3', 'Test4']
// find all the users that match those names, and collect the matched names into a List
List matched = User.findAll("from User as u where u.name in (:names)", [names: users]).collect { it.name }
// remove the matched names from the user list and arrive at an 'unmatched' names list
List unmatched = users.minus(matched)​​
This was written off the top of my head so please forgive any typos or other random assumptions.
UPDATED
Since you seem set on using SQL you might be able to do something like this instead
List users = ['Test1', 'Test2', 'Test3', 'Test4']
List placeholders = []
users.each { placeholders << '?' }
String select = "select distinct name from table a,table b where a.id=b.id and b.name in (${placeholders.join(',')})"
List matched = sql.rows(select, users)
List unmatched = users.minus(matched)​​

Query products with Doctrine ind Akeneo

I like to fetch some products from the Database with a custom command in akeneo.
I'm using the ProductRepositoryInterface
public function read()
{
return $this->repository->findBy(
[
'enabled' => true,
'family' => ['projector', 'projector_child', 'projector_parent'],
]
);
}
And this is the generated query:
SELECT t0.id AS id1, t0.is_enabled AS is_enabled2, t0.created AS created3, t0.updated AS updated4, t0.family_id AS family_id5 FROM pim_catalog_product t0 WHERE t0.is_enabled = ? AND t0.family_id IN (?)
As you can see in the Statement, the family is threaded as an Id. But I want to search by the family code.
What I have to change?
In the Pim/Component/Catalog/Model/AbstractProduct is an attribute for the family and familyId. So there have to be a way to query for the family code.
Maybe it's relevant, but this is an Akeneo 1.6 installation.
So first, to query products in Akeneo, you should use the Product Query Builder (PQB). If you're using the 1.6, here is the link to the documentation to use it, it's pretty straightforward: https://docs.akeneo.com/1.6/cookbook/catalog/product/query.html
To have an exhaustive list of the filters on attributes & fields that can be used with the PQB, you can use the php app/console pim:product:query-help command on your PIM.
As you noticed, the family is not an attribute but a field, you'll find it in the field filters of the command above:
php app/console pim:product:query-help
Useable field filters...
+-----------------+--------------------------------+-----------------------------------------------------------+
| field | operators | filter_class |
+-----------------+--------------------------------+-----------------------------------------------------------+
| family | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
| family.id | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
| family.code | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
+-----------------+--------------------------------+-----------------------------------------------------------+
You can see now that you can search on the family.code field.
For your example, you'll end up with something like this:
<?php
// Get a new instance of the PQB
$pqbFactory = $this->getContainer()->get('pim_catalog.query.product_query_builder_factory');
$pqb = $pqbFactory->create([
'default_locale' => 'en_US',
'default_scope' => 'ecommerce'
]);
// Now you can search for products with your family codes
$pqb->addFilter(
'family.code',
'IN',
['projector', 'projector_child', 'projector_parent']
);
// Retrieve your products
$productsCursor = $pqb->execute();
foreach ($productsCursor as $product) {
// your custom logic
}

search for text within string in laravel or mysql

I use my SQL as backend for my project, and I need to get all record from database where some part of inputted string is available in database string like:
table = seller
id company_name seller_name
1 companyname1 seller1
2 companyname2 seller2
3 companyname3 seller3
4 companyname4 seller4
Given string is 1105 companyname1 is outstanding
So i need to get id = 1 for a given string if it is possible with laravel or MySQL then please help me.
You can construct a query using like:
where $YourString like concat('%', companyname, '%')
Note: There are situations where one company name might be sufficiently like another ("company1" and "company10"). If this is an issue, regular expressions might help.
First, you have to convert that string to an array.
$str = "1105 companyname1 is outstanding";
$str = explode(' ' , $str);
$results = Seller::whereIn('company_name' , $str)->get();
Note:
As you are converting random strings to an array, there will be a mistake where the user input some more spaces.
Let say the user input a string like $str = "1105 companyname1 is outstanding"; this will create some more elements. Like
array(
0 => '1104',
1 => '',
2 => 'companyname1',
3 => 'is',
4 => 'outstanding'
)
So, to avoid that, I have to recommend you to split out some more spaces. You can do by
$str = preg_split('/\s+/', $string, -1, PREG_SPLIT_NO_EMPTY);

Rails intersect query with joint table

In my Rails 3.2 app, there is an AttendedUniversity model with the following fields:
university_name
major_name
university_type # 0 for undergrad and 1 for graduate
advisor_id # for the Advisor model
The Advisor model has many attended_universities and an attended_university belongs to an advisor.
I want to fetch advisors who had a certain undergrad and a certain graduate e.g. major1 for undergrad and major2 for graduate.
I've tried the following:
Advisor.includes(:attended_universities).
where("attended_universities.university_name = 'university1' AND
attended_universities.major_name = 'major1' AND
attended_universities.university_type = 0").
where("attended_universities.university_name = 'university2' AND
attended_universities.major_name = 'major2' AND
attended_universities.university_type = 1")
The above is giving me the empty results when it shouldn't.
What you're matching with your query is that each university in the row should have the name 'university1' AND the name 'university2'.
Similar checks are being performed for other conditions. Since no one university row in your table will have two names, two majors, or two types, you will be served with 0 results.
This is going to be a bit more complicated then you might have initially thought, if you want to do it the optimized way.
You would need to join the universities table twice to your advisors table and then apply the conditions separately to each joined table. The following should work:
joins_str = ->(tn) do
"INNER JOIN attended_universities AS #{tn} ON #{tn}.advisor_id = advisors.id"
end
criteria_one = {name: 'university1',
major_name: 'major1',
university_type: 0}
criteria_two = {name: 'university2',
major_name: 'major2',
university_type: 1}
Advisor.
joins(joins_str.call('au_one')).
joins(joins_str.call('au_two')).
where(au_one: criteria_one).
where(au_two: criteria_two).
group("advisors.id")
I would go with the top method.
You could also do it the unoptimized way, perhaps making the code a bit more readable:
advisors_with_unis = Advisor.joins(:attended_universities)
# fetch advisor ids that fulfill criteria_one
ids_one = advisors_with_unis.
where(attended_universities: criteria_one).
pluck("DISTINCT advisors.id")
# fetch advisor ids that fulfill criteria_two
ids_two = advisors_with_unis.
where(attended_universities: criteria_two).
pluck("DISTINCT advisors.id")
# get intersection of both id arrays
ids = ids_one & ids_two
# get advisors
Advisor.where(id: ids)
Yet another solution would be to fetch all records from the DB and then use Ruby for filtering:
def university_matches?(uni, name, major, uni_type)
uni.name == name &&
uni.major_name == major &&
uni.university_type == uni_type
end
Advisor.includes(:attended_universities).select do |advisor|
universities = advisor.attended_universities
universities.any? do |uni|
university_matches?(uni, 'university1', 'major1', 0)
end &&
universities.any? do |uni|
university_matches?(uni, 'university2', 'major2', 1)
end
end
As mentioned before, I'd go with the first one.

Neo4j Cypher: MERGE conditionally with values from LOAD CSV

I'm trying to import from a CSV where some lines have an account number and some don't. Where accounts do have numbers I'd like to merge using them: there will be records where the name on an account has changed but the number will always stay the same. For the other records without an account number the best I can do is merge on the account name.
So really I need some kind of conditional: if a line has a account number, merge on that, else merge on account name. Something like...
LOAD CSV WITH HEADERS FROM 'file:///testfile.csv' AS line
MERGE (x:Thing {
CASE line.accountNumber WHEN NULL
THEN name: line.accountName
ELSE number: line.accountNumber
END
})
ON CREATE SET
x.name = line.accountName,
x.number = line.accountNumber
Though of course that doesn't work. Any ideas?
To test for a 'NULL' value in a .csv file in LOAD CSV, you have to test for an empty string.
testfile.csv
acct_name,acct_num
John,1
Stacey,2
Alice,
Bob,4
This assumes the account names are unique...
LOAD CSV WITH HEADERS FROM 'file:///testfile.csv' AS line
// If acct_num is not null, merge on account number and set name if node is created instead of found.
FOREACH(number IN (CASE WHEN line.acct_num <> "" THEN [TOINT(line.acct_num)] ELSE [] END) |
MERGE (x:Thing {number:number})
ON CREATE SET x.name = line.acct_name
)
// If acct_num is null, merge on account name. This node will not have an account number if it is created instead of matched.
FOREACH(name IN (CASE WHEN line.acct_num = "" THEN [line.acct_name] ELSE [] END) |
MERGE (x:Thing {name:name})
)