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.