First of all, I am VERY new to Mongodb, so please bear with me. I have a mongodb collection that has data like below (in PHP array representation):
Array
(
[_id] => MongoId Object
(
[$id] => 8974439e66777114648b47dc
)
[mechFamily] => X07_B22
[mechName] => X07_B22_61
[mechType] => Original
[spans] => Array
(
[noOfSpans] => 5
[span] => Array
(
[0] => Array
(
[type] => solid
[clipShape] => rectangle
[id] => 2d8c1d2a6756323beca8e6e59823e3b
My requirement is that I need to pass condition to find() which will give me only those records where noOfSpansis 10.
I tried:
$myCollection->find(array("spans.noOfSpans" => 10));
But that does not return anything, I looked at the nest query sections in docs as well as SO, but the answers are pretty confusing. Can anyone tell me how to query this mongodb structure?
I'm not fluent in PHP's syntax, so I'm going to translate your document into mongo shell syntax and then propose a query. Let me know if I did it wrong and I will try to correct it.
{
"_id" : "8974439e66777114648b47dc",
"mechFamily" : "X07_B22",
"mechName" : "X07_B22_61",
"mechType" : "Original",
"spans" : {
"noOfSpans" : 5,
"span" : [
{
"type" : "solid",
"clipShape" : "rectangle",
"id" : "2d8c1d2a6756323beca8e6e59823e3b"
}
]
}
}
The query to match documents where spans.noOfSpans is 10 is
db.collection.find({ "spans.noOfSpans" : 10 })
which looks to be the same as your PHP query that you say isn't working. Is my document structure incorrect? What do you mean when you say the find isn't returning anything? It's returning a cursor with no results? How are you checking?
Related
Going to try this again as I think my previous post Inserting complex PHP array directly into mongodb was poorly asked, or didn't provide a tl;dr... I'm having a really hard time finding much documentation on anything past the basics.
My array looks like:
[publication] => Array
(
[_id] => 100000009
[title] => title
[author] => author
)
I'm using PHPdriver for mongodb and I directly add the array like this:
$result = $publications->insertOne([
$publication
]);
and while this works, it give me this:
{
"_id" : ObjectId("5a910438834a9c2314006cf6"),
"0" : {
"_id" : "100000009",
"title" : "uf",
"author" : "author"
}
}
How do I make it like this instead:
{
"_id" : "100000009",
"title" : "uf",
"author" : "author"
}
Without manually breaking each line out into a "id" => $publication['id'] inside the insertOne statement?
It seems you want to insert a document, not a mongodb array.
You are creating a new array wrapping your document.
Try this:
$result = $publications->insertOne($publication);
i'm doing
$sql = $_SESSION['data']->bySQL("SELECT * FROM worlds WHERE field='shippingLicenseItemName'");
and then printing result(i know it is very simple but i'm learning by myself through this site)
echo nl2br(print_r($sql, true));
and in result i'm getting array like this :
Array
(
[atlantis] => Array
(
[shippingLicenseItemName] => pkg_atlantisXfer
)
[australia] => Array
(
[shippingLicenseItemName] => pkg_australiaXfer
)
[avalon] => Array
(
[shippingLicenseItemName] => pkg_avalonXfer
)
)
but i want the results like :
World : "atlantis" and item : "shippingLicenseItemName" and code is : "pkg_atlantisxfer"
World : "australia" and item : "shippingLicenseItemName" and code is : "pkg_australiaxfer"
Don't know where you found the bySQL property, it seems to be returning the data keyed by a column on its own, making things a tad more complicated.
But i'ts nothing you cannot solve with a couple of loops :
foreach($sql as $o_key=>$o_data) {
foreach($o_data as $i_key=>$i_data) {
print "World $o_key and item : $i_key and code is $i_data <br />"l
}
}
Sorry if this might be a typical "RTM"-question, I am new to MongoDB and did some manual-reading but sadly I didn't find an attempt to solve that.
I have two collections, one collection is "articles" containing an array of "categories" which has one or more MongoID objects with IDs of my categories-collection.
I would like to display all categories with the number of articles refering to the category. Below my solution I found atfer some time of researching:
my collection of categories:
Array
(
[_id] => MongoId Object
(
[$id] => 54eb1510974f5590179702aa
)
[name] => Test
[multiplier] => 2
)
My collection of articles:
Array
(
[_id] => MongoId Object
(
[$id] => 54e5e39f974f5535248b4bdf
)
[productnumber] => 63483
[categories] => Array
(
//... other categories...
[1] => MongoId Object
(
[$id] => 54eb1510974f5590179702aa
)
)
[image] => /var/www/mongodbtest/Files/FTP/images/63483.jpg
)
My current PHP code:
foreach($oAllCategories as $oCategory)
{
$iArticleCount = $oArticles->find(array('categories' => $oCategory['_id']))->count();
// Debug
echo $oCategory['name'].' = '.$iArticleCount.' <br />';
}
Now the problem is, that with 70'000 articles and 2'200 categories this is slow and takes a lot of time. Also I can't sort my categories by the number of articles without iterating through all articles.
Is there a better way to do this?
I'm not familiar with PHP, so I'll use mongo shell syntax. You can use an aggregation pipeline to compute this server-side in one go:
db.articles.aggregate([
{ "$unwind" : "$categories" },
{ "$group" : { "_id" : "$categories", "count" : { "$sum" : 1 } } }
])
The $unwind stage "unwinds" each article document along its categories array, e.g.
{ "x" : 1, "categories" : ["a", "b", "c"] }
===>
{ "x" : 1, "categories" : "a" },
{ "x" : 1, "categories" : "b" },
{ "x" : 1, "categories" : "c" }
Then the $group stage merges all the documents along the values of categories and counts the number of elements in the group. The result looks like
{ "_id" : "c", "count" : 1 }
{ "_id" : "b", "count" : 1 }
{ "_id" : "a", "count" : 1 }
Your _id's would be category _id's, which you could join with the categories collection to turn into names. I think you should just store the category name along with the _id on the article, though. How often does a category name actually change?
Generally, you should avoid doing operations like this, though, because the aggregation is scanning every article, expanding it into multiple documents, the processing every one into its corresponding group. It's better to incrementally maintain this information in another collection For example, you could increment a count in each category document every time an article in that category is inserted.
MongoDB can not get the total.
MYSQL :
select sum(play) as toplam from videos
MongoDB :
$group = $naytu->db->videos->group(array(), array("toplam" => 0), "function(obj, prev) {prev.toplam = prev.toplam + obj.play - 0;}");
results (print_r)
Array
(
[retval] => Array
(
[0] => Array
(
[toplam] => NAN
)
)
[count] => 78656
[keys] => 1
[ok] => 1
)
where is the problem ?
In PHP #Neils answer is:
$mongo->videos->aggregate([
['$group' => ['_id' => null, 'toplam' => ['$sum' => '$play']]]
]);
The group() function is kind of an old form of achieving results like this, it will also not work with sharded collections. Better to familiarize yourself with aggregate:
db.collection.aggregate([
{"$group": { "_id": null, "toplam": {"$sum": "$play"} }}
]);
Output for you driver will just be a cursor just like using find.
for PHP Syntax
For the record, your usage seems to be way off. Much as above you just translate the relative JSON to Dicts, and if that is still a problem then use a JSON parser to show you how. All the arguments are valid JSON, so I'm reminding myself to do this for all examples now, so people can easily dump into their own language.
db.collection.group({
"key": {},
"reduce": "function(curr, result) { result.total += curr.play; }",
"initial": { "total": 0 }
})
But still, don't use this anyway. Stick to aggregate, it's a useful tool to learn.
Of course in all cases, make sure that all your data being applied to a sum is actually a number, or things will blow up on you.
Maybe this can help
"function(obj, prev) {prev.toplam = new NumberInt(prev.toplam) + new NumberInt(obj.play);}"
we're often dealing with a code that looks like the following:
return $this->find(
'all', [
'fields' => ['DISTINCT Tag.tag', 'COUNT(Tag.tag) as count'],
'group' => ['Tag.tag'],
'order' => ['count' => 'DESC']
]);
This query leads us to the following output:
[0] => Array
(
[Tag] => Array
(
[tag] => walls.io
)
[0] => Array
(
[count] => 15
)
)
As you can see, the query returns the results in a "somehow wrong" nesting. The "count" field is unfortunately put into a pseudo [0]-array.
IIRC, CakePHP uses internally a syntax like Tag__field for correctly nesting virtual fields.
When changing the code to the Model__-syntax, the problem stays the same:
return $this->find(
'all', [
'fields' => ['DISTINCT Tag.tag', 'COUNT(Tag.tag) as Tag__count'],
'group' => ['Tag.tag'],
'order' => ['COUNT(Tag.tag)' => 'DESC']
]);
Output:
[0] => Array
(
[Tag] => Array
(
[tag] => walls.io
)
[0] => Array
(
[Tag__count] => 15
)
)
Workaround 1: array_map
CakePHP pros: Is there a better/more elegant solution than manually mapping the array after the select statement?
$tags = array_map(function($tag) {
$tag['Tag']['count'] = $tag[0]['count'];
unset($tag[0]);
return $tag;
}, $tags);
Workaround 2: virtual field
As described above, the usage of a virtual field might solve this problem:
$this->virtualFields = ['count' => 'COUNT(Tag.Tag)'];
return $this->find(
'all', [
'group' => ['Tag.tag'],
'order' => [$this->getVirtualField('count') => 'DESC']
]);
Unfortunately, with this solution, it's not possible to specify ANY fields at all. only by completely leaving the "fields"-key, the nesting of the array works as expected. when selecting $fields = ['Tag.tag', $this->getVirtualField('count')] the nesting is wrong again.
CakePHP pros: Do you know a method, where the nesting is done right, even if you specify your own fields?
Looking at the CakePHP code, such a method does not exist.
Have a look at the file lib/Cake/Model/Datasource/Database/Mysql.php.
Find the method called: Mysql::resultSet( $results ); (around line 240).
That method maps the result to an array. To determine if a column is part of a table or not it uses PDOStatement::getColumnMeta(). For your "virtual column" that method will return an empty table and so the CakePHP code will put it separately, see the else branch
$this->map[$index++] = array(0, $column['name'], $type);
In order to avoid that else branch you would have to use Virtual fields, but then you run into the other problems that you have noticed.
So you are left with the array_map solution or you could try to overload that Mysql class and add your custom logic at how to identify where a column fits.