loopback model.find based on param from related model - loopbackjs

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

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.

Can't Programatically fetch data with Apollo Client

Most of the information out there about Apollo Client and GraphQL queries is about fetching data and immediately rendering something.
What about the common use case where I want to fetch data to, let say, update the state in which I clearly don't need to render JSX, I just want to run Javascript code.
Use the following code snippet as an example
onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
console.info('variables ready', variables)
return (
<Query query={RESOLVE_REF} variables={variables}>
{ ({ data, error }) => {
console.info('data response', data)
console.info('error response', error)
return data
}}
</Query>
)
}
Apollo forces me to use the Query component just to perform a query, even when I don't want to render anything. Also those console.info never log anything, but the variables ready text does appear.
I have found that the documentation is pretty clear on using the Query component, but obscure on every option which is different. I feel I'm missing something.
I'm also concerned about how Apollo doesn't seems respect the separation of responsibilities, apparently merging both data and presentation into a single responsibility (as is clear with the Query component), which in my current understanding is quite silly, but most likely I'm fucking things up.
Any insight is appreciated.
As long as you've configured and included an ApolloProvider at the top of your component tree, you can get your query instance using either the withApollo HOC, or the ApolloConsumer:
const MyComponent = ({ client }) => {
// use it!
}
withApollo(MyComponent)
<ApolloConsumer>
{client => (
// use it!
)}
</ApolloConsumer>
You can then use any of the methods that are available to the client instance, including query and mutation, both of which return a Promise that resolves to an ApolloQueryResult object that includes data and errors. The full documentation for the client's API can be found here. Your code would then look something like:
async onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
try {
const { data } = await this.props.client(RESOLVE_REF, { variables })
} catch (e) {
// Handle errors
}
}

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

StrongLoop loopback - how to exclude results without related model results

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

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.