Mongodb find $and


#1

I’m creating a query with Mongodb (I’m starting with it so sorry if I say something obvious/wrong) to show Food (collection) with some:

  • Ingredients: could be an array like ing = [‘onion’, ‘garlic’]
  • Type of food: another array like dishType = [‘breakfast’, ‘lunch’]

I need to pass the query to to correct view (when I send this query I’m in a filter view) to list this Food.

I’ve got something like this:

state.go('food', {
      filters: {
        $and: [
          {ingredients: { $in : this.ingredients }},
          {info: {dishType: { $in : this.whens }}}
        ]
      }
    });

If I remove this line {info: {dishType: { $in : this.whens }}} is finding the plates well but both are not working together, the result always is empty.

If I have selected onion and lunch I want to see plates maybe with more information in ingredients (not just onion) and with info but should have this combination of parameters.

What am I doing wrong? Can’t find the problem :expressionless:
https://docs.mongodb.com/manual/reference/operator/query/and/

Thank you all!


#2

Do you maybe want:

state.go('food', {
  filters: {
    $and: [
      { ingredients: { $in : this.ingredients } },
      { 'info.dishType': { $in : this.whens } }
    ]
  }
});

#3

Hi @robfallows, I tried with that but the result is the same (empty).

I’d prefer my way because I need to add more info properties (but with one I’d use yours, yes).


#4

Have you confirmed that this.whens is an array of dishTypes?


#5

this.whens is an array of strings and dishTypes is an string.

So I can select Lunch and Dinner in my filter and find plates with this dishTypes


#6

I suspect this.whens needs to be an array of strings (like [ 'potato', 'kale', 'carrot' ]) and the dishType field needs to be a simple string (like 'broccoli'). From the MongoDB doc for $in:

The $in operator selects the documents where the value of a field equals any value in the specified array.

So you can do something like:

Vegetables.find ({}, { name : { $in: [ 'potato', 'kale', 'carrot' ] } } );

#7

Sorry, I don’t know how could I said that :joy:. I think I was struggling with a different thing in the moment I wrote that down (edited now).

Anyway, as you said, this.whens (we can say dishTypes, in plural, to be more clear, sorry) is an array of strings and dishType is a string.

So resuming we’ve got in the filter view ingredients (array) and dishTypes (array) and in our Plate model ingredients (array) and dishType (string).


#8

Any ideas @robfallows? (just to remind you, the objects were as you said)


#9

You need to stick with dot notation for your subdocument. Your query should look something like:

  filters: {
    $and: [
      { ingredients: { $in : this.ingredients } },
      { 'info.dishType': { $in : this.dishTypes } }
    ]
  }

If this isn’t working, please paste in an example document from the mongo shell:

meteor mongo
db.food.findOne().pretty()

#10

I can see my collections with show collections but db.food.findOne().pretty() returns me this:

TypeError: Cannot call method ‘pretty’ of null

But anyway, if you want to see what contents one plate in food is this:

// ...some properties skipped
date: new Date(),
ingredients: ['rice', 'fish', 'sesame'],
recipe: [{text: 'Well, you should order a sushi restaurant to take away and say you made it'}],
info: {
   time: 30, servings: 5, dishType: 'Dinner', region: 'Japan', summary: 'The famoust plate from Japan'
},
likes: [],
// ...some properties skipped


#11

Can’t immediately see why that won’t work :confused:


#12

I tried just this:

$and: [
   {'info.dishType': { $in : this.dishTypes}}
]

… and it works!
The lines are ok, both work separtley but no together so I understand it’s something related with $and or/and $in.

But $and is indicating that I want BOTH properties and $in that what I’m looking for should be matches with that array.
Am I wrong?


#13

I run this in Minimongo:

db.getCollection('food').find({
$and: [
          {'info.dishType': { $in : ['Dinner','Lunch'] }},
          {ingredients: { $in : ['olive oil'] }}
        ]

      })

and I get 6 plates. I have copied the same query (hardcoded) in my controller and I get 5 plates.

:expressionless:

I’ll go a bit deeper into it


#14

I found the problem.

I have changed the controller which is listing the food so I’m applying the filter directly to be more sure (and don’t do more steps to check this out).

Resuming of the problem: I get 5 plates while with minimongo I get 6 (which is the correct number) with the same query.

This was my controller:

    this.perPage = 21;
    this.sort = {
      date: -1
    };

this.subscribe('food', () => [{
      limit: parseInt(this.getReactively('perPage')),
      sort: this.getReactively('sort')
    }
    ]);

// find() was NOT the problem

I’ve got a lazy loading for my plates. If we scroll till the 21st (default number) we load more.
BUT with 21 I can see 5 results. If I change it to 28 I’m able to see the 6 results.

If I still scrolling the app doesn’t find more plates, so doesn’t load more.

And the problem is not obviously related with this number, if I specified the limit in 6 I see 3 results.

Now I think I’m even more lost :disappointed_relieved:


#15

After a few days working in a different thing I came back into this again…

In my publish function I’m forcing the limit:
return Food.find(selector, {limit: 2});

And in my subscription I’m passing some details to show the third plate in my Food collection.

When I reload my app to see my plate I cannot see anything but if I change this limit to 3 I can see my plate.

(I’m using Robomongo always to see before the correct result)

So this limit is not a limit for my results is a limit for the normal collection. I’m not limiting my search to 3, I’m limiting the collection to search into it to 3!

Is this the normal behavior?
I so, how can I change this to filter just 3 results?

Cheers!

Edit: I just found the problem here


I had to filter in the publish function, no in the Mongodb query


[resolved] How to make publish/subscribe react to user set filters (with limited subscribe)