MongoDB 和PHP - 返回嵌套数组的计数

发布于 2024-12-04 11:22:51 字数 513 浏览 4 评论 0原文

想象一下,我有一个包含如下文档的 MonogDB 集合:

{name: 'Some Name', components: {ARRAY OF ITEMS}}

如何返回组件中项目的名称和数量? 我必须使用映射/归约吗?

我正在使用 PHP 的 Mongo 扩展。

编辑:PHP 中当前代码的片段(工作),但我只想计算组件的数量

$fields = array(
    'name', 'components'
);
$cursor = $this->collection->find(array(), $fields);
$cursor->sort(array('created_ts' => -1));

if (empty($cursor) == true) {
    return array();
} else {
    return iterator_to_array($cursor);
}

谢谢, 吉姆

Imagine I have a MonogDB collection containing documents as follows:

{name: 'Some Name', components: {ARRAY OF ITEMS}}

How can I return the name and the count of items in components?
Do I have to use a map/reduce?

I am using PHP's Mongo extension.

EDIT: Snippet of current code in PHP (working) but I just want count of the components

$fields = array(
    'name', 'components'
);
$cursor = $this->collection->find(array(), $fields);
$cursor->sort(array('created_ts' => -1));

if (empty($cursor) == true) {
    return array();
} else {
    return iterator_to_array($cursor);
}

Thanks,
Jim

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绮筵 2024-12-11 11:22:51

您可以使用map-reduce,也可以使用简单的 group 查询作为接下来。由于我假设您的 name 属性是一个唯一的键,因此即使这不是您通常使用 group 函数的原因,这也应该有效:

db.test.group({
 key: { name:true },
 reduce: function(obj,prev) {
  var count = 0;
  for(k in obj.components)
   count++;
  prev.count = count;
 },
 initial: { count: 0}
});

您提到您有一个组件数组,但看起来您是将组件存储为对象 {} 而不是数组 []。这就是为什么我必须在reduce函数中添加循环来计算组件对象的所有属性。如果它实际上是一个数组,那么您可以简单地使用 .length 属性。

在 PHP 中,它看起来像这样(来自手册):

$keys = array('name' => 1);
$initial = array('count' => 0);
$reduce =<<<JS
function(obj,prev) {
  var count = 0;
  for(k in obj.components)
   count++;
  prev.count = count;
 },
JS;

$m = new Mongo();
$db = $m->selectDB('Database');
$coll = $db->selectCollection('Collection');
$data = $coll->group($keys, $initial, $reduce);

最后,我强烈建议,如果您尝试定期访问组件的计数,请将计数存储为文档的附加属性,并在发生变化时进行更新。如果您尝试编写根据此计数进行过滤的查询,那么您还可以在该组件属性上添加索引。

You could use map-reduce or you could use a simple group query as follows. Since I am assuming that your name property is a unique key, this should work even though it isn't a reason that you'd normally use the group function:

db.test.group({
 key: { name:true },
 reduce: function(obj,prev) {
  var count = 0;
  for(k in obj.components)
   count++;
  prev.count = count;
 },
 initial: { count: 0}
});

You mentioned that you have an array of components, but it appears that you are storing components as an object {} and not and array []. That is why I had to add the loop in the reduce function, to count all of the properties of the components object. If it were actually an array then you could simply use the .length property.

In PHP it would look something like this (from the Manual):

$keys = array('name' => 1);
$initial = array('count' => 0);
$reduce =<<<JS
function(obj,prev) {
  var count = 0;
  for(k in obj.components)
   count++;
  prev.count = count;
 },
JS;

$m = new Mongo();
$db = $m->selectDB('Database');
$coll = $db->selectCollection('Collection');
$data = $coll->group($keys, $initial, $reduce);

Finally, I would strongly suggest that if you are trying to access the count of your components on a regular basis that you store the count as an additional property of the document and update it whenever it changes. If you are attempting to write queries that filter based on this count then you will also be able to add an index on that components property.

撑一把青伞 2024-12-11 11:22:51

您可以使用 db.eval() 并用 JavaScript 编写计算。

You could use db.eval() and write the calculation in JavaScript.

怼怹恏 2024-12-11 11:22:51

吉姆-

这是两个独立的操作;除非您想利用 PHP 对获得的结果的计数,否则您将执行以下操作:

$m = new Mongo();
$db = $m->selectDB('yourDB');
$collection = $db->selectCollection('MyCollection');
$cursor = $collection->find(array(), array("name"=>1, "components"=>1));
foreach($cursor as $key){
   echo($key['name'].' components: '.count($key['components']);
}

Jim-

These are two separate operations; Unless you want to leverage PHP's count on the results you get which you would then do something like:

$m = new Mongo();
$db = $m->selectDB('yourDB');
$collection = $db->selectCollection('MyCollection');
$cursor = $collection->find(array(), array("name"=>1, "components"=>1));
foreach($cursor as $key){
   echo($key['name'].' components: '.count($key['components']);
}
故乡的云 2024-12-11 11:22:51

今天遇到了这个,如果您使用带有聚合的新驱动程序,您可以在 php 中执行此操作(给定此架构)

   {name: 'Some Name', components: {ARRAY OF ITEMS}}

在 PHP 中:

   $collection = (new Client())->db->my_collection;
   $collection->aggregate([
       '$match' => ['name' => 'Some Name'],
       '$group' => [
             '_id' => null,
             'total'=> ['$sum' => "\$components"]
        ]
  ]);

PHP 的技巧是转义 $ 美元符号,这是基本上 mongo 文档在使用 size 或 sum

https://docs.mongodb.com/manual/reference/operator/aggregation/size/

https://docs.mongodb.com/manual/reference/operator/aggregation/sum/

我遇到的问题是 mongo 将字段放入 " $字段” PHP 根本不喜欢这样,因为它进行变量插值的方式。但是,一旦转义 $ ,它就可以正常工作。

我认为对于这种特殊情况,您需要做类似的事情,但使用 $project 而不是 $group 像这样

   $collection = (new Client())->db->my_collection;
   $collection->aggregate([
       '$match' => ['name' => 'Some Name'],
       '$project' => [
            'name' => "\$name",
            'total'=> ['$sum' => "\$components"]
        ]
   ]);

这是一个老问题,但没有答案选好了,我就把它留在这里。

Ran across this today, If your using the new driver with aggregate you can do this in php, ( given this schema )

   {name: 'Some Name', components: {ARRAY OF ITEMS}}

In PHP:

   $collection = (new Client())->db->my_collection;
   $collection->aggregate([
       '$match' => ['name' => 'Some Name'],
       '$group' => [
             '_id' => null,
             'total'=> ['$sum' => "\$components"]
        ]
  ]);

The trick here with PHP is to escape the $ dollar sign, this is basically what the mongo documentation says when using size or sum

https://docs.mongodb.com/manual/reference/operator/aggregation/size/

https://docs.mongodb.com/manual/reference/operator/aggregation/sum/

The problem I had is mongo puts fields in as "$field" and PHP doesn't like that at all because of the way it does variable interpolation. However, once you escape the $, it works fine.

I think for this particular case you'd need to do something similar but with $project instead of $group Like this

   $collection = (new Client())->db->my_collection;
   $collection->aggregate([
       '$match' => ['name' => 'Some Name'],
       '$project' => [
            'name' => "\$name",
            'total'=> ['$sum' => "\$components"]
        ]
   ]);

This is an old question but seeing as there is no answer picked, I'll just leave this here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文