StrongLoop loopback - how to exclude results without related model results - loopbackjs

I'm using this Node API JSON, which returns Customers, their instances, and the instance versions.
Customers.find({
"include": {
"relation": "instances",
"scope": {
"include": {
"relation": "versions"
}
}
}
});
I would like to exclude all customers which do not have any related instances, in the result JSON, there is an "instances" entry with empty [ ]. however when I try to use this in a "where" I get a server error... any ideas, or am I going about this the wrong way?

If you're using MongoDB as your database, then you could add a where property to your filter at the same level as your first include, like:
var filter = {
where: {
relationId: {
exists: false
}
},
include: {...}
};
Customers.find(filter, function ( err, results ) {...});
See issue #1009 for details/updates re: database implementation.
Alternatively, you could just filter the results with Lodash:
var customersWithInstances = _.filter( customers, function ( customer )
{
return customer.instanceId;
});

Related

How to resolve custom nested graphql query with Apollo CacheRedirects

We are using apollo-client in a react project. We made a cursor level on top of any list queries. For example:
query MediaList($mediaIds: [ID!], $type: [MediaType!], $userId: ID!) {
user {
id
medias_cursor(all_medias: true, active: true, ids: $mediaIds) {
medias {
id
type
category
name
}
}
}
}
Now for different MediaList query, the Media Objects might already exist in cache but we can not use it to skip network query. For example:
After we query medias_cursor({"all_medias":true,"active":true,"ids":["361","362","363"]}),
we've already got the three Media objects here - (Media:361, Media:362, Media:363).
So when we try to query medias_cursor({"all_medias":true,"active":true,"ids":["361","363"]}, we should have everything we need in the cache already. But right now, the apollo default behavior will just pass the cache and hit the network.
We tried to add a cacheRedirects config to solve this problem like this:
const cache = new InMemoryCache({
cacheRedirects: {
User: {
medias_cursor: (_, { ids }, { getCacheKey }) => {
if (!ids) return undefined
return {
medias: map(ids, id => {
return getCacheKey({ __typename: 'Media', id: id })
})
}
},
},
},
})
We are expecting that the cacheRedirects would help us to use the cache when it's available, but now it will skip the cache anyway.

Define graphQLSchema properly in Node.js

Doing graphQL first time.I searched for resources but could not found a helpful one.
I have written the following schema, got some help from another stackoverflow post.
schema.js
function getDataFromUrl(){
return [
{
"EventCode": "ET00029280",
"EventType": "CT",
"EventTitle": "OYSTERS Beach Park",
"VenueName": "Newexcelsior",
"VenueRegion": "Mumbai"
},
{
"EventCode": "ET00030629",
"EventType": "CT",
"EventTitle": "Stand-Up Comedy: The Trial Room",
"VenueName": "Newexcelsior",
"VenueRegion": "Mumbai"
}
];
}
const eventType = new GraphQLObjectType({
name: 'Event',
fields: {
EventTitle: {
type: GraphQLString,
description: 'Event Title'
},
},
});
const eventListType = new GraphQLObjectType({
name: 'EventList',
fields: {
events: {
type: new GraphQLList(eventType),
description: 'List of items',
},
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
eventList: {
type: new GraphQLList(eventListType),
resolve: () => getDataFromUrl(),
}
}
})
});
module.exports = schema;
When I query
{
eventList {
events {
EventTitle
}
}
}
I get this response:
{
"data": {
"eventList": [
{
"events": null
},
{
"events": null
}
]
}
}
I am expecting some changes in my schema, however my desired response is
{
"data": [
{
"EventTitle": "OYSTERS Beach Park"
},
{
"EventTitle": "Stand-Up Comedy: The Trial Room"
}
]
}
Please also suggest some links where I learn basics.
It looks like what's tripping you up the most right now is how you're defining a list. There's no need to define a separate type called EventList -- when you specify GraphQLList(someOtherType) you are already telling GraphQL to expect an array of that particular type. Your current Schema is expecting an array of an array of types. Because the structure of the data you're passing in doesn't match your schema, GraphQL can't find a field called EventTitle to match against and so it's returning null.
The fix in this case is to just get rid of eventListType altogether and change the type of your eventList field to eventType instead.
The docs for GraphQL.js are probably going to be your best bet as far as learning the basics. The only problem is the examples they include are way too basic. Here is a list of projects on GitHub that you can checkout to see GraphQL in action.
If you are starting out, I would also highly recommend using Apollo's graphql-tools. Even if you don't use Apollo on the client-side, graphql-tools makes it ridiculously easy to set up the server. Your schema would be much more readable, since you would write it as string rather than an object like you do in vanilla GraphQL.js. And you can easily set up a GraphiQL endpoint in addition to your GraphQL one, which makes debugging much easier :)

loopback model.find based on param from related model

I have a problem with falowing situation:
Model 1: Guest - props {"slug":"string"}
Model 2: Project - props {"prefix":"string"}
Relation: Project has many guests
How to write remote method: findGuestWithProject(prefix, slug) that will return guest with slug (exact match but case insensitive) and related project with exact prefix?
Problems I encountered:
Initial filter return Guests with similar but not exact slug f.e. if I pass "anna" .find could return guests with slug "anna-maria", so later on I need to check id slug is exactly the same.
Initial filter return Guests with different project.prefix so I need to do extra loop to find exact match.
I need to count iteration to return callback if not match found.
Guest.getGuestProject = function(prefix, slug, cb) {
if (!prefix) return;
var pattern = new RegExp(slug, "i");
app.models.Project.findOne({
"where": {"prefix": prefix}
},(err, project) => {
if (err) { throw err};
if (!project) cb(null, null);
return project.guests({
"where": {"slug": pattern },
"include": {"relation": "project", "scope": {"include": {"relation": "rsvps"}}}
}, (err, guests) => {
if (guests.length === 0) cb(null, null)
guests.forEach(guest => {
if (guest.slug.toLowerCase() === slug.toLowerCase()) {
cb(null, guest)
}
})
})
})
Regarding 1: Your regexp is checking for anything containing slug
For 2 and 3 I've just rewritten it. You haven't specified what db connector you are using (mongodb, mysql, postgres, etc) so I've written this example based on Postgresql, which is the one I usually use and one of the worst-case-scenarios, given that relational databases don't support filtering by nested properties. If you are using either Mongodb or Cloudant take a look at the example provided in https://loopback.io/doc/en/lb3/Querying-data.html#filtering-nested-properties because this snippet could be simpler.
If this answer is not what you were looking for then I'll probably need more details. I'm also using promises instead of callbacks.
Guest.getGuestProject = function(prefix, slug) {
const Project = Guest.app.models.Project;
// First of all find projects with the given prefix
return Project.find({
where: {
prefix: prefix
},
include: 'guests'
}).then(projects => {
projects.forEach(project => {
let guests = project.guests();
guests.forEach(guest => {
// See if guest.slug matches (case-insensitive)
if (guest.slug.match(new RegExp(slug, 'i'))) {
return guest;
}
});
});
});
};

How can i enable dynamic scripting in AWS ES?

I am using aws elastic service and indexed 650 000 data.
I need to add two new fields to the already indexed documents.
When I tried to call the updateByQuery function got the error, 'scripts of type [inline], operation [update] and lang [groovy] are disabled'.
I have fixed it by adding
script.engine.groovy.inline.aggs: on
script.engine.groovy.inline.update: on on elasticsearch.yml and it works perfectly on local .
How can I add this configuration on aws es ?
I am getting the same error when I am updating documents in aws elastic service.
Here is my code. I want to update all records ( where "device"= deviceVal) by adding new fields Site and Time.
var site = 'some value';
var deviceVal = '123';
var theScript = {
"inline": "ctx._source.Site = '"+ site + "';ctx._source.Time = '"+ new Date().getTime() + "'"
}
var match = {
"match": { "device": deviceVal }
}
client.updateByQuery({
index: 'my_index',
type:'txt',
"body": {
"query": match,
"script":theScript
}
}, function (error, response) {
// console.log("success")
console.log('error--',error)
console.log('response--',response)
});
Building on the other answer where we use logstash to reindex into an AWS ES cluster, you simply need to add one more transformation where # add other transformations here is mentioned.
In your case the input part needs to contain a query for the device:
input {
elasticsearch {
hosts => ["my-elasticsearch-domain.us-west-2.es.amazonaws.com:80"]
index => "my_index"
query => '{"query": {"match":{"device": "123"}}}'
docinfo => true
}
}
And the filter part would boil down to this, i.e. we rename the #timestamp field and add the Site field:
filter {
mutate {
remove_field => [ "#version" ]
rename => { "#timestamp" => "Time" }
add_field => { "Site" => "some value" }
}
}

How to Model.fetch(<object>) when the returned data is a single object

I want to make an API call for searching that looks like this:
https://myapi.com/search/<query>/<token>
where query is the search term and token (optional) is an alphanumeric set of characters which identifies the position of my latest batch of results, which is used for infinite scrolling.
This call returns the following JSON response:
{
"meta": { ... },
"results" {
"token": "125fwegg3t32",
"content": [
{
"id": "125125122778",
"text": "Lorem ipsum...",
...
},
{
"id": "125125122778",
"text": "Dolor sit amet...",
...
},
...
]
}
}
content is an array of (embedded) items that I'm displaying as search results. My models look like this:
App.Content = Em.Model.extend({
id: Em.attr(),
text: Em.attr(),
...
});
App.Results = Em.Model.extend({
token: Em.attr(),
content: Em.hasMany('App.Content', {
key: 'content',
embedded: true
})
});
In order to make that API call, I figured I have to do something like this:
App.Results.reopenClass({
adapter: Em.RESTAdapter.create({
findQuery: function(klass, records, params) {
var self = this,
url = this.buildURL(klass) + '/' + params.query;
if (params.token) {
url += '/' + params.token;
}
return this.ajax(url).then(function(data) {
self.didFindQuery(klass, records, params, data);
return records;
});
}
}),
url: 'https://myapi.com/search',
});
then somewhere in my routes do this:
App.Results.fetch({query: 'query', token: '12kgkj398512j'}).then(function(data) {
// do something
return data;
})
but because the API returns a single object and Em.RESTAdapter.findQuery expects an array, an error occurs when Ember Model tries to materialize the data. So how do I do this properly? I'm using the latest build of Ember Model.
By the way, I'm aware that it would be much more convenient if the API was designed in a way so I can just call App.Content.fetch(<object>), which would return a similar JSON response, but I would then be able to set the collectionKey option to content and my data would be properly materialized.
You simply need to override your models load() method to adjust the payload hash to what Ember.Model wants. There are no serializers in Ember.Model. There is both a class level load for handling collections and an instance level load for loading the JSON specific to a single model. You want to override the instance level load method to wrap the content key value in an array if its not one already.
I have been using Ember.Mode quite heavily and enhanced it for a number of my use cases and submitted PR's for both fixes and enhancements. Those PRs have been sitting there for a while with no response from the maintainers. I have now moved to Ember.Data which has been 'rebooted' so to speak and having a lot better result with it now.
I would strongly suggest walking away from Ember.Model as it appears dead with the new pragmatic direction Ember Data has taken and because the project maintainer doesn't appear to have any interest in it anymore.