Prueba Cloud Firestore: Descubre la base de datos flexible y escalable de Firebase y Google Cloud Platform. Obtén más información sobre Cloud Firestore.

Recupera datos

En este documento se abarcan 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 estos. La recuperación de datos en el SDK de Admin se implementa de forma ligeramente diferente en distintos lenguajes de programación.

  1. Receptores asíncronos: para recuperar los datos almacenados en Firebase Realtime Database, se debe adjuntar un agente de escucha asíncrono a la referencia de una base de datos. El agente de escucha se activa una vez para el estado inicial de los datos y otra vez cuando los datos cambian. Los agentes de escucha de eventos pueden recibir varios tipos diferentes de eventos. Este modo de recuperación de datos es compatible con Java, Node.js y los SDK de Admin de Python.
  2. Bloqueo de lecturas: para recuperar los datos almacenados en Firebase Realtime Database, se debe invocar un método de bloqueo sobre una referencia de base de datos. De esta forma, se obtienen los datos almacenados en la referencia. Cada llamada de método es una operación única. Esto 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 Admin de Python y Go.

Cómo comenzar

Revisemos nuevamente el ejemplo del blog del artículo anterior para comprender cómo leer datos desde una base de datos de Firebase. Recuerda que las entradas de blog de la app 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 tu entrada, 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());
  }
});
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)
}

Si ejecutas el código anterior, verás un objeto que contiene todas tus entradas registradas en la consola. En el caso de Node.js y Java, la función de agente de escucha se llama cada vez que se agregan datos nuevos en tu referencia de la base de datos; no es necesario que escribas más código para que esto suceda.

En Java y Node.js, la función de devolución de llamada recibe una DataSnapshot, que es una instantánea de los datos. Una instantánea es una imagen de los datos en una referencia particular de base de datos en un momento específico. Si se llama a val() / getValue() en una instantánea, se obtendrá la representación de los datos como objeto específico de 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 muestra una representación de Python de los datos directamente. La función Get() en Go desmarca los datos en una estructura de datos dada.

Ten en cuenta que usamos el tipo de evento value en el ejemplo anterior, lo que permite leer todo el contenido de una referencia de base de datos de Firebase, incluso si solo se modificó una parte de los datos. value es uno de los cinco tipos de eventos que se indican a continuación y que podemos usar para leer el contenido de la base de datos.

Cómo leer tipos de eventos en Java y Node.js

Valor

El evento value se usa para leer una instantánea estática del contenido de la ruta de una base de datos, tal como existía cuando se produjo el evento de lectura. Se activa una vez con el estado inicial de los datos y nuevamente cada vez que estos se cambian. La devolución de llamada de evento recibe una instantánea que contiene todos los datos de dicha ubicación, incluidos los datos de campos secundarios. En el ejemplo de código anterior, value mostró todas las entradas de blog en tu app. Cada vez que se agrega una nueva entrada de blog, la función de la devolución de llamada mostrará todas las entradas.

Child Added

Generalmente, el evento child_added se usa para recuperar una lista de elementos de la base de datos. A diferencia de value, que permite obtener todo el contenido de la ubicación indicada, child_added se activa una vez por cada campo secundario y cada vez que se agregue un nuevo campo secundario en la ruta especificada. La devolución de llamada del evento recibe una instantánea que contiene los datos del nuevo campo secundario. Para establecer un orden, también se recibe un segundo argumento que contiene la clave del anterior campo secundario.

Si deseas recuperar solo los datos de cada nueva entrada agregada a tu aplicación de blogs, puedes utilizar 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);
});

En este ejemplo, la instantánea contendrá un objeto con una entrada de blog individual. Debido a que el SDK convierte entradas en objetos mediante la recuperación del valor, puedes acceder a las propiedades de autor y título de la entrada llamando a author y a title, respectivamente. También tienes acceso al ID de la entrada anterior a partir del segundo argumento prevChildKey.

Child Changed

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

Puedes usar child_changed para leer datos actualizados en entradas 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) {}
});
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

El evento child_removed se activa cuando se quita un campo secundario inmediato. Generalmente, se usa junto con child_added y child_changed. La instantánea que se pasa a la devolución de llamada de eventos contiene los datos para el campo secundario que se quitó.

En el ejemplo del blog, puedes usar child_removed para registrar una notificación acerca de 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) {}
});
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

El evento child_moved se usa cuando se trabaja con datos ordenados. Este tema se aborda en la sección siguiente.

Garantías de evento

La base de datos de Firebase otorga varias garantías importantes acerca de los eventos:

Garantías de eventos de base de datos
Los eventos siempre se activan cuando el estado local se modifica.
Los eventos siempre reflejarán el estado correcto de los datos, incluso cuando las operaciones o tiempos locales generen diferencias temporales, como durante una pérdida temporal de la conexión de red.
Las escrituras desde un cliente único siempre se realizan en el servidor y se navegan en otros usuarios en orden.
Los eventos value siempre se activan al final y se garantiza que contienen actualizaciones de cualquier otro evento que haya ocurrido antes de que se tomara esa instantánea.

Debido a que los eventos value 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) {}
});
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);
});

Desconexión de devolución de llamada

Las devoluciones de llamada se quitan al especificar el tipo de evento y la función de la devolución de llamada a eliminar, de la siguiente manera:

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

// Remove listener
ref.removeEventListener(listener);
Node.js
ref.off("value", originalCallback);

Si pasaste un contexto de ámbito a on(), se debe transmitir cuando se desconecta la devolución de llamada:

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

Si quieres quitar todas las devoluciones de llamada en una ubicación, puedes hacer lo siguiente:

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

Lectura de datos una sola vez

En algunos casos, puede resultar útil invocar una devolución de llamada una sola vez y luego quitarla de inmediato. Hemos creado una función de ayuda para simplificar este proceso:

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 datos

La base de datos de Firebase permite realizar consultas que permiten recuperar datos según varios factores. Para crear una consulta en la base de datos, primero se especifica cómo se quiere ordenar los datos mediante una de las funciones de orden: orderByChild(), orderByKey() u orderByValue(). Estas funciones se pueden combinar con otros cinco métodos para realizar consultas complejas: limitToFirst(), limitToLast(), startAt(), endAt() y equalTo().

En Firebase, a todos nos gustan los dinosaurios. A continuación, usaremos un fragmento de una base de datos de muestra que contiene información sobre dinosaurios para demostrar cómo realizar consultas:

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

Puedes ordenar los datos por child key, key o value. Una consulta básica de base de datos comienza con una de estas funciones de ordenamiento, cada una de las cuales se explica a continuación.

Ordenamiento con una clave secundaria especificada

Para ordenar los nodos por una clave secundaria común, puedes pasar esa clave a orderByChild(). Por ejemplo, para ver 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.");
  }

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

Cualquier nodo que no tenga la clave secundaria que estamos consultando se ordena con el valor null; es decir, aparecerá primero en el orden. Para saber cómo se ordenan los datos, revisa la sección Cómo se ordenan los datos.

Las consultas también se pueden ordenar por campos secundarios profundamente anidados, en lugar de únicamente por campos secundarios de un nivel inferior. Esto resulta útil si tienes datos profundamente anidados, como estos:

{
  "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 de acceso completa del objeto en lugar de una única clave:

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

Las consultas solo pueden ordenarse por una clave a la vez. Si se llama a orderByChild() varias veces en la misma consulta, se produce un error.

Ordenamiento por clave

También puedes usar el método orderByKey() para ordenar los nodos por sus claves. 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());
  }

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

Ordenamiento por valor

Puedes ordenar los nodos por el valor de sus claves secundarias a través del método orderByValue(). Supongamos que los dinosaurios están realizando una competencia deportiva y que llevas un registro de sus puntajes con el siguiente formato:

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

Para ordenar los dinosaurios por sus puntajes, puedes realizar 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());
  }

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

Consulta la sección Cómo se ordenan los datos para ver una explicación sobre cómo se ordenan los valores null, booleanos, de string y de objeto cuando se usa orderByValue().

Consultas complejas

Ahora que sabes cómo se ordenan tus datos, puedes usar los métodos de límite o rango que se describen a continuación para realizar consultas más complejas.

Consultas de límites

Las consultas limitToFirst() y limitToLast() se utilizan para definir una cantidad máxima de elementos secundarios que se sincronizarán para una devolución de llamada específica. Si estableces un límite de 100, inicialmente obtendrás un máximo de 100 eventos child_added. Si tienes menos de 100 mensajes almacenados en tu base de datos, se activará un evento child_added por cada uno de ellos. Sin embargo, si tienes más de 100 mensajes, solo recibirás un evento child_added por 100 de ellos. Estos son los primeros 100 mensajes ordenados si usas limitToFirst() o los últimos 100 mensajes ordenados si usas limitToLast(). A medida que cambien los elementos, recibirás eventos child_added por los elementos que ingresen en la consulta y eventos child_removed por los elementos que la dejen, de modo que la cantidad total siga siendo 100.

Para buscar los dos dinosaurios de mayor peso, puedes usar la base de datos de características de los dinosaurios y orderByChild().

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

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á para cada nuevo dinosaurio más pesado que se agregue a la base de datos. En Python, la consulta muestra directamente un OrderedDict que contiene los dos dinosaurios más pesados.

Asimismo, puedes usar limitToFirst() para encontrar los dos dinosaurios más chicos:

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

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á si se quita uno de los dos primeros dinosaurios de la base de datos, ya que ahora un nuevo dinosaurio será el segundo más chico. En Python, la consulta muestra directamente un OrderedDict que contiene los dos dinosaurios más chicos.

También puedes realizar consultas con límites a través de orderByValue(). Si deseas crear una tabla de posiciones con los 3 dinosaurios que obtuvieron los mejores puntajes en la competencia deportiva, podrías 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());
  }

  // ...
});
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 por rango

Para seleccionar puntos arbitrarios de inicio y finalización de tus consultas, puedes usar startAt(), endAt() y equalTo(). Por ejemplo, si deseas buscar todos los dinosaurios que miden al menos tres metros de altura, puedes 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());
  }

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

Puedes usar endAt() para encontrar todos los dinosaurios cuyos nombres antecedan al pterodáctilo, lexicográficamente. En el siguiente ejemplo, se usa el término "Pterodactyl", en inglés:

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

Puedes combinar startAt() y endAt() para limitar ambos extremos de la consulta. El siguiente ejemplo permite encontrar todos los dinosaurios cuyos nombres empiecen 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());
  }

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

El método equalTo() te permite filtrar basándote en coincidencias exactas. Como sucede con las otras consultas por rango, se activará por cada nodo de campo secundario que se active. Por ejemplo, puedes usar la siguiente consulta para encontrar todos los dinosaurios que tengan una altura de 25 metros:

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

Las consultas por rango también son útiles cuando debes paginar tus datos.

Revisión general

Puedes combinar todas estas técnicas para crear consultas complejas. Por ejemplo, puedes encontrar el nombre del dinosaurio cuya altura es inferior a la del estegosaurio:

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

Cómo se ordenan los datos

En esta sección se explica la manera en la que los datos se ordenan al usar cada una de las cuatro funciones de ordenamiento.

orderByChild

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

  1. Los campos secundarios cuyas claves secundarias especificadas posean el valor null irán en primer lugar.
  2. Los campos secundarios cuyas claves secundarias especificadas posean el valor false vienen después. Si hay varios elementos secundarios con el valor false, se ordenan de manera lexicográfica por clave.
  3. Los campos secundarios cuyas claves secundarias especificadas posean el valor true vienen después. Si varios campos secundarios poseen el valor true, se ordenan lexicográficamente por clave.
  4. Luego vienen los campos secundarios con valor numérico, que se ordenan en sentido ascendente. Si varios campos secundarios tienen el mismo valor numérico para el nodo secundario especificado, se ordenan por clave.
  5. Las strings van después de los números y se ordenan de manera lexicográfica, en sentido ascendente. Si varios campos secundarios tienen el mismo valor para el nodo secundario especificado, se ordenan de manera lexicográfica por clave.
  6. Los objetos van al final y se ordenan de manera lexicográfica por clave, en sentido ascendente.

orderByKey

Cuando se usa orderByKey() para ordenar los datos, estos se muestran en sentido ascendente por clave, como se indica a continuación. Ten en cuenta que las claves solo pueden ser strings.

  1. Los campos secundarios con una clave que pueden analizarse como un elemento entero de 32 bits van primero, ordenados en sentido ascendente.
  2. Los campos secundarios con un valor de string como clave van después y ordenados de manera lexicográfica, en sentido ascendente.

orderByValue

Cuando usas orderByValue(), los elementos secundarios se ordenan según el valor. Se utiliza el mismo criterio de orden que para orderByChild(), excepto que se usa el valor del nodo en lugar del valor de una clave secundaria especificada.

Enviar comentarios sobre…

Firebase Realtime Database
¿Necesitas ayuda? Visita nuestra página de asistencia.