5

I hope somebody can assist me to the following implemenetation: I simply want to retrieve a random document from firestore.

Following this popular guide using the Auto-Id Version, I'm using auto generated document ids and Im saving them as field id. Firestore: How to get random documents in a collection

[..]Auto-Id version If you are using the randomly generated automatic ids provided in our client libraries, you can use this same system to randomly select a document. In this case, the randomly ordered index is the document id.[...]

It seems obvious to get a random id for the query, but for me as rookie im not sure where to get the auto id value randomly from what and how to fit in my _randomIndex without running the query. How do I have to define my _randomIndex now?

QuerySnapshot querySnapshot = await ref
        .where('id', isGreaterThanOrEqualTo: _randomIndex)
        .orderBy('id', descending: true)
        .limit(1)
        .get();

Solution

Thanks to all! I think maybe it isn't quite trivial for more people how to achieve the given approach by viewing the above guide and doing it with flutter.

Me for example hearing about uuids and random id's in flutter context, always refers me to the popular librarys such as https://pub.dev/packages/uuid/example and https://pub.dev/packages/flutter_guid/install

In my case they don't work, thanks to @Jigar Patel we can randomize it now perfectly. In the following example we can also determine the amount of documents:

import 'dart:math';

String getRandomGeneratedId() {
  const int AUTO_ID_LENGTH = 20;
  const String AUTO_ID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  
  const int maxRandom = AUTO_ID_ALPHABET.length;
  final Random randomGen = Random();
  
  String id = '';
  for (int i = 0; i < AUTO_ID_LENGTH; i++) {
    id = id + AUTO_ID_ALPHABET[randomGen.nextInt(maxRandom)];
  }
  
Future<List<Model>> getData() async {
    List<Model> dataList = [];
    CollectionReference myRef = _db.collection('data');
    final data = ['data','data']; // demo purposes only

    
    // Retrieves 2 random data in a loop
    for (int i = 0; i < 2; i++) {

      // generate a random index based on the list length
      // and use it to retrieve the element
      
      String _randomIndex = getRandomGeneratedId();
      
      QuerySnapshot querySnapshot = await myRef
        .where('data', arrayContainsAny: data)
        .where('id', isGreaterThanOrEqualTo: _randomIndex)
        .orderBy('id', descending: false)
        .limit(1)
        .get();
        
        dataList.addAll(_dataListFromSnapshot(querySnapshot));
    }
    return dataList;
  }
Marcel Dz
  • 2,321
  • 4
  • 14
  • 49
  • Hey thanks for the answer, this is what I've linked in my question. Im using the auto generated ids, how can I "define" my _randomIndex to receive a result? This is what I don't understand in the guide so far – Marcel Dz Apr 06 '21 at 09:42
  • Arf, deleted my comment, srry, I'll check how to do this – Maxouille Apr 06 '21 at 09:43

3 Answers3

3

In case of Auto-Id version, you need to create a random id (for _randomIndex) in the same way that firebase creates an id for a new document, like so.

import 'dart:math';

String getRandomGeneratedId() {
  const int AUTO_ID_LENGTH = 20;
  const String AUTO_ID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  
  const int maxRandom = AUTO_ID_ALPHABET.length;
  final Random randomGen = Random();
  
  final StringBuffer buffer = StringBuffer();
  for (int i = 0; i < AUTO_ID_LENGTH; i++) {
    buffer.write(AUTO_ID_ALPHABET[randomGen.nextInt(maxRandom)]);
  }
  
  return buffer.toString();
}
Jigar Patel
  • 4,953
  • 1
  • 12
  • 20
1

The post you linked mentions:

Later in our query section, the random value you generate is a new auto-id (iOS, Android, Web) and the field you query is the __ name __ field, and the 'low value' mentioned later is an empty string. This is by far the easiest method to generate the random index and works regardless of the language and platform.

For what I understand, you can generate a random value using the autoId() method from Util.java. This is the same method that is used by Firestore to create a document Id. It will return a random String corresponding to a random Id. This Id does not match with any of your documents (except if you have no luck and that the method generate a random Id already assign to a document). Then, the comparison between Ids is done with the following code with random set to the new generated Id:

queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: random)
                   .order(by: "random")
                   .limit(to: 1)
Maxouille
  • 2,729
  • 2
  • 19
  • 42
  • yes youre describing the Random Integer version, I'm trying to make the Auto-Id version working. (I want to avoid adding another field on my documents) This is where im stuck. I don't understand the approach with the given informations. Could you assist me there? the document ids are some kind of uuids like: DBkRLBNbXvJ6pK3aFiI2 – Marcel Dz Apr 06 '21 at 09:50
  • Edited my post, I think this is what you want though I wasn't able to test it. – Maxouille Apr 06 '21 at 09:59
  • great, thank you! this makes it a bit more clear. Im using flutter i just implemented this package here https://pub.dev/packages/uuid/example for generating a uuid, it seems that im still get the same results, do you have any idea why? – Marcel Dz Apr 06 '21 at 10:17
1

I don't Know if that's the best solution , what really crossed my mind is something less complex like this:

1: Get all the document of specific collection from firestore .

2: get them all into a list .

3: shuffle the whole list and get the first index .

i think it will be something like this :

import 'dart:math'
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
List allDoc = [];
class AllDocs{
  String collection = "collectionName";
  FirebaseFirestore _firestore = FirebaseFirestore.instance;
  Future<List> getAllDocs()async{
    _firestore.collection(collection).get().then((result) {
      for(DocumentSnapshot docss in result.docs ){
        allDoc.add(Model.fromSnapshot(docss));
      }
      return allDoc;
    });
  }
final _random = Random();
var element = allDoc[_random.nextInt(list.length)];
print (element);
}

        
Fahmi Sawalha
  • 682
  • 6
  • 19
  • 1
    Thanks for the reply, this is propably working, but you have to receive all the documents (let's say there are 1.000.000 docs) and you then have to pay for 1.000.000 doc reads to get the total amount (In sql it would be easy with .sum) Thats why aggregation is important in nosql context. Following the guys guide (And everyone except me seems like understanding what he is doing) In the auto-id version you can determine a random document if the docs are generated by firestore without saving another field on the document. I just don't understand how he gets this information – Marcel Dz Apr 06 '21 at 09:59
  • in that case i think that @Maxouille will do the trick – Fahmi Sawalha Apr 06 '21 at 10:08