Recuperar dados

Este documento abrange os conceitos básicos sobre a recuperação dos dados do banco, bem como a ordenação e as consultas simples deles. A recuperação de dados no Admin SDK é implementada de modo ligeiramente diferente em diferentes linguagens de programação.

  1. Listeners assíncronos: os dados armazenados em um Firebase Realtime Database são recuperados anexando-se um listener assíncrono a uma referência de banco de dados. Ele é acionado uma vez no estado inicial dos dados e posteriormente, quando há alterações. Um listener de eventos pode receber diversos tipos de eventos. Este modo de recuperação de dados é compatível com SDKs Admin para Java, Node.js e Python.
  2. Leituras de método de bloqueio: dados armazenados em um Firebase Realtime Database são recuperados ao invocar 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 detecte atualizações de dados subsequentes. Esse modelo de recuperação de dados é compatível com o SDKs Admin para Python e Go.

Primeiros passos

Vamos reutilizar o exemplo de blog do artigo anterior para compreender como devemos ler os dados de um banco de dados do Firebase. Lembre-se de que as postagens do blog do app de exemplo estão armazenadas no URL de banco de dados https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json. Para ler os dados da postagem, faça 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
// Import Admin SDK
var admin = require("firebase-admin");

// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("server/saving-data/fireblog/posts");

// Attach an asynchronous callback to read the data at our posts reference
ref.on("value", function(snapshot) {
  console.log(snapshot.val());
}, function (errorObject) {
  console.log("The read failed: " + errorObject.code);
});
Python
# 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())
Go

// 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 do Node.js e Java, a função listener é chamada quando novos dados são adicionados à referência de banco de dados. Não é necessário escrever nenhum código adicional para isso.

No 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 de um determinado momento. Chamar val() / getValue() em um instantâneo retornará uma representação do objeto específico da linguagem dos dados. Se não houver dados no local da referência, o valor do instantâneo será null. O método get() no Python retorna uma representação Python dos dados diretamente. A função Get() em Go decodifica 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 do Firebase, mesmo se apenas uma parte dos dados tiver sido alterada. value é um dos cinco tipos de evento diferentes listados a seguir que podem ser usados para ler dados do banco de dados.

Ler tipos de evento em Java e Node.js

Value

O evento value é usado para ler um instantâneo estático do conteúdo de um caminho de banco de dados específico, no estado original do momento do evento de leitura. O evento é acionado uma vez com os dados iniciais e sempre que os dados forem novamente alterados. O retorno de chamada do evento recebe um instantâneo que contém todos os dados do local, incluindo dados filho. No exemplo de código acima, value retornou todas as postagens do blog no seu aplicativo. Sempre que uma nova postagem for adicionada ao blog, a função de retorno de chamada retornará todas as publicações.

Child Added

Geralmente, o evento child_added é usado na recuperação de uma lista de itens do banco de dados. Ao contrário de value, que retorna todo o conteúdo do local, child_added é acionado uma vez para cada filho existente e sempre que um novo filho for adicionado ao caminho especificado. O retorno de chamada do evento recebe um instantâneo que contém os dados do novo filho. Para fins de classificação, ele também recebe um segundo argumento contendo a chave do filho anterior.

Se você quiser recuperar apenas os dados de cada nova postagem adicionada ao seu aplicativo de blogs, use 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", function(snapshot, prevChildKey) {
  var 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 ao recuperar o valor, você tem acesso às propriedades do autor e título da postagem ao chamar author e title respectivamente. Você também tem acesso ao código de postagem anterior do segundo argumento prevChildKey.

Child Changed

O evento child_changed é acionado sempre que um nó filho é modificado. Isso inclui todas as modificações nos descendentes do nó filho. Geralmente, ele é usado junto com child_added e child_removed para responder às alterações de uma lista de itens. O instantâneo transmitido ao 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 são editados:

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", function(snapshot) {
  var changedPost = snapshot.val();
  console.log("The updated post title is " + changedPost.title);
});

Child Removed

O evento child_removed é acionado quando um filho imediato é removido. Geralmente, ele é usado junto com child_added e child_changed. O instantâneo transmitido ao 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 publicação 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
var ref = db.ref("server/saving-data/fireblog/posts");

// Get the data on a post that has been removed
ref.on("child_removed", function(snapshot) {
  var deletedPost = snapshot.val();
  console.log("The blog post titled '" + deletedPost.title + "' has been deleted");
});

Child Moved

O evento child_moved é usado para trabalhar com dados ordenados, o que abordaremos na próxima seção.

Garantias de eventos

O banco de dados do Firebase oferece várias garantias importantes a respeito dos eventos:

Garantias de evento do banco de dados
Os eventos serão sempre acionados quando o estado local mudar.
No fim, eles sempre refletirão o estado correto dos dados, mesmo quando as operações ou o tempo locais causem diferenças temporárias, como a perda temporária de conexão com a rede.
As gravações de um único cliente sempre serão gravadas no servidor e transmitidas a outros usuários, respeitando a ordem.
Os eventos "value" sempre serão acionados por último e sempre conterão as atualizações de todos os outros eventos ocorridos antes da geração do instantâneo.

Como os eventos "value" sempre são acionados por último, o seguinte exemplo 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
var count = 0;

ref.on("child_added", function(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", function(snap) {
  console.log("initial data loaded!", snap.numChildren() === count);
});

Desconectar retornos de chamada

Para remover retornos de chamada, especifique o tipo de evento e a função de retorno a ser removida, da seguinte forma:

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ê transmitiu um contexto de escopo para on(), ele terá que ser transmitido ao desconectar o retorno de chamada:

Java
// Not applicable for Java
Node.js
ref.off("value", originalCallback, this);

Para remover todos os retornos de chamada de um local, faça 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();

Leitura de dados uma vez

Em alguns casos, pode ser útil chamar um retorno de chamada uma vez e removê-lo logo em seguida. Criamos uma função auxiliar para esse processo:

Java
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    // ...
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Node.js
ref.once("value", function(data) {
  // do some stuff once
});
Python
# 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())
Go
// 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)
}

Consulta de dados

Com consultas no banco de dados do Firebase, podemos recuperar dados seletivamente de acordo com vários fatores. Para criar uma consulta no seu banco de dados, comece especificando a ordenação dos dados usando uma das funções de ordenação: orderByChild(), orderByKey() ou orderByValue(). Em seguida, combine-as com outros cinco métodos para realizar consultas complexas: limitToFirst(), limitToLast(), startAt(), endAt() e equalTo().

Já que a equipe do Firebase gosta muito de dinossauros, usaremos um snippet de uma amostra de banco de dados de fatos sobre esses animais para demonstrar como consultar dados no 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 formas: por chave filha, por chave ou por valor. Uma consulta básica do banco de dados começa com uma dessas funções de ordenação. Todas elas são explicadas a seguir.

Ordenação por uma chave filha especificada

Você pode ordenar os nós por uma chave filha comum, transmitindo essa chave para orderByChild(). Por exemplo, para ler todos os dinossauros ordenados por altura, faça 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
var db = firebaseAdmin.database();
var ref = db.ref("dinosaurs");
ref.orderByChild("height").on("child_added", function(snapshot) {
  console.log(snapshot.key + " was " + snapshot.val().height + " meters tall");
});
Python
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))
Go

// 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 tiver a chave filha que estamos consultando será classificado com um valor null, o que significa que ele será o primeiro da ordenação. Para ver mais detalhes sobre como os dados são ordenados, consulte a seção Ordenação dos dados.

As consultas também podem ser ordenadas por filhos aninhados em vários níveis, em vez de apenas filhos de um nível inferior. Isso é útil se você tiver dados aninhados em vários níveis, como estes:

{
  "lambeosaurus": {
    "dimensions": {
      "height" : 2.1,
      "length" : 12.5,
      "weight": 5000
    }
  },
  "stegosaurus": {
    "dimensions": {
      "height" : 4,
      "length" : 9,
      "weight" : 2500
    }
  }
}

Agora, para consultar a altura, use 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
var ref = db.ref("dinosaurs");
ref.orderByChild("dimensions/height").on("child_added", function(snapshot) {
  console.log(snapshot.key + " was " + snapshot.val().height + " meters tall");
});
Python
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))
Go
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 podem ser ordenadas apenas de chave em chave. Chamar orderByChild() várias vezes na mesma consulta causará um erro.

Ordenação por chave

Você também pode ordenar os nós pelas 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", function(snapshot) {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
Go
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 das chaves filhas usando o método orderByValue(). Vamos supor que os dinossauros estejam participando de uma olimpíada jurássica e que estamos monitorando as pontuações deles no seguinte formato:

{
  "scores": {
    "bruhathkayosaurus" : 55,
    "lambeosaurus" : 21,
    "linhenykus" : 80,
    "pterodactyl" : 93,
    "stegosaurus" : 5,
    "triceratops" : 22
  }
}

Para ordenar os dinossauros por pontuação, podemos criar 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
var scoresRef = db.ref("scores");
scoresRef.orderByValue().on("value", function(snapshot) {
  snapshot.forEach(function(data) {
    console.log("The " + data.key + " dinosaur's score is " + data.val());
  });
});
Python
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))
Go
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 Ordenação dos dados para ver uma explicação de como os valores null, booleanos, de string e de objeto são ordenados usando orderByValue().

Consultas complexas

Agora que está claro como os dados são ordenados, use os métodos de limite ou intervalo descritos abaixo para criar consultas mais complexas.

Consultas de limite

As consultas limitToFirst() e limitToLast() são usadas para configurar o número máximo de filhos que serão sincronizados para um retorno de chamada específico. Se você configurar um limite de 100, inicialmente, receberá até 100 eventos child_added somente. Se tiver menos de 100 mensagens armazenadas no banco de dados, um evento child_added será disparado para cada mensagem. No entanto, se tiver mais de 100 mensagens, você receberá um evento child_added somente para 100 delas. Essas mensagens serão as 100 primeiras ordenadas, se limitToFirst() for usada, ou as 100 últimas, se limitToLast() for usada. À medida que os itens mudarem, você receberá eventos child_added para os itens que entrarem na consulta e eventos child_removed para os itens que saírem dela. Assim, o número total continuará sendo 100.

Usando o banco de dados de fatos de dinossauro 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
var ref = db.ref("dinosaurs");
ref.orderByChild("weight").limitToLast(2).on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
Go
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 será acionado exatamente duas vezes, a menos que haja menos de dois dinossauros armazenados no banco de dados. Ele também será acionado para todo dinossauro novo, com peso maior, adicionado ao banco de dados. Em Python, a consulta retorna diretamente um OrderedDict contendo os dois dinossauros mais pesados.

Do mesmo modo, você pode encontrar os dois dinossauros mais baixos 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
var ref = db.ref("dinosaurs");
ref.orderByChild("height").limitToFirst(2).on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
Go
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 é disparado 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á o segundo mais baixo. Em Python, a consulta retorna diretamente um OrderedDict contendo os dinossauros mais baixos.

Também é possível realizar consultas limitadas com orderByValue(). Se quiser criar um placar com os três dinossauros com a maior pontuação, faça 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
var scoresRef = db.ref("scores");
scoresRef.orderByValue().limitToLast(3).on("value", function(snapshot) {
  snapshot.forEach(function(data) {
    console.log("The " + data.key + " dinosaur's score is " + data.val());
  });
});
Python
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))
Go
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

O uso de startAt(), endAt() e equalTo() permite escolher pontos arbitrários de início e término para suas consultas. Por exemplo, se você quisesse encontrar todos os dinossauros com pelo menos três metros de altura, poderia 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
  var ref = db.ref("dinosaurs");
  ref.orderByChild("height").startAt(3).on("child_added", function(snapshot) {
    console.log(snapshot.key);
  });
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
Go
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 todos os dinossauros cujos nomes venham, alfabeticamente, antes de 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
var ref = db.ref("dinosaurs");
ref.orderByKey().endAt("pterodactyl").on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
Go
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 as duas extremidades da sua consulta. O seguinte exemplo encontra todos os dinossauros cujos nomes começam 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", function(snapshot) {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get()
for key in snapshot:
    print(key)
Go
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 nas outras consultas de intervalo, ele será acionado 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
var ref = db.ref("dinosaurs");
ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
Go
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())
}

As consultas de intervalo também são úteis quando você precisa paginar seus dados.

Funcionamento em conjunto

É possível combinar todas essas técnicas para criar consultas complexas. Por exemplo, você pode descobrir o nome do primeiro dinossauro mais baixo 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
var ref = db.ref("dinosaurs");
ref.child("stegosaurus").child("height").on("value", function(stegosaurusHeightSnapshot) {
  var favoriteDinoHeight = stegosaurusHeightSnapshot.val();

  var queryRef = ref.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2)
  queryRef.on("value", function(querySnapshot) {
    if (querySnapshot.numChildren() === 2) {
      // Data is ordered by increasing height, so we want the first entry
      querySnapshot.forEach(function(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");
    }
  });
});
Python
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')
Go
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")
}

Ordenação dos dados

Esta seção explica a ordenação dos seus dados usando cada uma das quatro funções de ordenação.

orderByChild

Ao usar orderByChild(), os dados que contêm a chave filha especificada são ordenados desta maneira:

  1. Filhos com valor null para a chave filha especificada vêm primeiro.
  2. Filhos com valor false para a chave filha especificada vêm em seguida. Se vários filhos tiverem o valor false, eles serão classificados lexicograficamente por chave.
  3. Filhos com valor true para a chave filha especificada vêm depois. Se vários filhos tiverem um valor true, eles serão ordenados alfabeticamente, por chave.
  4. Filhos com um valor numérico são os próximos, ordenados em ordem crescente. Se vários filhos tiverem o mesmo valor numérico para o nó filho especificado, eles serão ordenados por chave.
  5. As strings vêm depois dos números e são classificadas alfabeticamente em ordem crescente. Se vários filhos tiverem o mesmo valor para o nó filho especificado, eles serão ordenados alfabeticamente, por chave.
  6. Os objetos vêm por último e são ordenados alfabeticamente, por chave, em ordem crescente.

orderByKey

Quando usamos orderByKey() para ordenar os dados, eles são retornados em ordem ascendente, por chave, da seguinte maneira. Lembre-se de que as chaves só podem ser strings.

  1. Filhos com uma chave que possa ser analisada como um número inteiro de 32 bits vêm primeiro, ordenados em ordem crescente.
  2. Filhos com um valor de string como chave são os próximos, ordenados alfabeticamente em ordem crescente.

orderByValue

Ao usar orderByValue(), os filhos são ordenados pelo valor deles. Os critérios de ordenação são os mesmos de orderByChild(), com a exceção de que o valor usado é o do nó e não o de uma chave filha especificada.