Specify specific embedded array items (not just embedded array fields!) in a doc for projection and disregard the rest


#1

Let’s say I have docs in the following structure:

[
  ...,
  {
    name : 'Adam',
    favorites : [
      {
        id : 1,
        name : 'Drinks',
        desc : 'Drinks are good',
      },
      {
        id : 3,
        name : 'Food',
        desc : 'Food is good',
      },
      {
        id : 4,
        name : 'Games',
        desc : 'Games are good',
      },
    ]
  },
]

To reduce query size and reactive re-rendering, I would like to specify the specific fields I need when querying the collection. Ex. if I only needed the name field, I’d do:
col.find({}, { fields : { name : 1 } }).fetch()

I know it is possible to specify fields in embedded arrays. Ex:
col.find({}, { fields : { name: 1, favorites.name : 1 } }).fetch()
This would return:

[
  ...,
  {
    name : 'Adam',
    favorites : [
      {
        name : 'Drinks',
      },
      {
        name : 'Food',
      },
      {
        name : 'Games',
      },
    ]
  },
]

My problem is as follows: I don’t only want to specify which fields from the embedded array are projected - I also want to specify which array items to project and disregard the rest! Optimally, I would need the method to perform a match as well so it can be dynamic (i.e. I may not be able to easily determine the position(s) of embedded objects, so the solution can’t be to use the positional operator ($) or something similar).

Ex. only project the objects in the favorites array with id’s [1, 4]. Perhaps the query would look something like this:
col.find({}, { fields : { name: 1, favorites.name: { $match: { id : { $in: [1, 4] } } } } }).fetch()
which would result in excluding the second object in the favorites array entirely:

[
  ...,
  {
    name : 'Adam',
    favorites : [
      {
        name : 'Drinks',
      },
      {
        name : 'Games',
      },
    ]
  },
]

That may not be the clearest syntax but regardless, that’s the idea I’m trying to achieve. Any thoughts on how I can do this (+1 if there’s a method to query minimongo in the same way)?

Thanks!


#2

I think you want this:

col.find(
    { 'favorites.id': {$in: [1,4]}},
    { fields: { name: 1, 'favorites.name': 1 } }
).fetch()

#3

Thank you for trying to answer but this does not answer my question and only filters on the doc level. This would still return all names in the favorites array. What you suggested filters the docs to only return a doc w favorites.id === 1 || 4 but then it still returns the full array of favorites names (i.e. name : 'Food' will still be in the return). I am looking to exclude an embedded object in an array in a doc from being returned entirely.


#4

Why?

You can remove the array item server side by manually using the publish handle methods like added, changed and removed.

But putting holes in arrays… You’re going to be much better served with a model like this:

favorites : {
      
        "1": {
        name : 'Drinks',
        desc : 'Drinks are good',
      },
      
        "3": {
        name : 'Food',
        desc : 'Food is good',
      },
      
        "4": {
        name : 'Games',
        desc : 'Games are good',
      }
    }

Go crazy with the holes. If you need to index on fields inside the objects, just store both the array and the map form, query the array form and return only the map form.