sub iterator object in mongodb C driver - mongodb

Here's the structure part of my collection :
{
_id: {
id:"6a6ca923517f304900badd98",
target:"00badd6a6ca923517f304998e4df"
},
...
}
The use of :
if(bson_find(iterator, mongo_cursor_bson(cursor), "_id")){
bson_iterator_subiterator(iterator, sub);
id = (char*)bson_iterator_string(sub);
}
is "working" but in reality simply returns me the result of the first field of the array found...
How to recover precisely the value of the "id" or "target" field please ?

You can also use bson_iterator_more and bson_iterator_next upon the sub-iterator(It was still an iterator).
try this:
if(bson_find(iterator, mongo_cursor_bson(cursor), "_id"))
{
bson_iterator_subiterator(iterator, sub);
while(bson_iterator_more(sub))
{
if (bson_iterator_next(sub) != BSON_EOO)
{
printf("%s: %s\n", bson_iterator_key(sub), bson_iterator_string(sub));
}
}
}

Related

How can I return the element I'm looking for inside a nested array?

I have a database like this:
[
{
"universe":"comics",
"saga":[
{
"name":"x-men",
"characters":[
{
"character":"wolverine",
"picture":"618035022351.png"
},
{
"character":"cyclops",
"picture":"618035022352.png"
}
]
}
]
},
{
"universe":"dc",
"saga":[
{
"name":"spiderman",
"characters":[
{
"character":"venom",
"picture":"618035022353.png"
}
]
}
]
}
]
and with this code I manage to update one of the objects in my array. specifically the object where character: wolverine
db.mydb.findOneAndUpdate({
"universe": "comics",
"saga.name": "x-men",
"saga.characters.character": "wolverine"
}, {
$set: {
"saga.$[].characters.$[].character": "lobezno",
"saga.$[].characters.$[].picture": "618035022354.png",
}
}, {
new: false
}
)
it returns all my document, I need ONLY the document matched
I would like to return the object that I have updated without having to make more queries to the database.
Note
I have been told that my code does not work well as it should, apparently my query to update this bad, I would like to know how to fix it and get the object that matches these search criteria.
In other words how can I get this output:
{
"character":"wolverine",
"picture":"618035022351.png"
}
in a single query using filters
{
"universe": "comics",
"saga.name": "x-men",
"saga.characters.character": "wolverine"
}
My MongoDB knowledge prevents me from correcting this.
Use the shell method findAndModify to suit your needs.
But you cannot use the positional character $ more than once while projecting in MongoDb, so you may have to keep track of it yourself at client-side.
Use arrayFilters to update deeply nested sub-document, instead of positional all operator $[].
Below is a working query -
var query = {
universe: 'comics'
};
var update = {
$set: {
'saga.$[outer].characters.$[inner].character': 'lobezno',
'saga.$[outer].characters.$[inner].picture': '618035022354.png',
}
};
var fields = {
'saga.characters': 1
};
var updateFilter = {
arrayFilters: [
{
'outer.name': 'x-men'
},
{
'inner.character': 'wolverine'
}
]
};
db.collection.findAndModify({
query,
update,
fields,
arrayFilters: updateFilter.arrayFilters
new: true
});
If I understand your question correctly, your updating is working as expected and your issue is that it returns the whole document and you don't want to query the database to just to return these two fields.
Why don't you just extract the fields from the document returned from your update? You are not going to the database when doing that.
var extractElementFromResult = null;
if(result != null) {
extractElementFromResult = result.saga
.filter(item => item.name == "x-men")[0]
.characters
.filter(item => item.character == "wolverine")[0];
}

MongoDB: How to make a query that goes through an array

I have a database filled with classes and I want to be able to find all of the classes that match the subject code and the course number provided.
course_number = ['2920', '3100', '3200', '3300', '3500', '4100', '4200', '4300', '4310', '4400', '4500']
for doc in db.all_classes.find(
{"class_schedule.subject_code": "CSCI" },
{ "class_schedule.course_number": { '$elemMatch': { course_number } } } ):
print doc

sub-object in sub-array in mongodb-C

Here's the structure part of my collection :
{
...
list: [
{ id:'00A', name:'None 1' },
{ id:'00B', name:'None 2' },
],
...
}
Which method could you advise me to retrieve the list of values in the "id" and/or "name" field with C lib please ?
It seems you are asking for the equivalent of "db.collection.distinct" with the C driver. Is that correct? If so, you can issue distinct as a db command using the mongo_run_command function:
http://api.mongodb.org/c/current/api/mongo_8h.html#a155e3de9c71f02600482f10a5805d70d
Here is a piece of code you may find useful demonstrating the implementation:
mongo conn[1];
int status = mongo_client(conn, "127.0.0.1", 27017);
if (status != MONGO_OK)
return 1;
bson b[1]; // query bson
bson_init(b);
bson_append_string(b, "distinct", "foo");
bson_append_string(b, "key", "list.id"); // or list.name
bson_finish(b);
bson bres[1]; // result bson
status = mongo_run_command(conn, "test", b, bres);
if (status == MONGO_OK){
bson_iterator i[1], sub[1];
bson_type type;
const char* val;
bson_find(i, bres, "values");
bson_iterator_subiterator(i, sub);
while ((type = bson_iterator_next(sub))) {
if (type == BSON_STRING) {
val = bson_iterator_string(sub);
printf("Value: %s\n", val);
}
}
} else {
printf("error: %i\n", status);
}
The database is "foo" and the collection, containing documents similar to yours, is "test" in the above example. The query portion of the above is equivalent to:
db.runCommand({distinct:'foo', key:'list.id'})
Hope that helps.
Jake

How do I return JSON Arrays instead of objects using FuelPHP's ORM and Controller_Rest

Controller_Something extends Controller_Rest {
public function get_something() {
$query = Model_Something::query()->related('hasMany')->get();
return $this->response($query);
}
}
Returns:
{
stuff: here,
looks: good,
hasMany: {
151251: {
id: 151251,
other: stuff
}
}
}
I want the relations as arrays:
{
stuff: here,
looks: good,
hasMany: [
{
id: 151251,
other: stuff
}
]
}
This happens because the ORM returns related result arrays with keys corresponding to the record's PKEY, and JSON interprets this as an Object. I want these arrays to go through array_values() or something, so the JSON result will use Array.
Currently I am doing this to "solve" the problem:
$res = Format::forge($result_set)->to_array();
$res['hasMany'] = array_values($res['hasMany']);
return $this->response($res);
But this is only useful to one or two levels, where I know the data will be.
If there are relations that are not guaranteed, I don't what to have to error-check every potential subset of a complex Model.
I just want all the one-to-many arrays to be keyed sequentially instead of by the records PKEY.
$query = Model_Something::find()->related('hasMany');
returns a query object in < 1.6, an exception in 1.6, and null in 1.6.1+. So I assume you do something else that produces that result.
If you want arrays as a result instead of objects, you need to convert the result. You can do that by calling to_array() on a model object, or by using the Format class to convert an array of model objects to an array:
$result = \Format::forge($result)->to_array();
function object_to_array($data){
$new_data2= array();
$keys = array_keys((array)$data);
foreach ($keys as $key)
{
$value = $data[$key];
if(is_numeric($key))
{
$new_data2[] = object_to_array($value);
}elseif(is_string($value) || is_null($value))
{
$new_data2[$key] = $data[$key];
}else
{
$new_data2[$key] = object_to_array($value);
}
}
return $new_data2;
}
$formattedArray = \Format::forge(Model_Product::query()->get())->to_array();
$cleanData=object_to_array($formattedArray);
echo \Format::forge($cleanData)->to_json();
This way checks the array key; if key is number and value is object or array clean key
In short: you can't unless you create a hook in Query:hydrate https://github.com/fuel/orm/blob/1.7/develop/classes/query.php#L1083, or shadowing the Query class with some implementation that returns the very same results except for hydrate.
Programmatically it is possible to be done. Following the model below, but for very deep relationships is not interesting by the complexity of the algorithm.
Model:
class Model_Something extends \Orm\Model
{
...
public function relatedAsArray()
{
$this->relationsAsArray($this->_data_relations);
}
private function relationsAsArray(&$relations)
{
foreach ($relations as $key => $relation) {
foreach ($relation as $fields) {
foreach ($fields as $field) {
if (isset($field->_data_relations)) {
$this->relationsAsArray($field->_data_relations);
}
}
}
if (is_array($relation)) {
$relations[$key] = array_values($relation);
}
}
}
}
Call of method:
$something = Model_Something::find($somethingId, array('related' => array('hasMany', 'hasMany.hasOthers')));
$something->relatedAsArray();
The result was exactly like you wanted.
Result:
{
stuff: here,
looks: good,
hasMany: [
{
id: 151251,
other: stuff,
hasOthers: [
{
id: 12312,
field: other
}, ...
]
}, ...
]
}

Remove nested document with condition in MongoDB

For the following JSON how do I remove the dog whose height is the least
{
_id:0
"name":"Andy",
"pets":[
{
"type":"dog","name":"max","height":120
},
{
"type":"dog","name":"rover","height":44
},
{
"type":"dog","name":"katie","height":100
},
{
"type":"cat","name":"minni"
}
]
}
The problem is the array of subdocuments is not a collection, you can't sort or do something else on it. But if you have an access to any language interface like JavaScript or else it's possible. You just need to extract list of subdocuments, sort them by height, remember the first one and then run the command to pull it from the array based on its name and height.
It can be done for example using this JavaScript code right in the MongoDB shell:
var min = 0; var name = "";
db.animals.find({ query:{"_id" : 0} }).forEach(
function(record){
var sets = record.pets;
min = sets[0].height;
sets.forEach(function(set){
if(set.height <= min)
{min=set.height;
name=set.name;}
});
print(min);
print(name);
query = {"_id": 0}
update = { "$pull" : { "pets" : { "name" : name } } };
db.animals.update(query, update);
})
I suspect the solution is not the most elegant but anyway it works.