MONGO DB - PHP. Query in multiple nested data field - php

here expample of my data:
'_id' => new MongoId("54087e076c03943c3c8b456b"),
'fornitureFuture' =>
array (
'0' =>
array (
'data_start'▼ => new MongoDate(1412114400, 0),
'data_end' => new MongoDate(1414710000, 0),
'f1' => '65',
'f2' => new MongoInt32(0),
'f3' => '45',
'fornitore' => new MongoId("5346cb2ab9d6f0021e6b18a0"),
),
'1' =>
array (
'data_start' => new MongoDate(1420066800, 0),
'data_end' => new MongoDate(1427752800, 0),
'f1' => '63.75',
'f2' => new MongoInt32(0),
'f3' => '70.4',
'fornitore' => new MongoId("533406896c0394a62c8b4569"),
),
i need to find if exist a data in fornitureFuture with my MongoDate between data_start and data_end ...
first group data_start is 10/01/2014 and data_end 10/31/2014
Second group data_start is 01/01/2015 and data_end 03/31/2015
Something like:
//today is 09/19/2014
$dataTest = mktime(0,0,0,date('n')+2,14,date('Y')); //return 11/14/2014
$testMese = $this->db->getOne('MyCollection', array('_id'=> new \MongoId($thisPodPdr['_id']), 'fornitureFuture.data_start'=>array('$lte'=> new \MongoDate($dataTest)) , 'fornitureFuture.data_end'=>array('$gt'=> new \MongoDate($dataTest)) ) , array('fornitureFuture'=>1) );
I expect empty response but return the record..
2 example:
$dataTest = mktime(0,0,0,date('n')+7,14,date('Y')); //return 04/14/2015
$testMese = $this->db->getOne('MyCollection', array('_id'=> new \MongoId($thisPodPdr['_id']), 'fornitureFuture.data_start'=>array('$lte'=> new \MongoDate($dataTest)) , 'fornitureFuture.data_end'=>array('$gt'=> new \MongoDate($dataTest)) ) , array('fornitureFuture'=>1) );
return correctly empty!
I need to test in the same block ...something like
'fornitureFuture.$.data_start'=>array('$lte'=> new \MongoDate($dataTest)) , 'fornitureFuture.$.data_end'=>array('$gt'=> new \MongoDate($dataTest))
but dont work .
the \ is from namespace and this->db->getOne(collection,$query,$fields) is my function like $this->collection->findOne($query,$fields);
No syntax error.
sorry for my english and thanks for the help

Your first example, which returns 2 results when you expect none, runs the following query:
[
'_id' => new MongoId(...),
'fornitureFuture.data_start' => ['$lte' => new MongoDate(1418533200)],
'fornitureFuture.data_end' => ['$gt' => new MongoDate(1418533200)],
]
The example document you provided has two array elements, with the following date ranges:
1412114400 to 1414710000
1420066800 to 1427752800
This document matches because 1412114400 (of the first element) is less than 1418533200, and 1427752800 (of the second element) is greater than 1418533200. By simply referring to fornitureFuture.data_start and fornitureFuture.data_end, MongoDB's query matcher will be satisfied if any array element's sub-field meets the criteria.
You likely want to restrict start/end criteria to same array element, in which case $elemMatch is what you're looking for:
[
'_id' => new MongoId(...),
'fornitureFuture' => [
'$elemMatch' => [
'data_start' => ['$lte' => new MongoDate(1418533200)],
'data_end' => ['$gt' => new MongoDate(1418533200)],
],
],
]
This criteria should now match only when the start/end dates of the same element satisfy the range. On a related note, you may also be interested in the $ projection operator, to limit fornitureFuture to only the matched element(s).

Related

Alphabetically sort in new mongo db query command

I try to solved alphabetically sort using new MongoDB manager using command aggregation pipeline
below are my code:
$manager = new MongoDB\Driver\Manager("mongodb://localhost:27017");
$pipeline = [
[ '$match' => ['listingStatus' => ''] ],
[ '$group' => ['_id' => '$listingParticipants.email'] ],
[ '$limit' => 10 ],
[ '$skip' => 0],
[ '$sort' => ['listingParticipants.firstName' => 1]]
];
$aggregate = new \MongoDB\Driver\Command([
'aggregate' => 'Test_collection',
'pipeline' => $pipeline,
'cursor' => new stdClass
]);
$cursor = $manager->executeCommand('test_database', $aggregate);
in above code i use sort in pipeline with my string data type name field but it's not working properly so please write my code to solve this issue.
The problem here is, in a $group stage there is no name field that you are using for sort,
Better would be to sort after group
add listingParticipants.firstName in $group stage,
Also Note:
Mongo can not sort case-sensitive
meaning -- for the names like Abcd, ABcd, abcd, Bacd, bxyz
the sorted result will be (if sorted in ascending order).
Abcd, ABcd, Bacd, abcd, bxyz.
I suggest you add a new field sortName for the name with $toUpper or $toLower operator,
sortName: {$toUpper: "$name"}
and then use sortName for sorting.

Mongo $redact this object is already an operator expression Error

My goal is to subtract the current date from the stored date, and compared them to a saved interval value by the user.
I am running into the following error trying to use redact.
MongoResultException: localhost:27017: this object is already an operator expression, and can't be used as a document expression (at '0')
My code looks like this:
<?php
$ops = array(
array(
'$redact' => array(
'$cond' => array(
'if' => array(
'$gte' => array('$subtract' => array('$new Date()' , '$last_interacted_date'), '$reminder_interval')
),
'then' => '$$KEEP',
'else' => '$$PRUNE'
)
)
)
);
$results = $collection ->aggregate($ops);
For some reason the $subtract is causing this error. Why is the presence of subtract causing: "this object is already an operation expression'?
$new Date() is not valid here. You want MongoDate or MongoDB\BSON\UTCDateTime appropriate to your driver instead.
$ops = array(
array(
'$redact' => array(
'$cond' => array(
'if' => array(
'$gte' => array(
'$subtract' => array(
new UTCDateTime(round(microtime(true) * 1000)),
'$last_interacted_date'
),
'$reminder_interval'
)
),
'then' => '$$KEEP',
'else' => '$$PRUNE'
)
)
)
);
$results = $collection ->aggregate($ops);
The interpolation of the Date value actually happens in the "client" and "before" the aggregation pipeline is actually sent to the server. So you get the "milliseconds" as of "now" and send as a "BSON Date".
Note that this is "millseconds" in difference from the two BSON dates, so your "interval" needs to be in milliseconds, or otherwise do the math to convert further.

Mongodb find query with mutiple conditions in PHP

I have three working queries:
To find the rows with keyword in title field
$cursor = $collection->find(['title' => array('$regex'=>new MongoRegex($title_query))])->sort(array('timestamp'=>-1));
To find the rows with keyword in the author field
$cursor = $collection->find(['author' => array('$regex'=>new MongoRegex($author_query))])->sort(array('timestamp'=>-1));
To find the rows within a date range
$rangeQuery = array('timestamp' => array( '$gte' => $from_Id, '$lte' => $to_Id ));
$cursor = $collection->find($rangeQuery)->sort(array('timestamp'=>-1));
I want to combine the queries into 1&2, 1&3 and 2&3. However I am not able to write the correct query...
Here is my query for combining 1&2:
$cursor = $collection->find('title' => array('$regex'=>new MongoRegex($title_query)),
'author' => array('$regex'=>new MongoRegex($author_query)))->sort(array('timestamp'=>-1));
query for combining 1&3:
$rangeQuery = array('timestamp' => array( '$gte' => $from_Id, '$lte' => $to_Id ));
$cursor = $collection->find($rangeQuery, ['title' => array('$regex'=>new MongoRegex($title_query))])->sort(array('timestamp'=>-1));
Can anyone tell me how to write the correct query?
The $regex query operator should not be necessary if you are using the BSON regex type (i.e. MongoRegex in the PHP driver). Let's rewrite the original three queries:
Matching keyword in title, sorted by time descending:
$collection->find([
'title' => new MongoRegex($title_query),
])->sort(['timestamp' => -1]);
Matching keyword in author, sorted by time descending:
$collection->find([
'author' => new MongoRegex($author_query),
])->sort(['timestamp' => -1]);
Matching within a date range, sorted by time descending:
$collection->find([
'timestamp' => [
'$gte' => $from_Id,
'$lte' => $to_Id,
],
])->sort(['timestamp' => -1]);
There are several errors in the combined query examples you shared in the OP. For the "1&2" query, you were not passing an array as the first argument to find(), so that would have been a syntax error. For the "1&3" query, you're only passing the timestamp range as find() criteria, and the title regex is being incorrected passed as the second find() argument, which is reserved for specifying a project (i.e. which fields to return).
Combining the three queries is actually as easy as merging the criteria arrays. For example, we could combine all three like so:
$collection->find([
'title' => new MongoRegex($title_query),
'author' => new MongoRegex($author_query),
'timestamp' => [
'$gte' => $from_Id,
'$lte' => $to_Id,
],
])->sort(['timestamp' => -1]);
In some cases, it's not possible to merge criteria for the same field. For that reason, MongoDB has an $and query operator (see the examples for some use cases); however, in the examples above, the criteria is simple enough that you can simply combine the arrays.

MongoDB : Insert and Update in an array

I have "_id" and "ProductLot". How can I Update Qty "0" to "1 " in the Lot array if ProductLot if present or append a new Lot element if it is not present?
"_id" : ObjectId("5462e44c599e5c6c1300000a"),
"LocationName" : "Putaway",
"Owner" : "",
"Status" : "1",
"Scrap" : "0",
"Lot" : [{
"Qty" :"6",
"ProductLot" : ObjectId("5462dbd9599e5c200e000000"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"Movement" :[ {"OriginId" : "266",Qty:2,Type:"DN"},
{"OriginId" : "267" , Qty:1 , Type:"DN"},
{"OriginId" : "2" , Qty:3 , Type:"IM"},
]
},
{
"Qty" :"0",
"ProductLot" : ObjectId("5462dbd9599e5c200e000003"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"Movement" :[ {"OriginId" : "266",Qty:2,Type:"DN"},
{"OriginId" : "267" , Qty:1 , Type:"DN"},
{"OriginId" : "2" , Qty:-3 , Type:"IM"},
]
}]
}
EG: I have "ProductLot" : ObjectId("5462dbd9599e5c200e000000") present in array so it should update qty 0 to 1; however, "ProductLot" : ObjectId("5462dbd9599e5c200e00000a") is not available in array so that should append a new element to the array.
PHP code, which is not updating creating appending array at every time:
$inventoryId = new \MongoId($_POST["inventoryid"]);
$productLotId = new \MongoId($invlotid);
$originId = $_POST['id'];
//$lotcontent = [ /* whatever this looks like */ ];
$lotcontent = array(
'Qty' => $invqty,
'ProductLot' => new \MongoId($invlotid),
'Product'=>$invproduct,
'Movement'=> array(
array(
'OriginId' => $_POST['id'],
'Qty' => $invqty,
'Type'=> 'DN',
), )
);
$invcolname = 'Inventory';
$result = $mongo->$dbname->$invcolname->update(
// Match an inventory without the specific ProductLot/OriginId element
array(
'_id' => $inventoryId,
'Lot' => array(
'$not' => array(
'$elemMatch' => array(
'ProductLot' => $productLotId,
//'OriginId' => $originId,
),
),
),
),
// Append a new element to the Lot array field
array('$push' => array( 'Lot' => $lotcontent ))
);
$movementcontent = array(
'OriginId' => $_POST['id'],
'Qty' => $invqty,
'Type'=> 'DN',
);
$result = $mongo->$dbname->$invcolname->update(
// Match an inventory without the specific ProductLot/OriginId element
array(
'_id' => $inventoryId,
'Lot' => array(
//'$not' => array(
'$elemMatch' => array(
'ProductLot' => $productLotId,
'Movement'=>array(
'$not' => array(
'$elemMatch' => array(
'OriginId' => $originId,
)
)
)
),
// ),
),
),
// Append a new element to the Lot.Movement array field
array('$push' => array( 'Lot.$.Movement' => $movementcontent ))
);
$result = $mongo->$dbname->$invcolname->update(
// Match an inventory with a specific ProductLot/OriginId element
array(
'_id' => $inventoryId,
'Lot' => array(
'$elemMatch' => array(
'ProductLot' => $productLotId,
//'OriginId' => $originId,
'Movement'=>array(
'$elemMatch' => array(
'OriginId' => $originId,
)
)
),
),
),
// Update the "Qty" field of the first array element matched (if any)
//array( '$set' => array( 'Lot.$.Qty' => 'Updated' )),
array( '$set' => array( 'Lot.$.Movement.$.Qty' => $invqty )),
array('upsert' => true));
Please anyone help me to resolve this?
Using $addToSet is problematic in this case, because the following lot elements would be considered different:
{
"Qty" :0,
"ProductLot" : ObjectId("5462dbd9599e5c200e000003"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"OriginId" : "266"
}
{
"Qty" :5,
"ProductLot" : ObjectId("5462dbd9599e5c200e000003"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"OriginId" : "266"
}
The first element is likely what you would be adding (either with quantity 0 or 1), and the second element would be the same logical lot element, just with an incremented quantity. If the latter element already existed in the array, I imagine you'd like for your application to increment the quantity from 5 to 6 instead of adding the first element, which is essentially a duplicate.
We definitely need two updates here, but I would propose the following:
// Let's assume the following identifiers...
$inventoryId = new MongoId($_POST['inventoryid']);
$productLotId = new MongoId($invlotid);
$originId = new MongoId($_POST['id']);
$lotcontent = [ /* whatever this looks like */ ];
$result = $collection->update(
// Match an inventory without the specific ProductLot/OriginId element
[
'_id' => $inventoryId,
'Lot' => [
'$not' => [
'$elemMatch' => [
'ProductLot' => $productLotId,
'OriginId' => $originId,
],
],
],
],
// Append a new element to the Lot array field
[ '$push' => [ 'Lot' => $lotcontent ] ]
);
MongoCollection::update() will return a result document with an n field indicating the number of affected documents. Since we aren't using the multiple option and are also matching at most one document by _id, we can expect n to be either 0 or 1. If n was 0, we either couldn't find an inventory document with that _id or we found one but it already had a Lot element with the product and origin identifiers (i.e. our $elemMatch criteria matched something, invalidating our negation). If n was 1, that means we found the inventory document, it did not contain a matching Lot element, and we appended it (i.e. our job is done).
Assuming n was 0, we should issue another update and attempt to increment the quantity:
$result = $collection->update(
// Match an inventory with a specific ProductLot/OriginId element
[
'_id' => $inventoryId,
'Lot' => [
'$elemMatch' => [
'ProductLot' => $productLotId,
'OriginId' => $originId,
],
],
],
// Update the "Qty" field of the first array element matched (if any)
[ '$inc' => [ 'Lot.$.Qty' => 1 ] ]
);
Here, I'm using the $ positional update operator to access a specific array element that was matched in the criteria. This allows us to craft an $inc without worrying about the index of the matched element.
Again, we can check $result['n'] here. If it's still 0, then we can assume that no document matches our _id (a completely separate error). But if n is 1 at this point, we successfully incremented the quantity and our job is done.

mongodb update if a field does not exist

$db->akis->update(
array("h" => (string) $_SESSION["_id"], "m" => array('$exists' => false)),
array('$set' => array("k" => $name)),
array("multiple" => true)
);
what i did in here is, if there is an m field, do not update k. What I want to add is, "if m field exists" update i instead of k field, how can I manage this ?
thank you
I think you will need to do two separate queries here. That is just too conditional for MongoDB query parser to handle.
So you will need to put your logic into two separate queries with the second looking like:
$db->akis->update(
array("h" => (string) $_SESSION["_id"], "m" => array('$exists' => true)),
array('$set' => array("i" => $name)),
array("multiple" => true)
);
Running one after the other.

Categories