PHP and MongoDate query - php

Created a mongo collection using a PHP script with a sub-field initialized with MongoDate. The resulting field in the collection looks like this:
"ts_add" : {
"sec" : 1335468966,
"usec" : 420000
},
When I am building my query aginst this field in PHP, I am building it like this:
$val = new MongoDate(strtotime($strDate)); // $strDate = '2012-04-25'
...
$aryQuery = array(STRING_COL_NAME => array ('$gte' => $val));
And then I do some other stuff and exec the query with the find() command.
The build query structure in PHP looks like this, according to the debugger:
find(Array
(
[ts_add] => Array
(
[$gte] => MongoDate Object
(
[sec] => 1335337200
[usec] => 0
)
)
)
In my log files, I see this:
runQuery called coll.table { ts_add: { $gte: new Date(1335337200000) } }
But no data is ever returned....and I'm kind of weirded-out by all the extra zeros but I am thinking that's default timestamp data addded or some weird MongoDate-ism...
If I manually, from the cli, run this command:
> db.table.find({ "ts_add.sec": { $gte:1335337200 } })
The full data set (as expected) is returned.
Tried this, then, from the cli to try to mimic the MongoDate thing:
> var start = new Date(2012, 3, 10)
> db.addons_add.find({ ts_add : { $gte : start } } )
No data returned.
If I use the same input data and convert it to a MongoID, searching against the $_id field, the search is successful.
Any suggestions? What am I missing? Have the feeling I'm standing three-inches from the tree complaining about how I'm not seeing the forest...
thanks!

mongod prints everything in JavaScript format, which prints dates in milliseconds (which is where all the extra 0s are coming from). So querying for 1335337200 correctly turns into 1335337200 * 1000 = 1335337200000.
The initial document fragment you pasted looks wrong, though. "ts_add" : {"sec" : 1335468966, "usec" : 420000} is JSON, but the Date type shouldn't look like that in JavaScript. How are you saving dates? It looks like they're being converted to another type of object and then stored as "generic object" instead of "date type."

Related

php: how to parse graphQl query string to get the operation names

I would like to allow certain graphQl operations only for certain api users based on a confguration. Our stack is Symfony 6.2 + overblog/GraphQLBundle.
My current approach is to check in the authenticate method of a custom authenticator, if the current operation is cleared in the allowed operations config of the user. For this I would like to parse the graphql query into a kind of array, that I can interpret easily.
Anybody knows how this can be done? I was scanning the underlying webonyx/graphql-php library, but can not see how they do it.
As a simple example:
query myLatestPosts($followablesToFilter: [FollowableInput], $limit: Int, $offset: Int) {
my_latest_posts(followablesToFilter: $followablesToFilter, limit: $limit, offset: $offset) {
...PostFragment
__typename
}
my_roles
}
From this I would like to retrieve the operations my_latest_posts and my_roles.
Update 1
it's probably possible to write a simple lexer utilising preg_split - I'm just hesitating, as I'm sure someone has done this already... The spec appears to be well defined.
Alright, so it turned out webonyx/graphql-php has indeed all the lowlevel function needed for this :D. Especially the Visitor is very useful here.
With this code you can drill down the query to get the operation names (or selections, as they are called in the code)
This works for me:
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\Parser;
use GraphQL\Language\Visitor;
// whatever comes before
// ...
$graphQlRequest = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
$operations = [];
Visitor::visit(Parser::parse($graphQlRequest['query']), [
NodeKind::OPERATION_DEFINITION => function ($node) use (&$operations) {
// $node contains the whole graphQL query in a structured way
$selections = array_map(function ($selection) {
return $selection['name']['value'];
}, $node->toArray()['selectionSet']['selections']);
foreach ($selections as $selection) {
$operations[] = $selection;
}
return Visitor::stop();
}]
);
print_r($operations);
The result of $operations is then for my example above:
Array
(
[0] => my_latest_posts
[1] => my_roles
)
And this information is all I need to decide weather the user should have access to the endpoint or not.

Add new field to a MongoDB document parsing from String to Int using updateMany

In my MongoDB collection, all documents contain a mileage field which currently is a string. Using PHP, I'd like to add a second field which contains the same content, but as an integer value. Questions like How to change the type of a field? contain custom MongoDB code which I don't want to run using PHP, and questions like mongodb php Strings to float values retrieve all documents and loop over them.
Is there any way to use \MongoDB\Operation\UpdateMany for this, as this would put all the work to the database level? I've already tried this for static values (like: add the same string to all documents), but struggle with getting the data to be inserted from the collection itself.
Some further hints:
I'm looking for a pure PHP solution that does not rely on any binary to be called using exec. This should avoid installing more packages than needed on the PHP server
Currently, I have to use MongoDB in v4.0. Yes, that's not the most recent version, but I'm not in the position to perform an upgrade
Try this, please:
01) MongoDB Aggregate reference:
db.collectionName.aggregate(
[
{ "$addFields": {
"intField": { "$toInt": "$stringFieldName" }
}},
{ "$out": "collectionName" }
]
)
02) Possible PHP solution (Using as reference https://www.php.net/manual/en/mongocollection.aggregate.php):
$pipeline = array(
array(
'$addFields' => array(
'integerField' => array('$toInt' => '$mileage')
)
),
array(
'$out' => 'collection'
),
);
$updateResult = $collection->aggregate(pipeline);
You could use $set like this in 4.2 which supports aggregation pipeline in update.
$set stage creates a mileageasint based on the previous with $toInt value
db.collection.updateMany(
<query>,
[{ $set: { "mileageasint":{"$toInt":"$mileage" }}}],
...
)
Php Solution ( Using example from here)
$updateResult = $collection->updateMany(
[],
[['$set' => [ 'mileageasint' => [ '$toInt' => '$mileage']]]]
);

Push to a nested array in Mongo with Laravel

I have a structure of MongoDB that looks like this:
Object_id {
workflow {
tree_id {
other_ids {...}
other_ids {...}
other_ids {...}
subscribers {
subscriber_id {
email : value
}
}
}
}
}
}
It can be clearly seen on this screen:
My MongoDB structure
I want to add an another field, for example name : last_name, under the email : value. I tried it by using this code:
Model::where('_id', '5adde78993def907b71ce503')->push(array('workflow.5ace115a93def953d254b502.subscribers.5ad09c2993def90a2c59fa59' => ['test' => 'testvalue']));
However, this code doesn't work. It shows me an error saying:
The field 'workflow.5ace115a93def953d254b502.subscribers.5ad09c2993def90a2c59fa59' must be an array but is of type object in document {_id: ObjectId('5adde78993def907b71ce503')}
Updating with ['upsert' => true] also doesn't work, because this method removes my collection and adds data. How can I add something to this array?
Structure shown by you does not have any array in it. All are document or sub documents. you can not perform array operation on non array objects. To treat Subscriber_id as an array , it should be something like this
subscriber_id [{
email : value
}]
Model::where('_id', '5adde78993def907b71ce503')->push('workflow.5ace115a93def953d254b502.subscribers.5ad09c2993def90a2c59fa59' => ['test' => 'testvalue']);
please try this.

Query mongodb with php-mongo

I am building a simple messaging system, and i have a collection in mongodb with documents like this:
{ "_id" : ObjectId("50ad003f9811e5bc5c000000"), "between" : [ "user1,user2,user3" ] }
I want to perform this query:
db.conversations.find({'between': ["user1,user2,user3"]});
to get this exact document back. This query works in mongo shell.
in php-mongo, i tried this:
$collection->find(array("between"=>array("user1", "user2", "user3")));
but it does not work.
What am i doing wrong ?
Wouldn't you want to do an In query here?
db.collection.find( { "between" : { $in : ["user1", "user2", "user3"] } } );
See In query here:
Mongo Advanced $in query
making your PHP query look like:
$collection->find(array("between"=>array("$in"=>array("user1", "user2", "user3"))));
//untested, should be something similar to this.
or if you're trying to find it exactly wouldn't you just be able to do:
$collection->find(array("between"=>array("user1,user2,user3")));
First of all when you are saving your data you have to use array not a string
{ "between" : [ "user1,user2,user3" ] }
this stores in "between" an array of one element "user1,user2,user3"
Basically when you do your query in shell everything is ok, because you are asking for a array with one element. But in php you are asking for an array of three elements.
So, when you save your data, most probably that is what you need :
{ "between" : [ "user1","user2","user3" ] }
and this will give you an array of three users.
Then read the documentation http://www.mongodb.org/display/DOCS/Advanced+Queries to and adjust your query depends on what you need: either the exact array or only some elements in the array
Have you tried:
$collection->find(array("between"=>"user1,user2,user3"));
or
$collection->find(array( "$elemMatch" => array( "between"=>"user1,user2,user3" ));
The $in operator is analogous to the SQL IN modifier, allowing you to specify an array of possible matches.
Consider the following example which uses the $or operator.
$collection->find([
'select' => ['$in' => ['option 1', 'option 2', 'option 3']]
]);
References

MongoDB using timestamps to sort

I have a mongodb that will be storing visitor data. I need to delete the data after ten minutes of not being active and will run a command through a cron. How would I do this?
Currently the collection is setup like so:
{ "_id" : ObjectId("4fd33e0b0feeda3b2406f6be"), "name" : "Dugley Reanimator", "updated" : "Some form of timestmap" }
How should I go about storing a timestamp that I search the collection with I.E for my MySql version:
$sql = mysql_query('DELETE FROM `visitors` WHERE NOW() > DATE_ADD(`last_seen`, INTERVAL 10 MINUTE)');
The ObjectId has a timestamp component to it. see the docs here. This essentially gives you a free insert time that you can use for sorting and querying.
The mongodb drives should give you a way to created an ObjectId off of a timestamp.
In Python:
gen_time = datetime.datetime(2010, 1, 1)
dummy_id = ObjectId.from_datetime(gen_time)
In Java:
Date d = new Date(some timestamp in ms);
ObjectId id = new ObjectId(d)
So once you've created an ObjectId based on "10 minutes ago" you can do a delete query using $lt
in the js console it would be:
db.collectionName.remove({_id: {$lt: <Your Object Id that represents 10 minutes ago>})
The best way to do it (if the timestamp is the same when you insert) its by using the _id field.
The _id field can indicate you the time, and you can do a $lte query to delete old values.
I've written about it here: http://blog.dicarsio.com/post/10739857186/quick-snippet-get-creation-time-from-id-on-mongodb
Your driver will use a MongoDate time (this may map to a more native representation in PHP).
You can then query using something like the following mongo statement:
db.myCollection.find({updated : { $lte : new ISODate("2012-06-09T16:22:50Z") } })
A rough translation for PHP would be:
$search = array(
'updated' => array(
'$lte' => new MongoDate($tenMinutesAgo))
);
$collection->find($search)
Or (Caveat: not tested):
$tenMinutesAgo = new DateTime();
$tenMinutesAgo->modify('-10 minutes');
$search = array('updated' => array('$lte' => $tenMinutesAgo));
$collection->find($search)

Categories