Find query MongoDB using CakePHP - mongodb

The following is the json array I have in a collection called claims in MongoDB.
{
"xmllisting_id": "537f371fb2e380922fff0e2c",
"pharmacyfiles_id": "537f3402b2e380732aa6032d",
"claim": {
"MemberID": "097110330047532601",
"PatientShare": "0",
},
"modified": ISODate("2014-05-23T13:12:17.191Z"),
"created": ISODate("2014-05-23T13:12:17.192Z")
}
I need to find all claims with a specified MemberID.I have tried the following in CakePHP without any success.
$claims = $claimobj->find(
'all',
array(
'conditions' => array(
'claim' => array('MemberID' => '097110330047532601')
)
)
);
How can I do it?

Finding "nested" details in MongoDB usually requires "dot notation". Otherwise you are actually asking for an object that has "exactly" the key and "only" the key you are specifying to match. Which of course it does not, as there is more information there:
$claims = $claimobj->find(
'all',
array(
'conditions' => array(
'claim.MemberID' => '097110330047532601'
)
)
);
So the path is "claim.MemberID" and not 'claim' => array('MemberID' => '097110330047532601' ) as you have written.

Related

MongoDB Query select with inner select with Group By

In my old MYSQL database I use the following MYSQL query.
Because I migratie my database to mongoDB, I have to migrate the queries.
In my old mysql database I used the following query
SELECT idValue,
Timestamp,
Value
FROM (
SELECT *
FROM Metingen2
WHERE idGroep = 123
ORDER BY idValue ASC ,
Timestamp DESC
)
AS t GROUP BY t.idValue;
Can anybody explain how I can do a select in a inner select query?
I tried the following, but with no success:
$ops = array(
array(
'$match' => array(
'idGroep' => (int)123,
)
),
array(
'$sort' => array(
"idValue" => 1,
"Timestamp" => -1,
)
),
array(
'$group' => array(
"_id" => array("idValue" => '$idValue',
),
),
),
array(
'$project' => array(
"_id" => '$_id.idValue',
"Timestamp" => '$_id.Timestamp',
"Value" => '$value',
),
),
);
$cursor_metingen2 = $collection_metingen2->aggregate($ops);
At a pass it looks like the query is trying to first order the content and then "GROUP" together using a "idValue" and returning the "first" results on the grouping boundary, after filtering out fo "idGroep" of course.
The inner select does not really do much here other than "filter" and "sort". The aggregation pipeline handles these differently, so what is happening in that execution is of little consequence. It's all about the results.
As such, your aggregation pipeline needs to do the same things:
$cursor = $collection_metingen2->aggregate(array(
array(
'$match' => array( 'idGroep' => 123 )
),
array(
'$sort' => array(
'idValue' => 1,
'TimeStamp' => -1
)
),
array(
'$group' => array(
'_id' => '$idValue',
'TimeStamp' => array( '$first' => '$TimeStamp' ),
'Value' => array( '$first' => '$value' )
)
),
array(
'$sort' => array( '_id' => 1 )
)
));
Noting here that when you $group, not only must the other fields included use an "accumulator" such as $first ( which should be correct here ) but you also "must" include everything you want in output.
It is a "pipeline", so the only thing that goes "into" a following stage is what comes "out" of the stage you specifiy.
And of course $sort "both" before grouping for the correct boundary values as well as at the "end" of the pipeline. The last sort is because $group does not guarantee keys in any order, but this may or may not be of any consequence to what is processing the results from here.
Also be careful with "casting", as unless you really need to convert to an integer type for comparison with data ( and you probably do not ) then you might get mismatched results. So only "cast" when you know you need to.

Mongodb-PHP: find query with '$and' function is not working

i am working on mongodb & php & want to retrive data based on multiple conditions.
I want to retrive forms whose form_status is both Active & Draft only
My query is:
$formData = $formInfo->find(array('team_id' => $_GET['id'], '$and' =>array('form_status' => 'Active','form_status' => 'Draft')));
It is not working. What could be the right syntax in PHP??
The $and operator takes a "real array" of documents as it's argument. In PHP you wrap the array to produce that kind of syntax:
$formData = $formInfo->find(
array(
'team_id' => $_GET['id'],
'$and' => array(
array( 'form_status' => 'Active' ),
array( 'form_status' => 'Draft' )
)
)
);
Note that this really woudn't make any sense unless "form_status" is actually and array itself. In which case the $all operator is a much cleaner approach:
$formData = $formInfo->find(
array(
'team_id' => $_GET['id'],
'form_status' => array(
'$all' => array( 'Active', 'Draft' )
)
)
);
And again if this field was not an array then you really meant $or but that can also be more clearly written for the same field with $in:
$formData = $formInfo->find(
array(
'team_id' => $_GET['id'],
'form_status' => array(
'$in' => array( 'Active', 'Draft' )
)
)
);
So $all is to $and what $in is to $or, but just allows you to use the same field without specifying the full document form

With cakephp2.3 and mongoDB how to add array in a document?

how can I work with lists within the mongoDB?
For example, I have a document (class), and a list of students in this class, ie a subdocument (students).
What do not you, is to add more students to a class.
in Model:
public function salvar($name,$idClass){
$new = array(
"_id" => $idClass,
"student"=> array(
'idStudent' => new MongoId(),
'name' => $name));
return $this->save($new);
}
But when you add a new student, he is not working that way.
How to perform a $push update
The answer you've provided clarifies what you've been trying to do. However, the solution is a little convoluted. To perform a $push update with MongoDB, simply specify you want to perform a push update:
$this->Foo->save(array(
'_id' => $id,
'$push' => array(
"images" => array(
'id' => new MongoId(),
'name' => $name,
'size' => $size
)
)
);
You can use the mongoNoSetOperator property to achieve the same thing but
It's indirect, therefore not obvious
With multiple calls to save in the same request it can be confusing if it's not reset to $set if it's modified
It prevents using multiple operators in a single call.
There's more information about using different update operators in the documentation.
the answer to the question was:
public function salvar($name,$size,$id){
$this->mongoNoSetOperator = '$push';
// fixed array data structure
$susp = array(
"_id" => $id,
"images"=> array(
array(
'id' => new MongoId(),
'name' => $name,
'size' => $size
)
)
);
return $this->save($susp);
}
Note: Thank Garamon from github, by clear and objective answers.

MongoDB Aggregation query doesn't return all fields

I am trying to use the aggregation function to display info in a chart. For this example, a document in the collection looks like this (excluding unnecessary fields for this query):
{
'locid' : <someid>, #Reference to a city & state collection
'collat' : <dateobj>, #a date object when this entry was saved
'pid' : <someid>, #Reference to a person collection
'pos' : <int> #Value I am interested in matching with location & date
}
So I basically start with a pid. I use this as my first $match parameter to limit the amount of data that gets thrown into the pipeline.
array(
'$match' => array(
'pid' => new \MongoId($pid)
)
),
So now that I have selected the correct pid, I tell it I only want/need certain fields:
array(
'$project' => array(
'pos' => 1,
'collat' => 1,
'locid' => 1
)
),
The second match is to say I only care about these locations right now ($ids contains an array of locid):
array(
'$match' => array(
'locid' => array('$in' => $ids)
)
),
And finally, I am saying group all the returned documents by collat and locid
array(
'$group' => array(
'_id' => array(
'locid' => '$locid',
'collat' => '$collat'
)
)
)
While the query completes OK and returns data, I am not getting the pos field back, it is only returning the locid and collat.
Questions
Isn't that what $project is for? I use it to tell the driver what fields I want returned?
Once I get the pos field returning as well, how can I tell the driver I only want the lowest value for each locid & collat combo pair? So say there are two entries for that date, location, and person: 4 & 8. I only care about pos=4
My end goal is to create a line chart with the X-Axis as the dates (from collat) and the Y-Axis will be the pos field, and each line will plot individual locid data.
Here is the entire parameters being sent to the aggregation driver.
$ops = array(
array(
'$match' => array(
'pid' => new \MongoId($pid)
)
),
array(
'$project' => array(
'pos' => 1,
'collat' => 1,
'locid' => 1
)
),
array(
'$match' => array(
'locid' => array('$in' => $ids)
)
),
array(
'$group' => array(
'_id' => array(
'locid' => '$locid',
'collat' => '$collat'
)
)
)
);
$out = $myCollection->aggregate($ops);
Update This is the way I got it to group & return pos without throwing an error. I need to spot check it though to make sure it's actually returning the correct values though.
array(
'$group' => array(
'_id' => array(
'locid' => '$locid',
'collat' => '$collat'
),
array('$min' => '$pos')
)
)
Aggregation query is like an SQL statement group by. You are telling {$group} what field(s) you want to 'GROUP BY' but you are not telling it how you want to aggregate the grouped information.
The {$group} you want is probably something like:
{$group : { _id : { locid: "$locid", collat: "$collat"},
pos : {$min : "$pos"}
}
}

Zend_Validate_Db_RecordExists against 2 fields

I usualy use Zend_Validate_Db_RecordExists to update or insert a record. This works fine with one field to check against. How to do it if you have two fields to check?
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => $this->_name,
'field' => 'id_sector,day_of_week'
)
);
if ($validator->isValid($fields_values['id_sector'],$fields_values['day_of_week'])){
//true
}
I tried it with an array and comma separated list, nothing works... Any help is welcome.
Regards
Andrea
To do this you would have to extend the Zend_Validate_Db_RecordExists class.
It doesn't currently know how to check for the existence of more than one field.
You could just use two different validator instances to check the two fields separately. This is the only work around that I can see right now besides extending it.
If you choose to extend it then you'll have to find some way of passing in all the fields to the constructor ( array seems like a good choice ), and then you'll have to dig into the method that creates the sql query. In this method you'll have to loop over the array of fields that were passed in to the constructor.
You should look into using the exclude parameter. Something like this should do what you want:
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => $this->_name,
'field' => 'id_sector',
'exclude' => array(
'field' => 'day_of_week',
'value' => $fields_values['day_of_week']
)
);
The exclude field will effectively add to the automatically generated WHERE part to create something equivalent to this:
WHERE `id_sector` = $fields_values['id_sector'] AND `day_of_week` = $fields_values['day_of_week']
Its kind of a hack in that we're using it for the opposite of what it was intended, but its working for me similar to this (I'm using it with Db_NoRecordExists).
Source: Zend_Validate_Db_NoRecordExists example
Sorry for the late reply.
The best option that worked for me is this:
// create an instance of the Zend_Validate_Db_RecordExists class
// pass in the database table name and the first field (as usual)...
$validator = new Zend_Validate_Db_RecordExists(array(
'table' => 'tablename',
'field' => 'first_field'
));
// reset the where clause used by Zend_Validate_Db_RecordExists
$validator->getSelect()->reset('where');
// set again the first field and the second field.
// :value is a named parameter that will be substituted
// by the value passed to the isValid method
$validator->getSelect()->where('first_field = ?', $first_field);
$validator->getSelect()->where('second_field = :value', $second_field);
// add your new record exist based on 2 fields validator to your element.
$element = new Zend_Form_Element_Text('element');
$element->addValidator($validator);
// add the validated element to the form.
$form->addElement($element);
I hope that will help someone :)
Although, I would strongly recommend a neater solution which would be to extend the Zend_Validate_Db_RecordExists class with the above code.
Enjoy!!
Rosario
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
'validators' => array('EmailAddress', $obj= new Zend_Validate_Db_NoRecordExists(array('adapter'=>$dbAdapter,
'field'=>'email',
'table'=>'user',
'exclude'=>array('field'=>'email','value'=>$this->_options['email'], 'field'=>'is_deleted', 'value'=>'1')
))),
For those using Zend 2, If you want to check if user with given id and email exists in table users, It is possible this way.
First, you create the select object that will be use as parameter for the Zend\Validator\Db\RecordExists object
$select = new Zend\Db\Sql\Select();
$select->from('users')
->where->equalTo('id', $user_id)
->where->equalTo('email', $email);
Now, create RecordExists object and check the existence this way
$validator = new Zend\Validator\Db\RecordExists($select);
$validator->setAdapter($dbAdapter);
if ($validator->isValid($username)) {
echo 'This user is valid';
} else {
//get and display errors
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
This sample is from ZF2 official doc
You can use the 'exclude' in this parameter pass the second clause that you want to filter through.
$clause = 'table.field2 = value';
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => 'table',
'field' => 'field1',
'exclude' => $clause
)
);
if ($validator->isValid('value') {
true;
}
I am using zend framework v.3 and validation via InputFilter(), it uses same validation rules as zend framework 2.
In my case I need to check, if location exists in db (by 'id' field) and has needed company's id ('company_id' field).
I implemented it in next way:
$clause = new Operator('company_id', Operator::OP_EQ, $companyId);
$inputFilter->add([
'name' => 'location_id',
'required' => false,
'filters' => [
['name' => 'StringTrim'],
['name' => 'ToInt'],
],
'validators' => [
[
'name' => 'Int',
],
[
'name' => 'dbRecordExists',
'options' => [
'adapter' => $dbAdapterCore,
'table' => 'locations',
'field' => 'id',
'exclude' => $clause,
'messages' => [
'noRecordFound' => "Location does not exist.",
],
]
],
],
]);
In this case validation will pass, only if 'locations' table has item with columns id == $value and company_id == $companyId, like next:
select * from location where id = ? AND company_id = ?