Question

I have a collection with following document:

{
"_id" : ObjectId("51f1fd2b8188d3117c6da352"),
"cust_id" : "abc1234",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 27,
"items" : [{
  "sku" : "mmm",
  "qty" : 5,
  "price" : 2.5
}, {
  "sku" : "nnn",
  "qty" : 5,
  "price" : 2.5
}]
}

I want to use "$where" in the fields of "items", so something like this:

{$where:"this.items.sku==mmm"} 

How can I do it? It works when the field is not of array type.

Was it helpful?

Solution 2

You are comparing this.items.sku to a variable mmm, which isn't initialized and thus has the value unefined. What you want to do, is iterate the array and compare each entry to the string 'mmm'. This example does this by using the array method some which returns true, when the passed function returns true for at least one of the entries:

{$where:"return this.items.some(function(entry){return entry.sku =='mmm'})"} 

But really, don't do this. In a comment to the answer by JohnnyHK you said "my service is just a interface between user and mongodb, totally unaware what the field client want's to store". You aren't really explaining your use-case, but I am sure you can solve this better.

  • The $where operator invokes the Javascript engine even though this trivial expression could be done with a normal query. This means unnecessary performance overhead.
  • Every single document in the collection is passed to the function, so when you have an index, it can not be used.
  • When the javascript function is generated from something provided by the client, you must be careful to sanetize and escape it properly, or your application gets vulnerable to code injection.

OTHER TIPS

You don't need a $where operator to do this; just use a query object of:

{ "items.sku": mmm }

As for why your $where isn't working, the value of that operator is executed as JavaScript, so that's not going to check each element of the items array, it's just going to treat items as a normal object and compare its sku property (which is undefined) to mmm.

I've been reading through your comments in addition to the question. It sounds like your users can generically add some attributes, which you are storing in an array within a document. Your client needs to be able to query an arbitrary pair from the document in a generic manner. The pattern to achieve this is typically as follows:

 {
    .
    .
    attributes:[
     {k:"some user defined key",
      v:"the value"},
     {k: ,v:}
      .
      .
    ]
 }

Note that in your case, items is attributes. Now to get the document, your query will be something like:

eg)
db.collection.find({attributes:{$elemMatch:{k:"sku",v:"mmm"}}});
(index attributes.k, attributes.v)

This allows your service to provide a way to query the data, and letting the client specify what the k,v pairs are. The one caveat with this design is always be aware that documents have a 16MB limit (unless you have a use case that makes GridFS appropriate). There are functions like $slice which may help with controlling this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top