Loopback where filter not working inside include - loopbackjs

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.

Related

Apollo Link State Default Resolver Not Working (#client query parameter variables)

Example here: https://codesandbox.io/s/j4mo8qpmrw
Docs here: https://www.apollographql.com/docs/link/links/state.html#default
TLDR: This is a todo list, the #client query parameters don't filter out the list.
This is the query, taking in $id as a parameter
const GET_TODOS = gql`
query todos($id: Int!) {
todos(id: $id) #client {
id
text
}
}
`;
The query passes the variable in there
<Query query={GET_TODOS} variables={{ id: 1 }}>
/* Code */
</Query>
But the default resolver doesn't use the parameter, you can see it in the codesandbox.io example above.
The docs say it should work, but I can't seem to figure what I'm missing. Thanks in advance!
For simple use cases, you can often rely on the default resolver to fetch the data you need. However, to implement something like filtering the data in the cache or manipulating it (like you do with mutations), you'll need to write your own resolver. To accomplish what you're trying to do, you could do something like this:
export const resolvers = {
Query: {
todos: (obj, args, ctx) => {
const query = gql`
query GetTodos {
todos #client {
id
text
}
}
`
const { todos } = ctx.cache.readQuery({ query })
return todos.filter(todo => todo.id === args.id)
},
},
Mutation: {},
}
EDIT: Every Type we define has a set of fields. When we return a particular Type (or List of Types), each field on that type will utilize the default resolver to try to resolve its own value (assuming that field was requested). The way the default resolver works is simple -- it looks at the parent (or "root") object's value and if it finds a property matching the field name, it returns the value of that property. If the property isn't found (or can't be coerced into whatever Scalar or Type the field is expecting) it returns null.
That means we can, for example, return an object representing a single Todo and we don't have to define a resolver for its id or text fields, as long as the object has id and text properties on it. Looking at it another way, if we wanted to create an arbitrary field on Todo called textWithFoo, we could leave the cache defaults as is, and create a resolver like
(obj, args, ctx) => obj.text + ' and FOO!'
In this case, a default resolver would do us no good because the objects stored in the cache don't have a textWithFoo property, so we write our own resolver.
What's important to keep in mind is that a query like todos is just a field too (in this case, it's a field on the Query Type). It behaves pretty much the same way any other field does (including the default resolver behavior). With apollo-link-state, though, the data structure you define under defaults becomes the parent or "root" value for your queries.
In your sample code, your defaults include a single property (todos). Because that's a property on the root object, we can fetch it with a query called todos and still get back the data even without a resolver. The default resolver for the todos field will look in the root object (in this case your cache), see a property called todos and return that.
On the flip side, a query like todo (singular) doesn't have a matching property in the root (cache). You need to write a resolver for it to have it return data. Similarly, if you want to manipulate the data before returning it in the query (with or without arguments), you need to include a resolver.

Apollo-client's cacheRedirect vs dataIdFromObject

I'm trying to prevent re-fetch of previously cached data. But the documentation provides a couple of ways of achieving this through cacheRedirects and dataIdFromObject. I'm trying to understand when one technique is used over the other.
He's an example flow using dataIdFromObject -- would this provide enough context for Apollo to fetch the detail view data from cache, or do I additionally need a cacheRedirect to link the uuid query?
List view query:
query ListView {
books {
uuid
title
abstract
}
}
Detail view query:
query DetailView {
book(uuid: $uuid) {
uuid
title
abstract
}
}
cache constructor args with dataIdFromObject:
new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
case 'book': return `book:${object.uuid}`;
default: return defaultDataIdFromObject(object); // default handling
}
}
});
I believe you are incorrect when you say
But the documentation provides a couple of ways of achieving this
through cacheRedirects and dataIdFromObject.
I believe only cacheRedirects achieve what you want.
dataIdFromObject allows you to customize how ApolloClient should uniquely identify your objects. By default, ApolloClient assumes your objects have either a id or _id property, and it combines the object __typename with the id property to create a unique identifier.
By providing a dataIdFromObject function, you can customize this unique identifier. For example, if all of you objects have an id which is a uuid, then you could supply a dataIdFromObject function which simply instructs ApolloClient to use the object's id property, without appending __typename.

How to get from CouchDB only certain fields of certain documents with a single request?

create a view that return only a subset of values from a document, each with its key and value within a json string. like if one given view returns a document as this following, Is it possible to get some fields information for a one request? thank you
{
"total_rows":10,
"offset":3,
"rows":[{
"id":"doc1",
"key":"abc123",
"value": {
"_id":"aaaa",
"_rev":"bbb",
"field1":"abc",
"field2":"bcd",
"field3":"cde",
"field4":"123",
"field5":"789",
"field6":"aa#email.com",
"field7":"ttt",
"field8":"iii",
"field9":[{
"field91":"tyui",
"field92":"55555"
}],
"field10"::"0000",
"field11"::"55555",
"field12"::"0030".........
}
}
I just want to create a view that returns some fields only the following:
{
"field1":"abc",
"field2":"bcd",
"field3":"cde",
"field4":"123",
"field5":"789",
"field6":"aa#email.com",
"field7":"ttt",
"field8":"iii",
"field9":[{
"field91":"tyui",
"field92":"55555"
}]
}
A map function that emits a new document with selected fields only. As an example, let's map field1 (a string) and field9 (an array) only:
function map(doc) {
emit(doc._id, {
field1: doc.field1,
field9: doc.field9
});
}
In the above example, each document will be fired with a key being the original doc ID and the value being the mapped fields you require.
This is useful if you are planning to add a reduce function later.
Depending on your use case, you may just want to emit the mapped objects:
function map(doc) {
emit({
field1: doc.field1,
field9: doc.field9
});
}
Please see http://guide.couchdb.org/draft/views.html
The documentation on building data views is pretty good, you can discover a lot by experimenting..

Return join table attributes in incluce with loopback

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.

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