Loopback4: Access target model with nested inclusion in HasManyTrough - many-to-many

I am trying to access a target model and its related models in a hasManyThrough setup in Loopback 4. For example in the documentation:
https://loopback.io/doc/en/lb4/HasManyThrough-relation.html
how do I get all appointments of a specific doctor with the patient data included?
When I try to access /doctors (with exact same setup in the above article) with the following filter:
const filter = {
include: [
{ relation: 'patients' },
]};
I do get to see the list of patients.
However, I'd like access /doctors with the following filter:
const filter = {
include: [
{ relation: 'appointments',
scope: {
include: [{relation: 'patient'}],
},
},
]
};
Is there a way the hasManyThrough relationship supports this? Or do I need to connect my models differently through custom logic instead.

Related

MongoDB, change array of objects to an array of strings containing their ObjectId

I have an database full of objects that contain information, as well as other arrays of objects. I would like to change the inner arrays to only be arrays with each index as an ObjectId type with their respective ObjectId
I am using the mongoose populate function to retrieve this information later in the program. So only the ObjectId is needed for the reference.
job {
_id: 1,
name: "name",
parts: [
{ _id: ObjectId("5c790ce7d3dc8d00ccc2688c"), name: "name"},
{ _id: ObjectId("5c790ce7d3dc8d00ccc2688b"), name: "name"},
{ _id: ObjectId("5c790ce7d3dc8d00ccc2688a"), name: "name"},
]
}
Desired Result
job {
_id: 1,
name: "name",
parts: [
ObjectId("5c790ce7d3dc8d00ccc2688c"),
ObjectId("5c790ce7d3dc8d00ccc2688b"),
ObjectId("5c790ce7d3dc8d00ccc2688a")
]
}
I tried a few mongoDB queries from the command line but none of them are quite giving the result I need. This one has no errors, but it doesn't seem to change anything.
db.jobs.update(
{},
{
$set: {"parts.$[element]": "element._id"}
},
{
multi: true,
arrayFilters: [{ "element": {} }]
}
)
I'm not sure if this is possible or not using only the mongo shell.
Mongo v4.2 introduced pipelined updates this allows the use of aggregation operators within the update and more importantly updating a document using it's own values. which is what you want to achieve.
db.jobs.updateMany(
{},
[
{
'$set': {
'parts': {
'$map': {
'input': '$parts',
'as': 'part',
'in': '$$part._id'
}
}
}
}
]);
Unfortunately this is not possible for earlier Mongo versions. Specifically $arrayFilters allows you to filter an array but again prior to the new update syntax accessing own values within the update is not possible.
You'll have to iterate over all documents and do the update one by one in code.
As #Tom Slabbaert mentioned in the other answer you will have to use updates with aggregation pipelines available from v4.2 if you want to update the documents in bulk in one operation.
As an alternative to using $map, if you want only a single value at the top level and the value is accessible using the dot notation. You can simply use $set with the dot notation.
db.jobs.updateMany({}, [{
$set: { parts: "$parts._id" }
}])

LoopBack 4 REST API example for getting record with Mysql

i'm learning loopback 4, i have created model, repository and datasource, it also connected
to mysql and i can retrieve results from http://127.0.0.1:3000/myapi/{id}
in my default example getting by id is :
#get('/citySchedule/{id}', {
responses: {
'200': {
description: 'Schedule model instance',
content: {'application/json': {schema: {'x-ts-type': Schedule}}},
},
},
})
async findById(#param.path.number('id') id: number): Promise<Schedule> {
return await this.ScheduleRepository.findById(id);
}
However, i didnt found any tutorial for getting data with more parameters.
let say mysql table of schedule has contain column id, city_name, city_code, date, task, item.
for example, i want to get "SELECT task, item FROM schedule WHERE city_code=123 AND date=2019-05-01"
my question, how to write code to get those data at loopback controller ?
any example code...
my expectations, i can query from my api :
http://127.0.0.1:3000/myapi/{city_code}/{date}/ to get data results or
http://127.0.0.1:3000/myapi/{city_name}/{date}/
If you have generated your controller using loopback cli, you must have got another method in controller class like this
#get('/citySchedule', {
responses: {
'200': {
description: 'Array of Schedule model instances',
content: {
'application/json': {
schema: {type: 'array', items: {'x-ts-type': Schedule}},
},
},
},
},
})
async find(
#param.query.object('filter', getFilterSchemaFor(Schedule)) filter?: Filter,
): Promise<Schedule[]> {
return await this.ScheduleRepository.find(filter);
}
You can use this API to fetch more filtered data.
Considering your example
SELECT task, item FROM schedule WHERE city_code=123 AND
date=2019-05-01
for this query, you need to hit the API like this.
GET /citySchedule?filter=%7B%22where%22%3A%7B%22city_code%22%3A123%2C%22date%22%3A%222019-05-01%22%7D%2C%22fields%22%3A%7B%22task%22%3Atrue%2C%22item%22%3Atrue%7D%7D
Here, the filter query parameter value is actually url encoded string for the below json string
{
"where":{
"city_code":123,
"date":"2019-05-01"
},
"fields":{
"task":true,
"item":true
}
}

EmberJS 2.7 How to restructure/reformat/customize data returned from the store

I have what I think is a very simple issue, but I just don't get how to do this data manipulation. This sadly didn't help, even though it's the same pain I am feeling with Ember.
Here is a route:
route/dashboard.js:
import Ember from 'ember';
export default Ember.Route.extend({
// this is for testing, normally we get the data from the store
model: function() {
return this.get('modelTestData');
},
modelTestData: [{
name: 'gear',
colorByPoint: true,
data: [
{y: 10, name: 'Test1'},
{y: 12, name: 'Test2'},
{y: 40, name: 'Test3'}
]
}],
});
The structure of the 'modelTestData' object has to be exactly like that as it is passed into a child component that needs it structured that way.
I can easily get my data from the API and put it into the model:
model: function() {
return this.store.get('category');
},
But then I need to restructure it...but how?
I have to somehow iterate over the categories collection and extract parts of data from each record to replace the 'data' part of the modelTestData object.
So I have 3 issues I am completely stumped on:
How to 'get at' the attributes I need from the model?
How to structure them as an array of objects with 'y' and 'name'?
How to assign that structure to the 'data' property of modelTestData instead of it being hardcoded?
Categories is a JSONAPI object like this:
{
"data":[
{
"id":"1",
"type":"categories",
"attributes":{
"name":"Carrying system",
"total-grams":"0.0"
}
},
{
"id":"2",
"type":"categories",
"attributes":{
"name":"Shelter system",
"total-grams":"0.0"
}
}
]
}
I need to map the grams value to 'y' and the name to 'name' in modelTestData.
Note that the category data is used in other routes for other purposes exactly as returned by the API. So I don't want to change the model structure itself, or what the API returns...that will break other parts of the app that do use 'category' in its original structure.
This is a specific use case that this route needs to massage the data to pass to the child component as per the structure of modelTestData.
I also wonder whether this data manipulation task belongs in a route?
Should I somehow do this in the serliazer adapter, creating a new structure as say 'categoryWeights' so I can then do:
model: function() {
return this.store.get('categoryWeights');
},
EDIT
I have managed to do this in the route, but it just gives me an array of objects. I need a single object containing 2 properties and an embedded array of objects.
model() {
return this.store.findAll('category')
.then(categories => categories.map(category => {
let data = {
y: category.get('totalGrams'),
name: category.get('name')
};
return data;
}))
},
This should probably go into a computed property:
dataForSubModel: Ember.computed('model.#each.totalGrams', 'model.#each.name', {
get() {
return [{name: 'gear', colorByPoint: true, this.get('model').map(m => ({y:m.get('totalGrams'), name:m.get('name')}))}
}
}),
The serializer is the wrong place, because its not that you need to convert it between the server and your app, but between your app and a strange component.
Actually the best thing would be to refactor the component.
Ok I got this to work in the route.
model() {
return this.store.findAll('category')
.then( function(categories) {
let data = [];
data = categories.map(category => {
return {
y: category.get('totalGrams'),
name: category.get('name')
}
});
return [{name: 'gear', colorByPoint: true, data}];
})
},
I still have the feeling this should be done in the adapter or serializer. Would be happy to see answers that show how to do that.

Sails.js - Waterline Model - Association to itself

I want to create a Tree like storage structure to be used with my app, but can't find enough documentation on how to create a tree model using Waterline attributes.
Case is simple. I do need to have a set of folders, that can have multiple levels of subfolders and in the end, files. What you usually do in mySQL for this kind of data is to add a parent_id field to your model as a foreign key to the model itself.
How can this be done using attributes in sailsjs/waterline model?
I've tried doing something like the following, which generates quite a bit of redundant and orphaned data:
--
attributes: {
name: {
type: 'string'
},
parentFolder: {
model: 'Folder'
},
childFolders: {
model: 'Folder',
via: 'parentItem'
}
}
--
Any ideas?
And by the way, if this is possible, let's say for example using mySQL as a backend. How will it replicate to say, mongoDB?
This seemed to work:
name: {
type: 'string',
maxLength: 255,
required: true
},
parent: {
model: 'folder'
},
childs: {
collection: 'folder',
via: 'parent'
}
I do believe duplicates were being generated by posting data directly via GET in the browser. I'm posting data with a client via POST and it seems to work as expected. (At least from what I see in mySQL)

Yii2 Rest API many-to-many relationship

I have created a controller to handle api requests and return a data provider object, which Yii converts into JSON. (This is done through Yii ActiveController.) Now this model also contains some many-to-many relationships. How can I include the data in the many-to-many relationship in the JSON structure?
Ex.
{ sportsfan: {
id: 1, name: 'Bob', sport: 'Football', teams: [
{ id: 1, name: 'Arsenal'},
{ id:2, name: 'Real Salt Lake' }
]
}
I am going to answer my own question. All I needed to do is the following. I created the following method in my model:
public function fields()
{
return ['id', 'name', 'sport', 'teams'];
}
Note that "id" and "sport" are attributes of the model, "name" is a getter function, "teams" is a relation. Yii2 treats them all the same. This is a great feature!
There is method extraFields() for this purpose (https://github.com/yiisoft/yii2/blob/master/docs/guide/rest-resources.md#overriding-extrafields-)
public function extraFields()
{
return ['teams'];
}