I try to figure out how to set a very simple nested "treenode" model in loopback with mongodb. The idea is there will be only one model (for this): treenode which can contain other treenodes. I would like to store them at once via mongodb nested documents:
- TreeNode (document):
Name: "A",
Nodes: [
{
Name: "A-A",
Nodes: [
{
Name: "A-A-A",
Nodes: []
},
{
Name: "A-A-B",
Nodes: []
},
{
Name: "A-A-C",
Nodes: []
}
},
{
Name: "A-B",
Nodes: []
},
]
Additionally each node at any level has relations to other models.
There will be many top-level root treenodes (documents). Which relation type and how should I use for this?
Unfortunately, there isn't much documentation on this topic yet. For now, see http://docs.strongloop.com/display/LB/Embedded+models+and+relations
You should define the nested models separately and then declare them as transient models. Then loopback should store them in its parent model, as explained http://loopback.io/doc/en/lb2/Embedded-models-and-relations.html#transient-versus-persistent-for-the-embedded-model
Define a transient data source
server/datasources.json
{
...
"transient": {
"name": "transient",
"connector": "transient"
}
}
server/model-config.json
{
...
"BaseNode": {
"dataSource": "db",
"public": true
},
"NestedNode": {
"dataSource": "transient",
"public": false
}
}
And the model definitions should be somthing like this:
common/models/NestedNode.json
{
"name": "NestedNode",
"base": "Model",
"properties": {
"name": {
"type": "string"
}
},
"relations": {
"nodes": {
"type": "referencesMany",
"model": "NestedNode",
"options": {
"validate": true,
"forceId": false
}
}
}
common/models/BaseNode.json
{
"name": "BaseNode",
"base": "PersistedModel",
"properties": {
"name": {
"type": "string"
}
},
...
"relations": {
"nestedNode": {
"type": "embedsMany",
"model": "Link",
"scope": {
"include": "linked"
}
}
},
...
}
You also may experience curcular reference problems.
Related
I'm using JSON descriptors instead of proto format. Everithing works, unless the array of Todo. I need an array of Todos.
How define that? I put the "type": "array", but always return the error:
'Error: no such Type or Enum 'array' in Type .Todos'
My json file is like this:
const todo = {
"nested": {
"Services": {
"methods": {
"createTodo": {
"requestType": "Todo",
"requestStream": false,
"responseType": "Todo",
"responseStream": false
},
"readTodos": {
"requestType": "voidNoParam",
"requestStream": false,
"responseType": "Todos",
"responseStream": false
},
"readTodosStream": {
"requestType": "voidNoParam",
"requestStream": false,
"responseType": "Todo",
"responseStream": true
}
}
},
"Todo": {
"fields": {
"id": {
"type": "int32",
"id": 1
},
"text": {
"type": "string",
"id": 2
}
}
},
"Todos": {
"fields": {
"items": {
"type": "array",
"id": 1
}
}
},
"voidNoParam": {
"fields": {}
}
}
}
module.exports = todo
I found the problem, really simple.
"Todos": {
"fields": {
"items": {
"rule": "repeated",
"type": "Todo",
"id": 1
}
}
},
From my understanding of the JSON:API spec (specifically https://jsonapi.org/format/#document-resource-object-linkage) I should be able to include meta members for each member of a relationship.
I have been able to add a hash of meta data to the relationships object itself, but not one to each of the individual relationships within.
class PlanSerializer < ApplicationSerializer
attributes :id, :name
has_many :features do
meta value: "x"
end
end
I know I can use a block syntax for has_many, and think that's the way to achieve this. But I haven't got it working. Calling the meta method within the block adds the meta block to the features relationship object, and I need to add one to each entry in that array.
My questions:
Have I understood the spec correctly? Should I be able to add a meta object to each relationship?
How would I go about doing this with the active model serializers?
Background:
My goal is to represent a many-many from Plans to Features where each plan might have some extra information for it's own relationship to a given Feature (and that information is different for every Plan, so it doesn't belong on the Feature object)
If your answer is that I shouldn't be doing this, that's fine, but please present an alternative which you think is preferred.
// My desired output
{
"data": [
{
"id": "small",
"type": "plans",
"attributes": {
/* Some attributes */
},
"relationships": {
"features": {
"data": [
{
"id": "num-users",
"type": "features",
"meta": {
"value": 1
}
},
{
"id": "num-projects",
"type": "features",
"meta": {
"value": 5
}
}
]
}
}
},
{
"id": "large",
"type": "plans",
"attributes": {
/* Some attributes */
},
"relationships": {
"features": {
"data": [
{
"id": "num-users",
"type": "features",
"meta": {
"value": 5
}
},
{
"id": "num-projects",
"type": "features",
"meta": {
"value": 50
}
},
{
"id": "unlimited-downloads",
"type": "features"
}
]
}
}
}
],
"included": [
{
"id": "num-users",
"type": "features",
"attributes": {
"description": "Number of users"
}
},
{
"id": "num-projects",
"type": "features",
"attributes": {
"description": "Number of projects"
}
},
{
"id": "unlimited-downloads",
"type": "features",
"attributes": {
"description": "Unlimited downloads"
}
}
]
}
This is what my model looks like
"name": "mClass",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"mClassName": {
"type": "string",
"required": true,
"index": {
"unique": true
}
},
"mClassUrl": {
"type": "string"
},
"mCreatedBy": {
"type": "string",
"required": true
},
"mCreatedAt": {
"type": "date",
"required": true,
"default": "$now"
},
"mUpdatedAt": {
"type": "date"
},
"mDeletedAt": {
"type": "date"
}
},
"validations": [],
"relations": {},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "DENY"
}
],
"methods": {}
}
i want to make class name as unique. I tried index:{unique:true} but it seems that it is not working as i can still create classes with same name. Please help me on what changes to be made.
In case somebody is looking for the Loopback v4 solution to this problem. In Loopback v4 you amend the #property annotation with "index: {unqiue: true}":
#model()
export class Client extends Entity {
#property({
type: 'string',
required: true,
index: {
unique: true,
},
})
name: string;
}
Then you must update your schema:
npm run migrate
or recreate it:
npm run migrate -- --rebuild
PostgresQL is support and MySQL, too, I guess
Alternatively, in your mClass.js you can do the following:
module.exports = function (mClass) {
mClass.validatesUniquenessOf('mClassName');
};
What you need to do is to define index in your model.json:
"indexes": {
"indexName": {
"keys": {
"mClassName": 1
},
"options": {
"unique": true
}
}
}
Then you need to run autoupdate on your datasource when the server starts, great place for that would be a boot script, eg. ./server/boot/db-autoupdate.js:
'use strict';
module.exports = async function(app) {
// this will trigger the database structure update, like creating indexes (which is not handled by auto-migrate module
await app.dataSources.db.autoupdate();
};
From the docs:
id No Boolean Whether the property is a unique identifier. Default is false. See Id property below.
So I'd assume that instead of index: {...}, you have to use id: true.
I would like to include my product_product model inside product_template.
1 - Each product template has its own product_product variations "HasMany" .
2 - product_product has only one template "BelongsTo" product_template
3- product_template should be filled with only related product_product variations.
4- The two models are saved seprately, so when I call for find() function I would like to get a product_template model filled with the product_product related to it (Could be more than one)
Get product template function :
Producttemplate.find({
include: {
relation: 'variations',
scope: {
fields: ['sku', 'name', 'price', 'regular_price', 'weight', 'description', 'stock_quantity'],
},
},
})
product_product Model :
This model should be included in the product_template
{
"name": "product_product",
"base": "PersistedModel",
"strict": true,
"options": {
"validateUpsert": true
},
"properties": {
"_id_Odoo": {
"type": "number"
},
"sku": {
"type": "string",
"id": true,
"required": true,
"description": "Yes it's SKU"
},
#fields
},
"validations": [],
"relations": {
"product": {
"type": "belongsTo",
"model": "product_template",
"foreignKey": "_id_Odoo"
}
},
"acls": [],
"methods": {}
}
product_template Model :
This model should include the product_product
{
"name": "product_template",
"base": "PersistedModel",
"strict": true,
"options": {
"validateUpsert": true
},
"properties": {
"_id_Odoo": {
"type": [
"number"
]
}
"sku": {
"type": "string",
"id": true,
"required": true,
"description": "Yes it's SKU"
},
"name": {
"type": "string"
}
},
"scope": {
"include": "variations"
},
"hidden": ["_id_Odoo"],
"validations": [],
"relations": {
"variations": {
"type": "hasMany",
"model": "product_product",
"foreignKey": "_id_Odoo"
}
},
"acls": [],
"methods": {}
}
Result :
This the result of get product template above :
{ sku: 'AHWLI05942-FUSCHIA', variations: List [] },
{ sku: 'AHWLI05943-BLACK', variations: List [] },
{ sku: 'AHWLI05943-BURGUNDY', variations: List [] },
{ sku: 'AHWLI05944-BLACK', variations: List [] },
{ sku: 'AHWLI05944-MARRON', variations: List [] },
{ sku: 'AHWLI05945-BLUE', variations: List [] }
When I point into variations i get a function and into variations.list i get undefined any ideas how to get exact structure ?
example code part of my model "TeamRole" which belongsTo "Team" and User" in model level.
teamRole.json
"validations": [],
"relations": {
"team": {
"type": "belongsTo",
"model": "Team",
"foreignKey": ""
},
"user": {
"type": "belongsTo",
"model": "User",
"foreignKey": ""
}
}
Example search query for team role,
query.js
app.models.TeamRole.find({
where: {
userId: user.id
},
include: {
relation: 'team'
}
},function(err,teams){
console.log("teams will have all the include teams[] with team role ")
});
Hope using above example will help you.
sorry for late response, it was just misunderstanding of the relation and structure, i made a function that verifies if the product model exists in the template if yes, i push the template id in the product model and it worked fine.
product_product Model : I deleted the relation in the model below because it's useless, so i removed id property in sku and removed _id_Odoo field, then i added id field and parent_template field that contains the template model id.
{
"name": "product_product",
"base": "PersistedModel",
"strict": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number"
"id": true
}
"parent_template": {
"type": "number"
},
"sku": {
"type": "string",
"required": true,
"description": "Yes it's SKU"
},
#fields
}
product_template Model : I removed the _id_odoo and id property in sku and created an id for this model and in relation i made parent_template as foreignkey
{
"name": "product_template",
"base": "PersistedModel",
"strict": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number",
"id" : true
}
"sku": {
"type": "string",
"required": true,
"description": "Yes it's SKU"
},
"name": {
"type": "string"
}
},
"scope": {
"include": "variations"
},
##########
"relations": {
"variations": {
"type": "hasMany",
"model": "product_product",
"foreignKey": "parent_template"
}
},
#############
}
I work on an application link to a MySQL DB. I have different models, and i want to used a model in another model.js.
for example i have 2 models :
Server.json
{
"name": "server",
"base": "PersistedModel",
"idInjection": false,
"properties": {
"idserver": {
"type": "number",
"id": true
},
"Name": {
"type": "string"
},
"type": {
"type": "string"
},
"cpus": {
"type": "number"
},
"memory": {
"type": "number"
},
"storage": {
"type": "number"
},
"hypervisor": {
"type": "number"
},
"iddatastore": {
"type": "number"
},
"comment": {
"type": "string"
}
},
"validations": [],
"relations": {
"datastores": {
"type": "hasAndBelongsToMany",
"model": "Datastore"
}
},
"acls": [],
"methods": []
}
And Datastore.json
{
"name": "Datastore",
"plural":"Datastores",
"base": "PersistedModel",
"idInjection": false,
"properties": {
"iddatastore": {
"type": "number",
"id": true
},
"owner": {
"type": "number"
},
"size": {
"type": "number"
},
"type": {
"type": "string"
},
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"servers": {
"type": "hasAndBelongsToMany",
"model": "server"
}
},
"acls": [],
"methods": []
}
and i would like to get the data from Datastore in Server.js.
i try this way but it doesn't work well :
Server.js
var loopback = require('loopback');
var app = module.exports = loopback();
module.exports = function(Server) {
Server.on('attached',function(){
var override = Server.find;
var Datastore = Server.app.models.Datastore;
// the problem is here. It seems to return the description of the model when i look for the data
Server.findById = function(filter,callback){
var id = arguments[0];
// this isn't working
console.log(Datastore.find({where:{"idserver":id}}));
return override.apply(this, arguments);
};
});
};
I know it is possible by different way but if it is possible i prefer get the data from Datastore !
Sorry for my english if it is not clear, Do you have any idea ?
Loïc
Try var Datastore = loopback.getModel('Datastore');