45

I'm trying to run a simple query, where I search for a document that contains a value inside an object array.

For instance, look at my database structure:

firestore db

I want to run a query similar to this:

db.collection('identites').where("partyMembers", "array-contains", {name: "John Travolta"})

What is the correct way to achieve this, is it even possible with Firestore?

Thanks.

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
sisi mendel
  • 759
  • 2
  • 8
  • 17

4 Answers4

49

As Frank has explained in his answer it is not possible, with array-contains, to query for a specific property of an object stored in an array.

However, there is a possible workaround: it is actually possible to query for the entire object, as follows, in your case:

db.collection('identites')
  .where(
    "partyMembers",
    "array-contains",
    {id: "7LNK....", name: "John Travolta"}
  )

Maybe this approach will suit your needs (or maybe not....).

Philippe Fanaro
  • 6,148
  • 6
  • 38
  • 76
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • 1
    I've tried to query for an entire object (the exact example above) but without success. Are you sure it's possible ? can't find infos about on docs. Frank can you confirm if it's possible and if true provide an example of syntax please – Victor Dias Oct 17 '19 at 04:02
  • @VictorDias I confirm that it works. You must have an array of objects (Firestore `map` type) and query with the exact **and entire** object. If you have problem with this approach, please create a new SO question with your exact code and database structure and add a comment below with a link to it. I'll be happy to have a look at it. – Renaud Tarnec Oct 17 '19 at 07:36
  • @RenaudTamec, thanks a lot for your help, you can observe the behaviour on http://crud-angularfirestore-ionic4.web.app, by selecting a tag the list of items return empty. The source code of project is free to access on github, The lines responsible to query for the entire object (tag in this case) are the following https://github.com/meumobi/mmb-demos.crud-angularfirestore-ionic4/blob/master/src/app/shared/item.service.ts#L32-L50. I'd like to share with you a stackblitz but I can't achieve it due to a probably an issue https://github.com/angular/angularfire/issues/1794#issuecomment-543397312 – Victor Dias Oct 18 '19 at 03:14
  • 2
    Hi @RenaduTamec, finally it works, the name of requested field was misspelled. Thank you for support, your response convince me to insist and find the solution. – Victor Dias Oct 18 '19 at 10:47
  • 2
    @VictorDias Cool! Happy to hear that I could help you. You may upvote my answer if you think it helped you! Thanks – Renaud Tarnec Oct 18 '19 at 10:50
  • @RenaudTarnec Any progress in this type of query? as suggested, I am currently using a separate array of ID's but this requires extra maintenances and can cause issues since more then one source of truth. It will be a lot simpler and more organized if this type of query existed. – epic Oct 01 '20 at 17:51
  • I don’t think there is any new feature for this kind of query (at the time of writing) – Renaud Tarnec Oct 01 '20 at 18:36
  • Hi @RenaudTarnec, I am doing a very similar approach using 'array-contains' querying an array with the exact object structure, but get no results. Does this solution still work in a firebase function? – dbach Oct 28 '20 at 09:53
  • @dbach AFAIK, it should work in a Cloud Function. Be sure to update to the latest version of the Admin SDK. If you still encounter a problem, pls create a new question with all the detail and put the link here. – Renaud Tarnec Oct 28 '20 at 10:03
  • Hi @RenaudTarnec, still no results unfortunately. I have posted a new question - thanks for any help you can give https://stackoverflow.com/questions/64571288/how-to-query-an-array-of-objects-in-a-firebase-cloud-function-to-get-a-matching – dbach Oct 28 '20 at 10:52
  • @dbach See my answer. You write above that you are "using 'array-contains' querying an array with the exact object structure, but get no results." but apparently you are not. – Renaud Tarnec Oct 28 '20 at 11:08
  • @RenaudTarnec yeah I've tried both ways - using just an object key (as in my example code) and also an exact object { liveMeetingDate: '2020-10-27T11:42', active: false ....} – dbach Oct 28 '20 at 11:14
  • @dbach Your object has to be **exactly** similar: double check that this is the case with the timestamp. – Renaud Tarnec Oct 28 '20 at 11:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/223746/discussion-between-dbach-and-renaud-tarnec). – dbach Oct 28 '20 at 11:19
  • 1
    wonderful. works good on my use case. – redshift Jan 12 '22 at 14:26
31

The array-contains operations checks if an array, contains a specific (complete) value. It can't check if an array of objects, contains an item with a specific value for a property.

The only way to do your query, is to add an additional field to your document with just the value you want to query existence on. So for example: partyMemberNames: ["John Travolta", "Olivia Newton"].

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

If you control the doc structure and your queried term is unique and doesn't contain special characters, you could turn your array into a map:

partyMembers {
  "7LNKFB9ql": "John Travolta": 
}

and query with e.g. where('partyMembers.7LNKFB9ql', '!=', "")

Lots of ifs, but if dealing with unique user ids, could be an option. In the OPs example of querying by name ("John Travolta") and not id, this might be less feasible, but maybe for someone else.

Till - Appviewer.io
  • 4,529
  • 1
  • 31
  • 35
-1

If you want to extract name: "John Travolta" from "partyMembers" array in a document. you can achieve this by some similar approach in which you can loop through all arrays in a document to find this name.

const [names, setNames] = React.useState([])

const readAllNames = async() => {
const snapshot = await firebase.firestore().collection('identites').doc(documentID).get()
      const filterData = snapshot.data().question.map(val => val.name === "John Travolta" ? val : null)
      setNames( filterData.filter(e=>e) );
}
This technique is used in perticular Document as we are giving .doc(documentID) This way you can get all the arrays having name: "John Travolta" in names constant.
saud00
  • 465
  • 1
  • 4
  • 13