select sum column by with Mongodb and Laravel - php

I have MongoDB document which contains following data(xxx)
{
"_id" : "48e5f6b1-026f-48b8-98cb-6572cfa0eaa6",
"esti_delivery_time" : "2015-1-1",
"original_price" : NumberInt(4060),
"price":109.2,
"code" : "JS1709137",
"updated_at" : ISODate("2017-09-13T06:01:18.000+0000"),
"created_at" : ISODate("2017-09-13T03:45:54.000+0000")
}
I want to
SELECT "xxx" with SUM "price" and SUM "original_price"

Because you need the sums of the price and original_price elements, it is means that you need to group the documents by some data (one or more elements). For this task you need to use the $group aggregator.
MongoDB PHP Driver documentation
Example:
$con = new MongoDB\Client('mongodb://localhost:27017', [], [
'typeMap' => [
'root' => 'array',
'document' => 'array',
'array' => 'array'
]
]);
$collection = $con->selectDatabase("db_name")->selectCollection("collection_name");
$cursor = $collection->aggregate([
[
'$group' => [
"_id" => '$code',
"sum_price" => ['$sum' => '$price'],
"sum_original_price" => ['$sum' => '$original_price']
]
]
]);

My answer:
enter image description here
Thanks for your reply

Related

How to combine fulltext search with other optinals matches in MongoDb?

I try to query my collection with only one query and 3 potentials search method:
fulltext search
classic search
search regex
This 3 matches can be executed at the same time or just one of them.
The fulltext search is the first stage pipeline as we know. Does this fulltext search can be optional in my aggregate? Because if my default value of search is "", my query returns any data. And I need data to perform my other optionals matches.
Here is my Laravel 8 controller :
Product::raw(function ($collection) use($filters, $fullText, $likeKey, $likeValue){
return $collection->aggregate([
[
'$match' =>
[
'$text' =>['$search' => $fullText],
],
],
[
'$match' => $filters
],
[
'$match' =>
[
$likeKey =>
[
'$regex' => $likeValue,
'$options' => "i"
]
]
],
[
'$addFields' =>
[
'avgReviews' => ['$avg' => '$reviews.ranking'],
'price' => ['$min' => '$variants.price'],
'equipmentsList' => [
'$reduce' => [
'input' => '$equipments.list.list',
'initialValue' => [],
'in' =>
[
'$concatArrays' => [
'$$value',
'$$this'
]
]
]
]
],
]
]);
})
->when($operations, function($products) use ($operations){
foreach($operations as $key => $operation){
return $products
->where($operation[0],$operation[1],$operation[2]);
}
})
->forPage($page,$limit)
->sortBy($sortBy, SORT_REGULAR, $order == 'desc')
->values();
$filters is an array and works fine when it's the only one match. But if I want to use $filters without $text, it returns any data. And with the third match, nothing works. Can somebody help me with this?

(Elasticsearch) Convert Unix formatted data into timestamp (without changing the mappings)

We're executing an Elasticsearch query like this using PHP API:
$params = [
//please ignore the variables below,
//we made it in dynamic parameter-based in our function,
//that's why they're variables
'index' => $ourIndex,
'type' => $ourType,
'from' => $from,
'size' => $page_size,
'body' => [
"query" => [
'bool' => [
'must' => [
[
"query_string" => [
"default_field" => $content,
"query" => "$keywords"
]
],
[
"range" => [
"#timestamp" => [
"from" => $parseParams['pub_date_start'],
"to" => $parseParams['pub_date_end'],
'format' => "yy-MMM-dd'T'HH:mm:ss.SSS'Z'",
]
]
]
]
]
]
]
];
The query above works with our #timestamp field because its type is on date
"#timestamp" : {
"type" : "date"
}
And a sample value is like this:
"#timestamp" : "2019-06-17T16:53:55.778Z"
However, we want to target our pub_date field in our index, and in its mapping, the field has a type of long
"pub_date" : {
"type" : "long"
},
so it has this kind of values when we're displaying the documents:
"pub_date" : 1510358400
When we changed the query above to target instead of #timestamp to pub_date, it now displays an error like this:
Tried Solutions
I tried to add an additional format epoch_millis in the format property:
[
"range" => [
"pub_date" => [
"from" => $parseParams['pub_date_start'],
"to" => $parseParams['pub_date_end'],
'format' => "yyyy-MM-dd||yy-MMM-dd'T'HH:mm:ss.SSS'Z'||epoch_millis",
]
]
]
But still fails
Main Question
I feel that the Unix formatted values cant be recognized by the range query of Elasticsearch so that's why the query fails. Is there a work-around for this without changing the MAPPINGS of the index?
Because the other possible solutions suggested to change the mapping, but we already have around 25 million documents in the index, so we thought formatting it in PHP would be a nicer approach
Since the field is of type long and stores the unix timestamp, simply convert the date in $parseParams['pub_date_start'] and $parseParams['pub_date_end'] to unix timestamp using strtotime. Update the range query as below:
"range" => [
"pub_date" => [
"from" => strtotime($parseParams['pub_date_start']),
"to" => strtotime($parseParams['pub_date_end']),
]
]

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.

Find MongoDB data with PHP

Mongodb search with PHP code:
$cond=array();
$cond=array_merge($cond,array("clicks" => array('$gt' =>6)));
if (isset($lang)){
$cond=array_merge($cond,array("$or" => array(array("lang" =>'de'),array("lang" =>'fr'))));
}
if (isset($country)){
$cond=array_merge($cond,array("$or" => array(array("country" =>'us'),array("country" =>'uk'))));
}
Problem: On the last line, the second $or of country is replacing the first $or of lang. Would be great if anyone can suggest how can we avoid this overriding issue?
Actually I'm new to MongoDB, I want to create a find query in MongoDB. I have to build the query on the basis of some condition flags (e.g. if '$country=true' only then embed country filter) for each column. Similar to SQL The output I need is:
"Where clicks > 6 and (lang = 'de' or lang = 'fr') and (country = 'us' or country = 'uk')"
This is how your resulting array should look like:
$cond = [
'$and' => [
['clicks' => [ '$gt' => 6 ]],
['$or' => [
[ 'lang' => 'de' ],
[ 'lang' => 'fr' ]
]],
['$or' => [
[ 'country' => 'us' ],
[ 'lang' => 'uk' ]
]]
]
];
Now, to achieve this by using the modular approach you have specified, you could do something like this:
$cond = [
'$and' => [
['clicks' => [ '$gt' => 6 ]]
]
];
if (isset($lang)) {
$cond['$and'][] = ['$or' => [['lang' =>'de'], ['lang' =>'fr']]];
}
if (isset($country)) {
$cond['$and'][] = ['$or' => [['country' =>'us'], ['country' =>'uk']]];
}
Hopefully this will demonstrate the way to assemble nested or/and query in PHP, so you can adjust it further as your design will demand.

Laravel 5 execute aggregation with mongodb on where clause

Laravel 5 Eloquent sum of multiplied columns for mongo DB
This was my previous question and it got solved with the help of #Alex, now i need to add a where clause of $field != '0'
Here I am stuck I tried with the match but still I have no option left to get help from here.
Thanks
Using the aggregation pipeline where the $ne comparison query operator is in the $match pipeline:
DB::connection($this->MongoSchemaName)
->collection($this->InvoicesTable)
->raw(function($collection) use ($customer){
return $collection->aggregate([
['$match' => [
'ContactID' => (int)$customer->ContactID,
'Type' => 'PAYMENT',
'AmountDue' => [ '$ne' => 0 ]
]
],
['$group' => [
'_id' => '$ContactID',
'TotalInBaseCurrency' => [
'$sum' => ['$multiply' => ['$Total', '$CurrencyRate']]
]
]
]
]);
})

Categories