Find MongoDB docs where all sub-docs match criteria - mongodb

I have some Product documents that each contain a list of ProductVariation sub-documents. I need to find all the Product docs where ALL their child ProductVariation docs have zero quantity.
Schemas look like this:
var Product = new mongoose.Schema({
name: String,
variations: [ProductVariation]
});
var ProductVariation = new mongoose.Schema({
type: String,
quantity: Number,
price: Number
});
I am a little new to mongodb, so even sure where to start here.

Try using $not wrapped around { "$gt" : 0 }:
> db.products.find()
{ "_id" : ObjectId("5b7cae558ff28edda6ba4a67"), "name" : "widget", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 10 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
{ "_id" : ObjectId("5b7cae678ff28edda6ba4a68"), "name" : "foo", "variations" : [ { "type" : "color", "quantity" : 2, "price" : 15 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
{ "_id" : ObjectId("5b7cae7f8ff28edda6ba4a69"), "name" : "bar", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 15 }, { "type" : "size", "quantity" : 1, "price" : 5 } ] }
> db.products.find({"variations.quantity": { "$not" : { "$gt" : 0 } } })
{ "_id" : ObjectId("5b7cae558ff28edda6ba4a67"), "name" : "widget", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 10 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
It can also take advantage of an index on { "variations.quantity" : 1 }.

Related

Add unique group ids in group aggregate function of MongoDB query

I wanted to add unique group ids for each group created by multiple fields in the Mongo query.
{ "item" : "abc", "price" : 10, "quantity" : 2 },
{ "item" : "jkl", "price" : 20, "quantity" : 1 },
{ "item" : "xyz", "price" : 15, "quantity" : 10 },
{ "item" : "xyz", "price" : 5, "quantity" : 20 },
{ "item" : "abc", "price" : 10, "quantity" : 10 }
I wanted to group by items and price:
{ "_id" : { "item" : "abc", "price" : 10 } },
{ "_id" : { "item" : "jkl", "price" : 20 } },
{ "_id" : { "item" : "xyz", "price" : 10 } },
{ "_id" : { "item" : "xyz", "price" : 15 } }
But along with that I wanted to add group ids for each group and the output I want:
{ "gid" : "1", "_id" : { "item" : "abc", "price" : 10 } },
{ "gid" : "2", "_id" : { "item" : "jkl", "price" : 20 } },
{ "gid" : "3", "_id" : { "item" : "xyz", "price" : 10 } },
{ "gid" : "4", "_id" : { "item" : "xyz", "price" : 15 } }
db.collection('example').aggregate([
{
$group:{
_id:{item:"$item",price:"$price"}
},
{
$addFields: {
gid: {
$function: {
body: function() {
return UUID().toString().split('"')[1];
},
args: [],
lang: "js"
}
}
}
}
])

MongoDB - how to optimise find query with regex search, with sort

I need to execute the following query:
db.S12_RU.find({"venue.raw":a,"title":/b|c|d|e/}).sort({"year":-1}).skip(X).limit(Y);
where X and Y are numbers.
The number of documents in my collection is:
208915369
Currently, this sort of query takes about 6 minutes to execute.
I have the following indexes:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"venue.raw" : 1
},
"name" : "venue.raw_1"
},
{
"v" : 2,
"key" : {
"venue.raw" : 1,
"title" : 1,
"year" : -1
},
"name" : "venue.raw_1_title_1_year_-1"
}
]
A standard document looks like this:
{ "_id" : ObjectId("5fc25fc091e3146fb10484af"), "id" : "1967181478", "title" : "Quality of Life of Swedish Women with Fibromyalgia Syndrome, Rheumatoid Arthritis or Systemic Lupus Erythematosus", "authors" : [ { "name" : "Carol S. Burckhardt", "id" : "2052326732" }, { "name" : "Birgitha Archenholtz", "id" : "2800742121" }, { "name" : "Kaisa Mannerkorpi", "id" : "240289002" }, { "name" : "Anders Bjelle", "id" : "2419758571" } ], "venue" : { "raw" : "Journal of Musculoskeletal Pain", "id" : "49327845" }, "year" : 1993, "n_citation" : 31, "page_start" : "199", "page_end" : "207", "doc_type" : "Journal", "publisher" : "Taylor & Francis", "volume" : "1", "issue" : "", "doi" : "10.1300/J094v01n03_20" }
Is there any way to make this query execute in a few seconds?

Float number overflows when export MongDB collection to JSON via mongoexport

I'm new to MongoDB. I've inserted a float number into a collection. However, when I export that collection via mongoexport, the float number changes.
This is what in the database:
{ "_id" : ObjectId("56653e23a6b56616ba417bcd"), "id" : "601318", "name" : "中国平安", "buy" : [ { "time" : ISODate("2015-06-15T01:30:00Z"), "price" : 86.9, "quantity" : 1000, "value" : 87074.4 } ], "sell" : [ { "time" : ISODate("2015-07-07T01:30:00Z"), "price" : 80.88, "quantity" : 1000, "value" : 80636.76 } ] }
This is when it's exported to json:
{ "_id" : { "$oid" : "56653e23a6b56616ba417bcd" }, "id" : "601318", "name" : "中国平安", "buy" : [ { "time" : { "$date" : "2015-06-15T09:30:00.000+0800" }, "price" : 86.90000000000001, "quantity" : 1000, "value" : 87074.39999999999 } ], "sell" : [ { "time" : { "$date" : "2015-07-07T09:30:00.000+0800" }, "price" : 80.88, "quantity" : 1000, "value" : 80636.75999999999 } ] }
How to avoid this overflow?
Store the value as an integer: 8063676 (cents or whatever).
See this question.

MongoDB Aggregation - return default value for documents that don't match query

I'm having trouble figuring out the right aggregation pipe operations to return the results I need.
I have a collection similar to the following :-
{
"_id" : "writer1",
"Name" : "writer1",
"Website" : "website1",
"Reviews" : [
{
"Film" : {
"Name" : "Jurassic Park",
"Genre" : "Action"
},
"Score" : 4
},
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
{
"_id" : "writer2",
"Name" : "writer2",
"Website" : "website1",
"Reviews" : [
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
And this is my aggregation so far : -
db.writers.aggregate([
{ "$unwind" : "$Reviews" },
{ "$match" : { "Reviews.Film.Name" : "Jurassic Park" } },
{ "$group" : { "_id" : "$Website" , "score" : { "$avg" : "$Reviews.Score" },
writers :{ $push: { name:"$Name", score:"$Reviews.Score" } }
}}
])
This returns only writers who have a review of the matching film and also only websites that have at least 1 writer who has reviewed the film,
however, I need to return all websites containing a list of their all writers, with a score of 0 if they haven't written a review for the specified film.
so, I am currently getting : -
{ "_id" : "website1", "score" : 4, "writers" : [ { "name" : "writer1", "score" : 4 } ] }
When I actually need : -
{ "_id" : "website1", "score" : 2, "writers" : [ { "name" : "writer1", "score" : 4 },{ "name" :"writer2", "score" : 0 } ] }
Can anyone point me in the right direction?
Cheers

MongoDB flatten embedded array

i'd like to create a report of a collection. Its schema is :
(I simplified the schema, to focus on the problematic)
Mongoose Schema
var MobilHomeSchema = new Schema({
id: Schema.Types.ObjectId,
region: String,
equipments:[
{ id: ObjectId, label: String }
]
});
It contains lots of mobilhomes. These mobilhomes are in a campsite, on a region (I chose this group, it could be country, ...). Each mobilhome has some equipments, not always the sames.
I'd like to create a spreadsheet with these columns, to count the number of each equipments in a region (it's just an example)
Expected generic result format
region | equipments.label 1 | equipments.label 2 | equipments.label 3 | ....
Example with "real" values :
region|terrace|pergola|shower
Spain | 30 | 15 |150
France| 55 | 32 |540
...
in json format, it could be :
EDIT
[{
region: "Spain",
terrace: 30,
pergola: 15,
shower: 150
},
{
region: "France",
terrace: 55,
pergola: 32,
shower: 540
}]
/EDIT
How can I do ?
(map-reduce ? a most Business Intelligence tool ?)
Many Thanks !
Don't use map/reduce. Use aggregation. In the mongo shell,
> db.mobile.aggregate([
{ "$unwind" : "$equipments" },
{ "$group" : { "_id" : { "region" : "$region", "label" : "$equipments.label" }, "count" : { "$sum" : 1 } } }
])
On the documents
{ "region" : "France", "equipments" : [ { "_id" : 0, "label" : "terrace" }, { "_id" : 1, "label" : "pergola" } ] },
{ "region" : "France", "equipments" : [ { "_id" : 0, "label" : "shower" }, { "_id" : 1, "label" : "pergola" } ] },
{ "region" : "Spain", "equipments" : [ { "_id" : 0, "label" : "terrace" }, { "_id" : 1, "label" : "shower" } ] },
{ "region" : "Spain", "equipments" : [ { "_id" : 0, "label" : "veranda" }, { "_id" : 1, "label" : "pergola" } ] }
the result is
{ "_id" : { "region" : "Spain", "label" : "veranda" }, "count" : 1 }
{ "_id" : { "region" : "Spain", "label" : "terrace" }, "count" : 1 }
{ "_id" : { "region" : "Spain", "label" : "shower" }, "count" : 1 }
{ "_id" : { "region" : "France", "label" : "shower" }, "count" : 1 }
{ "_id" : { "region" : "France", "label" : "pergola" }, "count" : 2 }
{ "_id" : { "region" : "Spain", "label" : "pergola" }, "count" : 1 }
{ "_id" : { "region" : "France", "label" : "terrace" }, "count" : 1 }
Since you're using an array, presumably you don't know all the possible types of equipment ahead of time, which makes shoving the above results back into one object per region in the aggregation an unwieldy thing to attempt. Better to work with these results in the client.