Loopback-datasource-juggler: Auto increment not working with embedsMany

Created on 19 Oct 2014  路  26Comments  路  Source: loopbackio/loopback-datasource-juggler

Somehow I can not get the auto increment to work with "embedsmany". Always when I create a Picture (no matter if directly WITH the party or adding it later TO the party ) it complains that I did not supply an id. Also tried having the "id" set as optional "number" property. Does anybody know what I am doing wrong?

Do I also have to define the relationship in Picture somewhere?

Thanks!

Party:

{
  "name": "Party",
  "plural": "Parties",
  "base": "PersistedModel",
  "mixins": {
  "ObjectId": true
},
  "properties": {
  "name": {
    "type": "string",
      "required": true
  },
  "date": {
    "type": "date",
      "required": true
  }
},
  "validations": [],
  "relations": {
  "pictures": {
    "type": "embedsMany",
      "model": "Picture",
      "options": {
      "forceId": true,
        "validate": true,
        "autoId": true
    }
  }
},
  "acls": [],
  "methods": []
}

Picture:

{
  "name": "Picture",
  "plural": "Pictures",
  "base": "PersistedModel",
  "properties": {
  "filename": {
    "type": "string",
      "required": true
  },
  "date": {
    "type": "date",
      "required": true
  }
},
  "validations": [],
  "relations": {},
  "acls": [],
  "methods": []
}

Most helpful comment

@vasumahesh1 @sebastianhaas
Found a Solution for this Problem
in Answer.json File add Id Properties with following setting

"id": {
"type": "string",
"id": true,
"defaultFn": "uuid"
}
Rest all setting Same as @markleusink described Above

All 26 comments

Some of the options have changed, and there's a new option called persistent, which you should enable when you're using a PersistedModel like in your case:

https://github.com/strongloop/loopback-datasource-juggler/blob/master/test/relations.test.js#L2331

"pictures": {
  "type": "embedsMany",
  "model": "Picture",
  "options": {
       "persistent": true
  }
}

With this option enabled, the regular id generation will be triggered as expected.

Validation is true by default now.

Great, thanks a lot works fine now.

Not a big issue, just wondering about one thing. It currently creates now this long alphanumerical id. Is it also possible to have it simply start at 0 and auto-increment by 1 each? Just seems like a waist having a 24 letter string where also a simple int would do.

If your Picture model is really meant to be persisted separately (apart from also being embedded), then it will use the actual Id generated by your dataSource, which I presume to be MongoDB. If your Picture is a true embedded-only model, you should probably hook it up to a transient datasource instead. There you'll have the option of using an auto-incrementing id, if your id property is a Number.

I am totally new to loopback so did not think at all about having to change something else. It is really supposed to be saved embedded-only. Now changed under "server/model-config.json" the datasource of Picture to "db" (memory) and now it works fine.

Thanks a lot for your help!

Great, glad you've sorted it out. If you need more information on what I describe above, see: https://github.com/strongloop/loopback-datasource-juggler/blob/master/test/relations.test.js#L1936

Not work for me.

"steps": {
      "type": "embedsMany",
      "model": "Step",
      "options": {
        "persistent": true
      }
    },

Step model:

{
  "name": "Step",
  "base": "PersistedModel",
  "properties": {
    "pos": {
      "type": "number",
      "required": true
    },
    "text": {
      "type": "string",
      "required": true
    },
    "image": {
      "type": "string"
    }
  },
  "validations": [],
  "relations": {},
  "acls": [],
  "methods": []
}
{
  "error": {
    "name": "ValidationError",
    "status": 422,
    "message": "The `Recipe` instance is not valid. Details: `_steps` contains invalid item at index `0`: `id` is blank (value: [ { pos: 0,\n    text: 'Me...} ]).",
    "statusCode": 422,
    "details": {
      "context": "Recipe",
      "codes": {
        "_steps": [
          "invalid"
        ]
      },
      "messages": {
        "_steps": [
          "contains invalid item at index `0`: `id` is blank"
        ]
      }
    },
  }
}

@brunocascio check the additional options on the embedsMany above (forceId, validate, autoId).

Well, i added these properties. But not work.

"steps": {
      "type": "embedsMany",
      "model": "Step",
      "options": {
        "persistent": true,
        "forceId": true,
        "validate": true,
        "autoId": true
      }

Actually it's a bug?

I can't get this working either. Same error, same options

I got this to work in my setup. I'm using MongoDb as my data store and have a Page model that should have embedded comments:

  • define a 'transient' datasource in datasource.json
"transient": {
  "name": "transient",
  "connector": "transient"
},
  • use that for the model that you want to embed (in model-config.json):
"Comment" : {
 "dataSource" : "transient",
 "public" : false
}
  • setup the relation:
  "relations": {
    "comments": {
      "type": "embedsMany",
      "model": "Comment",
      "property": "comments",
      "options": {
        "validate": true,
        "forceId": false
      }
    }
  },

(even with forceId : false it automatically inserts an id for the embedded document)

You can then do a POST to /api/Pages/:parentId/comments: that will create/ append to an array in the parent model (named after the property and prefixed by an underscore: _comments). Note that the parent model (Page) is set up with a MongoDB data source.

@markleusink : this works only for transient datasource. How abt making it to persistent model as informed by @brunocascio. Well.. we want to query the comment based on the generated id..

Although the model ('Comment') uses a transient datasource in this setup, the data is actually stored as an embedded document array in the parent ('Page'). You can query Comments, but (AFAIK) only for a specific Page.

Thanks for the reply Mark. There is slight variation in my scenario. I want the same address (comment in your case) to linked with another entity. Hence I used the persistent datasource and not transient. Also the the unique primary key is not generated.

I still think this is a bug.

Thanks

The solution by @markleusink works for me except for id generation, it is not generated.

Is there a way to make it use "comments" instead of "_comments" as the database property?

The property name is prefixed with an underscore, because the relation name is the same as the property name. If you change it to (e.g.) this, it will use comments as the property name:

 "relations": {
  "commentsRel": {
    "type": "embedsMany",
    "model": "Comment",
    "property": "comments",
    "options": {
      "validate": true,
      "forceId": false
    }
  }
 },

Thanks @markleusink that worked!

Hey @markleusink This was working perfectly, until I had two embedsMany (One embedsMany within another embedsMany).... Once I got that It said theres a duplicate Index. :/ any thoughts on that?

{
  "error": {
    "name": "ValidationError",
    "status": 422,
    "message": "The `Quiz` instance is not valid. Details: `quizQuestions` contains invalid item at index `0` (`questionOptions` contains duplicate `id`) (value: [ { id: undefined,\n    qu...} ]).",
    "statusCode": 422,
    "details": {
      "context": "Quiz",
      "codes": {
        "quizQuestions": [
          "invalid"
        ]
      },
      "messages": {
        "quizQuestions": [
          "contains invalid item at index `0` (`questionOptions` contains duplicate `id`)"
        ]
      }
    },
    "stack": "ValidationError: The `Quiz` instance is not valid. Details: `quizQuestions` contains invalid item at index `0` (`questionOptions` contains duplicate `id`) (value: [ { id: undefined,\n    qu...} ]).\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:264:12\n    at ModelConstructor.<anonymous> (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\validations.js:472:11)\n    at ModelConstructor.next (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\hooks.js:75:12)\n    at ModelConstructor.<anonymous> (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\validations.js:469:23)\n    at ModelConstructor.trigger (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\hooks.js:65:12)\n    at ModelConstructor.Validatable.isValid (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\validations.js:435:8)\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:260:9\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\observer.js:106:23\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:189:25\n    at event (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-ds-timestamp-mixin\\index.js:29:5)"
  }
}

Data that was sent...

{
  "title": "This is a Quiz",
  "description": "Hello, Quiz",
  "isClosed": true,
  "duration": 10,
  "quizQuestions": [
    {
      "questionContent": {
        "data": "What is the Capital of India?",
        "type": "text"
      },
      "questionOptions": [
        {
          "isAnswer": false,
          "optionContent": {
            "data": "Mumbai",
            "type": "text"
          }
        },
        {
          "isAnswer": true,
          "optionContent": {
            "data": "New Delhi",
            "type": "text"
          }
        }
      ]
    }
  ]
}

so as long as "questionOptions" has one value it inserts by using the transient trick.... Once I put two values in that I get this error!

Bit of a noob here.... But I did as you followed and as I grew my Models I tested Question, Option & Content manually and all were working by the method you specified... But it seems to fail for this...

Update:Infact its with any embedsMany: If the Inner array was 2 I get the above error. If the quizQuestions was of count two, I get:

{
  "error": {
    "name": "ValidationError",
    "status": 422,
    "message": "The `Quiz` instance is not valid. Details: `quizQuestions` contains duplicate `id` (value: [ { id: undefined,\n    qu...} ]).",
    "statusCode": 422,
    "details": {
      "context": "Quiz",
      "codes": {
        "quizQuestions": [
          "uniqueness"
        ]
      },
      "messages": {
        "quizQuestions": [
          "contains duplicate `id`"
        ]
      }
    },
    "stack": "ValidationError: The `Quiz` instance is not valid. Details: `quizQuestions` contains duplicate `id` (value: [ { id: undefined,\n    qu...} ]).\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:264:12\n    at ModelConstructor.<anonymous> (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\validations.js:472:11)\n    at ModelConstructor.next (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\hooks.js:75:12)\n    at ModelConstructor.<anonymous> (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\validations.js:469:23)\n    at ModelConstructor.trigger (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\hooks.js:65:12)\n    at ModelConstructor.Validatable.isValid (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\validations.js:435:8)\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:260:9\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\lib\\observer.js:106:23\n    at P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:189:25\n    at event (P:\\schoolu\\server_workbench\\schoolu\\node_modules\\loopback-ds-timestamp-mixin\\index.js:29:5)"
  }
}

+1 for @vasumahesh1's issue described above

To me it seems like id generation for multiple inserts at once is broken. If I insert just one embedded Model at a time, everything works like expected. As soon as try to add more than one object at once I get the same error message as @vasumahesh1.

I tried all possible combinations of the options stated above.

@sebastianhaas Yeah Same ... This is actually blocking my work at the moment :( ... To solve around this issue i ll need to make a custom remote method accepting these values and change the embedsMany to hasMany and manually add the values in each case using add() of hasMany ... That way I can do an Embedded Insert ... Luckily I have embedsMany only at Two Places... So I can make two methods for now.. Won't hurt that much..

@vasumahesh1 @sebastianhaas
Found a Solution for this Problem
in Answer.json File add Id Properties with following setting

"id": {
"type": "string",
"id": true,
"defaultFn": "uuid"
}
Rest all setting Same as @markleusink described Above

Wow, thanks a lot @FELS-Rohit. Works for me.

yes thanks @FELS-Rohit this blocked me for a couple of days 馃憤

Setting "of defaultFn": "uuid" is this available on Loopback documentation "https://loopback.io/doc"

Was this page helpful?
0 / 5 - 0 ratings