I have a MongoDB document that is structured similar to the structure below follows. I am searching based on people.search_columns.surname and people.columns.givenname. So for example, when I search for the given name of "Valentine", I want to get the document back, but Nicholas Barsaloux should not be included.
Data structure:
[_id] => MongoId Object (
[$id] => 53b1b1ab72f4f852140dbdc9
)
[name] => People From 1921
[people] => Array (
[0] => Array (
[name] => Barada, Valentine
[search_columns] => Array (
[surname] => Array (
[0] => Mardan,
[1] => Barada
)
[givenname] => Array (
[0] => Valentine
)
)
)
[1] => Array (
[name] => Barsaloux, Nicholas
[search_columns] => Array (
[surname] => Array (
[1] => Barsaloux
)
[givenname] => Array (
[0] => Nicholas
)
[place] => Array (
)
)
)
)
Here is the code I was working on:
$criteria = array("people" => array(
'$elemMatch' => array("givenname" => "Valentine")
));
$projection = array("people" => true);
$documents_with_results = $db->genealogical_data->find($criteria, $projection)->skip(0)->limit(5);
Currently that code returns zero results.
Since the arrays are nested you cannot use basic projection as you can with find. Also in order to "filter" the array contents from a document you need to "unwind" the array content first. For this you use the aggregation framework:
$results = $db->genealogical_data->aggregate(array(
array( '$match' => array(
'people.search_columns.givenname' => 'Valentine'
)),
array( '$unwind' => '$people' ),
array( '$match' => array(
'people.search_columns.givenname' => 'Valentine'
)),
array( '$group' => array(
'_id' => '$id',
'name' => array( '$first' => '$name' ),
'people' => array( '$push' => '$people' )
))
));
The point of the first $match stage is to reduce the documents that possibly match your criteria. The second time is done after the $unwind, where the actual "array" items in the document are "filtered" from the results.
The final $group puts the original array back to normal, minus the items that do not match the criteria.
Related
Array from collection looks like this.
$result = $collection->find();
Array
(
[0] => Array
(
[_id] => MongoDB\BSON\ObjectId Object
(
[oid] => 5c52b90454851c44aa2987e2
)
[name] => Array
(
[date] => 2019-01-31 10:59:48
[value] => DESKTOP-TODTF5E
)
[network_addresses] => Array
(
[0] => Array
(
[ip_1] => 12.21.134
[ip_2] => 50
[mac] => xx:xx:xx:xx:xx:xx
)
[1] => Array
(
[ip_1] => 192.168.0
[ip_2] => 2
[mac] => yy:yy:yy:yy:yy:yy
)
)
)
)
I can find if some mac exist in specific row of sub array like this:
$result = $collection->find(["network_addresses.0.mac" =>
"xx:xx:xx:xx:xx:xx"]);
But I need to check if certain mac exists in any row of subarray, so instead of row index 0 I need to put some asterix or something.
How to do that ?
$query = (new Query)->select(["name"])
->from(['db_name','collection_name'])
->where(["network_addresses" => [ '$elemMatch' =>['mac' => "xx:xx:xx:xx:xx:xx"]]]);
$results = $query->all();
i am working on mongodb & php & want to retrive data based on multiple conditions.
I want to retrive forms whose form_status is both Active & Draft only
My query is:
$formData = $formInfo->find(array('team_id' => $_GET['id'], '$and' =>array('form_status' => 'Active','form_status' => 'Draft')));
It is not working. What could be the right syntax in PHP??
The $and operator takes a "real array" of documents as it's argument. In PHP you wrap the array to produce that kind of syntax:
$formData = $formInfo->find(
array(
'team_id' => $_GET['id'],
'$and' => array(
array( 'form_status' => 'Active' ),
array( 'form_status' => 'Draft' )
)
)
);
Note that this really woudn't make any sense unless "form_status" is actually and array itself. In which case the $all operator is a much cleaner approach:
$formData = $formInfo->find(
array(
'team_id' => $_GET['id'],
'form_status' => array(
'$all' => array( 'Active', 'Draft' )
)
)
);
And again if this field was not an array then you really meant $or but that can also be more clearly written for the same field with $in:
$formData = $formInfo->find(
array(
'team_id' => $_GET['id'],
'form_status' => array(
'$in' => array( 'Active', 'Draft' )
)
)
);
So $all is to $and what $in is to $or, but just allows you to use the same field without specifying the full document form
I have some difficulty in getting Mongodb aggregate to work. It keeps giving me null. Please help. Below are the codes written in php. Thanks.
What I want to do is to sum up the values of 2 fields, Requests and Responses, between 2 particular dates
try {
$mongodb = new MongoClient("mongodb://ad:pass2word1#localhost");
$database = $mongodb->selectDB('backend');
$collection = new MongoCollection($database, 'RequestSummary');
$pipeline = array(
array(
'$group' => array(
'_id' => array(
'request' => array('$sum' => '$Requests'),
'response' => array('$sum' => '$Responses')
)
)
),
array(
'$match' => array(
'RequestDate' => array(
'$gte' => intval($_SESSION['range_from']),
'$lte' => intval($_SESSION['range_to'])
)
)
)
);
$collection->aggregate($pipeline);
var_dump($g);
} catch (MongoConnectionException $exc) {
echo $exc->getTraceAsString();
}
The _id of your $group can't contain aggregation operators like $sum. Those sums need to be defined as fields at the same level as _id. If you don't want to group on a specific field you can use NULL for the _id like this:
array(
'$group' => array(
'_id' => NULL,
'request' => array('$sum' => '$Requests'),
'response' => array('$sum' => '$Responses')
)
),
I am trying to use the aggregation function to display info in a chart. For this example, a document in the collection looks like this (excluding unnecessary fields for this query):
{
'locid' : <someid>, #Reference to a city & state collection
'collat' : <dateobj>, #a date object when this entry was saved
'pid' : <someid>, #Reference to a person collection
'pos' : <int> #Value I am interested in matching with location & date
}
So I basically start with a pid. I use this as my first $match parameter to limit the amount of data that gets thrown into the pipeline.
array(
'$match' => array(
'pid' => new \MongoId($pid)
)
),
So now that I have selected the correct pid, I tell it I only want/need certain fields:
array(
'$project' => array(
'pos' => 1,
'collat' => 1,
'locid' => 1
)
),
The second match is to say I only care about these locations right now ($ids contains an array of locid):
array(
'$match' => array(
'locid' => array('$in' => $ids)
)
),
And finally, I am saying group all the returned documents by collat and locid
array(
'$group' => array(
'_id' => array(
'locid' => '$locid',
'collat' => '$collat'
)
)
)
While the query completes OK and returns data, I am not getting the pos field back, it is only returning the locid and collat.
Questions
Isn't that what $project is for? I use it to tell the driver what fields I want returned?
Once I get the pos field returning as well, how can I tell the driver I only want the lowest value for each locid & collat combo pair? So say there are two entries for that date, location, and person: 4 & 8. I only care about pos=4
My end goal is to create a line chart with the X-Axis as the dates (from collat) and the Y-Axis will be the pos field, and each line will plot individual locid data.
Here is the entire parameters being sent to the aggregation driver.
$ops = array(
array(
'$match' => array(
'pid' => new \MongoId($pid)
)
),
array(
'$project' => array(
'pos' => 1,
'collat' => 1,
'locid' => 1
)
),
array(
'$match' => array(
'locid' => array('$in' => $ids)
)
),
array(
'$group' => array(
'_id' => array(
'locid' => '$locid',
'collat' => '$collat'
)
)
)
);
$out = $myCollection->aggregate($ops);
Update This is the way I got it to group & return pos without throwing an error. I need to spot check it though to make sure it's actually returning the correct values though.
array(
'$group' => array(
'_id' => array(
'locid' => '$locid',
'collat' => '$collat'
),
array('$min' => '$pos')
)
)
Aggregation query is like an SQL statement group by. You are telling {$group} what field(s) you want to 'GROUP BY' but you are not telling it how you want to aggregate the grouped information.
The {$group} you want is probably something like:
{$group : { _id : { locid: "$locid", collat: "$collat"},
pos : {$min : "$pos"}
}
}
I have this document in mongo:
array (
'_id' => new MongoId("509356edf275130917000000"),
0 =>
array (
'id1' => '1000',
),
1 =>
array (
'book' =>
array (
'id2' => '0',
'name' => 'Example',
),
),
)
and i want to add a book to "book" array:
$coll->update(array(array("id1"=>'1000'),
'$push'=>array('book'=>array(
"id2"=>"1",
"name" =>'War and peace'))));
but nothing happens, anyone know how to do this?
book is single array. You can't update.
Update records based on a given criteria
$collection->update($criteria, $newdata);
Example:
[
'_id' => new MongoId("509356edf275130917000000"),
'ids' => [1, 2, 3],
'books' => ['Book1', 'Book2']
]
$data = ['books' => 'Book3'];
$collection->update(
// Criteria
["_id" => new MongoId("509356edf275130917000000")],
// New Object
['$push' => $data]
);