Recuperando datos

Este documento cubre los conceptos básicos de la recuperación de datos de bases 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 los diferentes lenguajes de programación.

  1. Oyentes asincrónicos: los datos almacenados en Firebase Realtime Database se recuperan adjuntando un oyente asincrónico a una referencia de base de datos. El oyente se activa una vez para el estado inicial de los datos y nuevamente cada vez que cambian los datos. 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. Lecturas de bloqueo: los datos almacenados en una base de datos en tiempo real de Firebase 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 a un 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 los SDK de Python y Go Admin.

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 del blog en la aplicación de ejemplo se almacenan en la URL de la base de datos https://docs-examples.firebaseio.com/server/served-data/fireblog/posts.json . Para leer los datos de tu publicación, puedes 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())
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)
}

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 momento único. Llamar a val() / getValue() en una instantánea devuelve una representación de los datos de un objeto específico del idioma. 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 descompone los datos en una estructura de datos determinada.

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

Leer tipos de eventos en Java y Node.js

Valor

El evento value se utiliza para leer una instantánea estática del contenido en una ruta de base de datos determinada, tal como existía en el momento del evento de lectura. Se activa una vez con los datos iniciales y nuevamente cada vez que los datos cambian. A la devolución de llamada del evento se le pasa una instantánea que contiene todos los datos en esa ubicación, incluidos los datos secundarios. En el ejemplo de código anterior, value devolvió todas las publicaciones del blog de 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 agregado

El evento child_added se utiliza normalmente al recuperar una lista de elementos de la base de datos. A diferencia value que devuelve todo el contenido de la ubicación, child_added se activa una vez por cada hijo existente y luego nuevamente cada vez que se agrega un nuevo hijo 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 orden, también se 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 publicaciones en objetos recuperando el valor, usted tiene acceso a las propiedades de autor y título de la publicación llamando a author y 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 hijo. Normalmente se utiliza junto con child_added y child_removed para responder a cambios en una lista de elementos. La instantánea pasada a la devolución de llamada del evento contiene los datos actualizados del niño.

Puede utilizar 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 niño inmediato. Normalmente se utiliza 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, puedes 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 utiliza cuando se trabaja con datos ordenados, lo cual 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.
Los eventos siempre reflejarán eventualmente el estado correcto de los datos, incluso en los casos en que las operaciones locales o el tiempo causen diferencias temporales, como en la pérdida temporal de la conexión de red.
Las escrituras de un único 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 esa 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);
});

Desvincular 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 se muestra a continuación:

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

Leer datos una vez

En algunos casos, puede resultar útil llamar una vez a una devolución de llamada y luego eliminarla inmediatamente. Hemos creado una función auxiliar 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())
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)
}

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 datos sobre dinosaurios para demostrar cómo puedes consultar datos en tu 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 básica de base de datos comienza con una de estas funciones de orden, cada una de las cuales se explica a continuación.

Ordenar por una clave secundaria específica

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

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 hijos profundamente anidados, en lugar de solo por hijos 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, puedes 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))
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)
}

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

Ordenar por clave

También puedes ordenar los nodos por sus claves usando 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)
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)

Ordenar por valor

Puede ordenar los nodos por el valor de sus claves secundarias utilizando el método orderByValue() . Digamos que los dinosaurios están teniendo una competencia deportiva de dinosaurios y usted realiza un seguimiento 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 puntuación, puedes 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))
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 la sección Cómo se ordenan los datos para obtener una explicación sobre cómo se ordenan los valores null , booleanos, de cadena y de objeto cuando se utiliza orderByValue() .

Consultas complejas

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

Limitar consultas

Las consultas 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 los elementos cambien, 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 permanezca en 100.

Usando la base de datos de datos de dinosaurios y orderByChild() , puedes 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)
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())
}

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 será despedido 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)
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())
}

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 disparará 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ímites con orderByValue() . Si desea crear una tabla de clasificación con los 3 dinosaurios deportivos 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))
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 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)
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())
}

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

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

Puede combinar startAt() y endAt() para limitar ambos extremos de su consulta. El siguiente ejemplo busca 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)
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())
}

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

Las consultas de rango también son útiles cuando necesitas paginar tus 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')
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")
}

Cómo se ordenan los datos

Esta sección explica cómo se ordenan sus datos cuando se utiliza cada una de las cuatro funciones de pedido.

ordenar por niño

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

  1. Los niños con un valor null para la clave secundaria especificada son lo primero.
  2. Los niños con un valor false para la clave secundaria especificada son los siguientes. Si varios hijos tienen un valor false , se ordenan lexicográficamente por clave.
  3. Los niños con un valor true para la clave secundaria especificada son los siguientes. Si varios hijos tienen un valor 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 hijos tienen el mismo valor numérico para el nodo hijo especificado, se ordenan por clave.
  5. Las cadenas van después de los números y están ordenadas 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 al final y se ordenan lexicográficamente por clave en orden ascendente.

ordenarPorClave

Cuando se utiliza orderByKey() para ordenar sus datos, los datos se devuelven en orden ascendente por clave de la siguiente manera. Tenga en cuenta que las claves sólo pueden ser cadenas.

  1. Los niños 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 niños se ordenan por su valor. El criterio de ordenación es el mismo que en orderByChild() , excepto que se utiliza el valor del nodo en lugar del valor de una clave secundaria especificada.