MongoDB 原则:$in 需要一个数组 - 多对多关系

Posted

技术标签:

【中文标题】MongoDB 原则:$in 需要一个数组 - 多对多关系【英文标题】:MongoDB Doctrine: $in needs an array - Many to Many Relation 【发布时间】:2014-10-27 03:51:56 【问题描述】:

我有一些包含产品的类别,而您可以将产品添加到类别中。这是多对多的关系,而产品不知道它们与类别的关系(因为它只保存在一个类别中)。我将首先发布我的设置,然后发布出现问题的查询。

/** @ODM\Document */
class Category

    /** @ODM\Id */
    private $id;

    /** @ODM\String */
    private $name;

    /** @ODM\ReferenceMany(targetDocument="Product", inversedBy="category") */
    private $products;

    public function setProducts($products)
    
        $this->products = $products;
    


/** @ODM\Document */
class Product

    /** @ODM\Id */
    private $id;

    /** @ODM\String */
    private $name;

    /** @ODM\Float */
    private $price;

    /** @ODM\ReferenceMany(targetDocument="Category", mappedBy="products") */
    private $category;

为了有一些数据,我这样创建:

$p1 = new Documents\Product();
$p1->setName('p1');
$p1->setPrice(1.99);

$p2 = new Documents\Product();
$p2->setName('p2');
$p2->setPrice(3.99);

$c1 = new Documents\Category();
$c1->setName('category1');
$c1->setProducts(array($p1, $p2));

$c2 = new Documents\Category();
$c2->setName('category2');
$c2->setProducts(array($p1, $p2));

$dm->persist($p1);
$dm->persist($p2);
$dm->persist($c1);
$dm->persist($c2);
$dm->flush();

查看 MongoDB,我现在有以下数据:

db.Category.find()
 "_id" : ObjectId("53e3d8d3e2afec2303d63afa"), "name" : "category1", "products" : [ DBRef("Product", ObjectId("53e3d8d3e2afec2303d63af8")), DBRef("Product", ObjectId("53e3d8d3e2afec2303d63af9")) ] 
 "_id" : ObjectId("53e3d8d3e2afec2303d63afb"), "name" : "category2", "products" : [ DBRef("Product", ObjectId("53e3d8d3e2afec2303d63af8")), DBRef("Product", ObjectId("53e3d8d3e2afec2303d63af9")) ] 

db.Product.find()
 "_id" : ObjectId("53e3d8d3e2afec2303d63af8"), "name" : "p1", "price" : 1.99 
 "_id" : ObjectId("53e3d8d3e2afec2303d63af9"), "name" : "p2", "price" : 3.99 

我现在要查询这个数据:Getting a product by id and getting all its categories,它属于:

$product = $dm->find('Documents\Product', '53e3d8d3e2afec2303d63af8');
var_export($product->getName());
$category = $product->getCategory();
var_export(sizeof($category));
foreach($category as $c)
    var_export($c->getName());


// Outputs: 'p1' 2 'category1' 'category2'

但是如果我想反过来查询数据:获取一个类别并获取其所有产品,该类别有:

$category = $dm->find('Documents\Category', '53e3d8d3e2afec2303d63afa');
var_export($category->getName());
$products = $category->getProducts();
var_export(sizeof($products));
foreach($products as $p)
    var_export($p->getName());


// Outputs: 'category1' 2

没有产品显示,我收到一个致命错误:

Fatal error: Uncaught exception 'MongoCursorException' with message 'localhost:27017: Can't canonicalize query: BadValue $in needs an array' in /private/var/www/mongo/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Cursor.php on line 288

如果我查看 MongoDB 日志,查询如下:

assertion 17287 Can't canonicalize query: BadValue $in needs an array ns:shop.Product query: $query:  _id:  $in:  53e3d8d3e2afec2303d63af8: ObjectId('53e3d8d3e2afec2303d63af8'), 53e3d8d3e2afec2303d63af9: ObjectId('53e3d8d3e2afec2303d63af9')   , $orderby: [] 

我的意思是,问题似乎很清楚:$in 需要一个数组,但没有给出 JSON 数组。但我不知道如何修复它。我究竟做错了什么?如果你不能回答这个特定的问题,你能给我提供一个 MongoDB with Doctrine 的工作示例,你有这样的多对多关系并且可以通过双方查询数据吗?

更新:我也许应该提到我使用的是哪个版本的学说。我使用了这个设置:


    "require":
        "doctrine/common":"2.3.*",
        "doctrine/dbal":"2.3.*",
        "doctrine/orm":"*",
        "doctrine/mongodb-odm": "1.0.0-BETA9"
    

然后我想更新到可用的最新版本并使用此设置:


     "require":
        "doctrine/common":"2.4.*",
        "doctrine/dbal":"2.3.*",
        "doctrine/orm":"*",
        "doctrine/mongodb-odm": "dev-master"
    

瞧:一切正常.... OMG。但是仍然欢迎对此进行任何解释,我想知道这里发生了什么。非常感谢。

【问题讨论】:

嗯,你应该avoid DBREF at all costs,即使你想使用,structure it well。产品集合应具有类别参考,not vice-versa。而且我从未实际使用过 DBREF。无论如何看看How to DBREF和/或Another Help 感谢您的评论。我现在使用简单的 ObjectIds,因为这对我来说完全足够了。但是我的问题陈述中提到的同样的错误仍然出现。但你说了一件有趣的事:not vice-versa。你对此有什么论据吗?我跟不上你,为什么反之亦然? vice-versa:如果您必须在您的情况下查询产品,那么您必须对类别集合应用查询以获取Product && Categories 数据,因为它具有产品字段的 ID。 并展示您新的/修改后的两个集合的集合样本。 但是如果你点击一个分类你想加载属于这个分类的所有产品。因此该类别包含产品? 【参考方案1】:

您遇到的错误已在 PR #743 中修复,该错误包含在 1.0.0-BETA10 中。这是与 MongoDB 2.6 兼容所必需的几个修复之一(请参阅 issue #741),因为服务器在验证查询条件方面变得更加严格。

2.4 之前的服务器版本接受 $in 的对象,并简单地忽略了密钥。如果您查看BSON specification,您会注意到除了类型代码之外,数组和对象具有非常相似的结构。每个都是一个键/值对序列,并且数组有效负载看起来非常像具有顺序数字字符串作为键的对象。这应该可以解释为什么旧版本的服务器很容易接受这两种类型。

【讨论】:

以上是关于MongoDB 原则:$in 需要一个数组 - 多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB in 数量限制

Mongodb $in 针对数组对象字段而不是数组对象

使用正则表达式元素数组的 MongoDB 查询 $in

使用正则表达式元素数组的 MongoDB 查询 $in

mongodb 怎么用id数组in查询

MongoDB 发现 $in 排序问题