Firebase의 여러 where 절을 기반으로 한 쿼리


244
{
"movies": {
    "movie1": {
        "genre": "comedy",
        "name": "As good as it gets",
        "lead": "Jack Nicholson"
    },
    "movie2": {
        "genre": "Horror",
        "name": "The Shining",
        "lead": "Jack Nicholson"
    },
    "movie3": {
        "genre": "comedy",
        "name": "The Mask",
        "lead": "Jim Carrey"
    }
  }  
 }

저는 Firebase 초보자입니다. 어떻게 위의 데이터에서 결과를 검색 할 수있는 genre = 'comedy'과를lead = 'Jack Nicholson' ?

어떤 옵션이 있습니까?

답변:


238

사용 중포 기지의 쿼리 API를 , 당신이 시도 유혹 될 수 있습니다

// !!! THIS WILL NOT WORK !!!
ref
  .orderBy('genre')
  .startAt('comedy').endAt('comedy')
  .orderBy('lead')                  // !!! THIS LINE WILL RAISE AN ERROR !!!
  .startAt('Jack Nicholson').endAt('Jack Nicholson')
  .on('value', function(snapshot) { 
      console.log(snapshot.val()); 
  });

그러나 Firebase의 @RobDiMarco는 주석에서 다음과 같이 말합니다.

여러 orderBy()통화에서 오류가 발생합니다

따라서 위의 코드는 작동하지 않습니다 .

나는 효과가있는 세 가지 접근법을 알고 있습니다.

1. 서버에서 대부분을 필터링하고 클라이언트에서 나머지를 수행하십시오.

당신이 할 수있는 일은 orderBy().startAt()./endAt()서버에서 하나 를 실행 하고 나머지 데이터를 끌어 내고 클라이언트의 JavaScript 코드로 필터링합니다.

ref
  .orderBy('genre')
  .equalTo('comedy')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      if (movie.lead == 'Jack Nicholson') {
          console.log(movie);
      }
  });

2. 필터링 할 값을 결합한 속성을 추가하십시오.

충분하지 않으면 사용 사례를 허용하도록 데이터 수정 / 확장을 고려해야합니다. 예를 들어, 장르 + 리드를이 필터에 사용하는 단일 속성에 넣을 수 있습니다.

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_lead": "comedy_Jack Nicholson"
},...

본질적으로 그런 식으로 자신의 다중 열 인덱스를 작성하고 다음을 사용하여 쿼리 할 수 ​​있습니다.

ref
  .orderBy('genre_lead')
  .equalTo('comedy_Jack Nicholson')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      console.log(movie);
  });

David East는 이러한 속성을 생성하는 데 도움이되는 QueryBase라는 라이브러리 를 작성했습니다 .

상대 / 범위 쿼리를 수행 할 수도 있습니다. 카테고리 및 연도별로 영화 쿼리를 허용한다고 가정 해 봅시다. 이 데이터 구조를 사용합니다.

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_year": "comedy_1997"
},...

그런 다음 90 년대 코미디를 요청하십시오.

ref
  .orderBy('genre_year')
  .startAt('comedy_1990')
  .endAt('comedy_2000')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      console.log(movie);
  });

연도 이상을 필터링해야하는 경우 다른 날짜 부분을 내림차순으로 추가하십시오 (예 :) "comedy_1997-12-25". 이런 식으로 Firebase가 문자열 값에서 수행하는 사전 순서는 시간 순서와 동일합니다.

이 속성 값 조합은 둘 이상의 값으로 작동 할 수 있지만 복합 속성의 마지막 값에 대해서만 범위 필터를 수행 할 수 있습니다.

매우 특별한 변형은 Firebase 용 GeoFire 라이브러리에 의해 구현됩니다 . 이 라이브러리는 위치의 위도와 경도를 소위 Geohash로 결합한 다음 Firebase에서 실시간 범위 쿼리를 수행하는 데 사용할 수 있습니다.

3. 프로그래밍 방식으로 맞춤 색인 만들기

또 다른 대안은이 새로운 Query API가 추가되기 전에 우리가 한 모든 작업을 수행하는 것입니다. 다른 노드에서 색인을 작성하십시오.

  "movies"
      // the same structure you have today
  "by_genre"
      "comedy"
          "by_lead"
              "Jack Nicholson"
                  "movie1"
              "Jim Carrey"
                  "movie3"
      "Horror"
          "by_lead"
              "Jack Nicholson"
                  "movie2"

아마도 더 많은 접근법이있을 것입니다. 예를 들어이 답변은 대체 트리 모양의 사용자 지정 인덱스 ( https://stackoverflow.com/a/34105063)를 강조합니다.


3
다음 주에 공식적으로 출시 orderBy()되면 클라이언트가 현재 예상치 못한 결과를 제공하기 때문에 여러 통화에서 오류가 발생합니다. 테스트에서 우연히 작동했을 수도 있지만 일반적 으로이 작업을 수행하도록 빌드되지 않았습니다 (추가하고 싶습니다!).
Rob DiMarco

18
2016 년에도 Firebase V3와 관련이 있습니까? 더 좋은 방법이 없습니까?
Pier

27
왜 우리 .equalTo('comedy')대신에 사용할 수 .startAt('comedy').endAt('comedy')없습니까?
JCarlosR

41
2018 년입니다. 이에 대한 간단한 해결책이 없습니까? 이것은 관계형 데이터베이스의 쿼리 101과 같습니다. 그리고 David #의 SQL # 8에 관한 비디오에 대한 모든 의견을 감안할 때 Google은 지금까지이를 수정하기를 기대했을 것입니다. 업데이트가 누락 되었습니까?

10
@Snake 2019 년 2 월 문제가 여전히 해결되지 않았습니다 .... Google이 너무 느립니다.
Supertecnoboff 2019

48

서버에서 모든 주문을 수행하면서 여러 값으로 주문할 수있는 개인 라이브러리를 작성했습니다.

Querybase를 만나십시오!

Querybase는 Firebase 데이터베이스 참조 및 색인을 생성 할 필드 배열을받습니다. 새 레코드를 만들면 여러 쿼리를 허용하는 키 생성이 자동으로 처리됩니다. 주의 사항은 직선 동등성을 지원한다는 것입니다.

const databaseRef = firebase.database().ref().child('people');
const querybaseRef = querybase.ref(databaseRef, ['name', 'age', 'location']);

// Automatically handles composite keys
querybaseRef.push({ 
  name: 'David',
  age: 27,
  location: 'SF'
});

// Find records by multiple fields
// returns a Firebase Database ref
const queriedDbRef = querybaseRef
 .where({
   name: 'David',
   age: 27
 });

// Listen for realtime updates
queriedDbRef.on('value', snap => console.log(snap));

라이브러리에 사용 가능한 TypeScript 정의가 있습니까?
Levi Fuller

1
TS로 작성되었습니다! 수입 :)
David East

7
그들이 왜 데이터베이스를 그렇게 제한적으로 만들 었는지 알고 있습니까?
Ced

1
모든 옵션은 IOS 스위프트 / 안드로이드 버전 수행하는
mding5692

1
데이비드, 안드로이드 / 자바와 동등한가?

3
var ref = new Firebase('https://your.firebaseio.com/');

Query query = ref.orderByChild('genre').equalTo('comedy');
query.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot movieSnapshot : dataSnapshot.getChildren()) {
            Movie movie = dataSnapshot.getValue(Movie.class);
            if (movie.getLead().equals('Jack Nicholson')) {
                console.log(movieSnapshot.getKey());
            }
        }
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {

    }
});

3
DataSnapshot 오브젝트에는 getLead () 메소드가 없습니다.
Parag Kadam

3
그는 그것을 getOad () 메토가 있다고 가정하고 그것을 Movie 객체로 변환했습니다.
Kim G Pham

1
고마워요 더 많은 where 절과 같은 작동
Nuwan Withanage

1

Frank의 대답은 좋지만 Firestore는 array-contains최근에 도입 되어 AND 쿼리를보다 쉽게 ​​수행 할 수 있습니다.

filters필터를 추가 할 필드를 만들 수 있습니다 . 필요한만큼 값을 추가 할 수 있습니다. 예를 들어 코미디Jack Nicholson 을 기준으로 필터링 하려면 값을 추가 할 수 comedy_Jack Nicholson있지만 코미디2014기준으로comedy_2014 더 많은 필드를 만들지 않고 값 을 추가 할 수 있습니다 .

{
"movies": {
    "movie1": {
        "genre": "comedy",
        "name": "As good as it gets",
        "lead": "Jack Nicholson",
        "year": 2014,
        "filters": [
          "comedy_Jack Nicholson",
          "comedy_2014"
        ]
    }
  }  
}

1

Firebase에서는 여러 조건으로 쿼리 할 수 ​​없습니다. 그러나 나는 이것에 대한 길을 찾았습니다.

데이터베이스에서 필터링 된 초기 데이터를 다운로드하여 배열 목록에 저장해야합니다.

                Query query = databaseReference.orderByChild("genre").equalTo("comedy");
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                        ArrayList<Movie> movies = new ArrayList<>();
                        for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
                            String lead = dataSnapshot1.child("lead").getValue(String.class);
                            String genre = dataSnapshot1.child("genre").getValue(String.class);

                            movie = new Movie(lead, genre);

                            movies.add(movie);

                        }

                        filterResults(movies, "Jack Nicholson");

                        }

                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {

                    }
                });

데이터베이스에서 초기 필터링 된 데이터를 얻은 후에는 백엔드에서 추가 필터링을 수행해야합니다.

public void filterResults(final List<Movie> list,  final String genre) {
        List<Movie> movies = new ArrayList<>();
        movies = list.stream().filter(o -> o.getLead().equals(genre)).collect(Collectors.toList());
        System.out.println(movies);

        employees.forEach(movie -> System.out.println(movie.getFirstName()));
    }

-1
ref.orderByChild("lead").startAt("Jack Nicholson").endAt("Jack Nicholson").listner....

작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.