5

When using the library mongoose-uuid, I am able to setup UUID types for my schemas, so when I read the data it is in string (utf-8) format and when I save the data it is in UUID ObjectID BSON type 4 format. This works great with top level or flat direct values and ref definitions in my schema. However, when I have a UUID's in an array of ref's in a schema, the array saves to the database correctly, However when it is presented it is in its raw type. Based on the example below you can see scope_id is presented in the right format but the entitlements are not.

Here are the versions I am using: mongoose-uuid - 2.3.0 mongoose - 5.5.11

I have tried modifying the library (mongoose-uuid) by changing the getter and converting the value, however, when I do so, it works when presenting but fails when it saves to the database. This is most likely due to the fact that the value is converted or casted before saving to the database.

Here is an example schema

    {
      "code": {
        "type": String,
        "required": true
      }, 
      "scope_id": {
        "type": mongoose.Types.UUID,
        "ref": "scopes"
      },
      "entitlements": [{
        "type": mongoose.Types.UUID,
        "ref": "entitlements"
      }]
    }

Example actual response

{
    "entitlements": [
        "zMihi1BKRomM1Q41p7hgLA==",
        "ztOYL7n1RoGA6aoc0TcqoQ=="
    ],
    "code": "APPUSR",
    "scope_id": "b8f80c82-8325-4ffd-bfd7-e373a90e7c45",
    "id": "32e79061-e531-45ad-b934-56811e2ad713"
}

Expected Response

{
    "entitlements": [
        "ccc8a18b-504a-4689-8cd5-0e35a7b8602c",
        "ced3982f-b9f5-4681-80e9-aa1cd1372aa1"
    ],
    "code": "APPUSR",
    "scope_id": "b8f80c82-8325-4ffd-bfd7-e373a90e7c45",
    "id": "32e79061-e531-45ad-b934-56811e2ad713"
}
thxmike
  • 614
  • 1
  • 7
  • 25
  • you might try the mongoose-uuid2 package - it might fix this, this firts version is not so popular – Sándor Bakos Aug 11 '19 at 09:49
  • `mongoose-uuid2` stores the ids as a string, whereas `mongoose-uuid` stores them as binary. The binary form is needed for performance, but is also related to the issues mentioned in the post. – jneander Aug 11 '19 at 13:21
  • @jneander is correct, mongoose-uuid is used for performance and storage concerns. "mongoose-uuid2" would not help in this case. – thxmike Aug 12 '19 at 13:02
  • I think we got that backwards. `mongoose-uuid2` uses binary, whereas `mongoose-uuid` uses strings. Same result, though. I got a little downstream from this issue and created https://github.com/Automattic/mongoose/issues/8062 on the mongoose repo. It seems like there might be a missing piece somewhere in the mongoose internals. – jneander Aug 12 '19 at 15:13
  • Short of a solution using `mongoose`, I have resorted to using the official `mongodb` Node driver directly. In a pinch, you can bypass `mongoose` with any queries it is not performing correctly. It does require extra work to normalize data in the same way that `mongoose` does. – jneander Aug 12 '19 at 15:16

2 Answers2

2

As mentioned above, the code does work but breaks another part of the code. I found a solution that corrects this:

It is a slight amendment to the code above

SchemaUUID.prototype.cast = function (value, doc, init) {

console.log("cast", value, doc, init)

  if (value instanceof mongoose.Types.Buffer.Binary) {
    if (init && doc instanceof mongoose.Types.Embedded) {
      return getter(value);
    }
    return value;
  }

  if (typeof value === 'string') {
    var uuidBuffer = new mongoose.Types.Buffer(uuidParse.parse(value));

    uuidBuffer.subtype(bson.Binary.SUBTYPE_UUID);

    return uuidBuffer.toObject();
  }

  throw new Error('Could not cast ' + value + ' to UUID.');
};

This alternate version of the code allows for updates such as POST and PATCH to be applied.

thxmike
  • 614
  • 1
  • 7
  • 25
1

As per my observation, if you change the below function in mongoose, it works fine

SchemaUUID.prototype.cast = function (value, doc, init) {
  console.log("cast", value, doc, init)

  if (value instanceof mongoose.Types.Buffer.Binary) {
    if (init) {
      return getter(value);
    } else {
      return value;
    }
  }

  if (typeof value === 'string') {
    var uuidBuffer = new mongoose.Types.Buffer(uuidParse.parse(value));

    uuidBuffer.subtype(bson.Binary.SUBTYPE_UUID);

    return uuidBuffer.toObject();
  }

  throw new Error('Could not cast ' + value + ' to UUID.');
};

Basically when you save objects init is false and when its initiated init is true

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • I attempted to apply this approach but I had the same results – thxmike Aug 18 '19 at 22:40
  • I had tested it and then only shared? https://i.stack.imgur.com/rsaAF.png, https://i.stack.imgur.com/UcaIa.png – Tarun Lalwani Aug 19 '19 at 03:53
  • Actually with further testing, I found the problem and it was something on my end. You are correct your solution fixes the problem. My mistake on the previous comments – thxmike Aug 22 '19 at 21:04
  • ***** On a side note with more testing, this does fix the problem but now breaks my patch operation. I can see it go through init for saving the id and failing it. If you have a recommendation for this it would help. If I have to I will create another post – thxmike Aug 23 '19 at 01:52
  • I would suggest you create another post as it would be different problem – Tarun Lalwani Aug 23 '19 at 02:15
  • Created the following post for the secondary problem this creates. https://stackoverflow.com/questions/57641291/using-modified-mongoose-uuid2-breaks-save-operation. – thxmike Aug 24 '19 at 20:28