Filter on level 2 properties - node.js

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) {
});

Related

Loopback relation

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) {
...
});

Loopback join/include two collections

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"
}
},
#############
}

Performance issue on a simple query

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.

StrongLoop REST API "hasManyThrough" include filter always returns empty

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"
}
}

Model Relation in Loopback framework

I want to establish a relation between two models, following the json files:
{
"name": "Surveyor",
"plural": "surveyors",
"base": "PersistedModel",
"strict": true,
"idInjection": true,
"options": {
"validateUpsert": true
},
"mongodb": {
"collection": "surveyors"
},
"settings": {
"mongodb": {
"allowExtendedOperators": true
}
},
"properties": {
"name": {
"type": "string",
"required": true
},
"surname": {
"type": "string",
"required": true
},
"dateOfBirth": {
"type": "date"
},
"birthPlace": {
"type": "string"
},
"provinceOfBirth": {
"type": "string"
},
"gender": {
"type": "string"
},
"registrationNumber": {
"type": "string",
"required": true,
"unique": true
}
},
"validations": [],
"relations": {}
},
"acls": [],
"methods": {}
}
{
"name": "Dossier",
"plural": "dossiers",
"base": "PersistedModel",
"strict": true,
"idInjection": true,
"options": {
"validateUpsert": true
},
"mongodb": {
"collection": "dossiers"
},
"settings": {
"mongodb": {
"allowExtendedOperators": true
}
},
"properties": {
"delCode": {
"type": "string",
"required": true
},
"client": {
"name": {
"type": "string",
"required": true
},
"surname": {
"type": "string",
"required": true
},
"taxCode": {
"type": "string"
}
},
"surveyor": {
"type": "string"
},
"examination": {
"type": "object"
},
"appointment": {
"type": "date"
},
"status": {
"type": "string",
"required": true
},
"notes": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
One dossier can have one surveyor, as key I want to use the property surveyor in the Dossier Model, corresponding to the property registrationNumber in the Surveyor Model.
I want to query the dossier Model and obtain the surveyor data in the dossier doc:
[GET] /api/dossiers
or with a filter:
[GET] /api/dossiers?filter={"include":"surveyor"}
if it is possible I want to configure the relation in the json file, not in the code.
You can use the relation generator to create that for you. In your case you're going to need to create a belongsTO relation from Dossier to Surveyor. If you want to query the other way around (dossiers from a surveyor) you're going to need to create a hasMany relation from Surveyor to Dossier.
In short, for what you've asked, you're going to end up with something like this:
In Dossier model:
...
"relations": {
"surveyor": {
"type": "belongsTo",
"model": "Surveyor",
"foreignKey": "surveyor",
"primaryKey": "registrationNumber"
}
}
...

Resources