从Firestore中选择随机文档

时间:2018-01-22 08:29:57

标签: java android firebase google-cloud-firestore

我在Cloud Firestore的单个集合中有1000个文档,是否可以获取随机文档?

比如说:Students是Firestore中的一个集合,我在该集合中有1000名学生,我的要求是在每次通话时随机挑选10名学生。

6 个答案:

答案 0 :(得分:1)

是的,为此,请使用以下代码:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference studentsCollectionReference = rootRef.collection("students");
studentsCollectionReference.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            List<Students> studentList = new ArrayList<>();
            for (DocumentSnapshot document : task.getResult()) {
                Student student = document.toObject(Student.class);
                studentList.add(student);
            }

            int studentCount = studentList.size();
            int randomNumber = new Random().nextInt(studentCount);

            List<Students> randomStudentList = new ArrayList<>();
            for(int i = 1; i < 10; i++) {
                randomStudentList.add(studentList.get(randomNumber));
            }
        } else {
            Log.d(TAG, "Error getting documents: ", task.getException());
        }
    }
});

答案 1 :(得分:1)

根据Alex's answer,我得到了从Firebase Firestore数据库中获取重复记录的提示(特别是少量数据)

我在他的问题中遇到了一些问题,如下:

  • 它提供与do $$ declare selectrow record; begin for selectrow in select 'ALTER TABLE '|| T.mytable || ' ADD COLUMN x_is_exported boolean DEFAULT FALSE' as script from (select tablename as mytable from pg_tables where schemaname ='public') t loop begin execute selectrow.script; EXCEPTION WHEN duplicate_column THEN CONTINUE; END; end loop; end; $$; 相同的所有记录,而不会更新。
  • 即使我们每次更新randomNumber,最终列表中的记录也可能重复。
  • 它可能已经显示了重复的记录。

我将答案更新如下:

randomNumber

希望这种逻辑对所有数据量少的人有帮助,我认为这不会对1000到5000个数据造成任何问题。

谢谢。

答案 2 :(得分:0)

Alex Mamo描述的第二种方法看起来与此类似:

  1. 获取具有存储的文档ID的数组列表
  2. 从该列表中获取许多字符串(我将文档ID存储为字符串)

在下面的代码中,您从数组中获得3个随机且唯一的字符串,并将其存储在列表中,您可以从中访问字符串并进行查询。我正在片段中使用此代码:

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_category_selection, container, false);

        btnNavFragCat1 = view.findViewById(R.id.btn_category_1);

        btnNavFragCat1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                questionKeyRef.document(tvCat1).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                        if (task.isSuccessful()) {

                            DocumentSnapshot document = task.getResult();
                            List<String> questions = (List<String>) document.get("questions"); // This gets the array list from Firestore

                            List<String> randomList = getRandomElement(questions, 0);

                            removeDuplicates(randomList);

                            ...
                        }
                    }
                });

            }
        });

        ...

        return view;
    }

    private List<String> getRandomElement(List<String> list, int totalItems) {
        int PICK_RANDOM_STRING = 3;
        Random rand = new Random();
        List<String> newList = new ArrayList<>();
        int count = 0;
        while (count < PICK_RANDOM_STRING) {

            int randomIndex = rand.nextInt(list.size());
            String currentValue = list.get(randomIndex);
            if (!newList.contains(currentValue)) {
                newList.add(currentValue);
                count++;
            }
        }

        return newList;
    }

    private void removeDuplicates(List<String> list) {
        try {
            Log.e("One", list.get(0));
            Log.e("Two", list.get(1));
            Log.e("Three", list.get(2));

            query1 = list.get(0); // In this vars are the strings stored with them you can then make a normal query in Firestore to get the actual document
            query2 = list.get(1);
            query3 = list.get(2);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

这是我从Firestore获得的数组: enter image description here

答案 3 :(得分:0)

我遇到了类似的问题(我只需要每24小时获取一个随机文档,或者当用户手动刷新页面时也可以,但是您也可以将这种解决方案应用于您的案例),对我有用的是:

技术

  1. 第一次阅读一小部分文档,比如说1到10个文档(在您的情况下为10到30或50)。
  2. 根据文档列表范围内随机生成的编号选择随机文档。
  3. 在客户端设备上本地保存您选择的文档的最后一个ID(也许像我一样在共享首选项中保存)。
  4. 如果您想要一个新的随机文档,则将使用保存的文档ID在保存的文档ID之后重新开始该过程(步骤1至3),这将排除之前出现的所有文档。
  5. 重复该过程,直到保存的文档ID之后没有更多的文档,然后假设这是您第一次运行此算法,则从头重新开始(通过将保存的文档ID设置为null并再次启动该过程(步骤1至4)。

技术的利与弊

优点:

  1. 每次获取新的随机文档时,您都可以确定跳转大小。
  2. 无需修改对象的原始模型类。
  3. 无需修改您已经拥有或设计的数据库。
  4. 向集合中添加新文档(如上述here的解决方案)时,无需在集合中添加文档,也无需为每个文档添加随机ID。
  5. 无需加载大量文档列表即可仅获取一个文档或小型文档列表,
  6. 如果您使用的是Firestore自动生成的ID,则效果很好(因为集合中的文档已经稍微随机化了)
  7. 如果您想要一个随机文档或一小份随机文档列表,效果很好。
  8. 可在所有平台(包括iOS,Android,Web)上使用。

缺点

  1. 处理保存文档ID以便在下一次获取随机文档的请求中使用(比处理每个文档中的新字段或将集合中每个文档的ID添加到新文档中的处理要好。主要收藏)
  2. 如果列表不够大(我认为这不是问题)并且我没有找到能完全避免这种情况的解决方案,则可能会多次获得一些文档。

实现(Android上的kotlin):

var documentId = //get document id from shared preference (will be null if not set before)
getRandomDocument(documentId)

fun getRandomDocument(documentId: String?) {
    if (documentId == null) {
        val query = FirebaseFirestore.getInstance()
                .collection(COLLECTION_NAME)
                .limit(getLimitSize())
        loadDataWithQuery(query)
    } else {
        val docRef = FirebaseFirestore.getInstance()
                .collection(COLLECTION_NAME).document(documentId)
        docRef.get().addOnSuccessListener { documentSnapshot ->
            val query = FirebaseFirestore.getInstance()
                    .collection(COLLECTION_NAME)
                    .startAfter(documentSnapshot)
                    .limit(getLimitSize())
            loadDataWithQuery(query)
        }.addOnFailureListener { e ->
            // handle on failure
        }
    }
}

fun loadDataWithQuery(query: Query) {
    query.get().addOnSuccessListener { queryDocumentSnapshots ->
        val documents = queryDocumentSnapshots.documents
        if (documents.isNotEmpty() && documents[documents.size - 1].exists()) {
            //select one document from the loaded list (I selected the last document in the list)
            val snapshot = documents[documents.size - 1]
            var documentId = snapshot.id
            //SAVE the document id in shared preferences here
            //handle the random document here
        } else {
            //handle in case you reach to the end of the list of documents
            //so we start over again as this is the first time we get a random document
            //by calling getRandomDocument() with a null as a documentId
            getRandomDocument(null)
        }
    }
}

fun getLimitSize(): Long {
    val random = Random()
    val listLimit = 10
    return (random.nextInt(listLimit) + 1).toLong()
}

答案 4 :(得分:0)

基于@ajzbc answer,我为Unity3D编写了此代码,并为我工作。

FirebaseFirestore db;

    void Start()
    {
        db = FirebaseFirestore.DefaultInstance;
    }

    public void GetRandomDocument()
    {

       Query query1 = db.Collection("Sports").WhereGreaterThanOrEqualTo(FieldPath.DocumentId, db.Collection("Sports").Document().Id).Limit(1);
       Query query2 = db.Collection("Sports").WhereLessThan(FieldPath.DocumentId, db.Collection("Sports").Document().Id).Limit(1);

        query1.GetSnapshotAsync().ContinueWithOnMainThread((querySnapshotTask1) =>
        {

             if(querySnapshotTask1.Result.Count > 0)
             {
                 foreach (DocumentSnapshot documentSnapshot in querySnapshotTask1.Result.Documents)
                 {
                     Debug.Log("Random ID: "+documentSnapshot.Id);
                 }
             } else
             {
                query2.GetSnapshotAsync().ContinueWithOnMainThread((querySnapshotTask2) =>
                {

                    foreach (DocumentSnapshot documentSnapshot in querySnapshotTask2.Result.Documents)
                    {
                        Debug.Log("Random ID: " + documentSnapshot.Id);
                    }

                });
             }
        });
    }

答案 5 :(得分:-1)

首先,您应该手动为每个学生文档分配一个ID,这意味着您在存储文档时不应使用add()方法。相反,请使用set()方法:

FirebaseFirestore db = FirebaseFirestore.getInstance().
db.collection("students").document(studentId).set(studentObj);

其次,要查询​​10个文档,您应该使用ID生成器和for循环分别显式调用每个文档:

for(int i = 0; i<10;i++){
  int studentRandomId = (int) Math.random()*1000;  // multiplied by 1000 since you have 1000 student documents
  db.collection("students").document(studentRandomId).addSnapshotListener(new EventListener<DocumentSnapshot>() {
    @Override
      public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
        randomStudentList.add(documentSnapshot.toObject(Student.class))
      }
  });
}

注意:您必须为学生的ID和已知范围使用连续排序的编号。