MongoDB find with key that starts with a string pattern - php

I'm trying to fetch a mongodb data using PHP.
My Data is in the following format:
$document = $collection->findOne(
array('_id' => 'set1'),
array('projection' => array('_id' => 1,'data'=>1))
);
Result:
{
"_id": "set1",
"data": {
"IND": {
"2015-01": 0.6753404,
"2015-02": 1.0502269,
"2015-03": 1.0902269
},
"AUS": {
"2015-01": 0.6753404,
"2015-02": 1.0502269,
"2015-03": 1.0902269
},
"IND_123": {
"2015-01": 0.6753404,
"2015-02": 1.0502269,
"2015-03": 1.0902269
}
}
}
I am trying to fetch the output such that the result only has "IND" and "IND_123".
Is there a way to write the projection such that the result only provides "IND*"
The following provides only "IND"
$document = $collection->findOne(
array('_id' => 'set1'),
array('projection' => array('_id' => 1,'data.IND'=>1))
);

You can achieve it using an aggregation pipeline, like this:
db.collection.aggregate([
{
"$match": {
"_id": "set1"
}
},
{
"$project": {
data: {
"$arrayToObject": {
"$filter": {
"input": {
"$objectToArray": "$data"
},
"as": "item",
"cond": {
"$regexMatch": {
"input": "$$item.k",
"regex": "IND.*"
}
}
}
}
}
}
}
])
Playground link.
Here, we match the required document using $match. Then we, recompute the data field in $project, we first convert the data object to an array using $objectToArray, and filter all keys matching the pattern IND.* using $filter. Finally, we convert the filtered array back to an object using $arrayToObject.

Related

Remove the item from array based on child value on object

I've got the following data structure:
Array -> Object -> Array -> Object -> Object
[
{
"id":6834,
"contract_id":13,
"schedule_column_values":[
{
"id":34001,
"field_value":{
"id":324241,
"value":10,
"field":{
"id":1,
"signature":"ios"
}
}
},
{
"id":34001,
"field_value":{
"id":324241,
"value":10,
"field":{
"id":1,
"signature":"android"
}
}
}
]
}
]
What I'm trying to achieve is that if a field has the signature of "android", remove its grandparent object from schedule_column_values. Basically, if a signature is "android", the final data would look like this:
[
{
"id": 6834,
"contract_id": 13,
"schedule_column_values": [
{
"id": 34001,
"field_value": {
"id": 324241,
"value": 10,
"field": {
"id": 1,
"signature": "ios"
}
}
}
]
}
]
This is just an example but the structure is always the same and we always know what signature we're looking for. It could be anything other than android but we know the string we're looking for.
I've tried a nested foreach loop and tried unset but it doesn't seem to work. The other way is I've set a NULL to object value of schedule_column_values when the signature of field is matched, but I cannot have NULL in the object.
What would be a good way to filter out this structure?
This is a perfect use case for array_filter:
$filtered_array = [];
foreach($array as $grandparent){
$filtered_schedules = array_filter(
$grandparent->schedule_column_values,
function($item){
return $item->field_value->field->signature !== 'android';
}
);
$altered_grandparent = clone $grandparent;
$altered_grandparent->schedule_column_values = $filtered_schedules;
$filtered_array[] = $altered_grandparent;
}

Elasticsearch array format for multiple must terms

I'm working on elasticsearch (2.3) and am trying to create a search query. For elasticsearch I need JSON, but I start with a simple php array and encode it to JSON afterwards.
I have a problem with creating the right array format.
This is a small part of the code I'm using:
$params['body']['query']['filtered']['filter']['bool']['must']["term"]['pprCategories']= "category1";
$params['body']['query']['filtered']['filter']['bool']['must_not']['exists']['field'] = "field1";
With an if statement I add a new line:
$params['body']['query']['filtered']['filter']['bool']['must']['term']['country_name']= "country1";
So the total code is:
$params['body']['query']['filtered']['filter']['bool']['must']["term"]['pprCategories']= "category1";
$params['body']['query']['filtered']['filter']['bool']['must_not']['exists']['field'] = "field1";
$params['body']['query']['filtered']['filter']['bool']['must']['term']['country_name']= "country1";
#output
echo "<pre>";
print_r(json_encode($params, JSON_PRETTY_PRINT));
echo "</pre>";
The is the actual result I get from the query I built:
{
"body": {
"query": {
"filtered": {
"filter": {
"bool": {
"must": {
"term": {
"pprCategories": "category1",
"country_name": "country1"
}
},
"must_not": {
"exists": {
"field": "field1"
}
}
}
}
}
}
}
}
However, the desired result would be as follows, i.e. with two elements in the bool/must array:
{
"body": {
"query": {
"filtered": {
"filter": {
"bool": {
"must": [
{
"term": {
"pprCategories": "category1"
}
},
{
"term": {
"country_name": "country1"
}
}
],
"must_not": {
"exists": {
"field": "field1"
}
}
}
}
}
}
}
}
As you can see the bool/must array contains two conditions instead of just one: pprCategories and country_name are two different 'must' conditions.
Thanks for helping me out!
If you want to add several conditions, you need to understand that bool/must should be an array. You can make your code work like this:
// first create the bool/must array with a single term condition
$params['body']['query']['filtered']['filter']['bool']['must'] = [
[
'term' => [
'pprCategories' => "category1";
]
]
];
// add the must_not condition
$params['body']['query']['filtered']['filter']['bool']['must_not']['exists']['field'] = "field1";
// test your condition
if (...) {
// now add a second constraint to the bool/must array
$params['body']['query']['filtered']['filter']['bool']['must'][] = [
'term' => [
'country_name' => "country1"
]
];
}
I think you should be going this way before taking it to json.
$params['body']['query']['filtered']['filter']['bool']['must'][0]["term"]['pprCategories']= "category1";
$params['body']['query']['filtered']['filter']['bool']['must'][1]["term"]['country_name']= "country1";
$params['body']['query']['filtered']['filter']['bool']['must'][2]["term"]['population_name']= "population1";
$params['body']['query']['filtered']['filter']['bool']['must'][3]["term"]['kilometers_amount']= "55145";

How to filter and restructure json data with php?

I couldn't find an answer, so I decided to ask.
I get this response from an API:
[
{
"seasonNumber":1,
"numWins":1,
"numHighBracket":2,
"numLowBracket":2,
"seasonXp":111,
"seasonLevel":5,
"bookXp":0,
"bookLevel":1,
"purchasedVIP":false
},
{
"seasonNumber":2,
"numWins":1,
"numHighBracket":21,
"numLowBracket":31,
"seasonXp":1651,
"seasonLevel":25,
"bookXp":9,
"bookLevel":11,
"purchasedVIP":false
},
{
"seasonNumber":3,
"numWins":9,
"numHighBracket":57,
"numLowBracket":127,
"seasonXp":4659,
"seasonLevel":68,
"bookXp":0,
"bookLevel":100,
"purchasedVIP":true
},
{
"seasonNumber":4,
"numWins":8,
"numHighBracket":19,
"numLowBracket":36,
"seasonXp":274,
"seasonLevel":33,
"bookXp":7,
"bookLevel":35,
"purchasedVIP":true
}
]
I am trying to change the json data to this:
{
"seasons":
[
{
"season":1,
"battle_pass":false
},
{
"season":2,
"battle_pass":false
},
{
"season":3,
"battle_pass":true
},
{
"season":4,
"battle_pass":true
}
]
}
In my current code I am using regex like this:
preg_match_all("/(?:\{\"seasonNumber\"\:(\w)|purchasedVIP\"\:(\w+))/", $response, $seasons);
echo '{"seasons":'.json_encode($seasons, JSON_FORCE_OBJECT, JSON_PRETTY_PRINT).'}';
It's basically putting everything in a separate array but that's not what I want.
Decode the json, restructure the data, re-encode.
Code: (Demo)
// your $json =
foreach (json_decode($json) as $set) {
$array[] = ["season" => $set->seasonNumber, "battle_pass" => $set->purchasedVIP];
}
echo json_encode(["seasons" => $array]);
Output:
{"seasons":[{"season":1,"battle_pass":false},{"season":2,"battle_pass":false},{"season":3,"battle_pass":true},{"season":4,"battle_pass":true}]}
p.s. if you want to force objects and pretty print, separate those flags with a pipe (|). https://3v4l.org/qsPb0

How to get json format for monogo db object [duplicate]

I am using PHP to connect with MongoDB. My code is as follows.
// connect
$m = new MongoClient($con_string); // connect to a remote host at a given port
$db = $m->main;
$customers = $db->customer->find();
i want to return $customers collection as json document to my HTML. How can i do this?
You can do this two ways:
echo json_encode(iterator_to_array($customers));
or you can manually scroll through it:
foreach($customers as $k => $row){
echo json_encode($row);
}
Each of MongoDBs objects should have their __toString() methods correctly implemented to bring back the representation of the value.
This also will work. And you can customize your json as well.
$arr = array();
foreach($customers as $c)
{
$temp = array("name" => $c["name"], "phone" => $c["phone"],
"address" => $c["address"]);
array_push($arr, $temp);
}
echo json_encode($arr);
Other answers work, but it is good to know that the generated JSON will have the following form (in this example I use an hypothetical "name" field for your customers):
{
"5587d2c3cd8348455b26feab": {
"_id": {
"$id": "5587d2c3cd8348455b26feab"
},
"name": "Robert"
},
"5587d2c3cd8348455b26feac": {
"_id": {
"$id": "5587d2c3cd8348455b26feac"
},
"name": "John"
}
}
So in case you don't want the Object _id to be the key of each of your result objects you can add a false parameter to iterator_to_array.
Your code would be:
echo json_encode(iterator_to_array($customers, false), true);
This creates the same result as
$result = Array();
foreach ($customers as $entry) {
array_push($result, $entry);
}
echo json_encode($result, true);
which is an array of JSON objects
[
{
"_id": {
"$id": "5587d2c3cd8348455b26feab"
},
"name": "Robert"
},
{
"_id": {
"$id": "5587d2c3cd8348455b26feac"
},
"name": "John"
}
]

mongodb get _id as string in find query

Here I have created a collection with a single document
db.getCollection('example').insert({"example":1});
I have tried to use Projection, and I get back the _id.
db.getCollection('example').find({"example":1},{"_id":1});
{
"_id" : ObjectId("562a6300bbc948a4315f3abc")
}
However, I need the below output as shown below.
id and not _id
ObjectId("562a6300bbc948a4315f3abc") vs "562a6300bbc948a4315f3abc"
{
"id" : "562a6300bbc948a4315f3abc"
}
Although I can process #1 and #2 on my app server(PHP based) to get the desired ouput, I am looking if there is a way to get the expected result on querying from mongo itself
MongoDB 4.0 adds the $convert aggregation operator and the $toString alias which allows you to do exactly that:
db.getCollection('example').aggregate([
{ "$match": { "example":1 } },
{ "$project": { "_id": { "$toString": "$_id" } } }
])
A main usage would most likely be though to use the _id value as a "key" in a document.
db.getCollection('example').insertOne({ "a": 1, "b": 2 })
db.getCollection('example').aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[{
"k": { "$toString": "$_id" },
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": ["$$this.k", "_id"] }
}
}
}
}]
]
}
}}
])
Which would return:
{
"5b06973e7f859c325db150fd" : { "a" : 1, "b" : 2 }
}
Which clearly shows the string, as does the other example.
Generally though there is usually a way to do "transforms" on the cursor as documents are returned from the server. This is usually a good thing since an ObjectId is a 12-byte binary representation as opposed to a 24 character hex "string" which takes a lot more space.
The shell has a .map() method
db.getCollection('example').find().map(d => Object.assign(d, { _id: d._id.valueOf() }) )
And NodeJS has a Cursor.map() which can do much the same thing:
let cursor = db.collection('example').find()
.map(( _id, ...d }) => ({ _id: _id.toString(), ...d }));
while ( await cursor.hasNext() ) {
let doc = cursor.next();
// do something
})
And the same method exists in other drivers as well ( just not PHP ), or you can just iterate the cursor and transform the content as is more likely the best thing to do.
In fact, whole cursor results can be reduced into a single object with great ease by simply adding to any cursor returning statement, when working in the shell
.toArray().reduce((o,e) => {
var _id = e._id;
delete e._id;
return Object.assign(o, { [_id]: e })
},{ })
Or for full ES6 JavaScript supporting environments like nodejs:
.toArray().reduce((o,({ _id, ...e })) => ({ ...o, [_id]: e }),{ })
Really simple stuff without the complexity of what needs to process in the aggregation framework. And very possible in any language by much the same means.
You need to use the .aggregate() method.
db.getCollection('example').aggregate([ { "$project": { "_id": 0, "id": "$_id" } } ]);
Which yields:
{ "id" : ObjectId("562a67745488a8d831ce2e35") }
or using the .str property.
db.getCollection('example').find({"example":1},{"_id":1}).map(function(doc) {
return {'id': doc._id.str }
})
Which returns:
[ { "id" : "562a67745488a8d831ce2e35" } ]
Well if you are using the PHP driver you can do something like this:
$connection = new MongoClient();
$db = $connection->test;
$col = $db->example;
$cursor = $col->find([], ["_id" => 1]);
foreach($cursor as $doc) { print_r(array("id" => $doc["_id"])); }
Which yields:
Array
(
[id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
Or using again the MongoCollection::aggregate method.
$result = $col->aggregate(array(["$project" => ["id" => "$_id", "_id" => 0]]))
Then using the foreach loop:
Array
(
[_id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
One simple solution for traversing MongoCursor on PHP side is to use Generators as well as foreach or array_map($function, iterator_to_array($cursor)).
Example:
function map_traversable(callable $mapper, \Traversable $iterator) {
foreach($iterator as $val) {
yield $mapper($val);
}
}
You can meet more at PHP documentation about generators syntax.
So, now you can use/reuse it (or similar implementation) for any propose of "projecting" your data on PHP side with any amount of mapping (just like pipeline does in aggregate) but with fewer iterations amount. And this solution is pretty convenient for OOP in a case of reusing your map functions.
UPD:
Just for your case example below:
$cursor = $db->getCollection('example')->find(["example":1],["_id":1]);
$mapper = function($record) {
return array('id' => (string) $record['_id']); //see \MongoId::__toString()
}
$traversableWithIdAsStringApplied = map_traversable($mapper, $cursor);
//...
now you can proceed with more mappings applied to $traversableWithIdAsStringApplied or use just iterator_to_array for simple array retrieving.

Categories