firebase中基于多where子句的查询

kd3sttzy  于 2021-07-14  发布在  Java
关注(0)|答案(7)|浏览(268)
{
  "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"
    }
  }
}

我是个新手。如何从上面的数据中检索结果 genre = 'comedy' 以及 lead = 'Jack Nicholson' ?
我有什么选择?

jm2pwxwz

jm2pwxwz1#

使用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()); 
  });

但正如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. 添加一个属性,该属性组合要筛选的值

如果这还不够好,您应该考虑修改/扩展您的数据以允许您的用例。例如:您可以将genre+lead填充到一个属性中,您只需将其用于此筛选器。

"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);
  });

davideast编写了一个名为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. 以编程方式创建自定义索引

另一种方法是执行我们在添加新查询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
如果这些选项都不适用于您,但是您仍然希望将数据存储在firebase中,那么您也可以考虑使用它的cloudfirestore数据库。
CloudFireStore可以在一个查询中处理多个相等筛选器,但只能处理一个范围筛选器。实际上,它使用的是相同的查询模型,但它就像自动为您生成复合属性一样。请参阅firestore关于复合查询的文档。

nwlqm0z1

nwlqm0z12#

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

认识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));
tcbh2hod

tcbh2hod3#

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

    }
});
0tdrvxhp

0tdrvxhp4#

弗兰克的回答很好,但firestore介绍说 array-contains 最近,这使它更容易做和查询。
您可以创建 filters 字段以添加筛选器。您可以根据需要添加任意多的值。例如,通过喜剧和杰克尼科尔森过滤你可以添加值 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"
      ]
    }
  }
}
atmip9wb

atmip9wb5#

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()));
    }
col17t5w

col17t5w6#

firebase实时数据库中的数据为\u internallinkedhashmap<dynamic,dynamic>。你也可以很容易地把它转换成Map和查询。
例如,我有一个聊天应用程序,我使用实时数据库来存储用户的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]
我是新来的颤栗如果你知道更多请更新答案。

dgsult0t

dgsult0t7#

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

这会有用的。

相关问题