获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Recuperando datos

Este documento cubre los aspectos básicos de la recuperación de datos de la base de datos, cómo se ordenan los datos y cómo realizar consultas simples sobre los datos. La recuperación de datos en Admin SDK se implementa de forma ligeramente diferente en diferentes lenguajes de programación.

  1. Agentes de escucha asincrónicos: los datos almacenados en Firebase Realtime Database se recuperan adjuntando un agente de escucha asincrónico a una referencia de la base de datos. El detector se activa una vez para el estado inicial de los datos y nuevamente cada vez que los datos cambian. Un detector de eventos puede recibir varios tipos diferentes de eventos . Este modo de recuperación de datos es compatible con los SDK de administración de Java, Node.js y Python.
  2. Bloqueo de lecturas: los datos almacenados en Firebase Realtime Database se recuperan invocando un método de bloqueo en una referencia de base de datos, que devuelve los datos almacenados en la referencia. Cada llamada de método es una operación única. Eso significa que el SDK no registra ninguna devolución de llamada que escuche las actualizaciones de datos posteriores. Este modelo de recuperación de datos es compatible con Python y Go Admin SDK.

Empezando

Revisemos el ejemplo de blogs del artículo anterior para comprender cómo leer datos de una base de datos de Firebase. Recuerde que las publicaciones de blog en la aplicación de ejemplo se almacenan en la URL de la base de datos https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json . Para leer los datos de su publicación, puede hacer lo siguiente:

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());
  }
});
Nodo.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ón
# 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())
Vamos

// 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)
}

Si ejecuta el código anterior, verá un objeto que contiene todas sus publicaciones registradas en la consola. En el caso de Node.js y Java, la función de escucha se llama cada vez que se agregan nuevos datos a la referencia de su base de datos, y no necesita escribir ningún código adicional para que esto suceda.

En Java y Node.js, la función de devolución de llamada recibe un DataSnapshot , que es una instantánea de los datos. Una instantánea es una imagen de los datos en una referencia de base de datos particular en un solo punto en el tiempo. Llamar a val() / getValue() en una instantánea devuelve una representación de objeto específica del idioma de los datos. Si no existen datos en la ubicación de la referencia, el valor de la instantánea es null . El método get() en Python devuelve una representación Python de los datos directamente. La función Get() en Go ordena los datos en una estructura de datos determinada.

Tenga en cuenta que usamos el tipo de evento de value en el ejemplo anterior, que lee todo el contenido de una referencia de base de datos de Firebase, incluso si solo cambió una parte de los datos. El value es uno de los cinco tipos de eventos diferentes que se enumeran a continuación y que puede usar para leer datos de la base de datos.

Leer tipos de eventos en Java y Node.js

Valor

El evento de value se usa para leer una instantánea estática de los contenidos en una ruta de base de datos determinada, tal como existían en el momento del evento de lectura. Se activa una vez con los datos iniciales y de nuevo cada vez que cambian los datos. La devolución de llamada del evento recibe una instantánea que contiene todos los datos en esa ubicación, incluidos los datos secundarios. En el ejemplo de código anterior, el value devolvió todas las publicaciones de blog en su aplicación. Cada vez que se agrega una nueva publicación de blog, la función de devolución de llamada devolverá todas las publicaciones.

Niño añadido

El evento child_added se usa normalmente cuando se recupera una lista de elementos de la base de datos. A diferencia de value que devuelve todo el contenido de la ubicación, child_added se activa una vez para cada elemento secundario existente y luego nuevamente cada vez que se agrega un elemento secundario nuevo a la ruta especificada. A la devolución de llamada del evento se le pasa una instantánea que contiene los datos del nuevo niño. Para fines de ordenación, también se le pasa un segundo argumento que contiene la clave del hijo anterior.

Si desea recuperar solo los datos de cada nueva publicación agregada a su aplicación de blogs, puede 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) {}
});
Nodo.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);
});

En este ejemplo, la instantánea contendrá un objeto con una publicación de blog individual. Debido a que el SDK convierte las publicaciones en objetos recuperando el valor, tiene acceso a las propiedades de autor y título de la publicación llamando al author y al title respectivamente. También tiene acceso al ID de la publicación anterior desde el segundo argumento prevChildKey .

Niño cambiado

El evento child_changed se activa cada vez que se modifica un nodo secundario. Esto incluye cualquier modificación a los descendientes del nodo secundario. Por lo general, se usa junto con child_added y child_removed para responder a los cambios en una lista de elementos. La instantánea pasada a la devolución de llamada del evento contiene los datos actualizados para el niño.

Puede usar child_changed para leer datos actualizados en publicaciones de blog cuando se editan:

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) {}
});
Nodo.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);
});

Niño eliminado

El evento child_removed se activa cuando se elimina un elemento secundario inmediato. Por lo general, se usa junto con child_added y child_changed . La instantánea pasada a la devolución de llamada del evento contiene los datos del elemento secundario eliminado.

En el ejemplo del blog, puede usar child_removed para registrar una notificación sobre la publicación eliminada en la consola:

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) {}
});
Nodo.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');
});

Niño movido

El evento child_moved se usa cuando se trabaja con datos ordenados, que se trata en la siguiente sección .

Garantías de eventos

La base de datos de Firebase ofrece varias garantías importantes con respecto a los eventos:

Garantías de eventos de base de datos
Los eventos siempre se activarán cuando cambie el estado local.
Eventualmente, los eventos siempre reflejarán el estado correcto de los datos, incluso en los casos en que las operaciones locales o el tiempo provoquen diferencias temporales, como la pérdida temporal de la conexión a la red.
Las escrituras de un solo cliente siempre se escribirán en el servidor y se transmitirán a otros usuarios en orden.
Los eventos de valor siempre se activan en último lugar y se garantiza que contienen actualizaciones de cualquier otro evento que haya ocurrido antes de que se tomara la instantánea.

Dado que los eventos de valor siempre se activan en último lugar, el siguiente ejemplo siempre 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) {}
});
Nodo.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);
});

Separación de devoluciones de llamada

Las devoluciones de llamada se eliminan especificando el tipo de evento y la función de devolución de llamada que se eliminará, como la siguiente:

Java
// Create and attach listener
ValueEventListener listener = new ValueEventListener() {
    // ...
};
ref.addValueEventListener(listener);

// Remove listener
ref.removeEventListener(listener);
Nodo.js
ref.off('value', originalCallback);

Si pasó un contexto de alcance a on() , debe pasarse al separar la devolución de llamada:

Java
// Not applicable for Java
Nodo.js
ref.off('value', originalCallback, ctx);

Si desea eliminar todas las devoluciones de llamada en una ubicación, puede hacer lo siguiente:

Java
// No Java equivalent, listeners must be removed individually.
Nodo.js
// Remove all value callbacks
ref.off('value');

// Remove all callbacks of any type
ref.off();

Lectura de datos una vez

En algunos casos, puede ser útil que una devolución de llamada se llame una vez y luego se elimine de inmediato. Hemos creado una función de ayuda para facilitar esto:

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

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Nodo.js
ref.once('value', (data) => {
  // do some stuff once
});
Pitón
# 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())
Vamos
// 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 datos

Con las consultas de la base de datos de Firebase, puede recuperar datos de forma selectiva en función de varios factores. Para construir una consulta en su base de datos, comience especificando cómo desea que se ordenen sus datos usando una de las funciones de ordenación: orderByChild() , orderByKey() o orderByValue() . Luego puede combinarlos con otros cinco métodos para realizar consultas complejas: limitToFirst() , limitToLast() , startAt() , endAt() y equalTo() .

Dado que todos nosotros en Firebase pensamos que los dinosaurios son geniales, usaremos un fragmento de una base de datos de muestra de hechos de dinosaurios para demostrar cómo puede consultar datos en su base de datos de Firebase:

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

Puede ordenar los datos de tres maneras: por clave secundaria , por clave o por valor . Una consulta de base de datos básica comienza con una de estas funciones de ordenación, cada una de las cuales se explica a continuación.

Ordenar por una clave secundaria especificada

Puede ordenar los nodos por una clave secundaria común pasando esa clave a orderByChild() . Por ejemplo, para leer todos los dinosaurios ordenados por altura, puedes hacer lo siguiente:

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

  // ...
});
Nodo.js
const ref = db.ref('dinosaurs');

ref.orderByChild('height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
Pitón
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))
Vamos

// 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)
}

Cualquier nodo que no tenga la clave secundaria que estamos consultando se ordena con un valor null , lo que significa que ocupará el primer lugar en el orden. Para obtener detalles sobre cómo se ordenan los datos, consulte la sección Cómo se ordenan los datos .

Las consultas también se pueden ordenar por elementos secundarios profundamente anidados, en lugar de solo elementos secundarios de un nivel inferior. Esto es útil si tiene datos profundamente anidados como este:

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

Para consultar la altura ahora, puede usar la ruta completa al objeto en lugar de una sola clave:

Java
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    // ...
  }

  // ...
});
Nodo.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ón
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))
Vamos
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)
}

Las consultas solo pueden ordenarse por una clave a la vez. Llamar orderByChild() varias veces en la misma consulta genera un error.

Pedido por clave

También puede ordenar los nodos por sus claves utilizando el método orderByKey() . El siguiente ejemplo lee todos los dinosaurios en orden alfabético:

Java
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Nodo.js
var ref = db.ref('dinosaurs');
ref.orderByKey().on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
Vamos
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)

Ordenar por valor

Puede ordenar los nodos por el valor de sus claves secundarias utilizando el método orderByValue() . Supongamos que los dinosaurios están teniendo una competencia deportiva de dinosaurios y usted lleva un registro de sus puntajes en el siguiente formato:

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

Para ordenar los dinosaurios por su puntaje, puede construir la siguiente 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());
  }

  // ...
});
Nodo.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ón
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))
Vamos
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 la sección Cómo se ordenan los datos para obtener una explicación sobre cómo se ordenan los valores null , booleano, de cadena y de objeto cuando se usa orderByValue() .

Consultas complejas

Ahora que está claro cómo se ordenan sus datos, puede usar los métodos de límite o rango que se describen a continuación para construir consultas más complejas.

Limitar consultas

Las limitToFirst() y limitToLast() se utilizan para establecer un número máximo de elementos secundarios que se sincronizarán para una devolución de llamada determinada. Si establece un límite de 100, inicialmente solo recibirá hasta 100 eventos child_added . Si tiene menos de 100 mensajes almacenados en su base de datos, se activará un evento child_added para cada mensaje. Sin embargo, si tiene más de 100 mensajes, solo recibirá un evento child_added para 100 de esos mensajes. Estos son los primeros 100 mensajes ordenados si usa limitToFirst() o los últimos 100 mensajes ordenados si usa limitToLast() . A medida que cambien los elementos, recibirá eventos child_added para los elementos que ingresan a la consulta y eventos child_removed para los elementos que la abandonan, de modo que el número total permanece en 100.

Usando la base de datos de hechos de dinosaurios y orderByChild() , puede encontrar los dos dinosaurios más pesados:

Java
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Nodo.js
const ref = db.ref('dinosaurs');
ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
Vamos
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())
}

La devolución de llamada child_added se activa exactamente dos veces, a menos que haya menos de dos dinosaurios almacenados en la base de datos. También se activará por cada dinosaurio nuevo y más pesado que se agregue a la base de datos. En Python, la consulta devuelve directamente un OrderedDict que contiene los dos dinosaurios más pesados.

De manera similar, puedes encontrar los dos dinosaurios más cortos usando limitToFirst() :

Java
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Nodo.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
Vamos
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())
}

La devolución de llamada child_added se activa exactamente dos veces, a menos que haya menos de dos dinosaurios almacenados en la base de datos. También se activará nuevamente si uno de los dos primeros dinosaurios se elimina de la base de datos, ya que un nuevo dinosaurio ahora será el segundo más bajo. En Python, la consulta devuelve directamente un OrderedDict que contiene los dinosaurios más cortos.

También puede realizar consultas de límite con orderByValue() . Si desea crear una tabla de clasificación con los 3 dinosaurios deportivos dino con mayor puntuación, puede hacer lo siguiente:

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

  // ...
});
Nodo.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ón
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))
Vamos
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 rango

El uso startAt() , endAt() y equalTo() le permite elegir puntos de inicio y finalización arbitrarios para sus consultas. Por ejemplo, si desea encontrar todos los dinosaurios que miden al menos tres metros de altura, puede combinar orderByChild() y startAt() :

Java
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Nodo.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
Vamos
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())
}

Puedes usar endAt() para encontrar todos los dinosaurios cuyos nombres vienen antes que Pterodactyl lexicográficamente:

Java
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Nodo.js
const ref = db.ref('dinosaurs');
ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
Vamos
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())
}

Puede combinar startAt() y endAt() para limitar ambos extremos de su consulta. El siguiente ejemplo encuentra todos los dinosaurios cuyo nombre comienza con la 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());
  }

  // ...
});
Nodo.js
var ref = db.ref('dinosaurs');
ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get()
for key in snapshot:
    print(key)
Vamos
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())
}

El método equalTo() le permite filtrar en función de coincidencias exactas. Como es el caso con las otras consultas de rango, se activará para cada nodo secundario coincidente. Por ejemplo, puede usar la siguiente consulta para encontrar todos los dinosaurios que miden 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());
  }

  // ...
});
Nodo.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Pitón
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
Vamos
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())
}

Las consultas de rango también son útiles cuando necesita paginar sus datos.

Poniendolo todo junto

Puede combinar todas estas técnicas para crear consultas complejas. Por ejemplo, puedes encontrar el nombre del dinosaurio que es un poco más bajo que Stegosaurus:

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) {
    // ...
  }
});
Nodo.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ón
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')
Vamos
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")
}

Cómo se ordenan los datos

Esta sección explica cómo se ordenan sus datos al usar cada una de las cuatro funciones de ordenación.

ordenar por niño

Cuando se usa orderByChild() , los datos que contienen la clave secundaria especificada se ordenan de la siguiente manera:

  1. Los elementos secundarios con un valor null para la clave secundaria especificada vienen primero.
  2. Los elementos secundarios con un valor false para la clave secundaria especificada vienen a continuación. Si varios hijos tienen un valor de false , se ordenan lexicográficamente por clave.
  3. Los elementos secundarios con un valor de true para la clave secundaria especificada vienen a continuación. Si varios hijos tienen un valor de true , se ordenan lexicográficamente por clave.
  4. Los niños con un valor numérico vienen a continuación, ordenados en orden ascendente. Si varios elementos secundarios tienen el mismo valor numérico para el nodo secundario especificado, se ordenan por clave.
  5. Las cadenas vienen después de los números y se clasifican lexicográficamente en orden ascendente. Si varios hijos tienen el mismo valor para el nodo hijo especificado, se ordenan lexicográficamente por clave.
  6. Los objetos van en último lugar y se ordenan lexicográficamente por clave en orden ascendente.

ordenar por clave

Al usar orderByKey() para ordenar sus datos, los datos se devuelven en orden ascendente por clave de la siguiente manera. Tenga en cuenta que las claves solo pueden ser cadenas.

  1. Los elementos secundarios con una clave que se puede analizar como un entero de 32 bits aparecen primero, ordenados en orden ascendente.
  2. Los niños con un valor de cadena como clave vienen a continuación, ordenados lexicográficamente en orden ascendente.

ordenar por valor

Cuando se usa orderByValue() , los elementos secundarios se ordenan por su valor. El criterio de ordenación es el mismo que en orderByChild() , excepto que se usa el valor del nodo en lugar del valor de una clave secundaria especificada.