How to get all node names between two nodes using neo4jPHP - php

Till now I was using PHP Rest Api in order to send requests with cypher queries and get a response back. The response is a huge string which makes it difficult to parse and can not be transformed to JSON.
I now installed Neo4jPHP and I am trying to figure out how to write the same query I had in cypher.
This is my query:
MATCH (n:RealNode)-[r:contains*]-(z) WHERE n.gid='123' RETURN n,z;")
What I actually want is to get a list of all the names of the nodes (name is a property inside each node) which is related to my n node. How do I do this?
I can not find many examples for Neo4jPHP onnline and the ones I found seem not to work. I downloaded the latest version from here (https://github.com/jadell/neo4jphp).
Thanks
D.
RE-EDITED
I try this query in neo4j Server:
MATCH (n)-[r:KNOWS*]-(z) WHERE n.name='Arthur Dent' AND z.name='Ford Prefect' RETURN n,z,r;
and I get all the 3 nodes which are connected to each other. The same query through neo4jPHP will return only the name of one node. Why is this happening?
$querystring="MATCH path=(n:RealNode {gid:'58731'})-[:contains*]-(z) RETURN [x in nodes(path) | x.id] as names";
$query=new Everyman\Neo4j\Cypher\Query($client,$querystring);
$result=$query->getResultSet();
print_r($result);
foreach($result as $row){
echo $row['x']->getProperty('name') . "\n";
}

On Cypher level you might use a query like:
MATCH path=(n {name:'Arthur Dent'])-[:KNOWS*]-(z {name:'Ford Perfect'})
RETURN [x in nodes(path) | x.name] as names
You assign a variable to a pattern, here path. In the RETURN you iterate over all nodes along that path and extract its name property.
2 additional hints:
consider assigning labels e.g. Person to your nodes and use a declarative index ( CREATE INDEX ON :Person(name) ) to speed up the look up for start/end node of your query
for variable path length matches [:KNOWS*] consider using an upper limit, depending on the size and structure of your graph this can get rather expensive. [:KNOWS*10] to limit on 10th degree.

After lots of trying and some help from Stefan Armbruster I made it work using Neo4jPHP.
This is how it looks:
$client = new Everyman\Neo4j\Client();
$querystring="MATCH path=(n {gid:'58731'})-[:contains*]-(z) RETURN LAST([x in nodes(path) | x.id]) as names";
$query=new Everyman\Neo4j\Cypher\Query($client,$querystring);
$result=$query->getResultSet();
foreach($result as $resultItem){
$resultArray[] = $resultItem['n'];
}
print_r($resultArray); // prints the array
Neo4jPHP is very handy tool but not very popular. Small community and few examples online. Hope this helps someone.

Related

Getting an XML value from a named field

Sorry to be asking this, but it's driving me crazy.
I've been using the php SimpleXMLElement as my XML go to parser, and I've looked at many examples, and have given up on this many times. But, now, I just need to have this working. There are many examples on how to get simple fields, but not so many with values in the fields...
I'm trying to get the "track_artist_name" value from this XML as a named variable in php.
<nowplaying-info-list>
<nowplaying-info >
<property name="track_title"><![CDATA[Song Title]]></property>
<property name="track_album_name"><![CDATA[Song Album]]></property>
<property name="track_artist_name"><![CDATA[Song Artist]]></property>
</nowplaying-info>
</nowplaying-info-list>
I've tried using xpath with:
$sxml->xpath("/nowplaying-info-list[0]/nowplaying-info/property[#name='track_artist_name']"));
But, I know it's all mucked up and not working.
I originally tried something like this too, thinking it made sense - but no:
attrs = $sxml->nowplaying_info[0]->property['#name']['track_artist_name'];
echo $attrs . "\n\n";
I know I can get the values with something such as this:
$sxml->nowplaying_info[0]->property[2];
Sometimes there are more lines in the XML results than other times, and so because of this, it is breaks the calculations with the wrong data.
Can someone shed some light on my problem? I'm just trying to the name of the artist to a variable. Many thanks.
*** WORKING UPDATE: **
I was unaware there were different XML interpreter methods, and was using the following XML interpreter version:
// read feed into SimpleXML object
$sxml = new SimpleXMLElement($json);
That didn't work, but have now updated to the following (for that section of code) thanks to the help here.
$sxml_new = simplexml_load_string($json_raw);
if ( $sxml_new->xpath("/nowplaying-info-list/nowplaying-info/property[#name='track_artist_name']") != null )
{
$results = $sxml_new->xpath("/nowplaying-info-list/nowplaying-info/property[#name='track_artist_name']");
//print_r($results);
$artist = (string) $results[0];
// var_dump($artist);
echo "Artist: " . $artist . "\n";
}
Your xpath expression is pretty much right, but you don't need to specify an index for the <nowplaying-info-list> element - it'll deal with that itself. If you were to supply an index, it would need to start at 1, not 0.
Try
$results = $sxml->xpath("/nowplaying-info-list/nowplaying-info/property[#name='track_artist_name']");
echo (string) $results[0];
Song Artist
See https://3v4l.org/eH4Dr
Your second approach:
$sxml->nowplaying_info[0]->property['#name']['track_artist_name'];
Would be trying to access the attribute named #name of the first property element, rather than treating it as an xpath-style # expression. To do this without using xpath, you'd need to loop over each of the <property> elements, and test their name attibrute.
Just in case if the node you are looking for is deeply residing some where, you could just add a double slash at the start.
$results = $sxml->xpath("//nowplaying-info-list/nowplaying-info/property[#name='track_artist_name']");
Also in case if you have multiple <nowplaying-info> elements. You could make of use of the index for that. (note the [1] index)
$results = $sxml->xpath("//nowplaying-info-list/nowplaying-info[1]/property[#name='track_artist_name']");

How to read child of interleaved XML in PHP

We need to update a somewhat old API a customer is using. We're getting the data with simplexml_load_file. The new structure is a bit more complex and I could not find a way to retrieve the new data.
The new structure looks like this:
Before we used $ratingPrice = $xml->hotel->ratingPrice; to get a value.
Now, I need to retrieve $xml->business->subcategoryRating->subcategoryRating->averageRating. I'm now stuck with that.
How am I able to get the average rating of wifi, or location and have them outputted in single variables?
If the order of your information is always the same (eg. Wi-Fi, Location, Apartment, Cleanliness) then you can do
$xml->business->subcategoryRating->subcategoryRating[x]->averageRating
so the x is the index of your xml list. Then
...subcategoryRating[0]->averageRating
corresponds to Wi-Fi and
...subcategoryRating[1]->averageRating
corresponds to Location and so forth.
You can extract the various ratings into an array and then use the relevant index to process the data your after. This uses the id attribute of the (for example) <category id="location"> element to use as the index...
$ratings = [];
foreach ( $xml->business->subcategoryRatings->subcategoryRating as $rating ) {
$ratings[(string)$rating->category['id']] = (string)$rating->averageRating;
}
echo $ratings["location"].PHP_EOL;
This last line will need to be updated to access the particular element your after.
As an alternative you could create an xpath expression and use xpath.
That would give you array of SimpleXMLElement objects.
$xml = simplexml_load_string($data);
$expression = '//business/subcategoryRatings/subcategoryRating[category[#id="wifi" or #id="location"]]';
foreach($xml->xpath($expression) as $item) {
$averageRating = (string)$item->averageRating;
}

Accessing XML attributes data

I have two lines of XML data that are attributes but also contain data inside then and they are repeating fields. They are being stored in a SimpleXML variable.
<inputField Type="Name">John Doe</inputField>
<inputField Type="DateOfHire">Tomorrow</inputField>
(Clearly this isnt real data but the syntax is actually in my data and I'm just using string data in them)
Everything that I've seen says to access the data like this, ,which I have tried and it worked perfectly. But my data is dynamic so the data isn't always going to be in the same place, so it doesn't fit my needs.
$xmlFile->inputField[0];
$xmlFile->inputField[1];
This works fine until one of the lines is missing, and I can have anywhere from 0 to 5 lines. So what I was wondering was is there any way that I can access the data by attribute name? So potentially like this.
$xmlFile->inputField['Name'];
or
$xmlFile->inputField->Name;
I use these as examples strictly to illustrate what I'm trying to do, I am aware that neither of the above lines of code are syntactically correct.
Just a note this information is being generated externally so I cannot change the format.
If anyone needs clarification feel free to let me know and would be happy to elaborate.
Maybe like this?
echo $xmlFile->inputField->attributest()->Name;
And what you're using? DOMDocument or simplexml?
You don't say, but I assume you're using SimpleXMLElement?
If you want to access every item, just iterate:
foreach ($xmlFile->inputField as $inputField) { ... }
If you want to access an attribute use array notation:
$inputField['Type']
If you want to access only one specific element, use xpath:
$xmlFile->xpath('inputField[#Type="Name"]');
Perhaps you should read through the basic examples of usage in the SimpleXMLElement documentation?
For example you can a grab a data:
$xmlFile = simplexml_load_file($file);
foreach($xmlFile->inputField as $res) {
echo $res["Name"];
}

I need help with php parsing of xml for insertion into mysql database

Aloha everyone,
I apologize in advance for the many questions, but I've been asked to develop a database and have no experience with PHP and MySQL. I thought it would be a good exercise for me to attempt to learn a little bit about them and try to develop a concept database for my work at the same time. Basically this is a database that uses SYDI to obtain WMI information from our Windows-based computers to use for patch management. The way I envision this working is like this:
SYDI is run and an XML file is generated with the information.
Using the PHP front end to our patch database, the XML report is parsed and the desired information is then inserted into the MySQL database.
Reports are generated from the database to compare with the latest known baseline for the activity. If computers are found to be below the baseline, the patch server is used to deliver the needed patches to the delinquent computer(s).
There are a couple of formats used in the XML report from SYDI, one with attributes in a single tag, and another where a single parent tag contains several child tags with attributes. I have figured out how to parse the first. Here's a sample of the data and the code for that (it's really pretty basic stuff) with the resulting ouput:
<machineinfo manufacturer="Dell Inc." productname="Precision M90" identifyingnumber="87ZGFD1" chassis="Portable" />
$xml = simplexml_load_file("sydiTest.xml");
foreach($xml->machineinfo[0]->attributes() as $a => $b)
{
echo $b, "</br>";
}
Dell Inc.
Precision M90
87ZGFD1
Portable
I didn't need the name of the attribute, only the value, so I only echo'd $b there. For the second, here's a sample of the data itself as well as the code and output for the parse:
<patches>
<patch description="Microsoft .NET Framework 1.1 Security Update (KB2416447)" hotfixid="M2416447" installdate="04-Feb-11" />
<patch description="Microsoft .NET Framework 1.1 Service Pack 1 (KB867460)" hotfixid="S867460" installdate="04-Feb-11" />
<patch description="Windows Management Framework Core" hotfixid="KB968930" installdate="2/4/2011" />
<patch description="Security update for MSXML4 SP2 (KB954430)" hotfixid="Q954430" installdate="04-Feb-11" />
<patch description="Security update for MSXML4 SP2 (KB973688)" hotfixid="Q973688" installdate="04-Feb-11" />
<patch description="Microsoft Internationalized Domain Names Mitigation APIs" hotfixid="IDNMitigationAPIs" installdate="6/30/2008" />
</patches>
foreach ($xml->patches->patch[0]->attributes() as $a => $b)
{
echo $b, "</br>";
}
Microsoft .NET Framework 1.1 Security Update (KB2416447)
M2416447
04-Feb-11
As you can see, I only got the first patch, not the rest of them. I figure that 'patch[0]' is most likely the issue, as it only references the first child tag. How can I get it to reference the rest of the children?
The results raise another issue. Is there any way to pick out specific attributes and disregard the rest? For example, in the first parse, the machineinfo parse gets all the information I need. In the second parse, I only need the description and hotfixid. Once I get the correct syntax for the parse, assuming it runs like the first one, I would most likely get all of the attributes. I don't need the install date.
Lastly, how can I assign the retrieved values to variables? The first parse results in the data I need, but not in the correct order. My table structure is like this:
CREATE TABLE InventoryItems
(InvSerNum VARCHAR(20) NOT NULL,
Make VARCHAR(20),
Model VARCHAR(20),
Platform VARCHAR(12),
CONSTRAINT Inventory_PK PRIMARY KEY (InvSerNum));
I need the identifyingnumber (InvSerNum) first. Of course, I could always reorder the fields in the table to match the XML, but I'd rather leave it as is. My thinking is that I can use an INSERT statement and just use the variables for the values to be input.
I'm trying to do all of this on my own, but got stuck on the XML parsing part. If anyone can assist me in understanding the process, I would be in your debt.
Try using RapidXML in PHP. Makes XML parsing a bit easier. It's still not that intuitive: you'll need a good debugger to get to the bottom of it.
The rest of your questions require you to do a bit of research into the mysql_(function_name) bindings in PHP. There's heaps of articles out there about this.
I figured out the second parse question. I used the following code:
foreach ($xml->patches->patch as $patch1) {
foreach ($patch1->attributes() as $a => $b) {
echo $b, "<br />";
}
}
and it worked like a charm! I still need to omit the last attribute, assign them to variables, the use the INSERT statement to get them into the database, but at least I'm that much closer to a resolution.

Assistance with building an inverted-index

It's part of an information retrieval thing I'm doing for school. The plan is to create a hashmap of words using the the first two letters of the word as a key and any words with the two letters saved as a string value. So,
hashmap["ba"] = "bad barley base"
Once I'm done tokenizing a line I take that hashmap, serialize it, and append it to the text file named after the key.
The idea is that if I take my data and spread it over hundreds of files I'll lessen the time it takes to fulfill a search by lessening the density of each file. The problem I am running into is when I'm making 100+ files in each run it happens to choke on creating a few files for whatever reason and so those entries are empty. Is there any way to make this more efficient? Is it worth continuing this, or should I abandon it?
I'd like to mention I'm using PHP. The two languages I know relatively intimately are PHP and Java. I chose PHP because the front end will be very simple to do and I will be able to add features like autocompletion/suggested search without a problem. I also see no benefit in using Java. Any help is appreciated, thanks.
I would use a single file to get and put the serialized string. I would also use json as the serialization.
Put the data
$string = "bad barley base";
$data = explode(" ",$string);
$hashmap["ba"] = $data;
$jsonContent = json_encode($hashmap);
file_put_contents("a-z.txt",$jsonContent);
Get the data
$jsonContent = file_get_contents("a-z.txt");
$hashmap = json_decode($jsonContent);
foreach($hashmap as $firstTwoCharacters => $value) {
if ($firstTwoCharacters == 'ba') {
$wordCount = count($value);
}
}
You didn't explain the problem you are trying to solve. I'm guessing you are trying to make a full text search engine, but you don't have document ids in your hashmap so I'm not sure how you are using the hashmap to find matching documents.
Assuming you want a full text search engine, I would look into using a trie for the data structure. You should be able to fit everything in it without it growing too large. Nodes that match a word you want to index would contain the ids of the documents containing that word.

Categories