具有复杂过滤器和复合索引的Firestore [英] Firestore with complex filter and composite indexes

查看:45
本文介绍了具有复杂过滤器和复合索引的Firestore的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Cloud Firestore,这是我的数据结构:

I use Cloud Firestore and this is my data structure :

   Firestore-root
   |
   --- collection (collection)
         |
         --- uid (document)
              |
              --- name: some_name
              |
              --- some_fields: some_data
              |
              --- origin: [origin_1, origine_2, ...] <- List
              |
              --- category: [category_1, category_2, ...] <- List
              |
              --- meals: [meals_1, meals_2, ...] <- List
              |
              --- some_other_filters: [.....] <- List
              |
              --- all_categories: [origin_1:true, origine_2:true, meals_1:true, ....]

我创建了Map字段all_categories来容纳所有不同的过滤器(来源,类别等),这样我就可以轻松地执行查询,例如:如果用户选择了foods_4和category_2,category_6和some_other_filter,我可以这样做:

I created the Map field all_categories to hold all different filters (origin, category, ...) so that I can perform queries easily like: if user select meals_4 and category_2, category_6 and some_other_filter I can do:

List<String> filters = getFilters(); 

query = recipeRepository.getUsersRecipeCollection()
            .orderBy(sorted_filter, DESCENDING).limit(5);

for (String item: filters)
    query = query.whereEqualTo("all_categories."+item, true);

query.get().addOnCompleteListener(...)

我不能使用 whereArrayContainsAny 函数,因为我需要根据选定的过滤器获取确切的项目.我需要的是"whereArrayContainsExactly .."

I cannot use the whereArrayContainsAny function since I need to get the exact items based on selected filters. What I need is something like "whereArrayContainsExactly.."

因此,例如,如果用户从我的过滤器页面中选择了1、2,..或25个不同的项目(例如:category_1,meats_2,origin_1,...),这是否意味着我需要创建所有25个组合综合索引的数量(通过我从控制台获得的链接)?还是我为该地图字段创建25个索引?

So, for example, if a user select 1, 2,.. or 25 different items from my filter page (like: category_1, meals_2, origin_1,... ), does it mean I need to create all 25 combinations of the composite indexes (through the link I get from the console) ? or I create 25 indexes of that map fields?

第二次尝试:

我对数据进行非规范化,如下所示:

I denormalised the data as following:

保存所有数据的集合:

过滤器集合:

如果选择饭食_1"和起源_1",则可以通过 whenAllSuccess 来获取所有文档ID,如下所示:

If I select meals_1 and origin_1, With whenAllSuccess I can get all document ids like the following:

Iterator<String> iterator = filters.iterator();
        List<Task> tasks = new ArrayList<>();
        while (iterator.hasNext()) {
            String filter = iterator.next();
            tasks.add(recipeRepository.getFilterCollection("filter_"+filter).get());
        }

        Tasks.whenAllSuccess(tasks.toArray(new Task[tasks.size()]))
                .addOnSuccessListener(new OnSuccessListener<List<Object>>() {
                    @Override
                    public void onSuccess(List<Object> objects) {
                        if (!objects.isEmpty()) {

                            for (Object querySnapshot: objects) {
                                QuerySnapshot querySnap = (QuerySnapshot)querySnapshot;

                                for (DocumentSnapshot id: querySnap.getDocuments()) {
                                    doc_ids.add(id.getId());
                                }
                            }

                            CollectionReference collectionReference = recipeRepository.getUsersRecipeCollection();

                            List<DocumentReference> listDocRef = new ArrayList<>();
                            for (String id: doc_ids) {
                                DocumentReference docRef = collectionReference.document(id);
                                listDocRef.add(docRef);
                            }

                            List<Task<DocumentSnapshot>> tasks = new ArrayList<>();
                            for (DocumentReference documentReference : listDocRef) {
                                Task<DocumentSnapshot> documentSnapshotTask = documentReference.get();
                                tasks.add(documentSnapshotTask);
                            }

                            Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
                                @Override
                                public void onSuccess(List<Object> list) {
                                    Log.e(TAG, ">> list objects: " + list.size() + " data: " + list);
                                    for (Object object : list) {
                                        Recipes recipes = ((DocumentSnapshot) object).toObject(Recipes.class);

                                        if (recipes == null) continue;

                                        Log.e(TAG, ">> recipe name: " + recipes.getName());
                                    }
                                }
                            });
                        }
                    }
                });

通过此操作,我获得了包含进餐_1或起源_1的所有数据(与使用 whereArrayContainsAny 的结果相同)

With this I get all data containing meals_1 OR origin_1 (same result as using whereArrayContainsAny).

我需要获取的餐单和origin_1集合中同时存在的文档ID的列表.

What I need is to get the list of docs ids that exists in both meals_1 and origin_1 collections.

推荐答案

因此,例如,如果用户从我的过滤器页面中选择了1、2,..或25个不同的项目(例如:category_1,meats_2,origin_1,...),这是否意味着我需要创建所有25个组合综合索引的数量(通过我从控制台获得的链接)?还是我为该地图字段创建25个索引?

So, for example, if a user select 1, 2,.. or 25 different items from my filter page (like: category_1, meals_2, origin_1,... ), does it mean I need to create all 25 combinations of the composite indexes (through the link I get from the console) ? or I create 25 indexes of that map fields?

正如我在for循环中看到的,在每次迭代中,您都在创建一个新的Query对象,这是因为索引是必需的.

As I see in the for loop, at each iteration you are creating a new Query object, and this is because Cloud Firestore queries are immutable. Because you are using in the same query a .orderBy() call along with a .whereEqualTo() call, an index is required.

要回答您的问题,是的,对于您执行的每个非常不同的查询都需要一个索引.

And to answer your question, yes, an index is required for each and very different query that you perform.

另一种解决方案可能是对数据进行非正规化,通过添加每种餐食的单独集合,并且每次添加过滤器时,您都应该为所选集合创建一个新查询.对于NoSQL数据库,这是一种常见的做法.

An alternative solution might be to denormalize the data, by adding separate collections of each type of meal, and every time a filter is added you should create a new query for the selected collection. This is a common practice when it comes to NoSQL databases.

这篇关于具有复杂过滤器和复合索引的Firestore的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆