Codeigniter: How to implement inner join in mongodb - php

I want to know how can I query inner joins in mongodb? I have this users and payments table. I only want to display records that users has payments if not, do not include it in the result. So far, I only got left join like results.
Here is my code:
$getData=[
[
'$match' => ['profile_status'=> 'buyer']
],
[
'$project'=>[
'_id' => ['$toString' => '$_id'],
'profile_image' => '$profile_image',
'full_name' => '$full_name',
'profile_status' => '$profile_status'
]
],
[
'$lookup' => [
'from' => 'payment_details',
'let' => [
'user_id' => ['$toString' => '$_id']
],
'pipeline' => [
[
'$match' => [
'$expr' => ['$eq' => ['$buyer_id','$$user_id']]
]
],
[
'$project' => [
'_id' => ['$toString' => '$_id'],
'price' => '$price'
]
],
],
'as' => 'paymentData'
]
]
];
$getresult= $db->users->aggregate($getData);
$getfinalresult= iterator_to_array($getresult);
This code returns users data even paymentData is empty, I only want users that has paymentData. Im using PHP/Codeigniter 3.
Thank you!

Related

How to filter mongodb field from linked collection

I want to know how can I propery filter field from another collection that is linked from main collection.
Here is my filter values
if(!empty($filterDataBuyer['price'])){
$findArray['price'] = intval($filterDataBuyer['price']);
}
if(!empty($filterDataBuyer['search'])){
$findArray['full_name'] = $filterDataBuyer['search'];
}
}
$findArray['type'] = 'buyer';
The price and type works fine but the full_name does not work
And here is for getting the data
$getData = [
[
'$match' => $findArray
],
[
'$project' => [
'_id' => ['$toString' => '$_id'],
'type' => '$type',
'created_date' => '$created_date',
'offer_id' => '$offer_id',
'buyer_id' => '$buyer_id',
'traveler_id' => '$raveler_id',
'order_id' => '$order_id',
'price' => '$price'
]
],
[
'$lookup' => [
'from' => 'users',
'let' => [
'admin_id' => ['$toObjectId' => '$buyer_id'],
],
'pipeline' => [
[
'$match' => [
'$expr' => [
'$eq' => [
'$_id',
'$$admin_id'
]
],
],
],
[
'$project' => [
'_id' => ['$toString' => '$_id'],
'profile_image' => '$profile_image',
'full_name' => '$full_name'
]
],
],
'as' => 'profileData'
]
],
What I want to do is if full_name field is available, it could also filter the result by it, price and type can filter successfully but full_name is not. Result returns only if full_name has value on it.
My PHP version is 7.4.27 and mongodb extension is 1.12.0
This is just two collections, payment_details and users where payment_details has buyer_id field that is reference to users id field
From mongocompass:
payment_details:
users:
Full Code update:
if(!empty($filterDataBuyer['price'])){
/* FLIGHT-31 fix */
$findArray['price'] = intval($filterDataBuyer['price']);
}
if(!empty($filterDataBuyer['search'])){
$findArray['full_name'] = $filterDataBuyer['search'];
}
$findArray['type'] = 'buyer';
$buyer_trasections = $db->payment_details->find($findArray);
$buyerRes_trasections = iterator_to_array($buyer_trasections);
$getData = [
[
'$match' => $findArray
],
[
'$project' => [
'_id' => ['$toString' => '$_id'],
'type' => '$type',
'created_date' => '$created_date',
'offer_id' => '$offer_id',
'buyer_id' => '$buyer_id',
'traveler_id' => '$raveler_id',
'order_id' => '$order_id',
'price' => '$price'
]
],
[
'$lookup' => [
'from' => 'users',
'let' => [
'admin_id' => ['$toObjectId' => '$buyer_id'],
],
'pipeline' => [
[
'$match' => [
'$expr' => [
'$eq' => [
'$_id',
'$$admin_id'
]
],
],
],
[
'$project' => [
'_id' => ['$toString' => '$_id'],
'profile_image' => '$profile_image',
'full_name' => '$full_name'
]
],
],
'as' => 'profileData'
]
],
[
'$skip' => $page
],
[
'$limit' => $config['per_page'],
],
[
'$sort' => [ 'created_date'=> -1]
]
];
$buyer_trasec = $db->payment_details->aggregate($getData);
$buyerRes_trasec = iterator_to_array($buyer_trasec);
echo "<pre>";print_r($buyerRes_trasec);exit;
And this is the sample, first result for echo "<pre>";print_r($buyerRes_trasec);exit;

MongoDB filter query results by $lookup array

MongoDB 3.4
I have a project and project_permission collections. The project_permission collection contains permissions to the projects for some users. A single user can have multiple different permissions to the project.
[
'$lookup' => [
'from' => ProjectPermission::collectionName(),
'localField' => '_id',
'foreignField' => 'project_id',
'as' => 'project_permissions'
]
],
[
'$project' => [
// ... irrelevant fields here
'permissions' => '$project_permissions'
]
],
this is how the project query results looks like without filtering:
// other project results
// ... other fields
'permissions' => [
0 => [
'_id' => '5d2873aafa873b2b7c000fad'
'project_id' => '56a9e5c5d18cacc72a485839'
'user_id' => '562f6bfc05dfe9570fb6e427'
'permission' => 'read'
'created_at' => 1562932138
'updated_at' => 1562932139
]
1 => [
'_id' => '5d2879fdfa873b2b7c000fbd'
'project_id' => '56a9e5c5d18cacc72a485839'
'user_id' => '562f6bfc05dfe9570fb6e427'
'permission' => 'write'
'created_at' => 1562932139
'updated_at' => 1562932140
]
2 => [
'_id' => '5db960b5fa873b1604005e8e'
'project_id' => '56a9e5c5d18cacc72a485839'
'user_id' => '582b30dd1e634e6362e1b504'
'permission' => 'write'
'created_at' => 1572430005
'updated_at' => 1572430005
]
]
What I would like to achieve is to return with only those projects where the client - who requested the query - has a specific permission to the project, for example write.
The way I tried it:
pipeline: [
0 => [
'$match' => [
// not related to the problem
]
]
1 => [
'$match' => [
'$and' => [
0 => [
'shared_permissions' => [
'$eq' => true
]
]
1 => [
'$or' => [
0 => [
'project_permissions' => [
'$exists' => true
'$ne' => []
]
]
1 => [
'owner_id' => [
'$ne' => MongoDB\BSON\ObjectId#1
(
[oid] => '582b30dd1e634e6362e1b504'
)
]
]
]
]
]
]
]
2 => [
'$lookup' => [
'from' => 'project_permission'
'localField' => '_id'
'foreignField' => 'project_id'
'as' => 'project_permissions'
]
]
3 => [
'$project' => [
// more not important fields here
'shared_permissions' => 1
'permissions' => [
'$map' => [
'input' => [
'$filter' => [
'input' => '$project_permissions'
'as' => 'project_permission'
'cond' => [
'$and' => [
0 => [
'$eq' => ['$$project_permission.user_id', MongoDB\BSON\ObjectId#1
(
[oid] => '582b30dd1e634e6362e1b504'
)
]
1 => [
'$eq' => ['$$project_permission.permission', 'write']
]
]
]
]
]
'as' => 'project_permission'
'in' => [
'user_id' => '$$project_permission.user_id'
'permission' => '$$project_permission.permission'
]
]
]
]
]
]
For this I almost get the correct response:
[
0 => [
'_id' => '56a9e5c5d18cacc72a485839'
'short_id' => 3
'title' => 'Modified title'
'owner_id' => '562f692a05dfe9560fb6e428'
'updated_at' => 1572435428
'owner_name' => 'Borat Sagdiyev'
'shared_permissions' => true
'permissions' => [
0 => [
'user_id' => '582b30dd1e634e6362e1b504'
'permission' => 'write'
]
]
]
1 => []
]
The problem with this is that empty array, where the result was filtered out - and it wouldn't be a problem if the empty array wouldn't be in the result, because if I use the pagination, then it says two results, instead of one. And we know that in the worst case we would get back an array of empty arrays only.
So what I would like to achieve is this last example results without empty arrays in a way where the pagination will be fine with it too.
ps.: unwind is not an option, because of some structural conventions.
Any ideas?
To perform an equality match between a field from the input documents with a field from the documents of the “joined” collection, the $lookup stage has the following syntax:
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}

Combine mongoDB $lookup with $project

Right now I'm using the $project aggregation for filtering out unnecessary fields. Also I'm using the $lookup aggregation to link two collections togethe and I know how to use both of them in the main collection.
Now my question is; how can I put this $project aggregation inside of a lookup?
What I have now is looks like this:
[
'$lookup' => [
'from' => Media::collectionName(),
'localField' => '_id',
'foreignField' => 'project_id',
'as' => 'mediaList'
]
],
[
'$project' => [
'title' => 1,
'owner_id' => 1,
'owner_name' => 1,
'created_at' => 1,
'updated_at' => 1,
'status' => 1,
'discount' => 1,
'company' => 1,
'media' => [
'$filter' => [
'input' => '$mediaList',
'as' => 'media',
'cond' => $mediaFilter
]
]
]
],
So I can filtering out the unnecessary fields in the main collection. How can I do this in the sub-collection?
If I understood your question correctly, you want to apply a $filter operation on your $mediaList field.
To avoid a redundant $project stage (where you have to declare every single field you want to keep), use $addFields stage instead, like this :
[
'$lookup' => [
'from' => Media::collectionName(),
'localField' => '_id',
'foreignField' => 'project_id',
'as' => 'media' // Note that I use the same name for the field
]
],
[
'$addFields' => [
'media' => [
'$filter' => [
'input' => '$media',
'as' => 'media',
'cond' => $mediaFilter
]
]
]
],

mongo1:27017: exception: aggregation result exceeds maximum document size (16MB)

I'm trying to run a mongo query in my laravel app but receiving the error listed in the title of this post. I think I need to set useCursor to true but am having trouble getting it to work. Here's my code:
$email_duplicates = User::raw(function ($collection) {
return $collection->aggregate(
[
[
'$group' => [
'_id' => [
'email' => '$email',
],
'uniqueIds' => [
'$addToSet' => '$_id',
],
'count' => [
'$sum' => 1,
],
],
],
[
'$match' => [
'count' => [
'$gt' => 1,
],
],
],
],
[
'allowDiskUse' => true,
],
[
'useCursor' => true,
]
);
});
anyone know what I'm doing wrong?
Thanks!

Elasticsearch not sure if filter or query should be first

I am using ES for my Laravel app.
What I want to do is a search filtering.
I do a fulltext search on the title field and then check that the price is between 0 - 9999 and that active is set to 1.
But both these queries seems to work fine on my test data. But what is the difference between them? Does the order "query" comes in play any diffrence?
Ignore the syntax, just take a look at the query structure.
First query
'filtered' => [
'query' => [
'match' => ['title' => Input::get('query')]
],
'filter'=> [
'bool' => [
'must' => [
['term' => [ 'active' => 1] ],
[ 'range' => [
'price' => [
'gte' => 1,
'lte' => 99999,
]
]
]
]
]
],
],
Second query
'filtered' => [
'filter' => [
'bool' => [
'must' => [
['term' => [ 'status' => 1] ],
[
'range' => [
'price' => [
'gte' => 1,
'lte' => 99999,
]
]
]
]
]
],
'query' => [
'match' => [
'title' => Input::get('query', '')
]
]
]
Thanks in advance.
It makes no difference at all in which order filter and query are mentioned in a filtered query. What dictates if query or filter is executed first for a document depends on an expert-level optional field called strategy of filtered query. For more information, read this.

Categories