I have a student model,a favorite model and media models eg music,video etc. I want to implement hasManyThrough ploymorphic relation in which the through model is favorite and then stores these favorites in favorite table in my case mongoDB. Am using loopback3 and its documentation isn't clear about this topic.Any lead?
Your models would look like this:
common/models/student.json
{
"name": "Student",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
},
"validations": [],
"relations": {
"favorites": {
"type": "hasMany",
"model": "Favorite",
"foreignKey": "studentId"
},
"videos": {
"type": "hasMany",
"model": "Video",
"foreignKey": "studentId",
"through": "Favorite",
"keyThrough": "favoriteId"
},
"musics": {
"type": "hasMany",
"model": "Music",
"foreignKey": "studentId",
"through": "Favorite",
"keyThrough": "favoriteId"
}
},
"acls": [],
"methods": {}
}
common/models/video.json
{
"name": "Video",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
},
"validations": [],
"relations": {
"favorites": {
"type": "hasMany",
"model": "Favorite",
"foreignKey": "videoId"
},
"students": {
"type": "hasMany",
"model": "Student",
"foreignKey": "videoId",
"through": "Favorite",
"keyThrough": "studentId"
}
},
"acls": [
],
"methods": {}
}
common/models/favorite.json
{
"name": "Favorite",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
},
"validations": [],
"relations": {
"student": {
"type": "belongsTo",
"model": "Student",
"foreignKey": "studentId"
},
"video": {
"type": "belongsTo",
"model": "Video",
"foreignKey": "videoId"
},
"music": {
"type": "belongsTo",
"model": "Music",
"foreignKey": "musicId"
}
},
"acls": [],
"methods": {}
}
Then, you simply need to POST a new Favorite item with the attributes studentId and videoId to add the new relation.
EDIT: Added music.json
common/models/music.json
{
"name": "Music",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
},
"validations": [],
"relations": {
"favorites": {
"type": "hasMany",
"model": "Favorite",
"foreignKey": "musicId"
},
"students": {
"type": "hasMany",
"model": "Student",
"foreignKey": "musicId",
"through": "Favorite",
"keyThrough": "studentId"
}
},
"acls": [
],
"methods": {}
}
Related
How to do the relation to match with an array of data in loopback
For e.g
My Models
// Regions model
{
"name": "regions",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
},
"images": {
"type": [
{
"target_id": {
"type": "string"
}
}
]
},
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
// Images model
{
"name": "images",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
},
"url": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": { },
"acls": [],
"methods": {}
}
Expected output:
[
{
"title": "Region 1",
"field_images": [{
"name": "Image 2",
"url": "/media/image-1600x650.jpg",
},{
"name": "Image 1",
"url": "/media/image-1600x650.jpg",
}]
} ]
Assuming that the relationship is HasMany
{
"name": "regions",
"base": "PersistedModel",
...
"relations": {
"images": {
"type": "hasMany",
"model": "images",
"foreignKey": "title", //region title
}
}
...
}
A relationship is defined between two models. So, I highly doubt you would get the images inside an array (if you want to define a relationship between them).
But on loopback you can use separate APIs to retrieve the field_images instead of getting it as an array.
For example - you could retrieve the images like this
region.images([filter],
function(err, images) {
...
});
When created a custom model extending the base model, the data is inserted into the base model, not to the custom model.
server/
server.js
passportConfigurator.init();
passportConfigurator.setupModels({
userModel: app.models.customer,
userIdentityModel: app.models.customerIdentity,
userCredentialModel: app.models.customerCredential,
});
model-config.json
{
"_meta": {
"sources": [
"../common/models",
"./models",
"./node_modules/loopback-component-passport/lib/models"
]
},
"User": {
"dataSource": "mongoDb",
"public": false
},
"AccessToken": {
"dataSource": "mongoDb",
"public": false
},
"customer": {
"dataSource": "mongoDb",
"public": true
},
"accessToken": {
"dataSource": "mongoDb",
"public": false
},
"customerCredential": {
"dataSource": "mongoDb",
"public": false
},
"customerIdentity": {
"dataSource": "mongoDb",
"public": false
},
"ACL": {
"dataSource": "mongoDb",
"public": false
},
"RoleMapping": {
"dataSource": "mongoDb",
"public": false
},
"Role": {
"dataSource": "mongoDb",
"public": false
}
}
common/models
customer.json
{
"name": "customer",
"plural": "customers",
"base": "User",
"relations": {
"accessTokens": {
"type": "hasMany",
"model": "accessToken",
"foreignKey": "customerId"
},
"customeridentities": {
"type": "hasMany",
"model": "customerIdentity",
"foreignKey": "customerId"
},
"customercredentials": {
"type": "hasMany",
"model": "customerCredential",
"foreignKey": "customerId"
}
},
"validations": [],
"acls": [],
"methods": []
}
customer-credential.json
{
"name": "customerCredential",
"plural": "customerCredentials",
"base": "UserCredential",
"properties": {},
"validations": [],
"relations": {
"customer": {
"type": "belongsTo",
"model": "customer",
"foreignKey": "customerId"
}
},
"acls": [],
"methods": []
}
accessToken.json
{
"name": "accessToken",
"plural": "accessTokens",
"base": "AccessToken",
"properties": {},
"validations": [],
"relations": {
"customer": {
"type": "belongsTo",
"model": "customer",
"foreignKey": "customerId"
}
},
"acls": [],
"methods": []
}
customerIdentity.json
{
"name": "customerIdentity",
"plural": "customerIdentities",
"base": "UserIdentity",
"properties": {},
"validations": [],
"relations": {
"customer": {
"type": "belongsTo",
"model": "customer",
"foreignKey": "customerId"
}
},
"acls": [],
"methods": []
}
This above is the configuration in code.
But in the database the table created were User and AccessToken not customer and accessToken.
When I remove the user and access token from the model-config.json.
"User": {
"dataSource": "mongoDb",
"public": false
},
"AccessToken": {
"dataSource": "mongoDb",
"public": false
}
The customer table and accessToken table (custom tables) were created in the mongo database.
But I get the following error message in console.
Error Message
The model "accessToken" configures "belongsTo User-like models" relation with target model "User". However, the model "User" is not attached to the application and therefore cannot be used by this relation. This typically happens when the application has a custom custom User subclass, but does not fix AccessToken relations to use this new model.
But when the User and AccessToken are added to the model-config the table created in the db is not the custom dbs customer and accessToken. Its the base data models User and AccessToken.
I have near ~10'000 records in the Image table and I want to retrieve them with a query.
The image model definition:
{
"name": "Image",
"plural": "Images",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": false
},
"uploaddate": {
"type": "date",
"required": true
},
"location": {
"type": "string"
},
"description": {
"type": "string"
},
"shootingdate": {
"type": "date"
},
"imageStatusTitle": {
"type": "string"
},
"category": {
"type": "string"
},
"canton": {
"type": "string"
},
"rights": {
"type": "boolean"
},
"earmarked": {
"type": "boolean"
}
},
"validations": [],
"relations": {
"imageStatus": {
"type": "embedsOne",
"model": "ImageStatus",
"foreignKey": "",
"options": {
"persistent": false
}
},
"categories": {
"type": "hasMany",
"model": "ImageCategory",
"foreignKey": ""
},
"votes": {
"type": "hasMany",
"model": "WebsiteUser",
"foreignKey": "imageId",
"through": "ImageVote"
},
"original": {
"type": "embedsOne",
"model": "File",
"property": "original",
"options": {
"persistent": false
}
},
"small": {
"type": "embedsOne",
"model": "File",
"property": "small",
"options": {
"persistent": true
}
},
"medium": {
"type": "embedsOne",
"model": "File",
"property": "medium",
"options": {
"persistent": true
}
},
"large": {
"type": "embedsOne",
"model": "File",
"property": "large",
"options": {
"persistent": true
}
},
"xlarge": {
"type": "embedsOne",
"model": "File",
"property": "xlarge",
"options": {
"persistent": true
}
},
"owner": {
"type": "belongsTo",
"model": "WebsiteUser",
"foreignKey": "fotographerId"
},
"widgets": {
"type": "hasMany",
"model": "Widget",
"through": "WidgetImage"
}
}
...
The query I will make is the following:
const query = {
fields: ['id', 'name', 'fotographerId'],
include: {
relation: 'owner',
fields: ['id', 'firstname', 'lastname', 'street', 'zip', 'city', 'email', 'phone', 'mobile', 'locale']
}
};
if (0 < whereFilter.length) {
query.where = {
and: whereFilter
};
}
app.models.Image.find(query, function(err, records) {
...
}
The problem is that this query takes 5 minutes to execute.
I then improve it by 4 minutes by removing the default ordering explained in this issue.
But anyways 4 minutes for such query on 10'000 document seems very slow to me. Do you see what can be the problem? Or do you have an idea how to speed up this query?
Just in case someone has the same problem. The cause of the performance issue was on a hook that execute some code every time a model was fetched/loaded. Now it takes 2 seconds to perform.
Premise
Pretty short and simple. In my testing/learning environment for StrongLoop, I have set up two models: CoffeeShop and Person. Persons can have many "employers", and CoffeeShops can have many "employees". This relationship is maintained by a "hasMany"/"through" type relationship on both the CofeeShop and Person models.
Problem
When querying either model through the REST API, an include filter always returns an empty array for the associated relation, even if relations exist. In other words, the API call
http://localhost:3000/api/CoffeeShops/67/employees
works as expected, but
GET: http://localhost:3000/api/CoffeeShops/67/?filter={"include":["employees"]}
Does not.
Could anyone help me figure out why this is? I've attached pictures and the .json model definitions files as well.
// /common/models/coffee-shop.json
{
"name": "CoffeeShop",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
},
"city": {
"type": "string",
"required": true
},
"numberEmployees": {
"type": "number"
},
"ownerId": {
"type": "number"
},
"isSmallBusiness": {
"type": "boolean"
}
},
"validations": [],
"relations": {
"owner": {
"type": "belongsTo",
"model": "Person",
"foreignKey": ""
},
"employees": {
"type": "hasMany",
"model": "Person",
"foreignKey": "employerId",
"through": "CoffeeShopPersonEmployeeEmployer"
}
},
"acls": [],
"methods": {}
}
// /omon/models/person.json
{
"name": "Person",
"plural": "Persons",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
},
"age": {
"type": "number"
}
},
"validations": [],
"relations": {
"shops": {
"type": "hasMany",
"model": "CoffeeShop",
"foreignKey": "ownerId"
},
"employers": {
"type": "hasMany",
"model": "CoffeeShop",
"foreignKey": "employeeId",
"through": "CoffeeShopPersonEmployeeEmployer"
}
},
"acls": [],
"methods": {}
}
// /common/models/coffee-shop-person-employee-employer.json
{
"name": "CoffeeShopPersonEmployeeEmployer",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"person": {
"type": "belongsTo",
"model": "Person",
"foreignKey": "employeeId"
},
"coffeeShop": {
"type": "belongsTo",
"model": "CoffeeShop",
"foreignKey": "employerId"
}
},
"acls": [],
"methods": {}
}
Images of REST requests
in your CoffeeShopPersonEmployeeEmployer Model:
add foreign keys properties:
"properties": {
"CoffeeShopId": {
"type": "number",
"required": true
},
"PersonId": {
"type": "number",
"required": true
}
}
and ajust the relations :
"relations": {
"person": {
"type": "belongsTo",
"model": "Person",
"foreignKey": "PersonId"
},
"coffeeShop": {
"type": "belongsTo",
"model": "CoffeeShop",
"foreignKey": "CoffeeShopId"
}
}
I have a FollowMap as follows
{
"name": "FollowMap",
.
.
.
"relations": {
"follower_acc": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "follower_id"
},
"following_acc": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "following_id"
}
}
}
I want to build a relation to my Account model, so I can get one users follower & following list, the Account model is as follows;
{
"name": "Account",
.
.
.
"followers": {
"type": "hasMany",
"model": "FollowMap",
"foreignKey": "following_id"
},
"following": {
"type": "hasMany",
"model": "FollowMap",
"foreignKey": "follower_id"
}
}
}
From the relation, I can fetch a Account's follower and following count however, I can't fetch the follower and following Account list from these relations. I hope it's clear.
I know this issue is maybe outdated, but for those who are still looking for the solution, i'll give mine. I wanted to do implement the publisher/subscriber relation between the profils entities in my database.
The most obvious way to do it is with the hasAndBelongsToMany relation, but strangely you cannot change the foreignKey used in model: it means you cannot declare "publishedId" and "subscriberId" to be used as foreign keys referencing a Profil entity.
You have to declare manually the third table used to reference those 2 foreign keys.
My working example for this third table:
//common/models/subscribing.json
{
"name": "Subscribing",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"subscriber": {
"type": "belongsTo",
"model": "Profil",
"as": "subscriber"
},
"publisher": {
"type": "belongsTo",
"model": "Profil",
"as": "publisher"
}
},
"acls": [],
"methods": {}
}
And my Profil entity related to itself through the Subscribings table:
//common/models/profil.json
{
"name": "Profil",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"followers": {
"type": "hasMany",
"model": "Profil",
"foreignKey": "publisherId",
"keyThrough": "subscriberId",
"through": "Subscribing"
},
"subscribings": {
"type": "hasMany",
"model": "Profil",
"foreignKey": "subscriberId",
"keyThrough": "publisherId",
"through": "Subscribing"
}
},
"acls": [],
"methods": {}
}