How to search neo4j for a node by a property name? - php

I am trying to set up some data and am fairly new to cypher queries. I m doing a project whereby nodes and relationships are created as part of a soccer team.
for example I am creating nodes on games and referees only at the moment. My logic is that I read in data for a game and if the referee does not exist as a node, to create him/her. If on a future game i see a referee that already exists that I just relate them to the game rather then creating a duplicate record. I am doing the following and wanted to see if my logic made sense before I even attempted to populate my data.
//JUST CREATED A GAME NODE called $GameNode
//let's search if the referee exists
$queryString = "START n=node({nodeId}) ".
"MATCH (n)<-[:REFEREED]-(x)".
"WHERE x.name = {name}".
"RETURN x";
$query = new Everyman\Neo4j\Cypher\Query($client, $queryString, array('nodeId' => 0, 'name' => $referee['referee_name']));
$result = $query->getResultSet();
$referee = 0;
if (count($result)==0)
{
//create referee as he/she does not exist
$referee= $client->makeNode();
$referee->setProperty('name', $referee['referee_name'])
->save();
}
else
{
//let's point the referee to this law case also
$referee = $client->getNode($result['x']->getProperty('id'));
}
$referee->relateTo($GameNode, 'REFEREED')
->save();
many thanks in advance

This should work but does a lot of roundtrips.
You can use Cypher only for this task:
Neo4j 1.9 use CREATE UNIQUE
START root=node({nodeId})
CREATE UNIQUE (root)<-[:REFEREED]-(referee {name: {name}})
RETURN referee
In Neo4j 2.0 you can use MERGE
MERGE (referee:Referee {name: {name}})
RETURN referee
Please also use better names for your identifiers.

Related

How to implement tree like structure in mysql and laravel

A question struck in my mind for 2 days and wondering whether it is possible to implement this type of tree structure in laravel and MySQL.
(First, take a look at the image attached. Thanks)
Suppose our platform uses refer system, and initially, a user 'A' join. Now, this user 'A' further refers 3 persons 'B','C','D'. Now, the total refer on A is 3 (because it refers 3 persons).
Now, let B further refers 'E','F' and 'C' further refers 'G','H', 'I' and 'D' refers 0. So, now refer of each person is "D = 0", "C = 3", "B = 2". and these refers will also add up on "A". So, it has "A = 8".
Now, 'G' refers 'J', so 'G' gets +1 and 'C' also gets +1 and 'C' is referred by 'A', so 'A' also gets +1. Now, total refer to each person is :
"j = 0","G=1","H=0","I=0", "D=0","E=0","f=0","B=2","C=4 (beacuse G refers J also)","A=9(beacuase 9 childers are refered by him)"
The chain continues until A gets total refer of 40.
In simple, if a person refers another person then it will get +1 and it's parent whom he gets refer also get +1 and so on until parent reaches 40, the chain continues.
I know, this is One-Many relationship between a user and refer and we can use a pivot table, but, How can we implement this type of logic. Give me some hints. Thanks.
I have written out something that should hopefully help you with this, using a while loop.
public function totalReferredBy(User $user)
{
// Initialise the queue to contain only the provided user
$queue = collect([$user]);
// This collection will eventually contain all of the "child"/referred users
$results = collect();
while ($queue->isNotEmpty() > 0) {
// Run a where in query to select all the referred users of the users in the queue.
$referredUsers = User::whereIn('referred_by', $queue->pluck('id'))->get();
// Merge the referredUsers we have found in the database with the results collection, so we can later count.
$results = $results->merge($referredUsers);
// Make the referredUsers we have just found in the database, the new queue. If the query did not return any
// referred users, the queue count would be 0 and the loop will exit.
$queue = $referredUsers;
}
// Now we should have all of the given user's "children" and "children of children" in the $results collection.
// We just need to return the count of that collection to get the total number of users that have been referred.
return $results->count();
}
You can use it like this:
$user = User::find(1);
$totalReferred = $this->totalReferredBy($user);
Then if your application does something when the user reaches 40 or more referred, you can just do:
if ($this->totalReferredBy($user) > 40) {
// Do something
}
This assumes that you have a referred_by column on the users table.

CakePHP & MVC – Potentially superfluous SQL queries when looking up 'names' associated with ids

I've probably murdered the whole concept of MVC somewhere along the line, but my current situation is thus:
I have participants in events and a HABTM relationship between them (with an associated field money_raised). I have a controller that successfully creates new HABTM relationships between pre-existing events and participants which works exactly as I want it to.
When a new relationship is created I wish to set the flash to include the name of the participant that has just been added. The actually addition is done using ids, so I've used the following code:
public function addParticipantToEvent($id = null) {
$this->set('eventId', $id);
if ($this->request->is('post')) {
if ($this->EventsParticipant->save($this->request->data)) {
$participant_id = $this->request->data['EventsParticipant']['participant_id'];
$money_raised = $this->request->data['EventsParticipant']['money_raised'];
$participant_array = $this->EventsParticipant->Participant->findById($participant_id);
$participant_name = $participant_array['Participant']['name'];
$this->Session->setFlash('New participant successfully added: ' . $participant_name . ' (' . $participant_id . ') ' . '— £' . $money_raised);
} else {
$this->Session->setFlash('Unable to create your event-participant link.');
}
}
}
This works, but generates the following SQL queries:
INSERT INTO `cakephptest`.`cakephptest_events_participants` (`event_id`, `participant_id`, `money_raised`) VALUES (78, 'crsid01', 1024) 1 1 0
SELECT `Participant`.`id`, `Participant`.`name`, `Participant`.`college` FROM `cakephptest`.`cakephptest_participants` AS `Participant` WHERE `Participant`.`id` = 'crsid01' LIMIT 1 1 1 0
SELECT `Event`.`id`, `Event`.`title`, `Event`.`date`, `EventsParticipant`.`id`, `EventsParticipant`.`event_id`, `EventsParticipant`.`participant_id`, `EventsParticipant`.`money_raised` FROM `cakephptest`.`cakephptest_events` AS `Event` JOIN `cakephptest`.`cakephptest_events_participants` AS `EventsParticipant` ON (`EventsParticipant`.`participant_id` = 'crsid01' AND `EventsParticipant`.`event_id` = `Event`.`id`)
This final one seems superfluous (and rather costly) as the second should give me all that I need, but removing $this->EventsParticipant->Participant->findById($participant_id) takes out both the second and third queries (which sort of makes sense to me, but not fully).
What can I do to remedy this redundancy (if indeed I'm not wrong that it is a redundancy)? Please tell me if I've made a complete hash of how these sorts of things should work – I'm very new to this.
This is probably due to the default recursive setting pulling the relationship. You can remedy this by setting public $recursive = -1; on your model (beware this will affect all find calls). Or, disable it temporarily for this find:
$this->EventsParticipant->Participant->recursive = -1;
$this->EventsParticipant->Participant->findById($participant_id);
I always suggest setting public $recursive = -1; on your AppModel and using Containable to bring in the related data where you need it.

Complex Zend Query from same user table

I have a rather unique set of conditions and orders in which I need to retrieve data from a "sellers" table for an application I'm building in Zend framework.
The client is basically requesting an application where the directory page lists sellers in a very particular order, which is:
Sellers who have been approved in the last 7 days (then order by #4 below)
Then, selllers who have paid for upgraded features on the site, and are more the 7 days old (then order by #4 below)
Then, Sellers who are more than 7 days old and are more than 7 days old (then order by #4 below)
For all of the above, secondary order by would be their launch date, then alpha by business name
I'm trying to figure out the most effective way to write an action helper that will return the data in the correct sequence above, knowing that some of my views only need 1,2 (and 4), whereas other views within the application will need all 4.
Right now, I've been writing two or three separate queries, and passing them to 2 or 3 partialloop's inside the view, but I strive for properly written code, and would like to either combine my 3 queries into one object I can pass to one partial loop, or.... write one query. How can this be done?
Here's my helper at the moment:
class Plugin_Controller_Action_Helper_ListSellers extends Zend_Controller_Action_Helper_Abstract
{
//put your code here
public function direct($regulars = false, $filter = false)
{
$dateMod = $this->dateMod = new DateTime();
$dateMod->modify('-7 days');
$formattedDate = $dateMod->format('Y-m-d H:i:s');
// get sellers initialized in last 7 days
$sellerTable = new Application_Model_DbTable_Seller();
// get sellers initialized in last 7 days
$select = $sellerTable->select()->setIntegrityCheck(false);
$select->from(array('b' => 'seller'),array('sellerID', 'businessName','sellerPicture'));
// select firstName, lastName, picture from user table, and businessName and sellerID from seller table. All records from seller table
$select->join(array('u' => 'user'), 's.userID = u.userID', array('firstName', 'lastName'));
$select->order('s.launchDate DESC','s.businessName ASC');
$select->where('s.active = 1 AND s.contentApproval = 1 AND s.paymentApproval = 1');
$select->where('s.launchDate > ?', $formattedDate);
if($filter){ $select->where('s.categoryID = ?', $filter);}
$newSellers = $sellerTable->fetchAll($select);
$query = $sellerTable->select()->setIntegrityCheck(false);
$query->from(array('b' => 'seller'),array('sellerID', 'businessName','sellerPicture'));
// select firstName, lastName, picture from user table, and businessName and sellerID from seller table. All records from seller table
$query->join(array('u' => 'user'), 's.userID = u.userID', array('firstName', 'lastName'));
$query->order('s.launchDate DESC','s.businessName ASC');
$query->where('s.active = 1 AND s.contentApproval = 1 AND s.paymentApproval = 1 AND s.featured = 1');
$query->where('s.launchDate < ?', $formattedDate);
if($filter){ $select->where('s.categoryID = ?', $filter);}
$featuredSellers = $sellerTable->fetchAll($query);
if($regulars){
$where = $sellerTable->select()->setIntegrityCheck(false);
$where->from(array('b' => 'seller'),array('sellerID', 'businessName','sellerPicture'));
// select firstName, lastName, picture from user table, and businessName and sellerID from seller table. All records from seller table
$where->join(array('u' => 'user'), 's.userID = u.userID', array('firstName', 'lastName'));
$where->order('s.launchDate DESC','s.businessName ASC');
$where->where('s.active = 1 AND s.contentApproval = 1 AND s.paymentApproval = 1 AND s.featured IS NULL');
$where->where('s.launchDate < ?', $formattedDate);
$regularSellers = $sellerTable->fetchAll($where);
}
}
}
I don't see any limits being applied to your queries. So does that mean you really want to select all matching records? For scalability reasons I'd guess that the answer should be no, there will be limits applied. In this case, you may just have to do 3 different queries.
But if there are no limits to be applied, then you could do a single simple query that selects all sellers, unfiltered and unsorted, and do your sorting and filtering in view helpers or just in your views.
Regardless, I recommend not putting database queries inside your controller layer, assuming you want to use the Model-View-Controller pattern which Zend is built for. Controllers should be thin. Your models should handle all database queries and just spit out the results into your controllers. I use the Data Mapper pattern extensively. Something like:
$mapper = new Application_Model_SellerMapper();
$newSellers = $mapper->fetchNewSellers();
$featuredSellers = $mapper->fetchFeaturedSellers();
$regularSellers = $mapper->fetchRegularSellers();
Each of your fetchX() methods would return an array of Application_Model_Seller instances, rather than Zend_Db_Table_Row instances.
This way you maintain Separation of Concerns and Single Responsibility Principle better, for more maintainable code. Even if you're the only developer on the project over the long-term, 6 months from now you won't remember what you wrote and why. And if someone else comes on the project, clarity becomes really important.

MongoDB: updating collection of objects to change owning side of reference

I have two collections, Group and User. Originally, my Group document contained an array of User references. But I've changed my mapping so that Users now hold a reference to a Group. I am wondering how I can write a query to update all the existing User documents to reference the group that is currently referencing them, then remove any references on the groups.
I didn't notice the language you are using at first - I had to do something very similar in Python so here is my example code:
# I changed my collection names to users/groups hope I mapped them right :)
users = db.users
groups = db.groups
for u in users.find():
grp = groups.find( { "users" : u['userId'] } )
u['groups'] = [ g['groupId'] for g in grp ]
users.save(u)
Basically, for every user, I look in groups to find a list of groups which contain userId in the array users, and I make a list called groups in u and when I save it adds an array of groups to users document.
Should be not hard to do it in PHP
$users = $db->users;
$groups = $db->groups;
// find all users
$ucursor = $users->find();
// iterate through users, find groups which have the user,
// update user with array of groups
foreach ($ucursor as $u) {
$gcursor = $groups->find( ... );
foreach ($gcursor as $g) {
...
}
}
But this seems like a one time operation, you could do it in mongo shell using something similar to:
var users = db.users.find();
while (users.hasNext()) {
u = users.next();
var gs = db.groups.find({"users":u.userId},{"groupId:1}).toArray();
db.users.update({"groupId":groupId},{$set:{"groups":gs}});
}

Recursing Properly Through Related Entities

I have a set of Organizations and their Board Members.
All organizations have board members and many board members are on the board of more than one organization.
I am using JIT Hypertree to illustrate their relationships. The JIT Hypertree schema requires that one item be the parent of all and is drawn based on a single JSON array.
I would love to have the re-centering event query and re-populate the graph based on the change. Then 2 levels would be fine but I have not been able to work out how to do that.
The code I have at present recurses manually for three levels from the starting organization but what I want is to re-curse through all related records.
So it would start with an Org and add Org's array of children (board members). Then fetch all of the boards (other than current Org) for each board member and add those as children of the board member.
This would continue until each trail dead ends - presumably at a board member who only belongs to one board.
Anyone have advice on how to create this array and avoid duplicates?
$board = $center->board();
$top['id'] = $center->ID;
$top['name'] = $center->Org;
$top['children'] = array();
if ($board) {
foreach ($board as $b) {
$child['id'] = $b->ID;
$child['name'] = (strlen(trim($b->Last)) > 0) ? $b->First . ' ' . $b->Last : 'Unknown';
$child['data']['orgname'] = $center->Org;
$child['data']['relation'] = $b->Role;
$child['data']['occupation'] = $b->Occupation;
$child['children'] = array();
$childboards = $b->boards();
if ($childboards) { foreach ($childboards as $cb) {
$gchild['id'] = $cb->ID;
$gchild['name'] = $cb->Org;
$gchild['data']['orgname'] = (strlen(trim($b->Last)) > 0) ? $b->First . ' ' . $b->Last : 'Unknown';
$gchild['children'] = array();
$childboardmembers = $cb->board();
if ($childboardmembers) { foreach ($childboardmembers as $cbm) {
$ggchild['id'] = $cbm->ID;
$ggchild['name'] = (strlen(trim($cbm->Last)) > 0) ? $cbm->First . ' ' . $cbm->Last : 'Unknown';
$ggchild['data']['orgname'] = $cb->Org;
$ggchild['data']['relation'] = $cbm->Role;
$ggchild['data']['occupation'] = $cbm->Occupation;
$ggchild['children'] = array();
$gchild['children'][]= $ggchild;
}}
$child['children'][]= $gchild;
}}
$top['children'][] = $child;
}
}
$top['data'] = array();
$top['data']['description'] = $center->Desc;
echo json_encode($top);
// Edit 2011.10.24 In Re hakre response
My data structure is a table of Organizations with unique IDs, a table of People with Unique IDs, and then a bridging table for the two specifying Organization (Entity) and Person and the Role the Person is playing in the Entity. A typical many-to-many. No sub-boards at all. I made an image of it which now seems kind of pointless but I'll add it at the bottom.
The JIT library data structure is a little nuts (to me) in that it goes like this in their band example:
Top: Nine Inch Nails
Child: Jerome Dillon
Child: Howlin Maggie (another band)
{all the bands' members and then all of their bands...}
So the organization (band) is treated as though it is a Person even though it is comprised of a number of Persons. And when I recurse using the code above I get (I think) terrible bloat but the JSON it makes works correctly despite bloat.
Example JSON and Example Visualization
// End Edit
Your question is hard to answer in the sense that your data-structure is mainly unknown.
For the graphical represenation you only need to provide simple relationships if I understand that correctly:
*- Parent
+- Child
+- Child
...
`- Child
Your data structure has a different format, I don't know specifically but it's something like:
Org <-- 1:n --> Board
Board <-- n:n --> Board # If you have sub-boards
Board <-- n:n --> Member
Whichever your data is represented, to map or transpose your data onto the required structure for the graphical representation, you need some functions that take care of that.
To do that you need to share classification/type between both and specific keys, so that you can look-up the needed data from the event to return the data. For example:
if (request_parent_is_org())
{
$id = request_parent_id();
$parent = data_get_board($id);
$parent->addChildrent(data_get_board_children($id));
}
else
{
... # All the other decisions you need to take based on request type
}
view_response_to_json($parent);
What you have with your many-to-many data model is a graph. JIT is designed for trees.
To put it another way, JIT will not correctly show the crossing lines that are represented in the data whenever a single person is connected to multiple organizations.
I'd recommend a proper network graph visualization - D3.js has a great implementation for modern browsers.
The JSON data format it uses is actually easier to implement given your table structure - for all the organizations and people, you define objects:
{
"name": "Mme.Hucheloup",
"group": 1
},
{
"name": "Afton School Board",
"group": 2
}
And for each association in your association table you define connection objects that wire them together:
{
"source": 1,
"target": 2
},
The fancy coding in D3 takes care of the rest. Good luck!
You can use function below:
function get_orgs_and_childs ($child_id, $found = array())
{
array_push ($found, $child['name']);
if($child['children'])){
$found[] = get_parents($child['id'], $found);
}
return $found;
}
Call it using:
$orgs = get_orgs_and_childs($child['id']);

Categories