如何从Firestore查询中排除元素?

时间:2018-08-15 13:28:07

标签: java android firebase kotlin google-cloud-firestore

我有一个用户集合,我想从数据库中查询所有用户,并将它们显示在RecyclerView中,除了 mine 。这是我的数据库模式:

users [collection]
  - uid [document]
     - uid: "fR5bih7SysccRu2Gu9990TeSSyg2"
     - username: "John"
     - age: 22
  - //other users

如何像这样查询数据库:

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
Query q = db.collection("users").whereNotEqualTo("uid", uid);

因此,我需要将此查询对象传递给FirestoreRecyclerOptions对象,以便显示RecyclerView中的所有其他用户。

这甚至可能吗?如果没有,我该如何解决?谢谢!

编辑:

options = new FirestoreRecyclerOptions.Builder<UserModel>()
        .setQuery(query, new SnapshotParser<UserModel>() {
            @NonNull
            @Override
            public UserModel parseSnapshot(@NonNull DocumentSnapshot snapshot) {
                UserModel userModel = documentSnapshot.toObject(UserModel.class);
                if (!userModel.getUid().equals(uid)) {
                    return userModel;
                } else {
                    return new UserModel();
                }
            }
        }).build();

8 个答案:

答案 0 :(得分:5)

经过数天的努力,终于找到了答案。没有@Raj的帮助,我无法解决这个问题。非常感谢@Raj的耐心和指导。

首先,根据@Frank van Puffelen在此post中的回答提供的答案,我停止寻找可以帮助我将两个查询传递给单个适配器的解决方案。

在这个问题中,我想要实现的只是查询数据库以获取除我之外的所有用户。因此,因为我们不能将两个查询合并到一个实例中,所以我发现可以将两个查询的结果合并。所以我创建了两个查询:

FirebaseFirestore db = FirebaseFirestore.getInstance();
Query firstQuery = db.collection("users").whereLessThan("uid", uid);
Query secondQuery = db.collection("users").whereGreaterThan("uid", uid);

我的用户对象有一个UserModel(POJO)类。我发现不是一个,而是两种解决问题的方法。第一个是查询数据库以获取与第一个条件相对应的所有用户对象,并将它们添加到列表中。之后,再次查询数据库并获取与第二个条件相对应的其他用户对象,并将它们添加到相同列表中。现在,我有了一个列表,其中包含我需要的所有用户,但一个包含具有查询中特定ID的用户。这是将来访问者的代码:

firstQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        List<UserModel> list = new ArrayList<>();
        if (task.isSuccessful()) {
            for (DocumentSnapshot document : task.getResult()) {
                UserModel userModel = document.toObject(UserModel.class);
                list.add(userModel);
            }

            secondQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (DocumentSnapshot document : task.getResult()) {
                            UserModel userModel = document.toObject(UserModel.class);
                            list.add(userModel);
                        }

                        //Use the list of users
                    }
                }
            });
        }
    }
});

第二种方法要短得多,因为我这样使用Tasks.whenAllSuccess()

Task firstTask = firstQuery.get();
Task secondTask = secondQuery.get();

Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
        @Override
        public void onSuccess(List<Object> list) {
            //This is the list that I wanted
        }
});

答案 1 :(得分:4)

Firestore不支持not equal to操作。因此,您需要在客户端过滤数据。由于您只有一种额外的物品,因此可以将其过滤掉。

为此,您可能需要构建自己的回收器实现,即在将数据添加到回收器适配器数据层时,只要数据符合您的!=条件,就对它们进行限制。

我还没有研究提供的回收站实现firebase,所以我不能说它是否支持对适配器数据的数据操纵。

这里是开始实施回收者视图的好资源:https://www.androidhive.info/2016/01/android-working-with-recycler-view/

答案 2 :(得分:3)

根据官方的Firestore文档:-

  

Cloud Firestore不支持以下类型的查询:

     

带有!=子句的查询。在这种情况下,您应该拆分查询   大于查询和小于查询。例如,尽管   查询子句where(“ age”,“!=”,“ 30”)不支持,您可以   通过组合两个查询获得相同的结果集,其中一个与子句   where(“ age”,“ <”,“ 30”)和带有子句where(“ age”,“>”,30)的子句。

如果您使用的是FirestoreRecyclerAdapter,则FirestoreRecyclerOptions将使用setQuery()方法直接接受查询,因此不允许您执行客户端过滤。

如果您在设置数据时尝试在onBindViewHolder()中应用过滤器,则可能导致回收站视图中的项目为空。为了解决该问题,请参考方法2。

因此,可能的解决方案是在每个文档下的用户集合中创建一个整数字段。例如:-

users [collection]
  - uid [document]
     - uid: "fR5bih7SysccRu2Gu9990TeSSyg2"
     - username: "John"
     - age: 22
     - check: 100

在此,我创建了一个'check'变量,其值为100。因此,在所有其他文档中将'check'的值设置为小于100。 现在,您可以轻松地进行查询,查找带有check <100的文档:-

Query q = db.collection("users").whereLessThan("check", 100);

这将检索您所有的文档,除了不需要的文档。并且在设置数据时,您可以设置其他参数来跳过检查变量。

方法2(客户端过滤)

我们可以在onBindViewHolder()方法中进行检查,如果检索到的uid与当前用户uid匹配,则将Recycler视图的高度设置为0dp。如:-

ViewUserAdapter.java

public class ViewUserAdapter extends FirestoreRecyclerAdapter<User, ViewUserAdapter.ViewUserHolder>
{
    String uid;
    FirebaseAuth auth;

    public ViewUserAdapter(@NonNull FirestoreRecyclerOptions<User> options)
    {
        super(options);
        auth = FirebaseAuth.getInstance();
        uid = auth.getCurrentUser().getUid();
    }

    @Override
    protected void onBindViewHolder(@NonNull ViewUserHolder holder, int position, @NonNull User model)
    {
        DocumentSnapshot snapshot =  getSnapshots().getSnapshot(position);
        String id = snapshot.getId();

        if(uid.equals(id))
        {
            RecyclerView.LayoutParams param = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
            param.height = 0;
            param.width = LinearLayout.LayoutParams.MATCH_PARENT;
            holder.itemView.setVisibility(View.VISIBLE);

        }
        else
        {
            holder.tvName.setText(model.name);
            holder.tvEmail.setText(model.email);
            holder.tvAge.setText(String.valueOf(model.age));
        }
    }
}

答案 3 :(得分:1)

2021 年更新:支持此功能

开发者好。看起来像 now supported 的 where 操作符是这样使用的:citiesRef.where("capital", "!=", false);

答案 4 :(得分:0)

最简单的解决方案是使用 PagedListAdapter 并为Firestore查询创建自定义的 DataSource 。在数据源中,可以将查询转换为Array或ArrayList,在将数据添加到方法callback.onResult(...)之前,可以轻松地删除其中的项目。

我在Firestore查询后使用了类似的解决方案来处理数据,以便按时间属性进行过滤和排序,然后在客户端中按质量得分属性进行重新排序,然后再将数据传递回{{1} }。

文档

数据源样本

callback.onResult(...)

答案 5 :(得分:0)

更简单,更早的客户端过滤(将项目添加到列表时):

  1. 使用Firestore的标准方法获取当前用户的ID。
  2. 获取用户集中所有用户的文档名称。
  3. 在将用户添加到之前 您的RecyclerView列表中,检查要添加到列表中的用户不是当前用户。

这样完成后,您可以在客户端使用“不等于”方法,而不会遇到任何Firestore问题。另一个好处是,您不必弄乱适配器,也不必从回收站中不需要的列表项中隐藏视图。

public void getUsers(final ArrayList<Users> usersArrayList, final Adapter adapter) {

    CollectionReference usersCollectionRef = db.collection("users");

    Query query = usersCollectionRef
            .whereEqualTo("is_onboarded", true);

    query.get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (QueryDocumentSnapshot document : task.getResult()) {

                            final String otherUserID = document.getId();

                             FirebaseUser user = mAuth.getCurrentUser();
                             String currentUserID = user.getUid();

                            if (!otherUserID.equals(currentUserId)) {

                              usersArrayList.add(new User(otherUserID));
                              adapter.notifyDataSetChanged(); //Ensures users are visible immediately
                                            }
                                        } else {
                                            Log.d(TAG, "get failed with ", task.getException());
                                        }
                                    }
                                });
                            }

                        }
                    } else {
                        Log.d(TAG, "Error getting documents: ", task.getException());
                    }
                }
            });
}

答案 6 :(得分:0)

您不必做所有这一切

只需执行常规查询并通过将getLayoutParams()。height和width分别设置为0即可隐藏布局。参见下面的示例。

  if(firebaseUserId.equalIgnoreCase("your_holder_firebase_user_id"){
    holder.mainLayout.setVisibility(View.INVISIBLE);
    holder.mainLayout.getLayoutParams().height = 0;
    holder.mainLayout.getLayoutParams().width = 0;

  }else {
   //show your list as normal
  }
  //This will hide any document snapshot u don't need, it will be there but hidden
 

答案 7 :(得分:0)

这是我的用户名扑朔迷离的解决方案

variable "build_name"