loopback hasandbelongstomany relation table doesn't exist - node.js

I have defined two models:
a 'part' model:
{
"name": "part",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"assemblies": {
"type": "hasAndBelongsToMany",
"model": "assembly",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
and an 'assembly' models:
{
"name": "assembly",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"parts": {
"type": "hasAndBelongsToMany",
"model": "part"
}
},
"acls": [],
"methods": {}
}
both models have an hasAndBelongToMany relation.
In a /server/boot/sample-model.js i have created some instance of this both models:
module.exports = function(app){
var Dev = app.dataSources.dev;
var Customer = app.models.customer;
var Order = app.models.order;
var Part = app.models.part;
var Assembly = app.models.assembly;
Dev.automigrate(['customer', 'order', 'assembly', 'part'], function(err) {
Customer.create([
{name: 'nicolas'},
{name: 'marie'},
{name: 'cyril'}
], function(err, customers){
Part.create([
{name: 'boulon'},
{name: 'ecrou'},
{name: 'cheville'},
], function(err, part){
//console.log(part[0])
Assembly.create([
{title: 'piece1'},
{title: 'piece2'},
{title: 'piece3'},
], function(err, assemblies){
//console.log(assemblies[0])
assemblies[0].parts.add(part[0], function(err){
if(err){
console.log(err)
}
})
})
})
});
});
}
but
assemblies[0].parts.add(part[0], function(err){
if(err){
console.log(err)
}
})
end with error:
{ [Error: ER_NO_SUCH_TABLE: Table 'database_development.assemblypart' doesn't exist]
code: 'ER_NO_SUCH_TABLE',
errno: 1146,
sqlState: '42S02',
index: 0 }
why loopback doesn't create the assemblypart table in my database ?

I ran into this same issue once and after many hours of struggling (using postgres connector), I've found several solutions.
Here's the shortest :
Instead of :
Dev.automigrate(['customer', 'order', 'assembly', 'part'], function(err) {
// Your code here
});
Try using this :
Dev.automigrate()
.then(function(err) {
// Your code here
});
I don't know exactly why, but in the second case the junction table is created.
Can you try this and let me know if it works for you ? If it's not can you provide some place where I can inspect and try running your code ?

Related

Removing the specific object from array in mongoose and update it

Hi i am new in mongoose and mongodb. I want to remove specific object from the Array in my document and return the updated document. I have tried a lot but it always return null. Here is my document structure.
{
"role": "Student",
"skills": [
"html",
"css",
"js"
],
"_id": "5ef583198e9b23cc8c606c10",
"user": "5ee5c9ef26333935647e54bc",
"__v": 24,
"status": "Intern",
"education": [],
"internships": [
{
"current": false,
"_id": "5ef894d48f601512340f25b5",
"title": "Web",
"company": "asdfadfd",
"from": "2010-02-04T00:00:00.000Z"
},
{
"current": false,
"_id": "5ef894f31dc9413bf89c44d8",
"title": "Django",
"company": "example",
"from": "2010-02-04T00:00:00.000Z"
}
]
}
And here is my updating function
exports.deleteStudentInternship = async (req, res, next) => {
const deleteInternship = await Student.findOneAndUpdate(
{ $and: [{ user: req.user.id }, { 'internships': { $elemMatch: { _id: req.params.intern_id } } }] },
{ '$pull': { 'internships': { _id: req.params.intern_id } } },
{
new: true,
useFindAndModify: false,
},
function (error) {
if (error) return validationError(404, { internship: 'Internship id not exist' }, next)
}
);
if (!deleteInternship) {
return validationError(404, { internship: 'Internship id not exist' }, next)
}
res.status(200).json(deleteInternship);
}
Please change the pull part I mean
{ '$pull': { 'internships': { _id: req.params.intern_id } } }
to this and try:
{ '$pull': { 'internships': req.params.intern_id } }

How can I populate "oem, category, subcategory" of the product array in the example document:

This is my router JS where I get document by Mongoose. So the thing is i have these nested data like oem, category and subcategory which I cannot get the whole details. I have been trying since one week but nothing complies with me. Can someone please help me out?
router.get("/product/:id/:product_id", (req, res) => {
ProReg.findById(req.params.id)
.populate({
path: "products",
populate: [
{
path: "products.oem",
model: "company",
select: "companyname"
},
{
path: "category",
model: "productcategory",
select: "category"
},
{
path: "subcategory",
model: "productsubcategory",
select: "subcategory"
}
]
})
.exec(function(err, products) {
if (err) {
res.json(err);
} else {
res.json(products);
}
});
});
This is the result that I am getting
{
"_id": "5cd37f4fcd79b01040124dbc",
"refno1": "klklklkl",
"refno2": "klklklkl",
"date": "2019-05-08T00:00:00.000Z",
"customer": "5c98bb0a42207b16d8fbd3cf",
"customertype": "5c7a1a1d4913fa08ac75c027",
"department": "5cbd67c709aeca1ea480157a",
"products": [],
"__v": 3
}
The details of the product is not coming only. Now how can i get this product data along with the details of oem, category, and subcategory?
Without Populate this is the result:
{
"_id": "5cd37f4fcd79b01040124dbc",
"refno1": "klklklkl",
"refno2": "klklklkl",
"date": "2019-05-08T00:00:00.000Z",
"customer": {
"_id": "5c98bb0a42207b16d8fbd3cf",
"customername": "Raghav"
},
"customertype": {
"_id": "5c7a1a1d4913fa08ac75c027",
"customertype": "Government "
},
"department": {
"_id": "5cbd67c709aeca1ea480157a",
"department": "Hardware"
},
"products": [
{
"warrantyfrom": "2019-05-09T00:00:00.000Z",
"warrantyto": "2019-05-30T00:00:00.000Z",
"oemwarrantyfrom": "2019-05-14T00:00:00.000Z",
"oemwarrantyto": "2019-05-14T00:00:00.000Z",
"_id": "5cd37f60cd79b01040124dbd",
"oem": "5cb6e026042460131454cf45",
"category": "5c960902e5cf3429e06beb6c",
"subcategory": "5ca35fbed6e1430df88954a6",
"modelno": "A123888",
"serialno": "fdsfdsfs"
},
{
"warrantyfrom": "2019-05-09T00:00:00.000Z",
"warrantyto": "2019-05-30T00:00:00.000Z",
"oemwarrantyfrom": "2019-05-14T00:00:00.000Z",
"oemwarrantyto": "2019-05-14T00:00:00.000Z",
"_id": "5cd37f65cd79b01040124dbe",
"oem": "5cb6e026042460131454cf45",
"category": "5c960902e5cf3429e06beb6c",
"subcategory": "5ca35fbed6e1430df88954a6",
"modelno": "A123888",
"serialno": "eeeeee"
},
{
"warrantyfrom": "2019-05-09T00:00:00.000Z",
"warrantyto": "2019-05-30T00:00:00.000Z",
"oemwarrantyfrom": "2019-05-14T00:00:00.000Z",
"oemwarrantyto": "2019-05-14T00:00:00.000Z",
"_id": "5cd37f6fcd79b01040124dbf",
"oem": "5c986a1e9b6bc614b8a551b9",
"category": "5c960902e5cf3429e06beb6c",
"subcategory": "5ca35fbed6e1430df88954a6",
"modelno": "QW123",
"serialno": "hhhhhh"
}
],
Did you try simply doing this?
ProReg.findById(req.params.id)
.populate('products.oem')
.populate('products.category')
.populate('products.subcategory')
.exec(function(err, products) {
if (err) {
res.json(err);
} else {
res.json(products);
}
});
});
Check out these related tickets:
Mongoose populate within an object?
Mongoose populate with array of objects containing ref

Filter on level 2 properties

I use LoopBack with MongoDB connector.
Models:
Application for a job:
{
"name": "application",
"plural": "applications",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"jobId": {
"type": "string",
"required": true
},
"staffId": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
Job:
{
"name": "job",
"plural": "jobs",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"applications": {
"type": "hasMany",
"model": "application",
"foreignKey": "jobId",
"primaryKey": "id"
}
},
"acls": [],
"methods": {}
}
If the user's id (staffId) is in the applications array I need to not show for user this job.
Example:
We have array of jobs with applications
[
{
id: 1,
title: "Job 1",
applications: [
{
jobId: 1,
staffId: 1
},
{
jobId: 1,
staffId: 2
}
]
},
{
id: 2,
title: "Job 2",
applications: [
{
jobId: 2,
staffId: 1
}
]
}
]
If user's id (staffId) is 2 then user sees only "Job 2".
I tried something like this:
/jobs?filter[include][applications]&filter[where not][applications][elemMatch][staffId]=2
But it doesn't work.
Any suggestions?
Thanks.
At this state of the development you can't filter on a level 2 properties with REST: https://github.com/strongloop/loopback/issues/517 (defect till 2014).
What you can do instead is to define a custom emote method that will contain something like:
var userId = 2;
Jobs.find({
include: {
relation: 'applications',
scope: {
where: {staffId: {eq: userId}},
}
}
}, function (err, jobs) {
});

Mongoose Querying Sub-Document Properties

I am trying to expose this functionality through a WEB API. The way it is set up in this code is that someone does a GET on URL providing a querystring along with it in the form of:
?field=value&anotherfield.subproperty=value
But I can't seem to get querying based on sub-document properties to work. Below you will find my barebones code, record I'm trying to receive and both of my test cases.
Code:
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var config = require('../config');
var User = require('../models/user');
var functions = require('../functions');
router.get('/', function(req,res,next) {
//Check Permissions associated with UID *TODO
var parameters = req.query;
console.log(parameters);
User.find(parameters, function(err, users) {
if (err)
{
json = functions.generateOperationOutcome("exception","error",err,"exception");
res.status(500);
res.json(json);
}
else
{
//Check for blank result
if (users.length === 0)
{
json = functions.generateOperationOutcome("not-found","warning","Non-Existent Resource","warning");
res.status(404);
res.json(json);
}
else {
res.status(200);
res.json(users);
}
}
});
});
Record:
{
"_id": "5871d2e814946a941d8611fb",
"resourceType": "testResource",
"link": [],
"communication": [],
"animal": {
"genderStatus": {
"coding": []
},
"breed": {
"coding": []
},
"species": {
"coding": []
}
},
"contact": [],
"photo": [],
"maritalStatus": {
"coding": []
},
"address": [],
"gender": "unknown",
"telecom": [
{
"system": "phone",
"value": "2019196553",
"use": "home"
}
],
"name": {
"suffix": [],
"prefix": [],
"given": [],
"family": []
},
"identifier": [
{
"use": "official",
"type": {
"coding": {
"system": "kylec laptop",
"version": "0.01",
"code": "UDI",
"display": "Universal Device Identifier",
"userSelected": false
},
"text": "test"
},
"system": "test system",
"value": "test value",
"assigner": {
"reference": "test assigner reference"
},
"period": {
"start": "1992-12-31T09:59:59+00:00"
}
}
]
}
Successful Query:
GET http://{{LOCAL}}/api/user?resourceType=testResource
Returns this one model from MongoDB.
Unsuccessful Query (no documents matching query found):
GET http://{{LOCAL}}/api/user?telecom.system=phone
Returns no models back and results in a 404.
You are not properly using dot-notation, as the property you're seeking is within an array:
"telecom": [
{
"system": "phone",
"value": "2019196553",
"use": "home"
}
]
Querying array content typically would require you to do a join against the array (for DocumentDB queries), and not a simple find().
If you wanted the ability to use dot-notation here, you'd need to create a subdocument, like:
"telecom": {
"system": "phone",
"value": "2019196553",
"use": "home"
}
At this point, you'd be able to address properties such as telecom.system, telecom.value, and telecom.use.

strongloop creating related model object in afterRemote method

say i have a game scenario.
a game belongs to a user.
game.json:
{
"name": "game",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"beer_points_required": {
"type": "number",
"required": true
},
"total_points": {
"type": "number",
"required": true
}
},
"validations": [],
"relations": {
"game_blngs_to_user": {
"type": "belongsTo",
"model": "user",
"foreignKey": ""
}
},
"acls": [],
"methods": []
}
user.json:
{
"name": "user",
"base": "User",
"idInjection": true,
"properties": {
"last_game": {
"type": "date",
"required": false
},
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": []
}
I'm attempting to create a game object for a user programmatically after the user has been created through CRUD, so inside the user.js i have:
var config = require('../../server/config.json');
var path = require('path');
var app = require('../app');
module.exports = function(user) {
user.afterRemote('create', function(context, user) {
console.log('> user.afterRemote triggered');
//create a game for each user thats created
var Game = app.models.game;
game.create({game_blngs_to_userId: user.id, beer_points_required: 0, total_points: 0},function(err, res){
if(err){
console.log('\n\n>>err');
console.log(err);
next(err);
return;
}
console.log(res);
});
});
However, this obviously didn't work lol so I'm wondering how to actually accomplish my goal. I've been staring at strong loops docs for a long time and it seems like actual usage of their api is not that well documented...well at least in my eyes. could anyone please shed some light on this for me?
Perhaps, you're missing 3rd parameter - next function in afterRemote callback.
user.afterRemote('create', function(context, user, next) {
...
var Game = app.models.game;
game.create({game_blngs_to_userId: user.id, beer_points_required: 0, total_points: 0},function(err, res){
if(err){
console.log(err);
next(err);
return;
}
next() // countinue execution
});
});
i think your user reference is undefined...try:
app.models.user.afterRemote

Resources