Este documento aborda os conceitos básicos de recuperação de dados de banco de dados, como os dados são ordenados e como realizar consultas simples nos dados. A recuperação de dados no Admin SDK é implementada de maneira um pouco diferente em diferentes linguagens de programação.
- Ouvintes assíncronos: os dados armazenados em um Firebase Realtime Database são recuperados anexando um ouvinte assíncrono a uma referência de banco de dados. O ouvinte é acionado uma vez para o estado inicial dos dados e novamente sempre que os dados são alterados. Um ouvinte de eventos pode receber vários tipos diferentes de eventos . Este modo de recuperação de dados é compatível com Java, Node.js e Python Admin SDKs.
- Leituras de bloqueio: os dados armazenados em um Firebase Realtime Database são recuperados invocando um método de bloqueio em uma referência de banco de dados, que retorna os dados armazenados na referência. Cada chamada de método é uma operação única. Isso significa que o SDK não registra nenhum retorno de chamada que ouça atualizações de dados subsequentes. Este modelo de recuperação de dados é compatível com SDKs de administração Python e Go.
Começando
Vamos revisitar o exemplo de blog do artigo anterior para entender como ler dados de um banco de dados Firebase. Lembre-se de que as postagens do blog no aplicativo de exemplo são armazenadas na URL do banco de dados https://docs-examples.firebaseio.com/server/ saving-data/fireblog/posts.json . Para ler os dados da sua postagem, você pode fazer o seguinte:
Java
public static class Post { public String author; public String title; public Post(String author, String title) { // ... } } // Get a reference to our posts final FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts"); // Attach a listener to read the data at our posts reference ref.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Post post = dataSnapshot.getValue(Post.class); System.out.println(post); } @Override public void onCancelled(DatabaseError databaseError) { System.out.println("The read failed: " + databaseError.getCode()); } });
Node.js
// Get a database reference to our posts const db = getDatabase(); const ref = db.ref('server/saving-data/fireblog/posts'); // Attach an asynchronous callback to read the data at our posts reference ref.on('value', (snapshot) => { console.log(snapshot.val()); }, (errorObject) => { console.log('The read failed: ' + errorObject.name); });
Pitão
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
Ir
// Post is a json-serializable type. type Post struct { Author string `json:"author,omitempty"` Title string `json:"title,omitempty"` } // Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
Se você executar o código acima, verá um objeto contendo todas as suas postagens registradas no console. No caso de Node.js e Java, a função listener é chamada sempre que novos dados são adicionados à referência do seu banco de dados, e você não precisa escrever nenhum código extra para que isso aconteça.
Em Java e Node.js, a função de retorno de chamada recebe um DataSnapshot
, que é um instantâneo dos dados. Um instantâneo é uma imagem dos dados em uma referência de banco de dados específica em um único momento. Chamar val()
/ getValue()
em um instantâneo retorna uma representação dos dados de objeto específico da linguagem. Se não existirem dados no local da referência, o valor do instantâneo será null
. O método get()
em Python retorna uma representação Python dos dados diretamente. A função Get()
em Go descompacta os dados em uma determinada estrutura de dados.
Observe que usamos o tipo de evento value
no exemplo acima, que lê todo o conteúdo de uma referência de banco de dados Firebase, mesmo que apenas um dado tenha sido alterado. value
é um dos cinco tipos de eventos diferentes listados abaixo que você pode usar para ler dados do banco de dados.
Leia tipos de eventos em Java e Node.js
Valor
O evento value
é usado para ler um instantâneo estático do conteúdo em um determinado caminho do banco de dados, conforme existia no momento do evento read. Ele é acionado uma vez com os dados iniciais e novamente sempre que os dados são alterados. O retorno de chamada do evento recebe um instantâneo contendo todos os dados naquele local, incluindo dados filho. No exemplo de código acima, value
retornou todas as postagens do blog em seu aplicativo. Cada vez que uma nova postagem no blog é adicionada, a função de retorno de chamada retornará todas as postagens.
Filho adicionado
O evento child_added
normalmente é usado ao recuperar uma lista de itens do banco de dados. Ao contrário value
que retorna todo o conteúdo do local, child_added
é acionado uma vez para cada filho existente e novamente sempre que um novo filho é adicionado ao caminho especificado. O retorno de chamada do evento recebe um instantâneo contendo os dados do novo filho. Para fins de ordenação, também é passado um segundo argumento contendo a chave do filho anterior.
Se quiser recuperar apenas os dados de cada nova postagem adicionada ao seu aplicativo de blog, você pode usar child_added
:
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Post newPost = dataSnapshot.getValue(Post.class); System.out.println("Author: " + newPost.author); System.out.println("Title: " + newPost.title); System.out.println("Previous Post ID: " + prevChildKey); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Retrieve new posts as they are added to our database ref.on('child_added', (snapshot, prevChildKey) => { const newPost = snapshot.val(); console.log('Author: ' + newPost.author); console.log('Title: ' + newPost.title); console.log('Previous Post ID: ' + prevChildKey); });
Neste exemplo, o instantâneo conterá um objeto com uma postagem de blog individual. Como o SDK converte postagens em objetos recuperando o valor, você tem acesso às propriedades autor e título da postagem chamando author
e title
respectivamente. Você também tem acesso ao ID da postagem anterior do segundo argumento prevChildKey
.
Criança alterada
O evento child_changed
é acionado sempre que um nó filho é modificado. Isto inclui quaisquer modificações nos descendentes do nó filho. Normalmente é usado em conjunto com child_added
e child_removed
para responder a alterações em uma lista de itens. O instantâneo passado para o retorno de chamada do evento contém os dados atualizados do filho.
Você pode usar child_changed
para ler dados atualizados nas postagens do blog quando elas são editadas:
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) { Post changedPost = dataSnapshot.getValue(Post.class); System.out.println("The updated post title is: " + changedPost.title); } @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Get the data on a post that has changed ref.on('child_changed', (snapshot) => { const changedPost = snapshot.val(); console.log('The updated post title is ' + changedPost.title); });
Filho removido
O evento child_removed
é acionado quando um filho imediato é removido. Normalmente é usado em conjunto com child_added
e child_changed
. O instantâneo passado para o retorno de chamada do evento contém os dados do filho removido.
No exemplo do blog, você pode usar child_removed
para registrar uma notificação sobre a postagem excluída no console:
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) { Post removedPost = dataSnapshot.getValue(Post.class); System.out.println("The blog post titled " + removedPost.title + " has been deleted"); } @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Get a reference to our posts const ref = db.ref('server/saving-data/fireblog/posts'); // Get the data on a post that has been removed ref.on('child_removed', (snapshot) => { const deletedPost = snapshot.val(); console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted'); });
Criança movida
O evento child_moved
é usado ao trabalhar com dados ordenados, que será abordado na próxima seção .
Garantias de eventos
O banco de dados Firebase oferece diversas garantias importantes em relação aos eventos:
Garantias de eventos de banco de dados |
---|
Os eventos sempre serão acionados quando o estado local mudar. |
Os eventos sempre refletirão o estado correto dos dados, mesmo nos casos em que as operações locais ou o tempo causam diferenças temporárias, como na perda temporária da conexão de rede. |
As gravações de um único cliente sempre serão gravadas no servidor e transmitidas para outros usuários em ordem. |
Os eventos de valor são sempre acionados por último e têm a garantia de conter atualizações de quaisquer outros eventos que ocorreram antes da captura do instantâneo. |
Como os eventos de valor são sempre acionados por último, o exemplo a seguir sempre funcionará:
Java
final AtomicInteger count = new AtomicInteger(); ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // New child added, increment count int newCount = count.incrementAndGet(); System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount); } // ... }); // The number of children will always be equal to 'count' since the value of // the dataSnapshot here will include every child_added event triggered before this point. ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { long numChildren = dataSnapshot.getChildrenCount(); System.out.println(count.get() + " == " + numChildren); } @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
let count = 0; ref.on('child_added', (snap) => { count++; console.log('added:', snap.key); }); // length will always equal count, since snap.val() will include every child_added event // triggered before this point ref.once('value', (snap) => { console.log('initial data loaded!', snap.numChildren() === count); });
Desanexando retornos de chamada
Os retornos de chamada são removidos especificando o tipo de evento e a função de retorno de chamada a ser removida, como a seguir:
Java
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
Se você passou um contexto de escopo para on()
, ele deverá ser passado ao desanexar o retorno de chamada:
Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);
Se quiser remover todos os retornos de chamada em um local, você pode fazer o seguinte:
Java
// No Java equivalent, listeners must be removed individually.
Node.js
// Remove all value callbacks ref.off('value'); // Remove all callbacks of any type ref.off();
Lendo dados uma vez
Em alguns casos, pode ser útil que um retorno de chamada seja chamado uma vez e imediatamente removido. Criamos uma função auxiliar para facilitar isso:
Java
ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // ... } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
Node.js
ref.once('value', (data) => { // do some stuff once });
Pitão
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
Ir
// Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
Consultando dados
Com as consultas ao banco de dados do Firebase, você pode recuperar dados seletivamente com base em vários fatores. Para construir uma consulta em seu banco de dados, você começa especificando como deseja que seus dados sejam ordenados usando uma das funções de ordenação: orderByChild()
, orderByKey()
ou orderByValue()
. Você pode então combiná-los com cinco outros métodos para conduzir consultas complexas: limitToFirst()
, limitToLast()
, startAt()
, endAt()
e equalTo()
.
Como todos nós do Firebase achamos que os dinossauros são muito legais, usaremos um trecho de um banco de dados de amostra de fatos sobre dinossauros para demonstrar como você pode consultar dados em seu banco de dados do Firebase.:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
Você pode ordenar os dados de três maneiras: por chave filha , por chave ou por valor . Uma consulta básica ao banco de dados começa com uma dessas funções de ordenação, cada uma delas explicada abaixo.
Ordenação por uma chave filha especificada
Você pode ordenar os nós por uma chave filha comum, passando essa chave para orderByChild()
. Por exemplo, para ler todos os dinossauros ordenados por altura, você pode fazer o seguinte:
Java
public static class Dinosaur { public int height; public int weight; public Dinosaur(int height, int weight) { // ... } } final DatabaseReference dinosaursRef = database.getReference("dinosaurs"); dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class); System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall."); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').get() for key, val in snapshot.items(): print('{0} was {1} meters tall'.format(key, val))
Ir
// Dinosaur is a json-serializable type. type Dinosaur struct { Height int `json:"height"` Width int `json:"width"` } ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
Qualquer nó que não tenha a chave filha que estamos consultando é classificado com um valor null
, o que significa que ele virá primeiro na ordem. Para obter detalhes sobre como os dados são ordenados, consulte a seção Como os dados são ordenados .
As consultas também podem ser ordenadas por filhos profundamente aninhados, em vez de apenas filhos um nível abaixo. Isso é útil se você tiver dados profundamente aninhados como estes:
{ "lambeosaurus": { "dimensions": { "height" : 2.1, "length" : 12.5, "weight": 5000 } }, "stegosaurus": { "dimensions": { "height" : 4, "length" : 9, "weight" : 2500 } } }
Para consultar a altura agora, você pode usar o caminho completo para o objeto em vez de uma única chave:
Java
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // ... } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('dimensions/height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('dimensions/height').get() for key, val in snapshot.items(): print('{0} was {1} meters tall'.format(key, val))
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
As consultas só podem ser ordenadas por uma chave por vez. Chamar orderByChild()
várias vezes na mesma consulta gera um erro.
Ordenação por chave
Você também pode ordenar os nós por suas chaves usando o método orderByKey()
. O exemplo a seguir lê todos os dinossauros em ordem alfabética:
Java
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
var ref = db.ref('dinosaurs'); ref.orderByKey().on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().get() print(snapshot)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } snapshot := make([]Dinosaur, len(results)) for i, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } snapshot[i] = d } fmt.Println(snapshot)
Ordenação por valor
Você pode ordenar os nós pelo valor de suas chaves filhas usando o método orderByValue()
. Digamos que os dinossauros estejam participando de uma competição esportiva de dinossauros e você esteja acompanhando suas pontuações no seguinte formato:
{ "scores": { "bruhathkayosaurus" : 55, "lambeosaurus" : 21, "linhenykus" : 80, "pterodactyl" : 93, "stegosaurus" : 5, "triceratops" : 22 } }
Para classificar os dinossauros por pontuação, você poderia construir a seguinte consulta:
Java
DatabaseReference scoresRef = database.getReference("scores"); scoresRef.orderByValue().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
Node.js
const scoresRef = db.ref('scores'); scoresRef.orderByValue().on('value', (snapshot) => { snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
Pitão
ref = db.reference('scores') snapshot = ref.order_by_value().get() for key, val in snapshot.items(): print('The {0} dinosaur\'s score is {1}'.format(key, val))
Ir
ref := client.NewRef("scores") results, err := ref.OrderByValue().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
Consulte a seção Como os dados são ordenados para obter uma explicação sobre como os valores null
, boolean, string e object são classificados ao usar orderByValue()
.
Consultas complexas
Agora que está claro como seus dados são ordenados, você pode usar os métodos de limite ou intervalo descritos abaixo para construir consultas mais complexas.
Limitar consultas
As consultas limitToFirst()
e limitToLast()
são usadas para definir um número máximo de filhos a serem sincronizados para um determinado retorno de chamada. Se você definir um limite de 100, inicialmente receberá apenas até 100 eventos child_added
. Se você tiver menos de 100 mensagens armazenadas em seu banco de dados, um evento child_added
será acionado para cada mensagem. No entanto, se você tiver mais de 100 mensagens, receberá apenas um evento child_added
para 100 dessas mensagens. Estas são as primeiras 100 mensagens ordenadas se você estiver usando limitToFirst()
ou as últimas 100 mensagens ordenadas se você estiver usando limitToLast()
. À medida que os itens mudam, você receberá eventos child_added
para itens que entram na consulta e eventos child_removed
para itens que saem dela, de modo que o número total permaneça em 100.
Usando o banco de dados de fatos sobre dinossauros e orderByChild()
, você pode encontrar os dois dinossauros mais pesados:
Java
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('weight').limit_to_last(2).get() for key in snapshot: print(key)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
O retorno de chamada child_added
é acionado exatamente duas vezes, a menos que haja menos de dois dinossauros armazenados no banco de dados. Ele também será acionado para cada dinossauro novo e mais pesado adicionado ao banco de dados. Em Python, a consulta retorna diretamente um OrderedDict
contendo os dois dinossauros mais pesados.
Da mesma forma, você pode encontrar os dois dinossauros mais curtos usando limitToFirst()
:
Java
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').limit_to_first(2).get() for key in snapshot: print(key)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
O retorno de chamada child_added
é acionado exatamente duas vezes, a menos que haja menos de dois dinossauros armazenados no banco de dados. Ele também será acionado novamente se um dos dois primeiros dinossauros for removido do banco de dados, já que um novo dinossauro será agora o segundo mais curto. Em Python, a consulta retorna diretamente um OrderedDict
contendo os dinossauros mais curtos.
Você também pode realizar consultas de limite com orderByValue()
. Se quiser criar uma tabela de classificação com os 3 dinossauros esportivos com maior pontuação, você pode fazer o seguinte:
Java
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
Node.js
const scoresRef = db.ref('scores'); scoresRef.orderByValue().limitToLast(3).on('value', (snapshot) =>{ snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
Pitão
scores_ref = db.reference('scores') snapshot = scores_ref.order_by_value().limit_to_last(3).get() for key, val in snapshot.items(): print('The {0} dinosaur\'s score is {1}'.format(key, val))
Ir
ref := client.NewRef("scores") results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
Consultas de intervalo
Usar startAt()
, endAt()
e equalTo()
permite que você escolha pontos iniciais e finais arbitrários para suas consultas. Por exemplo, se você quiser encontrar todos os dinossauros com pelo menos três metros de altura, poderá combinar orderByChild()
e startAt()
:
Java
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').start_at(3).get() for key in snapshot: print(key)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Você pode usar endAt()
para encontrar lexicograficamente todos os dinossauros cujos nomes vêm antes do Pterodáctilo:
Java
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().end_at('pterodactyl').get() for key in snapshot: print(key)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Você pode combinar startAt()
e endAt()
para limitar ambas as extremidades da sua consulta. O exemplo a seguir encontra todos os dinossauros cujo nome começa com a letra “b”:
Java
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
var ref = db.ref('dinosaurs'); ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get() for key in snapshot: print(key)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
O método equalTo()
permite filtrar com base em correspondências exatas. Como é o caso das outras consultas de intervalo, ela será acionada para cada nó filho correspondente. Por exemplo, você pode usar a seguinte consulta para encontrar todos os dinossauros com 25 metros de altura:
Java
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => { console.log(snapshot.key); });
Pitão
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').equal_to(25).get() for key in snapshot: print(key)
Ir
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Consultas de intervalo também são úteis quando você precisa paginar seus dados.
Juntando tudo
Você pode combinar todas essas técnicas para criar consultas complexas. Por exemplo, você pode encontrar o nome do dinossauro que é um pouco mais curto que o Estegossauro:
Java
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot stegoHeightSnapshot) { Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class); Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2); query.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Data is ordered by increasing height, so we want the first entry DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next(); System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey()); } @Override public void onCancelled(DatabaseError databaseError) { // ... } }); } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
Node.js
const ref = db.ref('dinosaurs'); ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => { const favoriteDinoHeight = stegosaurusHeightSnapshot.val(); const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2); queryRef.on('value', (querySnapshot) => { if (querySnapshot.numChildren() === 2) { // Data is ordered by increasing height, so we want the first entry querySnapshot.forEach((dinoSnapshot) => { console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key); // Returning true means that we will only loop through the forEach() one time return true; }); } else { console.log('The stegosaurus is the shortest dino'); } }); });
Pitão
ref = db.reference('dinosaurs') favotire_dino_height = ref.child('stegosaurus').child('height').get() query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2) snapshot = query.get() if len(snapshot) == 2: # Data is ordered by increasing height, so we want the first entry. # Second entry is stegosarus. for key in snapshot: print('The dinosaur just shorter than the stegosaurus is {0}'.format(key)) return else: print('The stegosaurus is the shortest dino')
Ir
ref := client.NewRef("dinosaurs") var favDinoHeight int if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil { log.Fatalln("Error querying database:", err) } query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2) results, err := query.GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } if len(results) == 2 { // Data is ordered by increasing height, so we want the first entry. // Second entry is stegosarus. fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key()) } else { fmt.Println("The stegosaurus is the shortest dino") }
Como os dados são ordenados
Esta seção explica como seus dados são ordenados ao usar cada uma das quatro funções de ordenação.
encomendarByChild
Ao usar orderByChild()
, os dados que contêm a chave filha especificada são ordenados da seguinte forma:
- Os filhos com um valor
null
para a chave filha especificada vêm primeiro. - Os filhos com um valor
false
para a chave filha especificada vêm em seguida. Se vários filhos tiverem um valorfalse
, eles serão classificados lexicograficamente por chave. - Os filhos com um valor
true
para a chave filha especificada vêm em seguida. Se vários filhos tiverem um valortrue
, eles serão classificados lexicograficamente por chave. - Os filhos com valor numérico vêm em seguida, classificados em ordem crescente. Se vários filhos tiverem o mesmo valor numérico para o nó filho especificado, eles serão classificados por chave.
- As strings vêm depois dos números e são classificadas lexicograficamente em ordem crescente. Se vários filhos tiverem o mesmo valor para o nó filho especificado, eles serão ordenados lexicograficamente por chave.
- Os objetos vêm por último e são classificados lexicograficamente por chave em ordem crescente.
pedidoByKey
Ao usar orderByKey()
para classificar seus dados, os dados são retornados em ordem crescente por chave, como segue. Lembre-se de que as chaves só podem ser strings.
- Os filhos com uma chave que pode ser analisada como um número inteiro de 32 bits vêm primeiro, classificados em ordem crescente.
- Os filhos com um valor de string como chave vêm em seguida, classificados lexicograficamente em ordem crescente.
pedidoPorValor
Ao usar orderByValue()
, os filhos são ordenados por seu valor. Os critérios de ordenação são os mesmos de orderByChild()
, exceto que o valor do nó é usado em vez do valor de uma chave filha especificada.