Is it possible to define the key when inserting into mongodb - php

Background: Using CodeIgniter with this MongoDB library.
This is my first go-round with mongodb and I'm enjoying it thus far. It's taken a while for me to separate myself from the sql way of thinking, but it is a perfect fit for my current project.
I'm trying to push an array into a document using...
$this->mongo_db->where(array('_id'=>$estimate_id))->push("measurements", $newData)->update('estimates');
If I encode $newData using json_encode($newData) I get {"levels":[{"level_qty":12,"level_uom":"ft"}]}
The problem is, when my function creates the measurements line in the mongodb document, it automatically starts an array with my insertion at [0]. Like this...
"measurements" : [ { "levels" : [ { "level_qty" : 12, "level_uom" : "ft" } ] }]
...leaving me with...
-measurements
--0
----levels
-----0
------level_qty => 2,
------level_uom => ft
What I really want is...
-measurements
--levels
---0
----level_qty => 2,
----level_uom => ft
I'm certain I'm missing something fairly elementary (i.e. php related & not mongodb related), but I'm an admitted amateur who has waded too deep.

$push is used to append a value to an array. In your example, measurements is an array and Mongo is appending $newData as its first element. This explains the 0 index between measurements and levels. In your desired result, measurements is an object equivalent to $newData (i.e. it has a levels property, which in turn has an array of objects within).
Either of the following examples should accomplish what you want:
// if $newData is {"levels": [{"level_qty":12,"level_uom":"ft"}]}
->set("measurements", $newData)
// if $newData is [{"level_qty":12,"level_uom":"ft"}]
->set("measurements.levels", $newData)
// if $newData is {"level_qty":12,"level_uom":"ft"}
->push("measurements.levels", $newData)
Note: $push is going to be more flexible if you want to append data with future updates, whereas $set will naturally overwrite the given field.

Related

How can I access a specific depth of an associative array with variable variables

I've been hitting my head on this problem for some time now. I am working on a piece of software that creates a tree from a MySQL result checking for changes between each row to decide where in the tree to put the new data.
I'm now at a dead end while trying to understand how I can dynamically tell PHP to address different parts of my array.
I though about using variable variables but it doesn't seem to be working. To make my life easier I tried to set up a test file in which to test this behavior and this is the result...
$array = [
0 => [
"name" => "test"
],
1 => [
"name" => "test",
"data" => [
"content" => 5
]
]
];
$ref = 'array["1"]["name"]';
echo $ref."\n";
echo $$ref;
Output
array["1"]["name"]
Notice: Undefined variable: array["1"]["name"] in P:\xampp\htdocs\assets\php\test.php on line 23
I was instead expecting something like test.
I would also like to mention that I've tried the ${} method but I doesn't affect the array, but instead adds the data to another variable those rare times it doesn't output an error.
Anyone can help? Thanks!
after thinking about the problem once again I came up with a work-around to achieve the intended result.
I decided to use references make with &$var.
I, so, decided to tweak the code to create an array of each step to do to arrive at the intended location, instead of a string. Example follows:
// Old method
$ref = 'array["1"]["name"];
// New method
$ref = ["1", "name"];
The code then follows by cycling through the array referencing the original array but slowly deeper...
// Referencing the original array
$referencedArray = &$array;
// Going one step at the time inside the nested array
foreach ($ref as $k => $v) {
$referencedArray = &$referencedArray[$rav];
}
I believe this solution could fit my case, but if you have any suggestions please let me know.

Set multiple keys having multiple values to Redis

I have a php backend using phpredis (a php client for the redis server) to store key value pairs to a Redis server. The data I need to store is of this form:
"key1" => "v1", "v2", "v3"
"key2" => "m1", "m2", "m3"
"key3" => "n1", "n2", "n3"
...
Based on my research, I can set multiple keys in a redis using the mset command like so:
$redis->mSet(array('key0' => 'value0', 'key1' => 'value1'));
But what I actually need is something like this:
$redis->mSet(array('key0' => array('v1','v2','v3') , 'key1' => array('m1', 'm2', 'm3')));
But this just stores the value for each key as "Array" instead of the actual array specified.
Is this possible to do with a single command like mset or do I need to iterate my data and set each key separately using something like lPush?
phpredis documentation: https://github.com/phpredis/phpredis
So rather than using mSet you can probably use sADD to get your desired functionality.
$redis->sAdd($key, ...$data);
Full documentation on it here.
This would mean iterating and doing it in multiple steps for which I'd reccomend reading into Redis Pipelines and the non-shameless plug link which contains more information.
Which would look something like;
$redis = new Redis();
$pipeline = $redis->multi(Redis::PIPELINE);
foreach ($dataset as $data) {
$pipeline->sAdd($data['key'], ...$data['values']);
}
$pipeline->exec();
I can't think off the top of my head a way to do this in a singular operation, someone else might come along though who knows more than me :)
Edit: Looks like I misunderstood your question a little as it was more focused on doing this in a single operation. Hopefully the above is still useful but to my knowledge you'll have to do this with multiple.

PHP Arrays, ordered or not?

I have an Array of product types in PHP, it looks pretty much like this:
$types = [
0 => "cars",
1 => "motorbikes",
2 => "boats",
3 => "airplanes"
];
So that when then user wants to get or save it, I can use the ID of the category to insert or get it from the database, like this:
select * from items where type = 0;
now, My doubt is the following: if I am using integer index keys, would it not be the same to use the following?
$types = ["cars", "motorbikes", "etc."];
Since PHP will give an integer auto-ordering.
Which one would you consider to be best practice in this case? The advantage of the first example seems to only be the fact that I can assign different keys, like for private categories or such, but I don't know really.
It is also important to consider that the values will need to be translated so, should I consider even just using IDs? like
$types = ["t1", "t2", "etc."];
and then insert the translation somewhere else?
There are 2 possible options.
Your current option 1 with manually set indexes.
A way better one - a table in the DB, holding these categories, which allow editing categories without losing the association between keys and names.
Your current option 2 is not an option at all - it will break the order the same moment you insert or delete a category
Here you set the index:
$types = [
3 => "airplanes"
index ^ ^ value
So, if you delete cars from the list, the index remain associated with it's value:
$types = [
1 => "motorbikes",
2 => "boats",
3 => "airplanes"
];
While if you don't set indexes manually
$types = ["motorbikes", "boats", "airplanes"];
airplanes will lose it's association, and become boats. It's not ht magic we expect from the web application
It all depends on the project. If I'm working with an array of items from the database I like the key to be the id from the database so it's available should I need it. If there's no need for the ids then there is no need to define a key. It will start at 0 anyways.
I think your first approach is a good one. You could even do it as default translation:
$types["en"] = []
Of course if you have a default language variable, you can use it from the start, and it will be much easier in the future to translate it further and expand
The arrays will be seen as the same so you can order it and take key as ID but it's not the best practice, all depends on how you design your relationship between arrays, php and database.
I might be missing your question but the array in PHP is ordered hash map basically. So ["cars", "motorcycles", "etc."] is guaranteed to preserver ordering (and have keys 0, 1, 2). But you might instead consider do the following
class AutoTypes {
const TYPE_CAR = 0;
const TYPE_MOTORCYCLE = 1;
const TYPE_BOAT = 2;
public static function getLabels() {
return array(self::TYPE_CAR => 'Car', self::TYPE_MOTORCYCLE => 'Motorcycle', self::TYPE_BOAT => 'Boat');
}
}
This way you can refer to the auto type by AutoTypes::CONSTANT. Because if you want to remove motorcycle at some point, not using clear indexes in the array, removing an item will brake your logic.
In my opinion, in this circumstance it's better to use
$types = ["cars", "motorbikes", "etc."];
As you say, PHP automatically assigns integer indexes starting from 0; so there's no need to specify the index that it would be assigned anyway.
It's up to you however, if you feel that it isn't clear enough just to declare an array without specifying the indexes manually then that's up to you. But personally I feel that it's obvious to any competent programmer and so therefore it's a waste of time and hard disk space to manually specify those indexes.

MongoDB - Is it possible to query by associative array key?

I need to store some data that is essentially just an array of key-value pairs of date/ints, where the dates will always be unique.
I'd like to be able to store it like an associative array:
array(
"2012-02-26" => 5,
"2012-02-27" => 2,
"2012-02-28" => 17,
"2012-02-29" => 4
)
but I also need to be able to query the dates (ie. get everything where date > 2012-02-27), and so suspect that I'll need to use a schema more like:
array(
array("date"=>"2012-02-26", "value"=>5),
array("date"=>"2012-02-27", "value"=>2),
array("date"=>"2012-02-28", "value"=>17),
array("date"=>"2012-02-29", "value"=>4),
)
Obviously the former is much cleaner and more concise, but will I be able to query it in the way that I am wanting, and if not are there any other schemas that may be more suitable?
You've described two methods, let me break them down.
Method #1 - Associative Array
The key tool for querying by "associative array" is the $exists operator. Here are details on the operator.
So you can definitely run a query like the following:
db.coll.find( { $exists: { 'field.2012-02-27' } } );
Based on your description you are looking for range queries which does not match up well with the $exists operator. The "associative array" version is also difficult to index.
Method #2 - Array of objects
This definitely has better querying functionality:
db.coll.find( { 'field.date': { $gt: '2012-02-27' } } );
It can also be indexed
db.coll.ensureIndex( { 'field.date': 1 } );
However, there is a trade-off on updating. If you want to increment the value for a specific date you have to use this unwieldy $ positional operator. This works for an array of objects, but it fails for anything with further nesting.
Other issues
One issue with either of these methods is the long-term growth of data. As you expand the object size it will take more space on disk and in memory. If you have an object with two years worth of data that entire array of 700 items will need to be in memory for you to update data for today. This may not be an issue for your specific data, but it should be considered.
In the same vein, MongoDB queries always return the top-level object. Again, if you have an array of 700 items, you will get all of them for each document that matches. There are ways to filter out the fields that are returned, but they don't work for "arrays of objects".

creating a multidimensional array from arrays in php

I'm trying to create an array of bar objects in php which consist of seven different attributes. The code I am using for this array is as follows:
$barsArray = array();
for($i = 0; $i < count($barNameArray); $i++)
{
$barsArray[] = array('BarName' => $barNameArray[$i], 'first' => $firstCover[$i], 'timeFirst' => $firstTimes[$i],
'second' => $secondCover[$i], 'timeSecond' => $secondTimes[$i],
'third' => $thirdCover[$i], 'timeThird' => $thirdTimes[$i]);
}
I have checked to make sure that all the other arrays are as I intend them. I just need to get this into one array of objects. Is this method completely off? Also, If I wanted to test to make sure that the correct objects are in the correct locations in a multidimensional array, how would I go about do that?
Thanks!
That code looks fine (although you may want to cache the count instead of performing it repeatedly).
I can't say for sure, not knowing your greater purpose, but you may want to make $barsArray an associative array by the bar name. To do that, use $barsArray[$barNameArray[$i]] =, instead of $barsArray[] = (and of course remove the BarName property). This would not keep it in the original ordering, but would make getting a particular bar easier.
You can get an element from $barsArray like so: $barsArray[3]['first'] (or $barsArray['some_bar_name']['first'] if you change it to an associative array).

Categories