Return join table attributes in incluce with loopback - loopbackjs

I've got a data structure fairly similar to the one described on the Loopback HasManyThrough documentation page.
For a given Physician (e.g. id 2), I would like to get all their patients with an appointment AND their appointment date.
I can do a GET operation like this:
GET /physicians/2
with the filter header { "include" : {"relation":"patients"} }
And I do get the physician, and the list of patients, but I lose the appointmentDate of the relation.
Or, I can do a GET operation on the relation table like the documentation shows:
GET /appointments
with the filter header { "include" : {"relation":"patient"}, "where":{"physicianId":2}} }
And I get the the appointments, with the date and the patient embedded, but not the physician details.
I can't seem to be able to combine the two.
Is there a way to get the whole data with one query?
The data would be something like this:
[
"name" : "Dr John",
"appointments" : [ {
"appointmentDate": "2014-06-01",
"patient": {
"name": "Jane Smith",
"id": 1
}
}]
]
One way hack I found is to define the relation twice. Once as a HasManyThrough and once as a HasMany to the appointments table, then I can do something like this:
GET /physicians/2
with the filter header { "include" : {"relation":"appointments","scope":{"include":["patient"]} } }
But that doesn't seem right, or could maybe lead to odd behaviours with the duplicated relation.. but maybe I'm paranoid.

You could include both models
GET /appointments
{ "include": ["patient", "physician"], "where": { "physicianId":2 } }
You will get quite a lot of duplicate data though (details of physician with id 2). I believe, that HasManyThrough relation model was initially not supposed to carry any extra data and therefore, it has some limitations. Here is a related github issue.

Related

Loopback where filter not working inside include

I have this filter written that i am passing to my endpoint written in Loopbackjs.
const filter = {
where: { status: { neq: "processed" } },
include: [
{ relation: "vendor", scope: { fields: ["id", "name"] } },
{
relation: "pickupLocation",
scope: { where: { cityId: "5ee134da0138634c27a6e1dd" } },
},
],
limit: 200,
skip: 0
};
return this.http.get<IPickupRequest[]>(
`${environment.url}/PickupRequests?filter=${encodeURIComponent(
JSON.stringify(filter)
)}`
);
The pickupRequest collection contains a belongsTo relation to pickupLocation collection. The pickupLocation collection contains a property called cityId on which i am trying to apply a where clause but it does not work. Moreover I also want to get the fields of pickupLocation too inside the pickupRequest object so that i cna use them in my table. The simple include works perfectly and shows the data as desired but when where clause is applied it just doesn't work and also shows only pickupRequest object but does not include pickupLocation data inside it. Moreover since i am using pagination I need it to return me exactly 200 records each time. Am i doing something wrong? What is the exact issue here? I am using Loopback 3
Here, you are expecting that where filter applied on the pickupLocation relation would act something similar to SQL's inner join. However, in loopback, whenever you do an include in the query, the framework internally makes two separate queries on collections or tables of each of those models and combine them; effectively making it work like SQL's left outer join. So, in the above query PickupRequest list will have all object, irrespective of where filter on pickupLocation model. The pickupLocation relation would be present only in those PickupRequest records where cityId = "5ee134da0138634c27a6e1dd" and remaining places, the relation would be an null object.
To overcome this problem, I would say, make your primary query on pickupLocation model as per the { cityId: "5ee134da0138634c27a6e1dd" } where filter and do an include of PickupRequest model.

loopbackjs: how to get specifics parents filtering related model

like a common joined relation i would like to filter my results based on filtered children in a parent-child relation.
i did this
{"include":{"relation":"children","scope":{"where":{"and":[{"name":"xxx"}]}}}}
that gave me ALL parents and inside them, children filtered by that name.
any clues? thanks
ref:
https://docs.strongloop.com/display/public/LB/Querying+related+models#Queryingrelatedmodels-Usingfiltersparameterswithincludedrelations
As per https://stackoverflow.com/a/32933383/344022 there's nothing officially available yet. There is a fork of the loopback-connector by #DiogoDoreto that does attempt to provide it. I haven't tried it, but if you were to use it you would do the following in a filter:
"where": {
"children": {
"where": {
"name": "xxx"
}
}
}

Loopback Filter Based On Related Model Properties

I want to be able to filter based on the properties of related models. For example, I have a Class object with a Building object. I want to get a list of classes that take place in a certain building.
This filter
{
"include":"building",
"scope":{
"where":{
"name":"warehouse"
}
}
}
returns all classes, but only includes building if its name is "warehouse". What I want is for the where clause on building name to apply to the whole filter so that I only get the class if it's building has that name.
Is there any way to do what I want?
Thanks!
You can do this in code, see include with filters in the docs.
I'm not sure about the JSON but I think it should look more like this:
"include": {
"relation": "building",
"scope": {
"where": {"name": "warehouse"}
}
}
At the moment this is not possible. The issue has been described in this topic; https://github.com/strongloop/loopback/issues/517
It looks like Strongloop is not going to implement this feature in the near future.
Whenever you want to use it on API CALL , you can follow a model like this one
and adapt it to your context.
//Here (as filter) , we get just the most recent message of a chat
{
"include" : {
"relation" : "messages" ,
"scope" :
{
"order" : "createdAt DESC" ,
"limit":1,
"skip":0
}
}
}
At the moment this is not possible in loopback, but you can use this component for resolve this ploblem. It is an easy to use and works perfectly.
https://www.npmjs.com/package/loopback-component-relation-filter
after you configure the component in the component-config.json file as the component documentation says. You can resolve your problem with.
assuming that the main model is called classes and that it has the relationship called building.
var filter = {
where: {
building:{ name:"warehouse" }
}
}
app.models.classes.find(filter, console.log);
That code only returns the classes when building name is warehouse.

Should I denormalize or run multiple queries in DocumentDb?

I'm learning about data modeling in DocumentDb. Here's where I need some advice
Please see what my documents look like down below.
I can take two approaches here both with pros and cons.
Scenario 1:
If I keep the data denormalized (see my documents below) by keeping project team member information i.e. first, last name, email, etc. in the same document as the project, I can get the information I need in one query BUT when Jane Doe gets married and her last name changes, I'd have to update a lot of documents in the Projects collection. I'd also have to be extremely careful in making sure that all collections with documents that contain employee information get updated as well. If, for example, I update Jane Doe's name in Projects collection but forget to update the TimeSheets collection, I'd be in trouble!
Scenario 2:
If I keep data somewhat normalized and keep only EmployeeId in the project documents, I can then run three queries whenever I want to get a projects list:
Query 1 returns projects list
Query 2 would give me EmployeeId's of all project team members that appear in the first query
Query 3 for employee information i.e. first, last name, email, etc. I'd use the result of Query 2 to run this one
I can then combine all the data in my application.
The problem here is that DocumentDb seems to have a lot of limitations now. I may be reading hundreds of projects with hundreds of employees in project teams. Looks like there's no efficient way to get all employee information whose Id's appear in my second query. Again, please keep in mind that I may need to pull hundreds of employee information here. If the following SQL query is what I'd use for employee data, I may have to run the same query a few times to get all the information I need because I don't think I can have hundreds of OR statements:
SELECT e.Id, e.firstName, e.lastName, e.emailAddress
FROM Employees e
WHERE e.Id = 1111 OR e.Id = 2222
I understand that DocumentDb is still in preview and some of these limitations will be fixed. With that said, how should I approach this problem? How can I efficiently both store/manage and retrieve all project data I need -- including project team information? Is Scenario 1 a better solution or Scenario 2 or is there a better third option?
Here's what my documents look like. First, the project document:
{
id: 789,
projectName: "My first project",
startDate: "9/6/2014",
projectTeam: [
{ id: 1111, firstName: "John", lastName: "Smith", position: "Sr. Engineer" },
{ id: 2222, firstName: "Jane", lastName: "Doe", position: "Project Manager" }
]
}
And here are two employee documents which reside in the Employees collection:
{
id: 1111,
firstName: "John",
lastName: "Smith",
dateOfBirth: "1/1/1967',
emailAddresses: [
{ email: "jsmith#domain1.com", isPrimary: "true" },
{ email: "john.smith#domain2.com", isPrimary: "false" }
]
},
{
id: 2222,
firstName: "Jane",
lastName: "Doe",
dateOfBirth: "3/8/1975',
emailAddresses: [
{ email: "jane#domain1.com", isPrimary: "true" }
]
}
I believe you're on the right track in considering the trade-offs between normalizing or de-normalizing your project and employee data. As you've mentioned:
Scenario 1) If you de-normalize your data model (couple projects and employee data together) - you may find yourself having to update many projects when you update an employee.
Scenario 2) If you normalize your data model (decouple projects and employee data) - you would have to query for projects to retrieve employeeIds and then query for the employees if you wanted to get the list of employees belonging to a project.
I would pick the appropriate trade-off given your application's use case. In general, I prefer de-normalizing when you have a read-heavy application and normalizing when you have a write-heavy application.
Note that you can avoid having to make multiple roundtrips between your application and the database by leveraging DocumentDB's store procedures (queries would be performed on DocumentDB-server-side).
Here's an example store procedure for retrieving employees belonging to a specific projectId:
function(projectId) {
/* the context method can be accessed inside stored procedures and triggers*/
var context = getContext();
/* access all database operations - CRUD, query against documents in the current collection */
var collection = context.getCollection();
/* access HTTP response body and headers from the procedure */
var response = context.getResponse();
/* Callback for processing query on projectId */
var projectHandler = function(documents) {
var i;
for (i = 0; i < documents[0].projectTeam.length; i++) {
// Query for the Employees
queryOnId(documents[0].projectTeam[i].id, employeeHandler);
}
};
/* Callback for processing query on employeeId */
var employeeHandler = function(documents) {
response.setBody(response.getBody() + JSON.stringify(documents[0]));
};
/* Query on a single id and call back */
var queryOnId = function(id, callbackHandler) {
collection.queryDocuments(collection.getSelfLink(),
'SELECT * FROM c WHERE c.id = \"' + id + '\"', {},
function(err, documents) {
if (err) {
throw new Error('Error' + err.message);
}
if (documents.length < 1) {
throw 'Unable to find id';
}
callbackHandler(documents);
}
);
};
// Query on the projectId
queryOnId(projectId, projectHandler);
}
Even though DocumentDB supports limited OR statements during the preview - you can still get relatively good performance by splitting the employeeId-lookups into a bunch of asynchronous server-side queries.

Create / Update multiple objects from one API response

all new jsfiddle: http://jsfiddle.net/vJxvc/2/
Currently, i query an api that will return JSON like this. The API cannot be changed for now, which is why I need to work around that.
[
{"timestamp":1406111961, "values":[1236.181, 1157.695, 698.231]},
{"timestamp":1406111970, "values":[1273.455, 1153.577, 693.591]}
]
(could be a lot more lines, of course)
As you can see, each line has a timestamp and then an array of values. My problem is, that i would actually like to transpose that. Looking at the first line alone:
{"timestamp":1406111961, "values":[1236.181, 1157.695, 698.231]}
It contains a few measurements taken at the same time. This would need to become this in my ember project:
{
"sensor_id": 1, // can be derived from the array index
"timestamp": 1406111961,
"value": 1236.181
},
{
"sensor_id": 2,
"timestamp": 1406111961,
"value": 1157.695
},
{
"sensor_id": 3,
"timestamp": 1406111961,
"value": 698.231
}
And those values would have to be pushed into the respective sensor models.
The transformation itself is trivial, but i have no idea where i would put it in ember and how i could alter many ember models at the same time.
you could make your model an array and override the normalize method on your adapter. The normalize method is where you do the transformation, and since your json is an array, an Ember.Array as a model would work.
I am not a ember pro but looking at the manual I would think of something like this:
a = [
{"timestamp":1406111961, "values":[1236.181, 1157.695, 698.231]},
{"timestamp":1406111970, "values":[1273.455, 1153.577, 693.591]}
];
b = [];
a.forEach(function(item) {
item.values.forEach(function(value, sensor_id) {
b.push({
sensor_id: sensor_id,
timestamp: item.timestamp,
value: value
});
});
});
console.log(b);
Example http://jsfiddle.net/kRUV4/
Update
Just saw your jsfiddle... You can geht the store like this: How to get Ember Data's "store" from anywhere in the application so that I can do store.find()?