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.
- 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.
- 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:
- Los elementos secundarios con un valor
null
para la clave secundaria especificada vienen primero. - Los elementos secundarios con un valor
false
para la clave secundaria especificada vienen a continuación. Si varios hijos tienen un valor defalse
, se ordenan lexicográficamente por clave. - Los elementos secundarios con un valor de
true
para la clave secundaria especificada vienen a continuación. Si varios hijos tienen un valor detrue
, se ordenan lexicográficamente por clave. - 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.
- 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.
- 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.
- Los elementos secundarios con una clave que se puede analizar como un entero de 32 bits aparecen primero, ordenados en orden ascendente.
- 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.