MongoDB 嵌入式对象没有 ID(空值)

Posted

技术标签:

【中文标题】MongoDB 嵌入式对象没有 ID(空值)【英文标题】:MongoDB Embedded Objects have no ID (null value) 【发布时间】:2012-06-30 14:10:18 【问题描述】:

我有一个关于 MongoDB 和 Spring Data 的问题。 我有这些域类:

@Document
public class Deal  
    @Id
    private ObjectId _id;
    private Location location;
    private User user;
    private String description;
    private String title;
    private String price;
    private boolean approved;
    private Date expirationDate;
    private Date publishedDate;


@Document
public class Location 
    @Id
    private ObjectId _id;
    private Double latitude;
    private Double longitude;
    private String country;
    private String street;
    private String zip;


@Document
public class User 
    @Id
    private ObjectId _id;
    private String email;
    private String password;
    private String profile_image_url;
    private Collection<Deal> deals = new ArrayList<Deal>();

通过这些域,我可以成功地进行 CRUD。只有一个问题。保存带有交易的用户时,交易和位置在将它们保存到 MongoDB 时将 _id 设置为 null。 为什么 MongoDB 不能为嵌入式对象生成唯一 ID?

用一笔交易保存用户后的结果:

 "_id" : ObjectId( "4fed0591d17011868cf9c982" ),
  "_class" : "User",
  "email" : "milo@gmail.com",
  "password" : "mimi",
  "deals" : [ 
     "_id" : null,
      "location" :  "_id" : null,
        "latitude" : 2.22,
        "longitude" : 3.23445,
        "country" : "Denmark",
        "street" : "Denmark road 77",
        "zip" : "2933" ,
      "description" : "The new Nexus 7 Tablet. A 7 inch tablet from Google.",
      "title" : "Nexus 7",
      "price" : "1300",
      "approved" : false,
      "expirationDate" : Date( 1343512800000 ),
      "publishedDate" : Date( 1340933521374 )  ] 

从结果中可以看出,交易和位置 ID 设置为 NULL。

【问题讨论】:

ID 标识根文档,而不是子文档。没有理由要为嵌套文档自动生成 id,因为 MongoDB 无论如何只能检索***文档。您实际上需要嵌套 id 做什么? 我只需要搜索交易而不是用户。现在我必须从用户中搜索,然后循环以编程方式将每笔交易扔给用户。 id 与您可以搜索的内容无关。搜索 'deals.price' : $gt : 1000 完全没问题。尽管如此,此查询仍会返回一个 User 对象,您必须从那时手动拉出 Deal。这是 MongoDB 的限制,Spring Data 没有暗示。 你是对的。我已经分别保存了用户和交易,但电子邮件与他们两人共享。我现在可以单独获得交易,而无需将用户文档扔给用户,对用户也是如此。 【参考方案1】:

MongoDB CRUD 操作(insertupdatefindremove)都只对***文档进行操作——当然您可以按嵌入文档中的字段进行过滤。嵌入文档总是在父文档中返回。

_id 字段是父文档的必填字段,通常不需要或不存在于嵌入文档中。如果你需要一个唯一的标识符,你当然可以创建它们,你可以使用_id 字段来存储它们,如果这对你的代码或你的心智模型方便的话;更典型的是,它们以它们所代表的内容命名(例如“用户名”、“其他系统密钥”等)。 MongoDB 本身和任何驱动程序都不会自动填充 _id 字段,***文档除外。

特别是在 Java 中,如果您希望为嵌入文档中的 _id 字段生成 ObjectId 值,您可以这样做:

someEmbeddedDoc._id = new ObjectId();

【讨论】:

感谢您解决此问题。因此,如果我只想列出交易,我必须从交易文档中分离或移动用户文档,以便交易是单独的文档,用户是单独的文档,然后查询。 要么调整您的代码以使用现有数据模型。如果您经常操纵交易,或者如果交易与多个用户相关联,那么规范化数据模型可能是有意义的。【参考方案2】:

在 REST 架构的上下文中,嵌套文档有自己的 Id 是很有意义的。

    持久性实现应该独立于资源表示。作为 API 使用者,我不在乎您使用的是 mongo 还是 mysql。如果您在 mongo 中没有 id 的嵌套文档,请尝试想象如何将持久层更改为关系数据库。现在做同样的练习,事先考虑过独立于实现的方法。在关系数据库中建模嵌套文档,根文档和嵌套文档将是不同的实体/表,每个都有自己的 id。根可能与嵌套文档具有一对多关系。 我可能需要直接访问嵌套文档而不是顺序访问。我可能不需要绝对唯一的 ID,如 mongo 发布的那些,但我仍然需要一些本地唯一标识符。这在根文档中是唯一的。

在我对嵌套文档中需要 id 的观点进行了争论之后,@dcrosta 已经就如何在 mongo 中填充 _id 字段给出了正确答案。

希望这会有所帮助。

【讨论】:

【参考方案3】:

默认情况下,仅在根文档上没有在子文档上设置 _id。

您需要在插入和更新时为您的子文档定义一个 _id。

【讨论】:

他不应该为我这样做吗?我怎样才能做到这一点?我应该单独插入每个文档并将它们放在一起吗?这就是我插入的方式: mongoOperation.insert(deal); MongoDB 不会为您执行此操作,但现在我再看一眼,也许您的驱动程序应该...您在使用 Morphia 吗? 感谢您的回复。我正在使用 Spring Data 和 mongo-java-driver。当我搜索交易时,我得到一个用户对象。我无法使用交易进行搜索,我必须扔掉用户,然后在用户中以编程方式查询交易。 我不能只查询具有此纬度或此经度的交易。我必须查询作为主文档的投掷用户。我很困惑。【参考方案4】:

Mongo 不会在嵌入文档上创建或需要_ids。如果需要,您可以添加 _id 字段 - 我已经完成了。

@Document
public class Location 
    @Id
    private ObjectId _id;

    public Location() 
        this._id = ObjectId.get();
    


@Document
public class User 
    @Id
    private ObjectId _id;

    public User() 
        this._id = ObjectId.get();
    

这对我很有用。

【讨论】:

【参考方案5】:

其他答案现在不再适用。每个子文档都会自动获得一个_id。 实际上,要避免将_id 应用于子文档,您必须显式传递选项

 _id: false 

在子文档架构中。

【讨论】:

以上是关于MongoDB 嵌入式对象没有 ID(空值)的主要内容,如果未能解决你的问题,请参考以下文章

Mongodb:通过“_id”字段搜索嵌入文档

如何为 MongoDB 中的嵌入式文档创建唯一 ID?

更新Mongodb中的嵌入式阵列值

如何在嵌入式数组mongodb中查询嵌入式文档

过滤和更新 mongodb mongoose 中的嵌入式数组

过滤 MongoDB 中的嵌入式数组