MongoDb fetch document subset using PHP - php

I have a MongoDB document structure like this:
[
{
"locale":"en",
"translations":[
{
"name":"translation1",
"value":"enValue"
},
{
"name":"translation2",
"value":"enValue"
},
{
"name":"translation3",
"value":"enValue"
}
]
},
{
"locale":"ru",
"translations":[
{
"name":"translation1",
"value":"ruValue"
},
{
"name":"translation2",
"value":"ruValue"
},
{
"name":"translation3",
"value":"ruValue"
}
]
}
]
and I need to get the translation with name translation1 for locale en.
The expected result I want is:
{
"_id" : ObjectId("5e845ba1005e625a6237d2e0"),
"translations" : [
{
"name" : "translation1",
"value" : "enValue"
}
]
}
I know how to do this with pure mongo, it should be like this:
db.translations.find({"locale" : "en"},
{ translations: { $elemMatch: { name: "translation1" } } } )
Here is the proof https://gyazo.com/fb9b1a505a898c7137ece5304d715171
but I can't make it work with PHP. I tried code like:
$collection = $this->database->{$group};
$collection->find(
[
'locale' => 'en',
'translations' => ['$elemMatch' => ['name' => 'translation1']
]
);
And Im getting all translations for en instead of only tranlsation1 as a result:
{
"_id" : ObjectId("5e845ba1005e625a6237d2e0"),
"locale" : "en",
"translations" : [
{
"name" : "translation1",
"value" : "enValue"
},
{
"name":"translation2",
"value":"enValue"
},
{
"name":"translation3",
"value":"enValue"
}
]
}
I tried as:
$collection = $this->database->{$group};
$collection->find(
['locale' => 'en'],
[
'translations' => ['$elemMatch' => ['name' => 'translation1']
]
);
also result is the same as above.
Tried like:
$collection = $this->database->{$group};
$collection->find(
[
'locale' => 'en',
[
'translations' => ['$elemMatch' => ['name' => 'translation1']
]
]
);
result is null
As a workaround, for now, I filter result on PHP side, but it extra work

This appears to be a bug in the driver.
This database command should be equivalent to the find you were running, but the command works as expected while the find does not.
$this->$database->command([
'find'=>'CollectionName',
'filter'=>['locale' => 'en'],
'projection'=>['translations' => ['$elemMatch' => ['name' => 'translation1']]]
])

Related

How to add "runtime_mappings" to the query for FOS/Elastica in PHP?

Need to add the next json query to php code with using FOSElasticaBundle:
"runtime_mappings": {
"Agreement": {
"type": "keyword",
"script": {
"source": "if(doc['winningBidder.edrpou'].size()>0 && doc['seller'].size()>0)\r\n{\r\nemit(\r\n doc['seller'].value+\":\"+\r\n doc['trading.id'].value+\":\"+\r\n doc['winningBidder.edrpou'].value+\":\"\r\n )\r\n}"
}
}
}
If I'm set this in simple method (\Elastica\Query)->addParam():
->addParam('runtime_mappings', [
'Agreement' => [
'type' => 'keyword',
'script' => [
'source' => "if(doc['winningBidder.edrpou'].size()>0 && doc['seller'].size()>0)\r\n{\r\nemit(\r\n doc['seller'].value+\":\"+\r\n doc['trading.id'].value+\":\"+\r\n doc['winningBidder.edrpou'].value+\":\"\r\n )\r\n}"
]
]
])
Then I get an error when I try to collect the query:
Unknown key for a START_ARRAY in [runtime_mappings].
Alternatively, you can create a query by starting with the "runtime_mappings" array and adding all other parameters after:
$query = \Elastica\Query::create(['runtime_mappings' => [
'Agreement' => [
'type' => 'keyword',
'script' => [
'source' => "if(doc['winningBidder.edrpou'].size()>0 && doc['seller'].size()>0)\r\n{\r\nemit(\r\n doc['seller'].value+\":\"+\r\n doc['trading.id'].value+\":\"+\r\n doc['winningBidder.edrpou'].value+\":\"\r\n )\r\n}"
]
],
]]);
// add other params to query...
$query->addParam();

Get specific value from json response not working

enter image description here
I have the following json response as in the image. I would want to access specific value, like: "ROUMANIE ROVA AROMANIA" but I can't seem to get to it. I tried the following:
$response = json_decode($r->getBody(),true);
foreach($response['ParsedResults'] as $key)
{
foreach($key['TextOverlay']['Lines'] as $bla)
{
echo $bla['LineText'];
echo $bla[0]['LineText'];
}
}
If I only echo one depth, it's working. I searched for a solution but none did the trick. Thanks.
0 is the current index of the first item, $bla contains already the data you're looking for, so doing this directly should work:
echo $bla['LineText'];
Here is how the full code should look like:
$response = [
'ParsedResults' => [
[
'TextOverlay' => [
'Lines' => [
[
'LineText' => 'ROUMANIE ROVA AROMANIA',
'Words' => [
[
'WordText' => 'ROUMANIE',
'OtherData' => 'whatever'
],
[
'WordText' => 'ROVA',
'OtherData' => 'whatever'
],
[
'WordText' => 'AROMANIA',
'OtherData' => 'whatever'
],
]
]
]
]
]
]
];
foreach($response['ParsedResults'] as $key)
{
foreach($key['TextOverlay']['Lines'] as $bla)
{
echo $bla['LineText'];
}
}
Tested here: http://sandbox.onlinephpfunctions.com/code/0577e854eed73dfb33193c391acc37dd81baf982

Using match_all with filters

I have a page which allows users to query datasets and apply filters. They can also apply filters without querying with a string. To do so I'm attempting to use match_all with filters but getting the following error
"{"error":{"root_cause":[{"type":"parsing_exception","reason":"[match_all]
malformed query, expected [END_OBJECT] but found
[FIELD_NAME]","line":1,"col":26}],"type":"parsing_exception","reason":"[match_all]
malformed query, expected [END_OBJECT] but found
[FIELD_NAME]","line":1,"col":26},"status":400}",
This is an example of the search parameters that I'm building and sending to the elastic client.
[
"type" => "events"
"index" => "events"
"body" => [
"query" => [
"match_all" => {}
"bool" => [
"filter" => [
"range" => [
"start_date.date" => [
"gte" => "01/05/2019"
"lte" => "05/2019"
"format" => "dd/MM/yyyy||MM/yyyy"
]
]
]
]
]
"from" => 0
"size" => 30
]
]
I can't seem to figure out how to use both of them. Any pointers? Thank you.
You will need to wrap your query in a bool query like this:
"query": {
"bool" : {
"must" : {
"match_all": {}
},
"filter": {
"range" : { /* your filter here*/ }
}
}
}
Just wrap the bool and a must query around your match_all and it should work.
I don't know the exact PHP syntax but it should be something like this:
[
"type" => "events"
"index" => "events"
"body" => [
"query" => [
"bool" => [
"must" => [ "match_all" => {}]
"filter" => [
"range" => [
"start_date.date" => [
"gte" => "01/05/2019"
"lte" => "05/2019"
"format" => "dd/MM/yyyy||MM/yyyy"
]
]
]
]
]
"from" => 0
"size" => 30
]
]
For reference see the docs Elasticsearch Reference [7.0] » Query DSL » Compound queries » Bool Query, it contains an example like yours with match_all combined with filters.

Craft Element API data output as a hash instead of an array

I'm building and API feed using Element API plugin from Craft, and I'd like the data output to be serialized as a hash, but currently it's returning an array, as example bellow:
<?php
namespace Craft;
return [
'endpoints' => [
'event-name/feed.json' => [
'elementType' => ElementType::Entry,
'criteria' => ['section' => 'event1'],
'transformer' => function(EntryModel $entry) {
$speakersList = [];
foreach ($entry->speakersList as $speaker) {
$speakersList[] = [
'name' => $speaker->speakerFullName,
'jobTitle' => $speaker->speakerJobTitle,
'company' => $speaker->speakerCompany,
];
}
return [
'speakers' => $speakersList,
];
},
],
];
And the output:
{
"data": [
{
"speakers": [
{
"name": "John Doe",
"jobTitle": "Marketing",
"company": "Company 1",
},
...
I've tried the serialize options in the documentation, but none seemed to solve the issue.
Currently if I'd like to access the speakers within data I'd have to first access index[0] to be able to get to the speakers key.
Is there a way to get rid of this array level?

How to do an aggregate query on an embedded document?

I am using jenssegers/laravel-mongodb library in a laravel application however I need to show counts of an embedded document. Using a generic example of comment/posts, while I can solve my problem by just pulling all the posts and looping through to get comments to count them but was just was not sure if I could query them.
I did set up my relationships. In my post class I did:
public function comments()
{
return $this->hasMany('App\Comment');
}
and in my comment class:
public function post()
{
return $this->belongsTo('App\Post');
}
Later in code:
$post->comments()->save($comment);
$comment->post()->associate($post);
my document structure:
"posts" : [
{
"_id" : ObjectId("5805a11e2594ee26543ea041"),
"Post_Num" : "166236001010",
"updated_at" : ISODate("2016-10-18T04:12:14.454Z"),
"created_at" : ISODate("2016-10-18T04:12:14.451Z"),
"comments" : [
{
"Comment_Num" : "3333333",
"_id" : ObjectId("5805a11e2594ee26543ea042"),
"post_id" : "5805a11e2594ee26543ea041",
},
{
"Comment_Num" : "3333333",
"_id" : ObjectId("5805a11e2594ee26543ea042"),
"post_id" : "5805a11e2594ee26543ea041",
}
]
},
{
"_id" : ObjectId("5805a11e2594ee26543ea041"),
"Post_Num" : "166236001010",
"comments" : [
{
"Comment_Num" : "3333333",
"_id" : ObjectId("5805a11e2594ee26543ea042"),
"post_id" : "5805a11e2594ee26543ea041",
}
]
}
]
Now when I try getting the comments like:
$post->comments()->count()
or
$post->comments()->get()->count()
or
$post->comments->get()->count()
I get a 0. The same logic works if it is not an embedded document but just was wondering if it was possible to do an aggregate query ? Perhaps is best to just let the code iterate and add everything?
As you can tell I need some minor hand holding. Thank You
UPDATE: I am trying the following
public function commentCount()
{
$commentsCount = Post::raw(function($collection)
{
return $collection->aggregate(['$project' => ['_id' => 1,
'comments_count' => ['$size' => '$comments']],
['$group' => ['_id' => null, 'count' => ['$sum' => '$comments_count']]]]);
});
return $commentsCount;
}
What I get now is:
$pipeline is not a list (unexpected index: "$project")
Just to be clear, you want a list of your posts with the number of comments on each post?
Aggregation has something to offer for that:
https://docs.mongodb.com/manual/reference/operator/aggregation/size/#exp._S_size
I'm not a php dev but this is my shot at it:
Post::raw()->aggregate(
['$project' => ['_id' => 1,
'Post_Num' => 1,
'comments_count' => ['$size' => '$comments']],
['$group' => ['_id' => null, 'count' => ['$sum' => '$comments_count']]]
]);

Categories