简体   繁体   中英

Firestore - Query multiple fields

I'm looking to build a simple chat app using Flutter + Firestore, storing users and chats in separate collections. Each chat document has two string fields [uid1, uid2] representing the user ids of the respective participants. I need to be able to search for all chats for a given user (uid) so some type of OR query, this would be possible by storing them in an array field and using array_contains.

However, to update a chat document, I need to be able to query on both fields directly to retrieve a specific chat between two users, which would not be possible using the array approach (nested array_contains are not allowed). Even storing both as two top-level string fields requires two firestore queries for either direction.

Appreciate any advice on how to solve this limitation of Firestore queries?

You can create a composite field that contains all the data you need for the query by concatenating both user IDs into a single string. Be sure to sort the IDs before doing so, in order to ensure that they have a predictable order.

So, if you have user "A" and user "B", your composite field value would be simply "AB" (and never "BA"), and you can filter on this field to find all message between these two users.

Sorry for my short explanation, I just find a proper time to share my experiences, so i messed up a bit, so later i hope i will update my post, thank you for your understanding.

My Message Model,

class Message {
  final DateTime time;
  final String text;
  final Map<String, dynamic> toFromUser;

  Message({
    required this.toFromUser,
    required this.time,
    required this.text,
  });

  Map<String, dynamic> getDataMap() {
    return {
      "toFromUser": toFromUser,
      "time": time,
      "text": text,
    };
  }
}

I send like this,

 Message message = Message(
      toFromUser: {
       "from": Auth().currentUserId(),
       "to": toUserId
       },
       time: DateTime.now(),
       text: messageText.text,
);

My Stream Builder like this, widget.userData.id is other user B. A is me, B is friend. :)

StreamBuilder<QuerySnapshot>(
          stream: FirebaseFirestore.instance
              .collection('messages')
              .where("toFromUser", whereIn: [ // index is used to use both where and orderBy.
                {"to": widget.userData.id, "from": Auth().currentUserId()},
                {"to": Auth().currentUserId(), "from": widget.userData.id}
              ])
              .orderBy('time')
              .snapshots(),
          builder: (BuildContext context,
              AsyncSnapshot<QuerySnapshot> snapshot) {
            if (snapshot.hasError) {
              print(widget.userData.id);
              return const Text('Something went wrong');
            }

            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Text("Loading");
            }
            // TODO: move list to bottom when new message is sent
            return ListView(
              padding: const EdgeInsets.all(20),
              children:
                  snapshot.data!.docs.map((DocumentSnapshot document) {
                Map<String, dynamic> data =
                    document.data()! as Map<String, dynamic>;

                  Message message = Message(
                    toFromUser: data["toFromUser"],
                    time: (data['time'] as Timestamp).toDate(),
                    text: data["text"],
                  );

                  final bool isMe =
                      data["toFromUser"]["from"] == Auth().currentUserId();
                  return chatBubble(context, message, isMe);

              }).toList(),
            );
          },
        ),

FireBase Firestore is like this 在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM