Using bookshelf.js, I'm trying to get a set of objects and then update them all.
function markPostsAsSent(postsIds) {
return new Promise((resolve, reject) => {
// Get posts
Search.Post
.where({ id: postsIds })
.fetchAll()
.then(posts => {
// For each post, update sent value
Promise.map(posts.toJSON(), post => post.save({ is_sent: true }))
.then(() => {
resolve(true);
}, err => {
reject(Dependencies.getException(exceptionName, 500, 'POST_UPDATE_ERROR', new Error(), err));
});
}, err => {
reject(Dependencies.getException(exceptionName, 500, 'POST_FETCH_ERROR', new Error(), err));
});
});
}
What I'm trying to achieve in Bookshelf.js is equivalent to this in SQL :
UPDATE searches_posts
SET is_sent = true
WHERE id IN (1, 2, 3, 4);
(1, 2, 3, 4) are obviously values from postsIds parameter.
The SQL seems so much more simpler than the Bookshelf.js method.
Is there a better way to update these rows all at the same time instead of looping on .save() method ?
Your solution seems solid, but of course, there is a better way.
E.g., you can specify explicitly that you want to perform an update (http://bookshelfjs.org/#Model-instance-save) and you can tap into the knex query builder and limit which entries you want to update (http://knexjs.org/#Builder-whereIn).
So, putting everything together, try something like this (this is some demo example I performed in order to test the solution):
new Person().query(function(qb) {
qb.whereIn('id', [2, 4]);
}).save({
number_of_children: 5
}, {
method: 'update'
}).then(function() {
//Entries with ids 2 and 4 have been updated!
});
Hope this helps!
What about update/insert? What is more eloquent way to do this?
yield Promise.map data, (oneEvent)->
eventModel = new Event oneEvent
eventModel.fetch().then (fromDB) ->
return do eventModel.save if not fromDB?
fromDB.save oneEvent
Related
I have
const {data, error, isLoading } = useFetchBulkClassesQuery(something)
to pass data to MySql via this API:
fetchBulkCclasses: builder.query ({
query: (something) => {
return {
url: '/bulkclasses',
params: {
class: something
},
method: 'GET'
at the backend, I have
tyapp.get("/bulkclasses", (req, res) => {
const q = 'select * from recent where ticker in (?)'
db.query(q, [[req.query.ticker]], (err, data) => {
if(err) {return res.json(err)}
return res.json(data)pe here
it al works fine if I use a single variable, like something = 'ClassA'
But I want to use it for multiple entries like ClassA, ClassB, ClassC, ...
But it only takes the first one (or the last one depending on what I try).
What am I doing wrong? Or what haven't I tried (or what do I not know)?
I tries:
Something = ['ClassA', 'ClassB', ...] -> the thing that get passed to the backend is 'ClassA, ClassB' en and it needs to be 'ClassA', 'ClassB', ...
Something = [[ClassA], [ClassB],...] -> same result
It seemed a bit impossible to do, so I did choose the easiest solution: changing the design of my tables and update the query.
I am a react-native developer and new to firebase. I am performing firebase realtime database operation, have a look at code below;
firebase.database().ref('events/wedding/items').push(object).then((data) => {
//success callback
dispatch(addPendingInvoice({ ...invoice, id: data.key }))
Alert.alert('Successfully added to Invoices', 'Please go to invoice section to clear first and continue.', [{ text: 'Ok' }])
}).catch((error) => {
//error callback
Alert.alert("Can't book package.", 'Please check your internet connection!', [{ text: 'OK', style: 'destructive' }])
})
Now, I wish to push another object to another node events/wedding/packages right after this firebase database function above. I can use another function inside then callback in above firebase functions. This is not a professional way to do this.
Is there any way to do this?
You can use the update() method to "simultaneously write to specific children of a node without overwriting other child nodes". Note that "simultaneous updates made this way are atomic: either all updates succeed or all updates fails", see the doc.
So in your case you would do along the following lines:
var newNodeKey = firebase.database().ref().child('events/wedding/items').push().key;
var updates = {};
updates['events/wedding/items/' + newNodeKey] = { foo: "bar" };
updates['events/wedding/packages/' + newNodeKey] = { bar: "foo" };
firebase.database().ref().update(updates)
.then(() => {
// The two writes are completed, do whatever you need
// e.g. dispatch(...);
});
All Firebase operations return a promise so you can use Promise.all() to run them all simultaneously.
Promise.all([
firebase.database().ref(reference).set({}),
firebase.database().ref(reference2).set({})
]).then(() => {
console.log("Operations Successful")
}).catch((e) => console.log(e))
You can also push all your operations to an array and then pass that array in Promise.all()
I have a messages entry that looks like this:
firbaseapp
messages
-KiG85eYMH7jfKph4bl3
created: 1492788734743
title: "title"
message: "message"
I want to send a notification when new entries are added to this list so
I added this cloud function:
exports.sendMessageNotification = functions.database.ref('/messages/').onWrite(event => {
event.data.forEach(message => {
if (message.val().notificationSent) {
return;
}
admin.messaging().sendToTopic(...)
.then(res => {
return db.ref('/messages').child(message.key).update({
notificationSent: (new Date()).getTime(),
});
})
});
});
Problem is message.key is messages-KiG85eYMH7jfKph4bl3 so when I try to save it, it creates a new entry instead of updating the existing one:
firbaseapp
messages
-KiG85eYMH7jfKph4bl3
created: 1492788734743
title: "title"
message: "message"
-messages-KiG85eYMH7jfKph4bl3
notificationSent: 123434554534
What I wanted is for notificationSent to be set on the existing entry.
I also tried using message.ref but I get the same result.
So what is the best way to update a list item in firebase in a cloud function?
I think this accomplishes what you want to do and also answers your questions in the comments:
exports.sendMessageNotification = functions.database.ref('/messages/{messageId}')
.onWrite(event => {
const messageId = event.params.messageId;
console.log('messageId=', messageId);
if (event.data.current.child('notificationSent').val()) {
console.log('already sent');
return;
}
const ref = event.data.ref; // OR event.data.adminRef
admin.messaging().sendToTopic(...)
.then(res => {
return ref.update({
// Caution: this update will cause onWrite() to fire again
notificationSent: (new Date()).getTime(),
});
})
});
I am struggling to convert Node streams to Rxjs Observables.
The streaming by itself works great when I try 1 URL.But, when I try to map the same function over an array of URLS, I get errors.
I am using Rx.Node to convert the stream into an Observable.
This is what I'm currently trying
// data_array is an array of 10 urls that I'm scraping data from.
let parentStream = Rx.Observable.from(data_array);
parentStream.map(createStream).subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'));
function createStream(url){
return RxNode.fromStream(x(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]).write().pipe(JSONStream.parse('*')))
}
But this is the output X 10(the number of URLS in data_array)
RefCountObservable {
source:
ConnectableObservable {
source: AnonymousObservable { source: undefined, __subscribe: [Function] },
_connection: null,
_source: AnonymousObservable { source: [Object], __subscribe: [Function: subscribe] },
_subject:
Subject {
isDisposed: false,
isStopped: false,
observers: [],
hasError: false } },
_count: 0,
_connectableSubscription: null }
I first thought flatMap would work because it's flattening observables in an observable....but when I try flatMap, I get this:
Complete
Error TypeError: unknown type returned
However, if I do this:
This works for 1 URL, but I can't capture all of the urls in the data_array in one stream.
let stream = RxNode.fromStream(x(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]).write().pipe(JSONStream.parse('*')))
stream.subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'))
I feel like I'm misunderstanding something not only because it clearing isn't working for multiple URLS, but even when it does work in the second example....I get 'Complete' first before all data comes in.
Clearly, I'm misunderstanding something. Any help would be wonderful. Thanks.
*UPDATE*
I tried a different path, which works, but does not use Node Stream. Node streams would be ideal, so still would like to make the above example work.
The approach I used next was to wrap a promise around my web scraping function, which is scrape below. This works, but the result is ten huge arrays with all the data from each URL in each array. What I really want is a stream of objects that I can compose a series of transformations as the data objects pass through.
Here is different, but working approach:
let parentStream = Rx.Observable.from(data_array);
parentStream.map(url => {
return Rx.Observable.defer(() => {
return scrape(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]);
})
})
.concatAll()
.subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'));
function scrape(url, selector, scope) {
return new Promise(
(resolve, reject) => x(
url,
selector,
scope
)((error, result) => error != null ? reject(error) : resolve(result))
);
}
*Solution*
I figured it out. I have attached the solution below:
Instead on using RxNode, I opted to use Rx.Observable.fromEvent().
Node streams emit events, whether it be an new data, error or on complete.
So the fromEvent static operator is listening for the 'data' event and creates a new Observable for each event.
I then mergeAll those, and subscribe. Here's the code:
let parentStream = Rx.Observable.from(data_array);
parentStream.map((url)=> { return createEventStream(url); } ).mergeAll().subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'));
function createEventStream(url){
return Rx.Observable.fromEvent(x(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]).write().pipe(JSONStream.parse('*')), 'data');
}
I'm guessing this should be somewhat easy to achieve but I've having trouble (conceptually, I guess) figuring out how to tackle it.
What I have is an API that returns an array of JSON objects. I need to step through these objects, and, for each object, make another AJAX call. The issue is the system that handles each AJAX call can only handle two active calls at a time (as it's quite a CPU-intensive task that hooks out into a desktop application).
I was wondering how I could achieve this using RxJS (either using version 5 or 4)?
EDIT: In addition, is it possible to have a chain of steps running concurrently. i.e.
Downloading File: 1
Processing File: 1
Converting File: 1
Uploading File: 1
Downloading File: 2
Processing File: 2
Converting File: 2
Uploading File: 2
Downloading File: 3
Processing File: 3
Converting File: 3
Uploading File: 3
I've tried doing something like:
Rx.Observable.fromPromise(start())
.concatMap(arr => Rx.Observable.from(arr))
.concatMap(x => downloadFile(x))
.concatMap((entry) => processFile(entry))
.concatMap((entry) => convertFile(entry))
.concatMap((entry) => UploadFile(entry))
.subscribe(
data => console.log('data', new Date().getTime(), data),
error => logger.warn('err', error),
complete => logger.info('complete')
);
However that doesn't seem to work. The downloadFile, for example doesn't wait for processFile, convertFile and uploadFile to all complete, rather, the next one will run again as soon as the previous one completes.
Here are 2 approaches, if you want the sequence of requests exactly like this
Downloading File: 1
Processing File: 1
Converting File: 1
Uploading File: 1
Downloading File: 2
Processing File: 2
...
You need to resolve all promises inside single concatMap method, like this
Rx.Observable.fromPromise(getJSONOfAjaxRequests())
.flatMap(function(x) { return x;})
.concatMap(function(item) {
return downloadFile(item)
.then(processFile)
.then(convertFile);
})
.subscribe(function(data) {
console.log(data);
});
see the working plunkr here: https://plnkr.co/edit/iugdlC2PpW3NeNF2yLzS?p=preview
This way, the new ajax call will be sent only when the previous is finished.
Another approach is that allow the files to send requests in parallel but the operations 'downloading,processing,converting,uploading' will be in sequence. For this you can get it working by
Rx.Observable.fromPromise(getJSONOfAjaxRequests())
.flatMap(function(x) { return x;})
.merge(2) // in case maximum concurrency required is 2
.concatMap(function(item) {
return downloadFile(item);
})
.concatMap(function(item) {
return processFile(item);
})
.concatMap(function(item) {
return convertFile(item)
})
.subscribe(function(data) {
//console.log(data);
});
see plunkr here: https://plnkr.co/edit/mkDj6Q7lt72jZKQk8r0p?p=preview
You could use merge operator with the maxConcurrency overload (Rxjs v4), so something like :
Rx.Observable.fromArray(aJSONs)
.map (function (JSONObject) {
return ajaxRequest(JSONObject) // that would be an observable (or a promise)
})
.merge(2)
You can have a look to see other examples of use at :
Limit number of requests at a time with RxJS,
or How to limit the concurrency of flatMap?
Official documentation :
merge(maxConcurrency)
How about something like this? You could use from to break the array into bite sized chunks and process them one by one using concatMap.
function getArr() {
return Rx.Observable.of([1, 2, 3, 4, 5, 6, 7, 8]);
}
function processElement(element) {
return Rx.Observable.of(element)
.delay(500);
}
getArr()
.concatMap(arr => {
return Rx.Observable.from(arr);
})
.concatMap(element => {
return processElement(element);
})
.subscribe(res => {
console.log(res);
});
Old post but I believe this could work, for console log we could use tap. Note editor would through intellisense error since from expects an array, but the code should work.
from(start()).pipe(
switchMap(files => from(files).pipe(
switchMap(file => from(downloadFile(file)).pipe(
map(_ => ({file: file, downloaded: true}))
)),
switchMap(attr => from(processFile(attr.file)).pipe(
map(_ => ({...attr, downloaded: true}))
)),
switchMap(attr => from(convertFile(attr.file)).pipe(
map(_ => ({...attr, converted: true}))
)),
switchMap(attr => from(uploadFile(attr.file)).pipe(
map(_ => ({...attr, uploaded: true}))
))
))
).subscribe(_ => {})