How to model this data on MongoDB - mongodb

I have this data:
{
"_id" : ObjectId("5a75baada0f20bd4e612d480"),
"Number" : 400,
"Page" : 24,
"DC" : "NE",
}
{
"_id" : ObjectId("5a75baada0f20bd4e612d489"),
"Number" : 300,
"Page" : 14,
"DC" : "100",
}
And i want to model this data so it stays like this:
{Crs:{[
{Cr: {
"_id" : ObjectId("5a75baada0f20bd4e612d480"),
"Number" : 400,
"Page" : 24,
"DC" : "NE",
}},
{Cr: {
"_id" : ObjectId("5a75baada0f20bd4e612d489"),
"Number" : 300,
"Page" : 14,
"DC" : "100",
}},
]}}
I read something about Model One-to-Many Relationships with Embedded Documents but i really dont know how it works.
https://docs.mongodb.com/manual/core/data-model-design/

use aggregation
> db.crs.aggregate(
[
{$group : {_id : null, crs : {$push : {cr : "$$ROOT"}}}},
{$project : {_id : 0}}
]
).pretty()
you can also write the result data to another collection using $out
add below as last stage in aggregate pipeline
{$out : "crs"} // create collection crs
output
> db.crs.aggregate([{$group : {_id : null, crs : {$push : {cr : "$$ROOT"}}}}, {$project : {_id : 0}}]).pretty()
{
"crs" : [
{
"cr" : {
"_id" : ObjectId("5a75baada0f20bd4e612d480"),
"Number" : 400,
"Page" : 24,
"DC" : "NE"
}
},
{
"cr" : {
"_id" : ObjectId("5a75baada0f20bd4e612d489"),
"Number" : 300,
"Page" : 14,
"DC" : "100"
}
}
]
}
>

I think the documentation here should be of some help: https://docs.mongodb.com/manual/reference/operator/update/positional/#update-documents-in-an-array
And in your case it would be something similar to this:
db.nameOfCollection.updateOne(
{ _id: ObjectId('idhere'), "Crs.Cr.Number": 400 },
{ $set: { "Crs.$.Cr.DC" : "SomethingNew" } }
)
One thing here, I'm not sure you're data structure seems correct as you have an array inside an object...why not just an array, i.e.
Crs: [{...}, {...}]

Related

How to create a root element inside a Collection in MongoDB?

I imported this:
{
"_id" : ObjectId("5a75baada0f20bd4e612d480"),
"Number" : 400,
"Page" : 24,
"DC" : "NE",
}
{
"_id" : ObjectId("5a75baada0f20bd4e612d489"),
"Number" : 300,
"Page" : 14,
"DC" : "100",
}
And i put the data inside an array like this:
{Crs:{[
{Cr: {
"_id" : ObjectId("5a75baada0f20bd4e612d480"),
"Number" : 400,
"Page" : 24,
"DC" : "NE",
}},
{Cr: {
"_id" : ObjectId("5a75baada0f20bd4e612d489"),
"Number" : 300,
"Page" : 14,
"DC" : "100",
}},
]}}
I have this data that i importet and I would like to know how I put it inside a my Main element and modeling like this, using aggregate.
{Main: {
"_id" : ObjectId("main1"),
"Name" : "MainNAme1",
{Crs:{[
{Cr: {
"_id" : ObjectId("5a75baada0f20bd4e612d480"),
"Number" : 400,
"Page" : 24,
"DC" : "NE",
}},
{Cr: {
"_id" : ObjectId("5a75baada0f20bd4e612d489"),
"Number" : 300,
"Page" : 14,
"DC" : "100",
}},
]}},
}}
can you try this aggregation
db.crs.aggregate(
[
{$group : {_id : null, crs : {$push : {cr : "$$ROOT"}}}},
{$addFields : {"Main._id" : "main1", "Main.name" : "MainNAme1", "Main.crs" : "$crs"}},
{$project : {_id : 0 , crs : 0}}
]
)

Mongodb query to group by multiple fields and filter

I want to be able to group each "Place" to show over time, how many "PatientIds" they are seeing on a given day and then be able to filter this by what the action is.
Basically Total Patients on y-axis, Date on x-axis and then a filter or stacked chart to show the action. I also thought about a mapreduce, but have never done that in mongo
I can't figure out the correct mongo query. Right now I have:
db.collection.aggregate({"$group":{_id:{place:"$place",date:"$date",action:"$action",count:{$sum:1}}},{$sort:{"_id.date":1,"_id.place":1}})
However, this is just listing out the data. I tried to do a match on all places, but that didn't give me the results I was looking for either. Any ideas?
Example json:
{
"_id" : ObjectId(""),
"patientId" : "100",
"place" : "1",
"action" : "DIAGNOSED",
"date" : ISODate("2017-01-20")
}
{
"_id" : ObjectId(""),
"patientId" : "101",
"place" : "1",
"action" : "PATIENT IN",
"date" : ISODate("2017-01-20)
}
{
"_id" : ObjectId(""),
"patientId" : "200",
"place" : "2",
"action" : "MEDICINE",
"date" : ISODate("2017-01-05")
}
{
"_id" : ObjectId(""),
"patientId" : "300",
"place" : "2",
"action" : "DIAGNOSED",
"date" : ISODate("2017-01-31")
}
EDIT - mapreduce
> var map = function(){emit(this.place,1)}
> var reduce = function(key,values){var res = 0;values.forEach(function(v){res+=1});return{count:res};}
> db.new.mapReduce(map,reduce,{out:"mapped_places"});
{
"result" : "mapped_places",
"timeMillis" : 88,
"counts" : {
"input" : 4,
"emit" : 4,
"reduce" : 2,
"output" : 2
},
"ok" : 1
}
> db.mapped_offices.find({})
{ "_id" : "1", "value" : { "count" : 2 } }
{ "_id" : "2", "value" : { "count" : 2 } }
>
You can try below aggregation query.
db.collection.aggregate([
{
"$group": {
"_id": {
"date": "$date",
"place": "$place"
},
"actions": {
"$push": "$action"
},
"count": {
"$sum": 1
}
}
},
{
"$unwind": "$actions"
},
{
"$sort": {
"_id.date": 1,
"_id.place": 1
}
}
]);
This should output something like
{ "_id" : { "date" : ISODate("2017-01-20T00:00:00Z"), "place" : "1"}, "count" : 2, "actions" : "PATIENT IN" }
{ "_id" : { "date" : ISODate("2017-01-20T00:00:00Z"), "place" : "1"}, "count" : 2, "actions" : "DIAGNOSED" }

Mongodb aggregate match array item with child array item

I would like to find documents that contains specific values in a child array.
This is an example document:
{
"_id" : ObjectId("52e9658e2a13df5be22cf7dc"),
"desc" : "Something somethingson",
"imageurl" : "http://",
"tags" : [
{
"y" : 29.3,
"brand" : "52d2cecd0bd1bd844d000018",
"brandname" : "Zara",
"type" : "Bow Tie",
"x" : 20,
"color" : "52d50c19f8f8ca8448000001",
"number" : 0,
"season" : 0,
"cloth" : "52d50d57f8f8ca8448000006"
},
{
"y" : 29.3,
"brand" : "52d2cecd0bd1bd844d000018",
"brandname" : "Zara",
"type" : "Bow Tie",
"x" : 20,
"color" : "52d50c19f8f8ca8448000001",
"number" : 0,
"season" : 0,
"cloth" : "52d50d57f8f8ca8448000006"
}
],
"user_id" : "52e953942a13df5be22cf7af",
"username" : "Thompson",
"created" : 1386710259971,
"occasion" : "ID",
"sex" : 0
}
The query I would like to do should look something like this:
db.posts.aggregate([
{$match: {tags.color:"52d50c19f8f8ca8448000001", tags.brand:"52d2cecd0bd1bd844d000018", occasion: "ID"}},
{$sort:{"created":-1}},
{$skip:0},
{$limit:10}
])
my problem is that I dont know how to match anything inside an array in the document like "tags". How can I do this?
You could try to do it without aggregation framework:
db.posts.find(
{
occasion: "ID",
tags: { $elemMatch: { color:"52d50c19f8f8ca8448000001", brand:"52d2cecd0bd1bd844d000018" } }
}
).sort({created: -1}).limit(10)
And if you want to use aggregation:
db.posts.aggregate([
{$match:
{
tags: { $elemMatch: { color:"52d50c19f8f8ca8448000001", brand: "52d2cecd0bd1bd844d000018" } },
occasion: "ID"
}
},
{$sort:{"created":-1}},
{$limit:10}
])

$elemMatch dosen't work after $unwind in MongoDB Aggregation Framework

I have a collection of the following data:
{
"_id" : ObjectId("51f1fcc08188d3117c6da351"),
"cust_id" : "abc123",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 25,
"items" : [{
"sku" : "ggg",
"qty" : 7,
"price" : 2.5
}, {
"sku" : "ppp",
"qty" : 5,
"price" : 2.5
}]
}
I am using the query:
cmd { "aggregate" : "orders" , "pipeline" : [
{ "$unwind" : "$items"} ,
{ "$match" : { "items" : { "$elemMatch" : { "qty" : { "$in" : [ 7]}}}}} ,
{ "$group" : { "price" : { "$first" : "$price"} , "items" : { "$push" : { "sku" : "$items.sku"}} , "_id" : { "items" : "$items"}}} ,
{ "$sort" : { "price" : -1}} ,
{ "$project" : { "_id" : 0 , "price" : 1 , "items" : 1}}
]}
Not able to understand what is going wrong
It's because you're doing $match after $unwind. $unwind generates a new stream of documents where items is no longer an array (see docs).
It emits each document as many times as there are items in it.
If you want to select documents with desired element in it and then process all of its documents, you should call $match first:
db.orders.aggregate(
{ "$match" : { "items" : { "$elemMatch" : { "qty" : { "$in" : [ 7]}}}}},
{ "$unwind" : "$items"},
...
);
If you want to select items to be processed after $unwind, you shoul remove $elemMatch:
db.orders.aggregate(
{ "$unwind" : "$items"},
{ "$match" : { "items.qty" : { "$in" : [7]}}},
...
);
In first case you'll get two documents:
{
"price" : 25,
"items" : [
{"sku" : "ppp"}
]
},
{
"price" : 25,
"items" : [
{"sku" : "ggg"}
]
}
and in second case you'll get one:
{
"price" : 25,
"items" : [
{"sku" : "ggg"}
]
}
Update. After $unwind your documents will look like:
{
"_id" : ObjectId("51f1fcc08188d3117c6da351"),
"cust_id" : "abc123",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 25,
"items" : {
"sku" : "ggg",
"qty" : 7,
"price" : 2.5
}
}
For small number of documents, unwind and match is fine. But large number of documents, it better to do - match ($elemMatch), unwind, and match again.
db.orders.aggregate(
{ "$match" : { "items" : { "$elemMatch" : { "qty" : { "$in" : [ 7]}}}}},
{ "$unwind" : "$items"},
{ "$match" : { "items.qty" : { "$in" : [7]}}}
...
...
);
The first match will filter only documents that match qty criteria. Among the selected documents, the second match will remove the subdocuments not matching the qty criteria.

Exact Reduce function

I have a collection of the type :
{
"_id" : ObjectId("51f1fcc08188d3117c6da351"),
"cust_id" : "abc123",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 25,
"items" : [{
"sku" : "ggg",
"qty" : 7,
"price" : 2.5
}, {
"sku" : "ppp",
"qty" : 5,
"price" : 2.5
}]
}
I want to fetch only the "items" object whose "items.qty">5 and and"items.sku"=="ggg".
I applied Map reduce:
cmd { "mapreduce" : "orders" , "map" : "function map(){var items_out={items:[]};for(i in this.items){items_out.items.push(this.items[i].sku);};emit(this._id,[items_out]);}" , "reduce" : "function reduce(key,values){return {'result':values};}" , "verbose" : true , "out" : { "replace" : "map_reduce"} , "query" : { "$where" : "return this.items.some(function(entry){return entry.qty>5})&&this.items.some(function(entry){return entry.sku=='ggg'})"}},
but I am getting all the sku values something like this:
{ "data": [ { "items": [ "ggg", "ppp" ] } ]}
Whereas it should give only ggg as this is the only value matching criteria.
Use the following command:
db.orders.aggregate(
{$unwind : "$items"},
{$match : {"items.qty": {$gt: 5 }}},
{$match : {"items.sku" : "ggg"}},
{$project : {_id:0, items:1}}
)