Consulta basada en las cláusulas de múltiple where en Firebase

firebase firebase-realtime-database


{
"movies": {
    "movie1": {
        "genre": "comedy",
        "name": "As good as it gets",
        "lead": "Jack Nicholson"
    },
    "movie2": {
        "genre": "Horror",
        "name": "The Shining",
        "lead": "Jack Nicholson"
    },
    "movie3": {
        "genre": "comedy",
        "name": "The Mask",
        "lead": "Jim Carrey"
    }
  }  
 }

Soy un novato de Firebase. ¿Cómo puedo recuperar un resultado de los datos anteriores donde genre = 'comedy' Y lead = 'Jack Nicholson' ?

¿Qué opciones tengo?





Answer 1 Frank van Puffelen


Usando la API de consulta de Firebase , puede tener la tentación de probar esto:

// !!! THIS WILL NOT WORK !!!
ref
  .orderBy('genre')
  .startAt('comedy').endAt('comedy')
  .orderBy('lead')                  // !!! THIS LINE WILL RAISE AN ERROR !!!
  .startAt('Jack Nicholson').endAt('Jack Nicholson')
  .on('value', function(snapshot) { 
      console.log(snapshot.val()); 
  });

Pero como @RobDiMarco de la Base de Fuego dice en los comentarios:

múltiples llamadas de orderBy() arrojarán un error

Entonces mi código anterior no funcionará .

Sé de tres enfoques que funcionarán.

1.filtrar la mayoría en el servidor,hacer el resto en el cliente

Lo que puede hacer es ejecutar un orderBy().startAt()./endAt() en el servidor, extraiga los datos restantes y filtre eso en el código JavaScript en su cliente.

ref
  .orderBy('genre')
  .equalTo('comedy')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      if (movie.lead == 'Jack Nicholson') {
          console.log(movie);
      }
  });

2.Añade una propiedad que combina los valores que quieres filtrar

Si eso no es suficiente,debería considerar modificar y ampliar sus datos para permitir su uso.Por ejemplo:podrías rellenar género+liderazgo en una sola propiedad que sólo usas para este filtro.

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_lead": "comedy_Jack Nicholson"
},...

Esencialmente estás construyendo tu propio índice multicolumna de esa manera y puedes consultarlo con:

ref
  .orderBy('genre_lead')
  .equalTo('comedy_Jack Nicholson')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      console.log(movie);
  });

David East ha escrito una biblioteca llamada QueryBase que ayuda a generar tales propiedades .

Incluso podrías hacer consultas de rango relativo,digamos que quieres permitir la consulta de películas por categoría y año.Usarías esta estructura de datos:

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_year": "comedy_1997"
},...

Y luego buscar comedias de los 90 con:

ref
  .orderBy('genre_year')
  .startAt('comedy_1990')
  .endAt('comedy_2000')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      console.log(movie);
  });

Si necesita filtrar más allá del año, asegúrese de agregar las otras partes de la fecha en orden descendente, por ejemplo, "comedy_1997-12-25" . De esta forma, el orden lexicográfico que Firebase hace en los valores de cadena será el mismo que el orden cronológico.

Esta combinación de valores en una propiedad puede funcionar con más de dos valores,pero sólo se puede hacer un filtro de rango en el último valor de la propiedad compuesta.

Una variante muy especial de esto es implementada por la biblioteca GeoFire para Firebase . Esta biblioteca combina la latitud y la longitud de una ubicación en un llamado Geohash , que luego se puede usar para realizar consultas de rango en tiempo real en Firebase.

3.crear un índice personalizado de forma programada

Otra alternativa es hacer lo que todos hemos hecho antes de que se añadiera esta nueva API de consulta:crear un índice en un nodo diferente:

  "movies"
      // the same structure you have today
  "by_genre"
      "comedy"
          "by_lead"
              "Jack Nicholson"
                  "movie1"
              "Jim Carrey"
                  "movie3"
      "Horror"
          "by_lead"
              "Jack Nicholson"
                  "movie2"

Probablemente hay más enfoques. Por ejemplo, esta respuesta destaca un índice personalizado alternativo en forma de árbol: https://stackoverflow.com/a/34105063




Answer 2 David East


He escrito una biblioteca personal que le permite ordenar por múltiples valores,con todo el ordenamiento hecho en el servidor.

¡Conoce Querybase!

La base de datos Querybase toma una referencia de la base de datos de la base de fuego y una serie de campos que desea indexar.Cuando se crean nuevos registros,automáticamente se encargará de la generación de claves que permitan realizar múltiples consultas.La salvedad es que sólo admite equivalencia directa (no menor o mayor que).

const databaseRef = firebase.database().ref().child('people');
const querybaseRef = querybase.ref(databaseRef, ['name', 'age', 'location']);

// Automatically handles composite keys
querybaseRef.push({ 
  name: 'David',
  age: 27,
  location: 'SF'
});

// Find records by multiple fields
// returns a Firebase Database ref
const queriedDbRef = querybaseRef
 .where({
   name: 'David',
   age: 27
 });

// Listen for realtime updates
queriedDbRef.on('value', snap => console.log(snap));



Answer 3 Andrew Lam Yat Weng


var ref = new Firebase('https://your.firebaseio.com/');

Query query = ref.orderByChild('genre').equalTo('comedy');
query.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot movieSnapshot : dataSnapshot.getChildren()) {
            Movie movie = dataSnapshot.getValue(Movie.class);
            if (movie.getLead().equals('Jack Nicholson')) {
                console.log(movieSnapshot.getKey());
            }
        }
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {

    }
});



Answer 4 Tidder Jail


La respuesta de Frank es buena, pero Firestore introdujo array-contains recientemente que hace que sea más fácil hacer Y consultas.

Puede crear un campo de filters para agregar sus filtros. Puede agregar tantos valores como necesite. Por ejemplo, para filtrar por comedia y Jack Nicholson , puede agregar el valor comedy_Jack Nicholson pero si también lo desea por comedia y 2014 , puede agregar el valor comedy_2014 sin crear más campos.

{
"movies": {
    "movie1": {
        "genre": "comedy",
        "name": "As good as it gets",
        "lead": "Jack Nicholson",
        "year": 2014,
        "filters": [
          "comedy_Jack Nicholson",
          "comedy_2014"
        ]
    }
  }  
}



Answer 5 mayur


La Base de Fuego no permite consultas con múltiples condiciones.Sin embargo,encontré una forma de evitarlo:

Necesitamos descargar los datos filtrados iniciales de la base de datos y almacenarlos en una lista de matriz.

                Query query = databaseReference.orderByChild("genre").equalTo("comedy");
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                        ArrayList<Movie> movies = new ArrayList<>();
                        for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
                            String lead = dataSnapshot1.child("lead").getValue(String.class);
                            String genre = dataSnapshot1.child("genre").getValue(String.class);

                            movie = new Movie(lead, genre);

                            movies.add(movie);

                        }

                        filterResults(movies, "Jack Nicholson");

                        }

                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {

                    }
                });

Una vez que obtengamos los datos filtrados iniciales de la base de datos, necesitamos hacer más filtros en nuestro backend.

public void filterResults(final List<Movie> list,  final String genre) {
        List<Movie> movies = new ArrayList<>();
        movies = list.stream().filter(o -> o.getLead().equals(genre)).collect(Collectors.toList());
        System.out.println(movies);

        employees.forEach(movie -> System.out.println(movie.getFirstName()));
    }