Angularfire: database list query with multiple Subject parameters

Created on 8 Oct 2016  路  12Comments  路  Source: angular/angularfire

Here's a plunker of what I'm trying to do, and it has the issues I'm seeing with needing to perform a search twice before getting results back
http://plnkr.co/edit/HaWIwkhPWz08qJT6mYuJ?p=preview

Basically I have composite keys so I can search multiple fields with a single query, and based on the search terms supplied I need to change which key I search on. So I'm setting query.orderByChild and query.equalTo to two different Subject variables and calling next() on them both during a search.

I dont know if there's a different way to accomplish this, but what I'm doing doesn't seem to work.

I also tried setting the whole query object to a Subject variable and calling next on it (example next({orderByChild: 'state', equalTo: 0}), but that didnt work either.

discuss feature

Most helpful comment

I have a similar problem caused with the same root cause. I have stored list of calendar events in the Firebase and using Fullcalendar to display them. Due to thousands of records from several years I can not have to loaded all events. I read only events for the period what the fullcalendar is currently showing. So I have to query the database with startAt and endAt parameters. Next period is loaded, for example, when the user click on the _Next month_ button in the calendar. I've started to code like:

export class EventsCalendarComponent {
  private start$: Subject<string> = new Subject<string>();
  private end$: Subject<string> = new Subject<string>();

  constructor(private db: AngularFireDatabase) {
    db.list('/events', {
      query: {
        orderByChild: 'date',
        startAt: this.start$,
        endAt: this.end$
      }
    }).subscribe(events => {
      // update fullcalendar component with events
    });
  }

  filterByPeriod(start: Moment, end: Moment) {
    this.start$.next(start.getTime());
    this.end$.next(end.getTime());
  }
}

I think this is next very common use case. So the support of multiple Subject parameters in query would be a very useful feature. Or what about enabling to have all the query object observable, not only its values?... Usage could be like:

export class EventsCalendarComponent {
  private query$: Subject<Object> = new Subject<Object>();

  constructor(private db: AngularFireDatabase) {
    db.list('/events', {
      query: this.query$;
    }).subscribe(events => {
      // update fullcalendar component with events
    });
  }

  filterByPeriod(start: Moment, end: Moment) {
    this.query$.next({
      orderByChild: 'date',
      startAt: start.getTime(),
      endAt: end.getTime()
    });
  }
}

All 12 comments

@nelsonad Looking at your plnkr code, it seems you're trying to do something similar as given in the example app in the guide here.

The below example works for me
`
import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2';
import { Subject } from 'rxjs/Subject';

@Component({
selector: 'app-root',
template: <ul> <li *ngFor="let item of items | async"> {{ item.text }} </li> </ul> <div> <h4>Filter by size</h4> <button (click)="filterBy('size','small')">Small</button> <button (click)="filterBy('size','medium')">Medium</button> <button (click)="filterBy('size','large')">Large</button> </div> ,
})
export class AppComponent {
items: FirebaseListObservable sizeSubject: Subject;
sizeSubjectName: Subject;

constructor(af: AngularFire) {
this.sizeSubject = new Subject();
this.sizeSubjectName = new Subject();
this.items = af.database.list('/items', {
query: {
orderByChild: this.sizeSubjectName,
equalTo: this.sizeSubject,
}
});
}
filterBy(sizeName: string, size: string) {
this.sizeSubject.next(size);
this.sizeSubjectName.next(sizeName);
}
}
`

If this doesn't helps you should also look at this stackoverflow article
and library Querybase from @davideast that allows you to order by multiple values.

Hope that helps.

@mukesh51 Thanks for your reply. My problem is similar to the example, but different because the example doesnt change the orderByValue, the search is always on the size property.

I've updated my plunker example to use the tshirt example and added a color property that is also searched and it still has the same issues.

I actually got the idea to use composite keys from the querybase project, but I'm not using it and I'm not sure it would solve the issue here.

Here's the new plunkr: http://plnkr.co/edit/s6zhTRdIYBuOuxACPIHJ?p=preview

I think the best option would be to add support on the query object so that it can be observable like it's child keys, this way multiple parameters can be changed during a single event.

But I'm not great with what the rxjs library offers, so I'm not sure if there's already a way to support what I'm trying to do.

Any feedback/workarounds would be appreciated

It's not possible to order by multiple criteria. This is a limitation of the database and outside the scope of AngularFire2 to tackle. QueryBase is indeed the most useful resource for this sort of problem.

@katowulf I think you misunderstood me.

It is possible to filter by multiple criteria using composite keys, which querybase is doing, and I'm doing manually.

What's not possible is to change two parameters of the query object with observables.

https://github.com/angular/angularfire2/blob/master/docs/4-querying-lists.md#creating-a-query-with-observable-values

You can change orderByChild with an observable and equalTo with an observable independently. But you cant change both with an observable without issue.

That makes sense; thanks for clarifying. So the problem, as you've described it, is that you must use two observables and that's too annoying? Or it doesn't work with two?

@katowulf it doesn't work with two.

Looks like my plunker above stopped working, I'll try to get it updated so you can see the problem.

Update:
The plunker template that's linked to in the ReadMe doesnt seem to be working either:
http://plnkr.co/edit/4IbB5IvfkBYcj2iVAIM1?p=preview

But the code in mine should be easy enough to reproduce and see the problem
http://plnkr.co/edit/s6zhTRdIYBuOuxACPIHJ?p=preview

Update 2:
I'm not sure what's going on with the plunkers. I checked back again and mine is working at the moment but the readme one still does not.

If you check mine out, you'll see that you have to click the first search button twice to get any results. And it takes two clicks for the other searches to work also.

If the top level query object could support being observable I believe the problem would go away.

Should this still be closed?

Just chiming in to say I've noticed the same behaviour.

When using 2 (or 3) subjects to update a list query, it requires you to push the values twice to the subjects to perform the query.

    return this.angularFire.database.list(`/${CLIENT_FEED_PATH}/${clientId}`, {
      query: {
        orderByChild: this.orderByField$,
        equalTo: this.fieldEqualTo$,
        limitToFirst: this.itemLimit$
      }
    })

Whatever function I use to push new values to these subjects using .next() needs to be fired twice for the values emitted by the .list() to update to the correctly-filtered ones.

I have a similar problem caused with the same root cause. I have stored list of calendar events in the Firebase and using Fullcalendar to display them. Due to thousands of records from several years I can not have to loaded all events. I read only events for the period what the fullcalendar is currently showing. So I have to query the database with startAt and endAt parameters. Next period is loaded, for example, when the user click on the _Next month_ button in the calendar. I've started to code like:

export class EventsCalendarComponent {
  private start$: Subject<string> = new Subject<string>();
  private end$: Subject<string> = new Subject<string>();

  constructor(private db: AngularFireDatabase) {
    db.list('/events', {
      query: {
        orderByChild: 'date',
        startAt: this.start$,
        endAt: this.end$
      }
    }).subscribe(events => {
      // update fullcalendar component with events
    });
  }

  filterByPeriod(start: Moment, end: Moment) {
    this.start$.next(start.getTime());
    this.end$.next(end.getTime());
  }
}

I think this is next very common use case. So the support of multiple Subject parameters in query would be a very useful feature. Or what about enabling to have all the query object observable, not only its values?... Usage could be like:

export class EventsCalendarComponent {
  private query$: Subject<Object> = new Subject<Object>();

  constructor(private db: AngularFireDatabase) {
    db.list('/events', {
      query: this.query$;
    }).subscribe(events => {
      // update fullcalendar component with events
    });
  }

  filterByPeriod(start: Moment, end: Moment) {
    this.query$.next({
      orderByChild: 'date',
      startAt: start.getTime(),
      endAt: end.getTime()
    });
  }
}

@tomasklima You might be better to submit a new issue as they closed this one and don't seem interested in discussing it. I've moved away from firebase because of this.

Did anything come of this?

Was this page helpful?
0 / 5 - 0 ratings