Reputation: 23
I am using Mongodb, but I can migrate to mysql, My problem is, I have a collection which consists datas like this one,
array(
"L"=>"cars",
"M"=>"wolkswagen"
)
array(
"L"=>"cars",
"M"=>"bmw"
)
array(
"L"=>"cars",
"M"=>"mercedes"
)
array(
"L"=>"bike",
"M"=>"bianchi"
)
array(
"L"=>"bike",
"M"=>"trek"
)
array(
"L"=>"bike",
"M"=>"brook"
)
What I want to get as a result, any bike and car, but 2 results for each of them, so the result should be,
array(
"L"=>"cars",
"M"=>"some car"
)
array(
"L"=>"cars",
"M"=>"some car"
)
array(
"L"=>"bike",
"M"=>"some bike"
)
array(
"L"=>"bike",
"M"=>"some bike"
)
I tried to do this with $in, but does not seem to work, any ideas ?
Upvotes: 0
Views: 73
Reputation: 65313
There are a few ways to solve this problem using the Aggregation Framework in MongoDB 2.2+.
Unfortunately the Aggregation Framework as at 2.2.2 doesn't support a $slice
or array subset operator yet, so some extra manipulation is required.
Note: The examples below are using the mongo
shell, but should be straightforward to translate to the PHP driver.
Assuming you aren't fussed which two elements you get for each grouping, you could select the $first
and $last
:
db.cars.aggregate(
// Group by $L and find the first and last elements
{ $group: {
_id: '$L',
first: { $first: "$M" },
last: { $last: "$M" },
}},
// Add an extra $index for # of array elements
{ $project: {
first: 1,
last: 1,
index: { $const:[0,1] }
}},
// Split into document stream based on $index
{ $unwind: '$index' },
// Re-group data using conditional to create array
{ $group: {
_id: '$_id',
M: {
$push: { $cond:[ {$eq:['$index', 0]}, '$first', '$last'] }
}
}}
)
Sample output:
{
"result" : [
{
"_id" : "cars",
"M" : [
"wolkswagen",
"mercedes"
]
},
{
"_id" : "bike",
"M" : [
"bianchi",
"brook"
]
}
],
"ok" : 1
}
An alternative to the above would be a simpler pipeline with $addToSet
and a post-processing step to trim the array elements:
var agg = db.cars.aggregate(
{ $group: {
_id: '$L',
'M': { $addToSet: "$M" },
}}
)
// Iterate to trim each result to the desired # of elements
agg.result.forEach(function(doc) {
// Note: you could randomly select 2 (or n) elements here
doc.M = doc.M.slice(0,2);
})
The output format will be the same as with example #1, but instead of selecting the first & last elements, example #2 (as written) selects the first two elements.
Upvotes: 1