Horizon/RethinkDB api - rethinkdb

So I have one error and two questions about Horizon. (http://horizon.io/docs/)
I have a simple table and 1 record inside, this is the row:
id: "o34242-43251-462...",
user_id: "3lw5-6232s2...",
other_id: "531h51-51351..."
When I run hz serve I'm getting this error:
Unexpected index name (invalid field): "hz_[["user_id"],[["other_id","0"]]]".
Okay, okay, invalid field... But I didn't find any informations about "valid" fields. Anybody knows the answer? What can I do?
My questions:
How to run Horizon "forever" on ubuntu? Now I'm using just hz serve and "&",
If I have few queries, for example:
let table = this.horizon('firstTable');
let table2 = this.horizon('secondTable');
table.find(someId).fetch().subscribe((item) => {
//and then I want to run other query, e.g:
table2.find(item.id).fetch().subscribe((value) => {
//and here again run other query... <-- how to avoid this?
});
});
How to e.g return a value from horizon's query, and then use this value inside other query? I don't want to write it all in one function...
Thanks for any help.

Since 'fetch' returns an RxJS observable, and it yield one result only, you can use 'toPromise' to consume it in a convenient fashion.
let table = this.horizon('firstTable');
let table2 = this.horizon('secondTable');
let item1 = await table.find(someId).fetch().toPromise();
let item2 = await table2.find(item1.id).fetch().toPromise();
Or without ES7 await, just using Promise.then:
table.find(someId).fetch().toPromise().then((item1) => {
table2.find(item1.id).fetch().toPromise().then((item2) => {
// take over the world from here
});
});

Related

Cypress: Get JQuery value without needing `then` or `each`

I'm hoping someone can help, but I've posted this as a Cypress discussion as well, although it might just be my understanding that's wrong
I need to get the Cypress.Chainable<JQuery<HTMLElement>> of the cell of a table using the column header and another cell's value in the same row.
Here's a working example JQuery TS Fiddle: https://jsfiddle.net/6w1r7ha9/
My current implementation looks as follows:
static findCellByRowTextColumnHeaderText(
rowText: string,
columnName: string,
) {
const row = cy.get(`tr:contains(${rowText})`);
const column = cy.get(`th:contains(${columnName})`)
const columnIndex = ???
return row.find(`td:eq(${columnIndex})`)
}
This function is required because I want to write DRY code to find cells easily for content verification, clicking elements inside of it etc.
The only example I've seen is this https://stackoverflow.com/a/70686525/1321908, but the following doesn't work:
const columns = cy.get('th')
let columnIndex = -1
columns.each((el, index) => {
if (el.text().includes(columnName) {
columnIndex = index
}
cy.log('columnIndex', columnIndex) // Outputs 2 as expected
})
cy.log('finalColumnIndex', columnIndex) // Outputs -1
My current thinking is something like:
const columnIndex: number = column.then((el) => el.index())
This however returns a Chainable<number> How to turn it into number, I have no idea. I'm using this answer to guide my thinking in this regard.
Using .then() in a Cypress test is almost mandatory to avoid flaky tests.
To avoid problems with test code getting ahead of web page updating, Cypress uses Chainable to retry the DOM query until success, or time out.
But the Chainable interface isn't a promise, so you can't await it. You can only then() it.
It would be nice if you could substitute another keyword like unchain
const column = unchain cy.get(`th:contains(${columnName})`)
but unfortunately Javascript can't be extended with new keywords. You can only add methods like .then() onto objects like Chainable.
Having said that, there are code patterns that allow extracting a Chainable value and using it like a plain Javascript variable.
But they are limited to specific scenarios, for example assigning to a global in a before() and using it in an it().
If you give up the core feature of Cypress, the automatic retry feature, then it's just jQuery exactly as you have in the fiddle (but using Cypress.$() instead of $()).
But even Mikhail's thenify relys on the structure of the test when you add a small amount of asynchronicity
Example app
<foo>abc</foo>
<script>
setTimeout(() => {
const foo = document.querySelector('foo')
foo.innerText = 'def'
}, 1000)
</script>
Test
let a = cy.get("foo").thenify()
// expect(a.text()).to.eq('def') // fails
// cy.wrap(a.text()).should('eq', 'def') // fails
cy.wrap(a).should('have.text', 'def') // passes
let b = cy.get("foo") // no thenify
b.should('have.text', 'def') // passes
Based on your working example, you will need to get the headers first, map out the text, then find the index of the column (I've chosen 'Col B'). Afterwards you will find the row containing the other cell value, then get all the cells in row and use .eq() with the column index found earlier.
// get headers, map text, filter to Col B index
cy.get("th")
.then(($headers) => Cypress._.map($headers, "innerText"))
.then(cy.log)
.invoke("indexOf", "Col B")
.then((headerIndex) => {
// find row containing Val A
cy.contains("tbody tr", "Val A")
.find("td")
// get cell containing Val B
.eq(headerIndex)
.should("have.text", "Val B");
});
Here is the example.

combineAll does not emit on empty array

JSBIN Sample
I have a changeable set of child components (POJO object) that each have its own state stream. Each time a user triggers addChild/removeChild/clearChildren, a new set of children state streams is emitted with #switchMap. So far so good! (And so amazed by RxJS!)
With Rx.Observable.from(arrayOfStateStreams).combineAll() I get a good result as long as the arrayOfStateStreams isn't an empty array.
Since this is a partial state that is combined(Latest) on a higher level, I need to get an empty array emitted or the global state tree will contain old state data that is no longer true!
I can emit some reserved token like ['EMPTY-ARRAY-PLACEHOLDER-TOKEN'], but that's just weird.
A better way would be to always append one last stream into the array so the last index can be considered trash. Still confusing code and state though.
Using [null] is not OK, since we could have a child state of 'null'.
Anyone who can solve this in a good way? Can't this be supported since there should be no other representation of an empty array after #combineAll?
Credits go to github user trxcllnt who provided the following answer:
combineAll won't emit unless the combined Observables emit at least
one value, but you could check to ensure the collection you're
combining is empty or not, and either combine or emit an empty Array:
var arrayOfStreamsStream = Rx.Observable
.of(
[], [
Rx.Observable.of('blah-1'), // component state.
Rx.Observable.of('blah-2'),
Rx.Observable.of('blah-3')
], [], [
Rx.Observable.of('foo-1'),
Rx.Observable.of('qux-2')
]
)
.switchMap(function onMap(coll) {
return coll.length === 0 ?
Observable.of(coll) :
Observable.combineLatest(...coll);
})
.subscribe(function onSubscribe(data) {
console.log('onSubscribe START')
console.dir(data)
console.log('onSubscribe END')
})
This has nothing to do with combineAll. The problem is that Observable.from results in nothing (an empty observable) when passed an empty array.
The only viable solution that I can think of if you have to get a result from an empty array is to return something else in that case.
Ann example to illustrate the problem and a possible solution.
var data = [1, 2, 3, 4, 5];
log('With data: ');
Rx.Observable.from(data)
.subscribe(function (d) { log('data: ' + d); });
// Prints:
// With data:
// data: 1
// data: 2
// data: 3
// data: 4
// data: 5
var data = [];
log('Without data: ');
var nullDataObject = { msg: 'my null data object' };
Rx.Observable.from(data.length == 0 ? [nullDataObject] : data)
.subscribe(function (d) { log('data: ' + d); });
// Prints:
// With data:
// data: [object Object]
Runnable example on jsfiddle.
When consuming this you simply filter away the object representing an empty array where appropriate.
a possible workaround is to just pipe it with startWith();
combineLatest(potentiallyEmptyArray).pipe(
startWith<any>([])
);
Note: Similar issues exist with combineLatest() (the static version) which can be solved using defaultIfEmpty() - which works, but it screws up the typing of the output.
// array of Observables
const animals: Observable<{ species: 'dog' | 'cat' }>[] = [];
// Type '{ species: "dog" | "cat"; }[]' is not assignable to type 'never[]'.
combineLatest(animals).pipe(defaultIfEmpty([]));
In TypeScript you need to either know the type of the object or use <any>[] which means you then lose typing completely.
If you have a concrete type you can use one of these:
defaultIfEmpty<Animal[]>([])
defaultIfEmpty([] as Animal[])
I often don't have a concrete type for the return value of an observable. So I came up with an operator:
export const emptyArrayIfEmpty = () => <T>(observable: Observable<T[]>) =>
observable.pipe(defaultIfEmpty([] as T[]));
Then I can add the following and get out an empty array if animals === [] without losing any typing information:
combineLatest(animals).pipe(emptyArrayIfEmpty());

dynamic asc desc sort

I am trying to create table headers that sort during a back end call in nhibernate. When clicking the header it sends a string indicating what to sort by (ie "Name", "NameDesc") and sending it to the db call.
The db can get quite large so I also have back end filters and pagination built into reduce the size of the retrieved data and therefore the orderby needs to happen before or at the same time as the filters and skip and take to avoid ordering the smaller data. Here is an example of the QueryOver call:
IList<Event> s =
session.QueryOver<Event>(() => #eventAlias)
.Fetch(#event => #event.FiscalYear).Eager
.JoinQueryOver(() => #eventAlias.FiscalYear, () => fyAlias, JoinType.InnerJoin, Restrictions.On(() => fyAlias.Id).IsIn(_years))
.Where(() => !#eventAlias.IsDeleted);
.OrderBy(() => fyAlias.RefCode).Asc
.ThenBy(() => #eventAlias.Name).Asc
.Skip(numberOfRecordsToSkip)
.Take(numberOfRecordsInPage)
.List();
How can I accomplish this?
One way how to achieve this (one of many, because you can also use some fully-typed filter object etc or some query builder) could be like this draft:
Part one and two:
// I. a reference to our query
var query = session.QueryOver<Event>(() => #eventAlias);
// II. join, filter... whatever needed
query
.Fetch(#event => #event.FiscalYear).Eager
var joinQuery = query
.JoinQueryOver(...)
.Where(() => !#eventAlias.IsDeleted)
...
Part three:
// III. Order BY
// Assume we have a list of strings (passed from a UI client)
// here represented by these two values
var sortBy = new List<string> {"Name", "CodeDesc"};
// first, have a reference for the OrderBuilder
IQueryOverOrderBuilder<Event, Event> order = null;
// iterate the list
foreach (var sortProperty in sortBy)
{
// use Desc or Asc?
var useDesc = sortProperty.EndsWith("Desc");
// Clean the property name
var name = useDesc
? sortProperty.Remove(sortProperty.Length - 4, 4)
: sortProperty;
// Build the ORDER
order = order == null
? query.OrderBy(Projections.Property(name))
: query.ThenBy(Projections.Property(name))
;
// use DESC or ASC
query = useDesc ? order.Desc : order.Asc;
}
Finally the results:
// IV. back to query... call the DB and get the result
IList<Event> s = query
.List<Event>();
This draft is ready to do sorting on top of the root query. You can also extend that to be able to add some order statements to joinQuery (e.g. if the string is "FiscalYear.MonthDesc"). The logic would be similar, but built around the joinQuery (see at the part one)

The type arguments cannot be inferred from the query

my linq query works, I tested in the linqpad, got result back. the error happened at the last step, when I try to use EntityVM ViewModel. I dont really understand what this error message means. can someone please explain that to me, and show me how to fix it. thank you.
Error Message: "the type arguments cannot be inferred from the query"
var entityVersions = EntityVersionRepository.Get().Where(x => x.Entity.ClientId == clientId);
var groups = from ev in entityVersions
group ev by ev.EntityId
into g
select g.OrderByDescending(x => x.TaxYear).FirstOrDefault();
var result = from g in groups
select (en => new EntityVM
{
Name = en.EntityName,
Id = en.EntityId
});
It looks like it could be because you aren't actually evaluating the groups query (by calling .ToList() or something similar). Try evaluating groups first:
var groups = (from ev in entityVersions
group ev by ev.EntityId
into g
select g.OrderByDescending(x => x.TaxYear).FirstOrDefault())
.ToList();
If this is the problem, then it's probably a prime example of why you shouldn't use var when you're returning concrete types from a query. If you'd of changed var groups to List<Group> groups, that code wouldn't of even compiled, so you'd have known straight away that you hadn't actually evaluated the property (it would of returned an IQueryable<Group> instead).

Entity Framework query

I have a piece of code that I don't know how to improve it.
I have two entities: EntityP and EntityC.
EntityP is the parent of EntityC. It is 1 to many relationship.
EntityP has a property depending on a property of all its attached EntityC.
I need to load a list of EntityP with the property set correctly. So I wrote a piece of code to get the EntityP List first.It's called entityP_List. Then as I wrote below, I loop through the entityP_List and for each of them, I query the database with a "any" function which will eventually be translated to "NOT EXIST" sql query. The reason I use this is that I don't want to load all the attached entityC from database to memory, because I only need the aggregation value of their property. But the problem here is, the looping will query the databae many times, for each EntityP!
So I am wondering if anybody can help me improve the code to query the database only once to get all the EntityP.IsAll_C_Complete set, without load EntityC to memory.
foreach(EntityP p in entityP_List)
{
isAnyNotComoplete = entities.entityC.Any(c => c.IsComplete==false && c.parent.ID == p.ID);
p.IsAll_C_Complete = !isAnyNotComoplete;
}
Thank you very much!
In EF 4, you can do:
var ids = entityP_List.Select(p => p.ID);
var q = (from p in entities.entityP
where ids.Contains(p => p.ID)
select new
{
ID = p.ID,
IsAll_C_Complete = !p.entityCs.Any(c => !c.IsComplete)
}).ToList();
foreach (var p in entityP_List)
{
p.IsAll_C_Complete = q.Where(e.ID == p.Id).Single().IsAll_C_Complete;
}
...which will do the whole thing in one DB query. For EF 1, Google BuildContainsExpression for a replacement for the .Contains( part of the above.
I would base EntityP on a SQL View instead of a table. Then I would define the relationship, and aggregate the value for child table within the view.

Resources