mongodb: finding the highest numeric value of a column - php

I have MongoDB collection of documents containing several fields. One of the columns/fields should be numeric only, but some of these fields contain non-numerical (corrupt) data as string values. I should find the highest numerical value of this column, excluding the corrupt, non-numerical data. I am aware of the question Getting the highest value of a column in MongoDB, but AFAIK, this extended case was not covered.
The example below depicts the issue. For the highest value, the document with "age": 70 should be returned:
[
{
"id": "aa001",
"age": "90"
},
{
"id": "bb002",
"age": 70
},
{
"id": "cc003",
"age": 20,
}
]
Providing a PHP example for the find() / findOne() query would be of much help. Thanks a lot!
JohnnyHK came up with the perfect solution. Here's the working PHP code:
$cursor = $collection->find(array('age' => array('$not' => array('$type' => 2))), array('age' => 1));
$cursor->sort(array('age' => -1))->limit(1);

You can use the $type operator with $not in your query to exclude docs where age is a string. In the shell your query would look like:
db.test.find({age: {$not: {$type: 2}}}).sort({age: -1}).limit(1)
Or in PHP from Martti:
$cursor = $collection->find(array('age' => array('$not' => array('$type' => 2))), array('age' => 1));
$cursor->sort(array('price' => -1))->limit(1);

with PHP driver (mongodb)
using findOne()
$filter=[];
$options = ['sort' => ['age' => -1]]; // -1 is for DESC
$result = $collection->findOne(filter, $options);
$maxAge = $result['age']

You can use aggregate function to get maximum number from collections like this.
$data=$collection->aggregate(array
( '$group'=>
array('_id'=>'',
'age'=>array('$max'=>'$age'.)
)
)
);

This works for me
$options = ['limit' => 100,'skip' => 0, 'projection' => ['score' => ['$meta' => 'textScore']], 'sort' => ['score' => ['$meta' => 'textScore']]];

Related

Collection query

I have a collection which contains these values
'sales marketing|telemarketing',
what I'm trying to do is query/filter the items in collection but just based on the individual type so the for example value of 'telemarketing'. I have tried
$results = $data->where('Department', 'contains', $type); and also tried LIKE but because of the format with the pipe it's not picking the type/value.
This might be a dumb question but any ideas would be great!
The where-method also can handle only two Parameters. For example:
$data= collect([
['Department' => 'sales', 'price' => 200],
['Department' => 'marketing', 'price' => 100],
['Department' => 'telemarketing', 'price' => 150],
['Department' => 'marketing', 'price' => 100],
]);
$departmentName = "marketing";
$results = $data->where('Department', $departmentName);
dd($results);
Given your example:
[
"Employee" => "Some Company",
"Name" => "John Something",
"Usages" => "sales marketing|telemarketing",
"StartDate" => "1st Mar 2021",
"EndDate" => ""
]
The main issue is that the "Usage" property is a string containing multiple values, with the pipe character acting as a separator.
One solution to filter by one of those values is by mapping your original collection to transform the string in an array with the explode method and then use the filter method to filter based on the Usages you're interested in.
The resulting code might look like this:
$mappedCollection = $collection->map(function($el) {
$el['Usages'] = explode('|', $el['Usages']); // Transform the string into an array
return $el;
});
$result = $mappedCollection->filter(function($el) {
return in_array('sales marketing',$el['Usages']); // Change 'sales marketing' with the desired Usage
});

Why is this array_search returning 0

Consider the following:
$characterStats = [
['strength' => 500],
['dexterity' => 200],
['agility' => 1000],
['intelligence' => 1200],
['health' => 675],
];
$stat = array_search(max($characterStats), $characterStats);
echo $stat;
What I expect: ['intelligence' => 1200]
What I get: 0
Can some one help me out to achieve what I want?
Try the following:
$characterStats = array(
'strength' => 500,
'dexterity' => 200,
'agility' => 1000,
'intelligence' => 1200,
'health' => 675,
);
$stat = array_search(max($characterStats), $characterStats);
echo $stat;
I changed the way the array is declared. I believe you may need to indicate the field name you would like to search if using nested arrays with the following call:
$stat = array_search(max($characterStats), array_column($characterStats, 'KEYNAME'));
However, since each sub array has only 1 element with different "key" it may not be the best approach. For your scenario, you may need to use another approach, where you loop through each element and store the max value found.
With the array as you have it at the moment, the easiest way I can think of doing it as a standard foreach() and keep the maximum value as well as the element where it's found (save doing another search to get the full entry)...
$characterStats = [
['strength' => 500],
['dexterity' => 200],
['agility' => 1000],
['intelligence' => 1200],
['health' => 675],
];
$maxStat = null;
$max = null;
foreach ( $characterStats as $stat ){
if ( current($stat) > $max ) {
$max = current($stat);
$maxStat = $stat;
}
}
print_r( $maxStat);

Insert array into another array based on if command - putting it in wrong place

I have this array:
$arr = array(
'reportDescription' => array(
'reportSuiteID' => 'globretailprod',
'elements' => array(
0 => array(
'id' => $queryElement
)
),
'metrics' => array(
0 => array(
'id' => $queryMetric
)
)
)
);
I'm trying to insert some code into the array using an if command. This is what I have:
if (isset($querySegment)) {
$arr['reportDescription']['segments'] = $querySegment;
}
However that gives me the wrong result, what I am trying to achieve is this:
{
"reportDescription": {
"reportSuiteID": "rbsglobretailprod",
"dateFrom": "2018-09-09",
"dateTo": "2018-09-10",
"dateGranularity": "day",
"metrics": [{
"id": "pageviews"
}],
"elements": [{
"id": "page"
}],
"segments": [{
"id": "jjj"
}]
}
}
Notice there are two issues with this. Firstly, segments isn't isn't insert with an id, it's just inserted as a value. Secondly, I am a bit concerned about the trailing comma after metrics in my original array, since I need to be able to add a comma after the metrics array if I do include segments.
Just use the same format as you use for the other items to get the same structure...
if (isset($querySegment)) {
$arr['reportDescription']['segments'] = array(
0 => array(
'id' => $querySegment
)
);
}
As for the comma, this should be added automatically as needed if your using json_encode()

Associative array in correct format from mysql stored procedure and php json_decode

I'm trying to use a mysql stored procedure to return a JSON string which can be turned into an associative array using PHP's json_decode function. I have a valid JSON string being returned from the s.p but it isn't in the correct form or json_decode($skusJson, true); doesn't like it for some reason. The PHP code for getting the s.p results and doing the json_decode is:
$partsQuery = $this->Orders->callSP('part_skus_dropdown1');
$skusJson = $partsQuery->fetchAll('assoc');
$partsQuery->closeCursor();
$skus = json_decode($skusJson[0]['json'], true);
The mysql s.p is:
CREATE DEFINER=`root`#`localhost` PROCEDURE `part_skus_dropdown1`()
BEGIN
SELECT
CONCAT('[',
GROUP_CONCAT(
CONCAT('{"id":',id),
CONCAT(',"sku":"',sku,'"}')
),
']')
AS json FROM parts where id < 25;
END
The resulting JSON string is:
[
{
"id" : 1,
"sku" : "1"
},
{
"id" : 3,
"sku" : "3"
},
{
"id" : 6,
"sku" : "6"
},
{
"id" : 7,
"sku" : "7"
},
{
"id" : 9,
"sku" : "9"
}
]
(The sku's will not always match the id's and are stored as strings rather than ints). The results of the json_decoding are:
[
(int) 0 => [
'id' => (int) 1,
'sku' => '1'
],
(int) 1 => [
'id' => (int) 3,
'sku' => '3'
],
(int) 2 => [
'id' => (int) 6,
'sku' => '6'
],
(int) 3 => [
'id' => (int) 7,
'sku' => '7'
]
]
Is there anyway that I can get the resulting array to be in the form of [ id => sku ]: (alt. sku format shown)
[
(int) 1 => '1',
(int) 3 => '3',
(int) 58 => '3-BOX100'
]
I'm stuck... I've been following the http://php.net/json_decode documentation and using http://jsonlint.com/ to check the results of the s.p but think its time to ask for help... thanks in advance :)
Change your procedure so that it returns a single JSON object, not an array, and then uses id as the key and sku as the value in each element
CREATE DEFINER=`root`#`localhost` PROCEDURE `part_skus_dropdown1`()
BEGIN
SELECT
CONCAT('{',
GROUP_CONCAT(
CONCAT('"',id, '": "',sku,'"')
),
'}')
AS json FROM parts where id < 25;
END
DEMO
Note that the keys of a JSON object are always strings, they can't be integers. But both PHP and Javascript will automatically convert between integers and strings when accecssing the array.
your problem is that you need to turn the rows into an associative array keyed by the id. this will do that
function rowsToAssoc ($rows) {
$assoc = Array();
for ($rows as $r) {
$assoc[$r['id']] = $r['sku'];
}
return $assoc;
}
then you can json_encode/decode as you wish.
if you are using PDO for your MySQL calls, check out this answer to avoid looping completely

Indeed API XML feed always returns only 25 results

I'm trying to use the Indeed.com XML Feed API's in a PHP website. I use this script https://github.com/indeedlabs/indeed-php, see how it works on the Github page (very good script, thanks to the author).
It works but Indeed always returns only 25 results for jobs, even when I set the 'limit', 'start' and 'end' parameters.
Here are the parameters I send :
$aParams = array(
"q" => "php",
"l" => "paris",
"co" => "FR",
"limit" => 10000,
"sort" => "date",
"start" => 0,
"end" => 100,
"userip" => $_SERVER["REMOTE_ADDR"],
"useragent" => $_SERVER["HTTP_USER_AGENT"],
"v" => 2,
"format" => "json",
"publisher" => "123456789"
);
An array is returned and contains :
[version] = 2
[query] = 'php'
[location] = 'paris'
[dupefilter] = 'true'
[highlight] = 'true'
[start] = 1
[end] = 25
[totalResults] = 2068
[pageNumber] = 0
[results] = an array which contains the jobs informations
As we can see, totalResults is equal to 2058 but real the jobs results array always contains only 25 entries.
It seems to be a pagination issue (read here : http://forums.appthemes.com/report-jobroller-bugs/indeed-integration-api-37420) but I don't understand the goal : why proceed like this and not more simply ? So I have to do many requests : one to know first the 'totalResults' and save it (in session for example) and other requests to paginate the results 25 by 25 until the last?
Are there any developers who use this API and how do you proceed?
Thanks
Right Indeed limits the feed to 25 at a time. I have written script to get round this. In the Indeed $params you can specify a 'start' which as default is 0.
I have created a script which using the job count creates a foreach loop and loops the API changing the 'start' to keep getting different results until theres no more left. Then it puts it all into a single PHP array.
Single API request just so we can get totalResults (count of total jobs)
$client = new Indeed("YOUR_ID");
$args_count = array(
"q" => "YOUR SEARCH QUERY",
"l" => "",
"co" => "GB",
"userip" => "1.2.3.4",
"limit" => 10000,
"useragent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2)"
);
Once we have the total job count we divide it by 25 and round the result up. This is so we know how many times we need to run our foreach
$totalResults = $client->search($args_count);
$totalCount = $totalResults['totalResults'] / 25;
$loop_to_count = ceil($totalCount);
We create a array starting with 0 and going up in 25s to as many as you require. My below will return 150 results.
$counter = 0;
$loop_options = array('0', '25', '50', '75', '100', '125', '150');
Then we start the main foreach:
$results = '';
foreach ($loop_options as $options) {
$params = array(
"q" => "YOUR SEARCH QUERY",
"l" => "",
"co" => "GB",
"userip" => "1.2.3.4",
"limit" => 10000,
"start" => $options,
"useragent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2)"
);
$getResults = $client->search($params);
foreach ($getResults['results'] as $rawResults) {
$Subresults[] = array(
'jobtitle' => $rawResults['jobtitle'],
'company' => $rawResults['company'],
'city' => $rawResults['city'],
'state' => $rawResults['state'],
'country' => $rawResults['country'],
'language' => $rawResults['language'],
'formattedLocation' => $rawResults['formattedLocation'],
'source' => $rawResults['source'],
'date' => $rawResults['date'],
'snippet' => $rawResults['snippet'],
'url' => $rawResults['url'],
'onmousedown' => $rawResults['onmousedown'],
'jobkey' => $rawResults['jobkey'],
'sponsored' => $rawResults['sponsored'],
'expired' => $rawResults['expired'],
'indeedApply' => $rawResults['indeedApply'],
'formattedLocationFull' => $rawResults['formattedLocationFull'],
'formattedRelativeTime' => $rawResults['formattedRelativeTime'],
'stations' => $rawResults['stations']
);
}
$counter++;
if ($counter == $loop_to_count) { break; }
}
Finally all our results are inside this array:
$results = array ('results' => $Subresults);
$results will contains all the jobs you have posted on Indeed
please give limit attribute i gave 50 eg :
http://api.indeed.com/ads/apisearch?publisher=1772xxxxxxxxx&q=java&l=austin%2C%20tx&sort=&radius=&st=&jt=&start=&limit=50&fromage=&filter=&latlong=1&co=us&chnl=&userip=1.2.3.4&useragent=Mozilla/%2F4.0%28Firefox%29&v=2
Here you are using a library so you have to modify library function process_request() , in that function add a line $args["limit"] = 50; . here i just gave 50 you can initialize whatever number you want.

Categories