Auto-increment in MongoDB with CodeIgniter Alex Bilbie library - php

First of all, sorry for my english and I'm beginning to study MongoDB. :)
I'm trying to insert a record using the CI library to MongoDB (https://github.com/alexbilbie/codeigniter-mongodb-library/tree/v2).
The insert works perfectly, but I can't insert using the recommended function to auto-increment getNextSequence (http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/).
I tried the following ways without success.
Controller:
$data = array('_id' => 'getNextSequence("myid")',
'name' => 'Test '.time(),
'email' => 'test#test.com');
$this->default_model->add($this->collection, $data);
Model:
function add($collection, $data){
return $this->mongo_db->insert($collection, $data);
}
This returns the string 'getNextSequence("relatoriosid")' as the "_id".
I also tried used the command function, like this:
Controller:
$query = 'db.collection.insert({_id: getNextSequence("myid"), name: "Test '.time().'"});';
$ret = $this->default_model->execute($query);
var_dump($ret);
Model:
function execute($query){
return $this->mongo_db->command($query);
}
This way, returns the error:
["errmsg"]=> string(136) "exception: JavaScript execution failed: ReferenceError: getNextSequence is not defined near 'quence("myid"), name: "Teste 1374' "
["code"]=> int(16722)
["ok"]=> float(0)
Has anyone implemented something similar?
Thanks in advance!

I have implemented once mongoDB in one of my project.
I had faced similar problem. But one important thing i remember that we can not modify _id. Its MongoDB inbuilt properly. Neither its good to have the auto-increament in MongoDB because you need to implement that manually.
I will recommend that have another column in structure, you can call it anything, which will be virtual unique/primary key for you, lets say that column name is data_id.
$data = array('data_id' => md5(timestamp() . "<your string>"),
'name' => 'Test '.time(),
'email' => 'test#test.com');
if we check the documentation, is specified that Generally in MongoDB, you would not use an auto-increment pattern for the _id field, or any field, because it does not scale for databases with large numbers of documents. Typically the default value ObjectId is more ideal for the _id. Here i am referring to this link.
Even on the same link, its specified that getNextSequence() we need to write it. Its not inbuilt function.
Hope this helps you.

I got that problem too here is my solution.
Add this function to appflow\application\libraries
public function getNextSequence($name = ""){
if (empty($name))
{
show_error("In order to retrieve documents from MongoDB, a collection name must be passed", 500);
}
try{
$collection_1 = 'counters';
$collection = $this->db->{$collection_1};
$result = $collection->findAndModify(
['_id' => $name],
['$inc' => ['seq' => 1]],
['seq' => true],
['new' => true, 'upsert' => true]
);
if (isset($result['seq']))
{
return $result['seq'];
}
else
{
return false;
}
}
catch (MongoCursorException $e)
{
if(isset($this->debug) == TRUE && $this->debug == TRUE)
{
show_error("MongoDB query failed: {$e->getMessage()}", 500);
}
else
{
show_error("MongoDB query failed.", 500);
}
}
}
This is the way to use.
$nextID = $this->mongo_db->getNextSequence("maindata_child");
$this->mongo_db->where(array('_id'=>$mongo_id))->push('maindata_child', array('id'=>$nextID,'data'=>$maindata_child))->update('main_data');
and don't forget to add collection "counters".
db.counters.insert(
{
_id: "userid",
seq: 0
}
)
PS. I'm use this library https://github.com/bcit-ci/CodeIgniter/wiki/Using-MongoDB-in-Codeigniter
Reference for solution : http://tbsmc.com/a/mwbwswmm-mongodb-how-to-insert-record-using-autoincrement-functionality.html

Related

Phalcon PhP - is there an EOF for model::find

I'm writing a piece of code and in it I would like to know if the result of a find if empty or not. Here is my piece of code:
public function signatureAction()
{
$info = $this->session->get('current_quote');
$object_list = ApplicationSignatureFile::find(array('conditions' => 'application_id = ?1 AND quote_id = ?2',
'bind' => [
1 => $info['application_id'],
2 => $info['quote_id'],
]));
$this->view->setVar('object_list', $object_list);
if ($object_list) {
$this->view->setVar('has_files',true);
} else {
$this->view->setVar('has_files',false);
}
}
What I don't know yet if how to check if $object_list is EOF so I can set the has_files variable better. Currently it is not working. How can I do that in a controller and in a .volt view?
This is pretty strange actually. Using findFirst or any other ORM method returns false on fail, however using find does not.
A simple workaround in your case would be to use the count method on the result set:
$test = \Models\Objects::find([
'conditions' => 'is_active = 42'
]);
if ($test->count()) {
print('I have records with is_active = 42');
d($test->toArray());
} else {
print('I do not have any records with is_active = 42');
}

How to echo last query string in Phalcon?

I have worked a lot on codeigniter. In codeigniter , if there is need to get query string that is executed last, we can get it using:
echo $this->db->last_query();
exit;
But currently I am working on phalcon and I am just at beginner level in this framework. I am curious if there is a way to echo last query string in phalcon.
Thank you.
Using Raw Queries
Let us have the following query:
$phql = 'UPDATE `news` SET `category_id` = 5 WHERE `id` = :id';
$this->db->execute($phql, ['id' => 1]);
We can get debug query info with the following methods:
print_r($this->db->getSQLStatement());
UPDATE news SET category_id = 5 WHERE id = :id
print_r($this->db->getSqlVariables());
Array (
[id] => 1 )
More info about DB methods you can find here: https://docs.phalconphp.com/en/latest/api/Phalcon_Db_Adapter_Pdo.html
Working with Models
Setting up your DB connection and profiler service:
use Phalcon\Db\Profiler as ProfilerDb;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo;
$di->set('profiler', function () {
return new ProfilerDb();
}, true);
$di->set('db', function () use ($di) {
$eventsManager = new EventsManager();
// Get a shared instance of the DbProfiler
$profiler = $di->getProfiler();
// Listen all the database events
$eventsManager->attach('db', function ($event, $connection) use ($profiler) {
if ($event->getType() == 'beforeQuery') {
$profiler->startProfile($connection->getSQLStatement());
}
if ($event->getType() == 'afterQuery') {
$profiler->stopProfile();
}
});
$connection = new MysqlPdo(
array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
)
);
// Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);
return $connection;
});
Using it to debug your Queries:
// Send some SQL statements to the database
Robots::find();
Robots::find(
array(
"order" => "name"
)
);
Robots::find(
array(
"limit" => 30
)
);
// Get the generated profiles from the profiler
$profiles = $di->get('profiler')->getProfiles();
foreach ($profiles as $profile) {
echo "SQL Statement: ", $profile->getSQLStatement(), "\n";
echo "Start Time: ", $profile->getInitialTime(), "\n";
echo "Final Time: ", $profile->getFinalTime(), "\n";
echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n";
}
More info on Profiler service: https://docs.phalconphp.com/en/latest/reference/models.html#profiling-sql-statements
Phalcon Prophiler Widget
I'm using a lovely debug widget for Phalcon made by Fabian Fülling. You can check the repository here: https://github.com/fabfuel/prophiler A sample screen shot of the widget in action below:
If you are running queries directly on your model instance and you are lazy, you can also do it like this:
$result = $this->_userEntriesE‌​ntries->find(array("c‌​onditions" => "FeaturedPost = 1 and FeaturedPostStatus = 1", "order" => "ID DESC", "limit" => 4))
var_dump($result);
var_dump the result object of your query. Within the PDO dump you will notice a key named _pdoStatement. This is your generated SQL query.
This is not the recommended way, just a dirty trick.

How to create data and return properly formatted json using ApiGility and RPC

I am using the RPC service of ApiGilty to return some data. I would like to double check whether or not this is the correct way of formatting and returning the data as I am not 100% sure of the correct process.
EDIT: To clarify
The data is being built from a number of entities:
main
main_extra
main_data
main_data_days
main_data_tiers
Is there a way to hit main and get all the sub entities? Currently I am building my data from scratch and returning an array.
My RPC Controller is as follows:
use My\Data\Controller\DataInterface;
use Zend\Mvc\Controller\AbstractActionController;
use ZF\ContentNegotiation\ViewModel;
class MyDataController extends AbstractActionController
{
const GENERAL_ERROR = 'api.rpc.my-data.my-data-controller';
public function __construct(
MyDataInterface $myData
)
{
$this->myData = $myData;
}
public function myDataAction()
{
$my_id = (int) $this->params()->fromRoute('my_id', 0);
if ($my_id == 0)
{
$data = $this->myData->getMyData();
} else
{
$data = $this->myData->getMyData($my_id);
}
$result = new ViewModel(array(
'data' => $data
));
return $result;
}
}
Now to create the data I am doing something like this:
public function getMyData( $my_id = null )
{
$returnArray = [];
$array1 = [
'key_1' => [1,2,3,4],
'key_2' => '123',
'key_3' => ['a','b','c']
];
$array2 = [
'key_1' => [1,2,3,4,5,6,7,8],
'key_2' => '123456',
'key_3' => ['a','b','c','d']
];
if ($my_id == 1) {
$array3 = ['some','or','other'];
} else {$array3 = []; }
$final_array = [
'data1' => $array1,
'data2' => $array2,
'data3' => $array3
];
$returnArray['data'] = $final_array;
$returnArray['success'] = 'true';
$returnArray['reason'] = '';
return $returnArray;
}
When checking with postman, I get the following:
Now since I have nothing to reference this against, my question is simply. Have I gone about this in the correct way and is this how the return code should be formatted?
Thanks!
Right now the Hal plugin is not used to render your result? You are responding a custom json object. Is this really what you want?
The response you currently return is not formatted according to HAL specifications. A proper HAL response should hold at least a _links key with a self href. It would be wrong to return this result with Content-Type headers set to application/hal+json. You should use application/json instead.
Here you can find documentation on how to respond HAL from an RPC-contoller.
I am not sure what you want to achieve but maybe you can be a bit more specific in your question so others can help out...
Doesn't look too bad, perhaps adhere to a standard such as jsend http://labs.omniti.com/labs/jsend or you could use hal-json, matthew weier o'phinney has a good blog post on this https://mwop.net/blog/2014-03-26-apigility-rpc-with-hal.html
Also you don't need to return a view model as you can just return an array and apigility will return JSON. You could also write a jsendViewModel if you go down that route.
Not exactly an answer but hope this helps you!

Zend Framework 2 PostgreSQL lastInsertValue returns null

I have code for inserting data to table. Here is my code:
public function createPage(Pages $page)
{
$data = array(
'page_name' => $page->page_name,
'parent_page_id' => $page->parent_page_id,
'category' => $page->category,
'create_date' => $page->create_date,
);
try {
$id = $this->tableGateway->insert($data);
$id = $this->tableGateway->lastInsertValue;
} catch (Exception $ex) {
throw new Exception($ex->getMessage());
}
return $id;
}
When I use MySql, I've got the row's id, but with PostgreSQL I've got null.
Yes,
$id = $this->tableGateway->getAdapter()->getDriver()->getLastGeneratedValue("sequence_name");
Or
$id = $this->tableGateway->getAdapter()->getConnection()->getLastGeneratedValue
Depending on what zf2 version you have.
However, there is some difference between Postgresql and MySql using the native php PDO class, which zf2 wraps and provide the tableGateway.
With native php PDO you may need to do something like:
"INSERT INTO tbl_table (table_name) values ('foo') RETURNING table_id;"

Predis Alias Sharding

I'm trying to use Predis sharding by alias, as described here. My code is basically identical, but I'm only returning empty arrays. Do my hash keys need {} around them? (EDIT: Nope, just tried it)
$api->get("/test", function () {
$servers = [
["alias" => "metadata", "port" => 6380],
["alias" => "relations", "port" => 6381],
["alias" => "dim_provider", "port" => 6382],
["alias" => "dim_revctrcode", "port" => 6383],
["alias" => "dim_enccode", "port" => 6384],
["alias" => "dim_pos", "port" => 6385]
];
$options = [
"nodehash" => function ($connection) { return $connection->getParameters()->alias; },
"cluster" => function ($options) {
$replicas = Predis\Cluster\Distribution\HashRing::DEFAULT_REPLICAS;
$hashring = new Predis\Cluster\Distribution\HashRing($replicas, $options->nodehash);
$cluster = new Predis\Connection\PredisCluster($hashring);
return $cluster;
}
];
$redis = new Predis\Client($servers, $options);
try {
$test = $redis->scard("dim_provider");
print_r($test); // Prints 0 for scard or empty Array for hgetall
} catch (Exception $e) {
print $e->getMessage();
}
$redis = new Predis\Client(["port" => 6382]);
$test = $redis->scard("dim_provider");
print_r($test); // Works.
});
EDIT: It also works if I only put one server in the $servers array. So it seems the hashing is not working right. When I throw some echos in front of the return value in nodehash I can see that it's returning the alias.
Assigning a dim_provider alias to a Redis connection and trying to get a key named dim_provider from a server are two different things.
In your script you are trying to set up a cluster of Redis instances using connection aliases (instead of the usual ip:port pairs) to calculate the distribution of your keyspace among multiple Redis servers acting as your data shards. Using this setup, the key dim_provider is sharded accordingly to the underlying distribution algorithm and could be stored on any of the 6 servers composing your cluster and defined in the $servers array.
I wanted to add how trivially easy it was to implement my clustering strategy once nrk got me on the right track. This is a really well-written library.
$api->get("/test", function () {
Class KeyCluster extends Predis\Connection\PredisCluster {
public function __construct() {
$this->pool = Array();
}
public function add (Predis\Connection\SingleConnectionInterface $connection) {
$parameters = $connection->getParameters();
if (isset($parameters->table)) {
$this->pool[$parameters->table] = $connection;
} else {
$this->pool[] = $connection;
}
}
public function getConnection (Command\CommandInterface $command) {
$key = $command->getArgument(0);
$table = explode(":", $key)[0];
return isset($this->pool[$table]) ? $this->pool[$table] : null;
}
}
$redis = new Predis\Client([
"tcp://127.0.0.1:6382?table=dim_provider",
"tcp://127.0.0.1:6383?table=dim_pos"
],[
"cluster" => new KeyCluster
]);
$result = $redis->scard("dim_provider");
print_r($result);
});

Categories