What is the r.expr() order of execution? - rethinkdb

given:
r.expr([
r.table('someTable1').get(id1).update({key : 'value1'}),
r.table('someTable2').get(id2).update({key : 'value2'})
])
.run(rethinkConnection, function(err, results) {});
will the items in the array which is the argument to r.expr() evaluate in a predictable order or in an arbitrary order?

r.expr([query1, query2]) sends off the queries in order, however there is no gauranteethey will be processed by the cluster in order. This may lead to race conditions. A method which will execute in order but make all query results visible to the final processing callback is:
var result1 = null;
var result2 = null;
r.table('table1')
.filter(function(row) {
// return a boolean
})
.run(rethinkConnection)
.then(function(localResult) {
result1 = localResult;
return r.table('table2')('someField').run(rethinkConnect);
})
.then(function(localResult) {
result2 = localResult;
// rest of processing here
})
.catch(function(error) {});

Related

MERN Pagination & Sorting

hopefully, you guys will be able to help me out please.
I can't get the pagination to work properly. It always counts the total documents and ignores the filter data. For example, there are 24 total documents, but when filters by a particular item it returns one, but still returns the total amount of pages (which is 3 as I have pageSize set to 9).
Please find my code below:
router.get('/', async (req, res) => {
try {
const pageSize = 9;
const page = Number(req.query.page) || 1;
let query;
const queryObject = { ...req.query };
const excludeFields = ['page', 'sort', 'limit', 'fields'];
excludeFields.forEach((el) => delete queryObject[el]);
let queryString = JSON.stringify(queryObject);
queryString = queryString.replace(
/\b(gte|gt|lte|lt)\b/g,
(match) => `$${match}`
);
query = Vehicle.find(JSON.parse(queryString));
if (req.query.sort) {
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort(sortBy);
} else {
query = query.sort('-createdAt');
}
const count = await Vehicle.countDocuments();
const vehicles = await Vehicle.find(JSON.parse(queryString))
.limit(pageSize)
.skip(pageSize * (page - 1));
if (!vehicles) {
return res.status(200).json({ success: true, data: [] });
}
res
.status(200)
.json({ vehicles, page, totalPages: Math.ceil(count / pageSize) });
} catch (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
});
How would you go about adding functioning sorting into this also please, for some reason sorting doesn't work at all for me?
Thanks very much, G.
To get the count to include the filtered data you need to add the query parameter in the countDocuments:
const count = await Vehicle.countDocuments(JSON.parse(queryString));
To add sorting functionality you can append .sort() onto the end of you find query:
const vehicles = await Vehicle.find(JSON.parse(queryString))
.limit(pageSize)
.skip(pageSize * (page - 1))
.sort(<your sort query>)
https://www.mongodb.com/community/forums/t/sorting-with-mongoose-and-mongodb/122573

Angular5 RXJS recursive http requests

I currently have this situation:
#Service My Service
private users = ['user1','user2'];
//Generate list of requests to join
private getHttpList(): any[] {
let gets = new Array();
for(let index in this.users)
gets.push(this.http.get('https://api.github.com/users/' + this.users[index]))
return gets;
}
...
getList(): Observable<any[]> {
return forkJoin(this.getHttpList())
}
And in my component, I do the subscribe
this.MyService.getList().subscribe(results => {
for(let res in results) {
//...Do something here
//..I wanna do the get in of https://api.github.com/users/{user}/starred
}
})
Suppose that I just know that the "starred url" after the result of getList(), how to I can "synchronous" this part, or what's the correct form to do this?
**I try do it hardcoded --Result id wrong, because the "res" is a "interable"
this.MyService.getList().subscribe(results => {
let url = 'https://api.github.com/users/';
for(let res in results) {//This don't do the things "synchronous"
this.http.get(url + res.login +'/starred').catch(err => {
throw new Error(err.message);
}).subscribe(starred_res => {
//So we set the starred_list
res.starred_list = starred_res
})
}
})
Thanks...
As I understand you want to get starred list for every user.
The simplest way is to get all starred lists and match them with users result.
// Get users
this.MyService.getList().subscribe((results: any[]) => {
const url = 'https://api.github.com/users/';
// Create requests to get starred list for every user
const starredRequests = results.map(
res => this.http.get('https://api.github.com/users/' + res.login + '/starred')
);
// Wait when all starred requests done and map them with results array
Observable.forkJoin(starredRequests).subscribe(starred => {
results.forEach((res, index) => {
res.starred_list = starred[index];
});
console.log(results);
});
});

How to batch additions to arrays/lists returned by rxjs observables?

have an observable that returns arrays/lists of things: Observable
And I have a usecase where is is a pretty costly affair for the downstream consumer of this observable to have more items added to this list. So I'd like to slow down the amount of additions that are made to this list, but not loose any.
Something like an operator that takes this observable and returns another observable with the same signature, but whenever a new list gets pushed on it and it has more items than last time, then only one or a few are added at a time.
So if the last push was a list with 3 items and next push has 3 additional items with 6 items in total, and the batch size is 1, then this one list push gets split into 3 individual pushes of lists with lengths: 4, 5, 6
So additions are batched, and this way the consumer can more easily keep up with new additions to the list. Or the consumer doesn't have to stall for so long each time while processing additional items in the array/list, because the additions are split up and spread over a configurable size of batches.
I made an addAdditionalOnIdle operator that you can apply to any rxjs observable using the pipe operator. It takes a batchSize parameter, so you can configure the batch size. It also takes a dontBatchAfterThreshold, which stops batching of the list after a certain list size, which was useful for my purposes. The result also contains a morePending value, which you can use to show a loading indicator while you know more data is incomming.
The implementation uses the new requestIdleCallback function internally to schedule the batched pushes of additional items when there is idle time in the browser. This function is not available in IE or Safari yet, but I found this excelent polyfill for it, so you can use it today anyways: https://github.com/aFarkas/requestIdleCallback :)
See the implementation and example usage of addAdditionalOnIdle below:
const { NEVER, of, Observable } = rxjs;
const { concat } = rxjs.operators;
/**
* addAdditionalOnIdle
*
* Only works on observables that produce values that are of type Array.
* Adds additional elements on window.requestIdleCallback
*
* #param batchSize The amount of values that are added on each idle callback
* #param dontBatchAfterThreshold Return all items after amount of returned items passes this threshold
*/
function addAdditionalOnIdle(
batchSize = 1,
dontBatchAfterThreshold = 22,
) {
return (source) => {
return Observable.create((observer) => {
let idleCallback;
let currentPushedItems = [];
let lastItemsReceived = [];
let sourceSubscription = source
.subscribe({
complete: () => {
observer.complete();
},
error: (error) => {
observer.error(error);
},
next: (items) => {
lastItemsReceived = items;
if (idleCallback) {
return;
}
if (lastItemsReceived.length > currentPushedItems.length) {
const idleCbFn = () => {
if (currentPushedItems.length > lastItemsReceived.length) {
observer.next({
morePending: false,
value: lastItemsReceived,
});
idleCallback = undefined;
return;
}
const to = currentPushedItems.length + batchSize;
const last = lastItemsReceived.length;
if (currentPushedItems.length < dontBatchAfterThreshold) {
for (let i = 0 ; i < to && i < last ; i++) {
currentPushedItems[i] = lastItemsReceived[i];
}
} else {
currentPushedItems = lastItemsReceived;
}
if (currentPushedItems.length < lastItemsReceived.length) {
idleCallback = window.requestIdleCallback(() => {
idleCbFn();
});
} else {
idleCallback = undefined;
}
observer.next({
morePending: currentPushedItems.length < lastItemsReceived.length,
value: currentPushedItems,
});
};
idleCallback = window.requestIdleCallback(() => {
idleCbFn();
});
} else {
currentPushedItems = lastItemsReceived;
observer.next({
morePending: false,
value: currentPushedItems,
});
}
},
});
return () => {
sourceSubscription.unsubscribe();
sourceSubscription = undefined;
lastItemsReceived = undefined;
currentPushedItems = undefined;
if (idleCallback) {
window.cancelIdleCallback(idleCallback);
idleCallback = undefined;
}
};
});
};
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
let testSource = of(
[1,2,3],
[1,2,3,4,5,6],
).pipe(
concat(NEVER)
);
testSource
.pipe(addAdditionalOnIdle(2))
.subscribe((list) => {
// Simulate a slow synchronous consumer with a busy loop sleep implementation
sleep(1000);
document.body.innerHTML += "<p>" + list.value + "</p>";
});
<script src="https://unpkg.com/rxjs#6.5.3/bundles/rxjs.umd.js"></script>

Chain Observable Queue

Coming from the Promise world, I can implement a queue function that returns a Promise that won't execute until the previous Promise resolves.
var promise = Promise.resolve();
var i = 0;
function promiseQueue() {
return promise = promise.then(() => {
return Promise.resolve(++i);
});
}
promiseQueue().then(result => {
console.log(result); // 1
});
promiseQueue().then(result => {
console.log(result); // 2
});
promiseQueue().then(result => {
console.log(result); // 3
});
// -> 1, 2, 3
I'm trying to recreate this queue-like function using Observables.
var obs = Rx.Observable.of(undefined);
var j = 0;
function obsQueue() {
return obs = obs.flatMap(() => {
return Rx.Observable.of(++j);
});
}
obsQueue().subscribe(result => {
console.log(result); // 1
});
obsQueue().subscribe(result => {
console.log(result); // 3
});
obsQueue().subscribe(result => {
console.log(result); // 6
});
// -> 1, 3, 6
Every time I subscribe, it re-executes the history of the Observable, since at the time of subscription the "current Observable" is actually an Observable which emits multiple values, rather than the Promise that just waits until the last execution has completed.
flatMap isn't the answer for this use case, and nearly all the "chain" and "queue" answers I can find online are about chaining several Observables that are part of one overall Observable, where flatMap is the correct answer.
How can I go about creating the above Promise queue function using Observables?
For context, this queue function is being used in a dialog service, which dictates only one dialog can be shown at a time. If multiple calls are made to show different dialogs, they only appear one at a time in the order that they were called.
If you change:
return obs = obs.flatMap...
With
return obs.flatMap...
You will see the same output as you do with promises (1, 2, 3).
To chain observables such that the next one is not executed until the previous one is complete, use the concat operator
let letters$ = Rx.Observable.from(['a','b','c']);
let numbers$ = Rx.Observable.from([1,2,3]);
let romans$ = Rx.Observable.from(['I','II','III']);
letters$.concat(numbers$).concat(romans$).subscribe(e=>console.log(e));
//or...
Rx.Observable.concat(letters$,numbers$,romans$).subscribe(e=>console.log(e));
// results...
a b c 1 2 3 I II III
Live demo
Figured it out! May not be quite as elegant as the Promise chain, and I'm definitely open to suggestions to clean it up.
var trigger = undefined;
function obsQueue() {
if (!trigger || trigger.isStopped) {
trigger = new Rx.Subject();
return createObservable(trigger);
} else {
var lastTrigger = trigger;
var newTrigger = trigger = new Rx.Subject();
return lastTrigger.last().mergeMap(() => {
return createObservable(newTrigger);
});
}
}
var j = 0;
function createObservable(trigger) {
// In my use case, this creates and shows a dialog and returns an
// observable that emits and completes when an option is selected.
// We want to make sure we only create the next dialog when the previous
// one is closed.
console.log('creating');
return new Rx.Observable.of(++j).finally(() => {
trigger.next();
trigger.complete();
});
}
obsQueue().subscribe(result => {
console.log('first', result);
});
obsQueue().subscribe(result => {
console.log('second', result);
});
obsQueue().subscribe(result => {
console.log('third', result);
});
var timer = setTimeout(() => {
obsQueue().subscribe(result => {
console.log('fourth', result);
});
}, 1000);
// Output:
// creating
// first 1
// creating
// second 2
// creating
// third 3
// creating
// fourth 4
Rather than try to figure out how to chain them in order, I have each observable create its own trigger to let the next observable know when to create itself.
If all the triggers have been completed (setTimeout case, we queue up another one later), then the queue starts again.

How to return from within an observer?

I was trying to return filter function but return doesn't seem to work with callbacks. Here this.store.let(getIsPersonalized$) is an observable emitting boolean values and this.store.let(getPlayerSearchResults$) is an observable emiting objects of video class.
How do I run this synchronously, can I avoid asynchronus callback altogether given that I can't modify the observables received from store.
isPersonalized$ = this.store.let(getIsPersonalized$);
videos$ = this.store.let(getPlayerSearchResults$)
.map((vids) => this.myFilter(vids));
myFilter(vids) {
this.isPersonalized$.subscribe((x){
if(x){
return this.fileterX(vids);//Return from here
}
else {
return this.filterY(vids);//Or Return from here
}
});
}
fileterX(vids) {
return vids.filter((vid) => vids.views>100;);
}
fileterY(vids) {
return vids.filter((vid) => vids.views<20;);
}
I got it working this way, you don't need myFilter(vids) at all if you can get the branching out on isPersonalized$'s subscribe. Here is the updated code.
this.store.let(getIsPersonalized$);
videos$: Observable<any>;
ngOnInit() {
this.isPersonalized$.subscribe((x) => {
if (x) {
this.videos$ = this.store.let(getPlayerSearchResults$)
.map((vids) => this. fileterX(vids));
} else {
this.videos$ = this.store.let(getPlayerSearchResults$)
.map((vids) => this. fileterY(vids));
}
});
}
fileterX(vids) {
return vids.filter((vid) => vids.views>100;);
}
fileterY(vids) {
return vids.filter((vid) => vids.views<20;);
}
It looks like you want to evaluate the latest value of isPersonalized$ within the map function, i'd do that via withLatestFrom (Example: The first one toggles true/false every 5s, the second emits an increasing number every 1s):
const isPersonalized$ = Rx.Observable.interval(5000)
.map(value => value % 2 === 0);
const getPlayerSearchResults$ = Rx.Observable.interval(1000)
.withLatestFrom(isPersonalized$)
.map(bothValues => {
const searchResult = bothValues[0];
const isPersonalized = bothValues[1];
...
});

Resources