查询基于Firebase中的多个where子句

时间:2014-11-02 15:25:45

标签: firebase firebase-realtime-database

{
"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' 上方的数据中检索结果?

我有哪些选择?

7 个答案:

答案 0 :(得分:207)

使用Firebase's Query 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撰写了library called QueryBase that helps with generating such properties

您甚至可以进行相对/范围查询,假设您希望允许按类别和年份查询电影。您将使用此数据结构:

"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对字符串值执行的字典顺序将与按时间顺序排列相同。

属性中值的组合可以使用两个以上的值,但是您只能对复合属性中的最后一个值执行范围过滤。

GeoFire library for Firebase实现了一个非常特殊的变体。该库将位置的纬度和经度组合成一个所谓的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

答案 1 :(得分:38)

我写了一个个人库,允许您按多个值排序,所有订单都在服务器上完成。

Meet 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));

答案 2 :(得分:2)

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 :(得分:1)

弗兰克的回答很好,但是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"
        ]
    }
  }  
}

答案 4 :(得分: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()));
    }

答案 5 :(得分:0)

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

这将起作用。

答案 6 :(得分:0)

来自 firebase 实时数据库的数据为 _InternalLinkedHashMap。 您也可以将其转换为您的地图并非常轻松地进行查询。

例如,我有一个聊天应用程序,我使用实时数据库来存储用户的 uid 和 bool 值,无论用户是否在线。如下图。

现在,我有一个 RealtimeDatabase 类和一个静态方法 getAllUsersOnineStatus()。

static getOnilineUsersUID() {
var dbRef = FirebaseDatabase.instance;
DatabaseReference reference = dbRef.reference().child("Online");
reference.once().then((value) {
  Map<String, bool> map = Map<String, bool>.from(value.value);
  List users = [];
  map.forEach((key, value) {
    if (value) {
      users.add(key);
    }
  });
  print(users);
});

}

它将打印 [NOraDTGaQSZbIEszidCujw1AEym2]

我是 flutter 新手如果你知道更多,请更新答案。