Implementing debouncing batching queue with RxJS - rxjs

I was trying to understand if RxJS would be a good fit for solving the problem that this node module performs: https://github.com/ericdolson/debouncing-batch-queue
It's description says: "A queue which will emit and clear its contents when its size or timeout is reached. Ideal for aggregating data for bulk apis where batching in a timely manner is best. Or anything really where batching data is needed."
If so, could someone walk me through how to implement the simple example in this npm module with RxJS? Ideally with ES5 if possible.

There's an operator for thatâ„¢: bufferwithtimeorcount. If you need it to be truly equivalent, the input stream would be a Subject, with group_by for namespaces, like the following:
var dbbq$ = new Subject();
dbbq$.group_by(function(v_ns) { return v_ns[1]; })
.flatMap(function(S) {
return S.bufferwithtimeorcount(1000, 2)
});
dbbq$.next([ 'ribs 0' ]);
dbbq$.next([ 'more ribs', 'bbq1' ]);
// is analogous to
var dbbq = new DBBQ(1000, 2);
dbbq.add('ribs 0');
dbbq.add('more ribs', 'bbq1');

No way I'm doing this with ES5 :)
const dataWithNamespace = (data, namespace) => ({data, namespace});
const source = [
dataWithNamespace('ribs 0'),
dataWithNamespace('ribs 1'),
dataWithNamespace('ribs 2'),
dataWithNamespace('ribs 3'),
dataWithNamespace('ribs 4'),
dataWithNamespace('more ribs', 'bbq1'),
dataWithNamespace('more ribs', 'bbq1'),
dataWithNamespace('brisket', 'best bbq namespace')
];
const DBBQ = (debounceTimeout, maxBatchSize) =>
source$ => source$
.groupBy(x => x.namespace)
.mergeMap(grouped$ => grouped$
.switchMap(x =>
Rx.Observable.of(x.data)
.concat(Rx.Observable.of(undefined)
.delay(debounceTimeout)
)
)
.bufferCount(maxBatchSize)
.filter(x => x.length == maxBatchSize)
.map(x => x.filter(x => x !== undefined))
);
const source$ = Rx.Observable.from(source);
DBBQ(1000, 2)(source$).subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>

Related

How to run some code in an RxJS chain given there were no errors

I am trying to find a way to run some code only if there was no error in a given rxjs chain. Consider the following, is there something like the artificial NO_ERROR_OCCURED_RUN_HAPPY_PATH_CODE operator in rxjs?
private wrap(obs: Observable<any>): Observable<any> {
return of(1).pipe(
tap(() => this.spinner.startSpinner()),
mergeMap(() =>
obs.pipe(
NO_ERROR_OCCURED_RUN_HAPPY_PATH_CODE(() => this.generic_success_popup()),
catchError(this.handleError),
)
),
finalize(() => this.spinner.stopSpinner())
);
}
Basically almost all operator will be invoke if no error is thrown along the pipe, apart from finalize
obs.pipe(
tap(_=>console.log('no error, will run'),
// throw some error
map(_=>throwError('some error'),
finalize(_=>console.log('will be called when there is error or upon observable complete')),
tap(_=>console.log('this will not run')),
catchError(this.handleError),
)

Delay release from buffer until promises after it are done

Here is my pseudo-code:
const s = new Subject();
s.pipe(
bufferCount(1).pipe(
concatMap(() => new Promise()),
concatMap(() => new Promise()),
concatMap(() => new Promise()),
)
)
s.next('a');
s.next('b');
s.next('c');
I want "b" and "c" held in the buffer UNTIL "a" is done processing.
Is this possible?
I suppose you want a source Observable to trigger some task and then only have the next value from source trigger the next task when the previous task completed. You can achieve this by zipping your source with a second trigger (startNext) that indicates that the previous task is done and the next value from source starting the next task can be emitted.
import { Subject, zip, of, BehaviorSubject } from 'rxjs';
import { map, tap, delay, concatMap } from 'rxjs/operators';
const source = new Subject();
const startNext = new BehaviorSubject(null);
zip(source, startNext)
.pipe(
map(([s, n]) => s), // discard the 'startNext' trigger
concatMap(s => of(s).pipe(delay(1000))),
concatMap(s => of(s).pipe(delay(200))),
concatMap(s => of(s).pipe(delay(3000))),
tap(_ => startNext.next(null))
).subscribe(s => console.log('result for', s));
source.next('a');
source.next('b');
source.next('c');
https://stackblitz.com/edit/rxjs-g5efuc

Elasticsearch NEST 5.x Search Query that is built dynamically

I have the following query that I build piecemeal/dynamically using "&=". Elasticsearch 5.x and Nest 5.x.
QueryContainer qfilter = null;
qfilter = Query<ClassEL>.Term(m => m.OrderID, iOrderID);
qfilter &= Query<ClassEL>
.Range(r => r
.Field(f => f.Distance)
.GreaterThan(100))
&&
.Query<ClassEL>.Term(t => t.Active, true);
var searchDes = new SearchDescriptor<ClassEL>()
.From(0)
.Size(10)
.Query(qfilter); <===== *** ERROR HERE ***
In Visual Studio, it shows the following error message tip:
Error: Cannot convert from 'Nest.QueryContainer' to 'System.Func<Nest.QueryContainerDescriptor<ClassEL>, Nest.QueryContainer>'
The problem is I can't get the searchDescriptor to accept the query I built. Examples online show Search + Query rolled into one which differs from what I'm trying to accomplish. Below is common example that I want to avoid:
var response = client.Search<Tweet>(s => s
.From(0)
.Size(10)
.Query(q =>
q.Term(t => t.User, "kimchy")
|| q.Match(mq => mq.Field(f => f.User).Query("nest"))
)
);
EDIT: Using the Andrey's answer works just fine. A problem arises however when I try to get the results back from the search query:
List<ClassViewEL> listDocuments = response.Documents.ToList();
Visual Studio doesn't highlight it as an error immediately, but during compile time has a problem:
error CS0570:
'Nest.ISearchResponse.Documents' is
not supported by the language
Debugging and choosing to IGNORE the above error works fine, the code executes just as expected no problems. However the compile time error will prevent code deployments. How can this error be fixed?
Solution to EDIT: One of my dependencies in my projects (Newtonsoft.Json.dll) were targeting an older version causing the error to appear. Cleaning the solution and rebuilding fixes it.
You should use Func<SearchDescriptor<ClassEL>, ISearchRequest> or pass descriptor in separate method. For example:
var queryContainer = Query<ClassEL>.Term(x => x.Field(f => f.FirstName).Value("FirstName"));
queryContainer &= Query<ClassEL>.Term(x => x.Field(f => f.LastName).Value("LastName"));
Func<SearchDescriptor<ClassEL>, ISearchRequest> searchFunc = searchDescriptor => searchDescriptor
.Index("indexName")
.Query(q => queryContainer);
var response = _client.Search<ClassEL>(searchFunc);
or like this
ISearchRequest ExecuteQuery(SearchDescriptor<ClassEL> searchDescriptor, QueryContainer queryContainer)
{
return searchDescriptor.Index("indexName")
.Query(q => queryContainer);
}
public void GetResults()
{
var queryContainer = Query<ClassEL>.Term(x => x.Field(f => f.FirstName).Value("FirstName"));
queryContainer &= Query<ClassEL>.Term(x => x.Field(f => f.LastName).Value("LastName"));
var response = _client.Search<ClassEL>(s => ExecuteQuery(s, queryContainer));
}

Entity Framework 7 Include() Order

In MVC6/EF7, should there be a difference in an order I use Include() to include navigation properties into a query?
This query works
var vt = await db.VehicleTypes
.Include(t => t.Photos)
.Include(t => t.VehicleModels)
.ThenInclude(m => m.Units)
.Include(t => t.Rates)
.ThenInclude(r => r.DailyPrice.Currency)
.ToListAsync()
But this query throws an exception at ToListAsync()
var vt = await db.VehicleTypes
.Include(t => t.Photos)
.Include(t => t.Rates)
.ThenInclude(r => r.DailyPrice.Currency)
.Include(t => t.VehicleModels)
.ThenInclude(m => m.Units)
.ToListAsync()
The error is
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
I understand it's Beta, there may be bugs. In this case - is it a bug or a designed behavior?
Looks like a bug; the order shouldn't matter. Would you mind creating an issue?

Filling data in Ext.net FormPanel

I am using Ext.net 2.0 and I am trying to load the first record of the store inside a form panel. I always get no records (getCount() = 0) in the store ? Am I missing something ?
#(Html.X().Store()
.ID("myStore")
.AutoSync(true)
.AutoDataBind(true)
.Proxy(proxy =>
proxy.Add(
Html.X().AjaxProxy().API(api =>
{
api.Create = "/Property/Save/";
api.Read = "/Property/GetById/";
})
.Reader(reader => reader.Add(Html.X().JsonReader().Root("data").IDProperty("P_ID")))
.Writer(writer => writer.Add(Html.X().JsonWriter().AllowSingle(true)))
))
.Listeners(c =>
{
c.DataChanged.Handler ="var store = Ext.getStore('myStore');" +
"alert(store.getCount());";
})
.AutoLoadParams(parameters =>
{
parameters.Add(Html.X().Parameter().Name("id").Value("document.location.href.split('/')[5]").Mode(ParameterMode.Raw));
})
.Model(model => model.Add(
Html.X().Model()
.Fields(fields =>
{
fields.Add(Html.X().ModelField().Name("ID").Type(ModelFieldType.Int));
fields.Add(Html.X().ModelField().Name("DispalyName").Type(ModelFieldType.String));
fields.Add(Html.X().ModelField().Name("Title").Type(ModelFieldType.String));
fields.Add(Html.X().ModelField().Name("ShortDescription").Type(ModelFieldType.String));
})
))
)
For the form panel
#(
Html.X().FormPanel()
.ID("myPanel")
.Layout(LayoutType.Form)
.Width(350)
.FieldDefaults(d => {
d.LabelWidth = 150;
})
.BodyPadding(10)
.Items(item =>
{
item.Add(Html.X().TextField().ID("Id").Name("ID").FieldLabel("Id").Hidden(true));
item.Add(Html.X().TextField().ID("DispalyName").Name("IdDispalyName").FieldLabel("Id Dispaly Name").MaxLength(400));
item.Add(Html.X().TextField().ID("Title").Name("Title").FieldLabel("Title").AllowBlank(false).MaxLength(200));
item.Add(Html.X().TextField().ID("ShortDescription").Name("ShortDescription").FieldLabel("Short Description").MaxLength(200));
}
) )
Thanks in advance.
More appropriate event is Load (it is fired when data is loaded to the store from a remote source)
See the following description
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Store-event-datachanged

Resources