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

47d*_*7d_ 227 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"
    }
  }  
 }
Run Code Online (Sandbox Code Playgroud)

我是Firebase的新手.我如何可以检索从上面的数据结果,其中genre = 'comedy'lead = 'Jack Nicholson'

我有什么选择?

Fra*_*len 220

使用Firebase的查询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()); 
  });
Run Code Online (Sandbox Code Playgroud)

但正如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);
      }
  });
Run Code Online (Sandbox Code Playgroud)

2.添加一个组合要筛选的值的属性

如果这还不够好,您应该考虑修改/扩展您的数据以允许您的用例.例如:您可以将类型+引导填充到您刚刚用于此过滤器的单个属性中.

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_lead": "comedy_Jack Nicholson"
},...
Run Code Online (Sandbox Code Playgroud)

您实际上是以这种方式构建自己的多列索引,并可以使用以下方法查询它:

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

David East编写了一个名为QueryBase库,它可以帮助生成这些属性.

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

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_year": "comedy_1997"
},...
Run Code Online (Sandbox Code Playgroud)

然后查询90年代的喜剧:

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

如果您需要过滤的不仅仅是年份,请确保按降序添加其他日期部分,例如"comedy_1997-12-25".这样,Firebase对字符串值执行的字典顺序将与按时间顺序排列相同.

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

一个非常特殊的变体是由FirebaseGeoFire库实现的.该库将位置的纬度和经度组合成所谓的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"
Run Code Online (Sandbox Code Playgroud)

可能有更多的方法.例如,此答案突出显示了另一种树形自定义索引:https://stackoverflow.com/a/34105063

  • 它是2018年,对此没有简单的解决方案吗?这就像关系数据库中的查询101.考虑到大卫的视频谈论SQL#8的所有评论,我希望谷歌现在能够修复它.我错过了一些更新吗? (35认同)
  • 为什么我们不能使用```.equalTo('comedy')```而不是```.startAt('comedy').endAt('comedy')```? (26认同)
  • 这与Firebase V3在2016年仍然相关吗?有没有更好的方法来做到这一点? (16认同)
  • @Snake 2019年2月,问题仍然没有解决.... Google太慢了。 (8认同)
  • 当下周正式发布时,多个`orderBy()`调用将抛出错误,因为客户端当前会产生意外结果.它可能在你的测试中巧合地工作,但不是为了普遍地工作(尽管我们喜欢添加它!). (3认同)
  • @Snake不,你没有。我不知道为什么,但是Google在改进Firebase数据库方面进展缓慢。我知道Firestore,但事实并非如此。缺乏良好的查询。 (3认同)
  • 它仍然同样重要. (2认同)
  • 我已经编写了一个个人库来帮助将#2解决方案包装到易于使用的API中。该库称为Querybase,可供JS用户使用:https://github.com/davideast/Querybasehttps://github.com/davideast/Querybase (2认同)
  • Cloud Firestore 可以在单个查询中处理多个相等过滤器,但只能处理一个范围过滤器。在底层,它本质上使用相同的查询模型,但它就像自动为您生成复合属性。请参阅 https://firebase.google.com/docs/firestore/query-data/queries#compound_queries (2认同)

Dav*_*ast 42

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

认识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));
Run Code Online (Sandbox Code Playgroud)

  • 你知道为什么他们让数据库如此限制吗? (5认同)
  • 是用TS写的!所以只需导入:) (2认同)
  • 任何制作 IOS Swift/Android 版本的选项 (2认同)
  • David,有 Android/Java 的等效项吗? (2认同)

And*_*eng 6

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) {

    }
});
Run Code Online (Sandbox Code Playgroud)

  • 他将它转换为 Movie 对象并假设有一个 getLead() 方法。 (4认同)
  • DataSnapshot 对象上没有 getLead() 方法。 (3认同)
  • 非常感谢。它正在工作,例如更多 where 子句 (2认同)