MongoDB query for a specific field for each document? - mongodb

Let's say I have 2 users in my user collection:
id: "1",
outer: {
field1: {
inner1: {
a: cats
b: car
}
}
}
id: "2",
outer: {
field1: {
inner1: {
a: dogs
b: bus
}
}
}
Let's say I have 10 users. I want to get the value of field "a" for every user in my database. How do I make this query (in mongo shell)?

db.user.find({'field1.a': { $exists: true }, { 'field1.a': 1 });
Edit: As mentioned in the comments, you can also just do a project using the aggregation framework.
db.user.aggregate({ $project: { a: '$field1.inner1.a' } });

It doesn't matter how nested the objects get, just use dot notation. I wrote out an example below. Your documents were incomplete and ill-formed in a bunch of different ways, so I took the liberty of completing them while trying to match what I think you meant for them to look like.
> db.nested.insert([
{
"_id" : "1",
"outer" : {
"field1" : {
"inner1" : {
"a" : "cats",
"b" : "car"
}
}
}
},
{
"_id" : "2",
"outer" : {
"field1" : {
"inner1" : {
"a" : "dogs",
"b" : "bus"
}
}
}
}])
> db.nested.find({}, { "_id" : 0, "outer.field1.inner1.a" : 1 })
{ "outer" : { "field1" : { "inner1" : { "a" : "cats" } } } }
{ "outer" : { "field1" : { "inner1" : { "a" : "dogs" } } } }
Selecting a subset of the fields of a document is called projection.

Related

Query to count the number of documents for each user

I have a collection named 'captures' and the documents within it have the field 'username'
a document looks something like this
/* 1 */
{
"_id" : ObjectId("622b951a026ca3a73f5a2a1c"),
"username" : "andre",
"data" : {
"metadata" : {
"start" : "2022-02-24T09:32:22.390Z",
...
},
...
}
}
/* 2 */
{
"_id" : ObjectId("9255941b026ca3a73f5a2a1c"),
"username" : "andre",
"data" : {
"metadata" : {
"start" : "2022-05-10T03:12:23.440Z",
...
},
...
}
}
/* 3 */
{
"_id" : ObjectId("7775941b026ca3a73f5a2a1c"),
"username" : "bob",
"data" : {
"metadata" : {
"start" : "2022-05-16T12:24:12.002Z",
...
},
...
}
}
/* 4 */
{
"_id" : ObjectId("3215331b026ca3a73f5a2a1c"),
"username" : "bob",
"data" : {
"metadata" : {
"start" : "2022-05-18T12:24:12.002Z",
...
},
...
}
}
I'd like to return a count of documents for each distinct username, where 'start' is after 2022-02-24T09:32:22.390Z
the above example would return something like:
{ "user" : "andre", "count" : 1 }
{ "user" : "bob", "count" : 2 }
I've tried using count, distinct, aggregate without success...
This is pretty simple to do with the aggregation framework:
[
{
$project: {
_id: 0,
user: '$username',
start: {
$toDate: '$data.metadata.start'
}
}
},
{
$match: {
start: {
$gt: Date('2022-02-24T09:32:22.390Z')
}
}
},
{
$group: {
_id: '$user',
user: {
$first: '$user'
},
count: {
$sum: 1
}
}
}
]
By the way you should store dates as Date objects, not strings, it will make your life easier.

MongoDB aggregation query based on multiple fields with similar values

I have documents that look like this:
{
"_id" : "001",
"a" : {
"b" : {
"c" : {
"custId" : "cust1"
},
"d" : {
"custId" : "cust2"
}
}
}
}
{
"_id" : "002",
"a" : {
"b" : {
"c" : {
"custId" : "cust1"
},
"d" : {
"custId" : "cust3"
}
}
}
}
{
"_id" : "003",
"a" : {
"b" : {
"c" : {
"custId" : null
},
"d" : {
"custId" : "cust2"
}
}
}
}
{
"_id" : "004",
"a" : {
"b" : {
"c" : {
"custId" : null
},
"d" : {
"custId" : "cust1"
}
}
}
}
I would like to obtain an aggregation which shows a sorted count of customer ids, ignoring null customer ids, like this:
{
"_id" : "cust1",
"count" : 3,
"records" : [
"001", "002", "004"
]
}
{
"_id" : "cust2",
"count" : 2,
"records" : [
"001", "003"
]
}
{
"_id" : "cust3",
"count" : 1,
"records" : [
"002"
]
}
I think each document needs to be broken down into 1 or 2 customer based arrays than then unwound back into documents, but I have been unable to determine a workable solution.
make an array of custId, $map to iterate loop of b after converting from object to array using $objectToArray
$unwind deconstruct custIds array
$match to filter none null custIds documents
$group by custIds and get count of total records and make unique array of _id using $addToset
db.collection.aggregate([
{
$project: {
custIds: {
$map: {
input: { $objectToArray: "$a.b" },
in: "$$this.v.custId"
}
}
}
},
{ $unwind: "$custIds" },
{ $match: { custIds: { $ne: null } } },
{
$group: {
_id: "$custIds",
count: { $sum: 1 },
records: { $addToSet: "$_id" }
}
}
])
Playground

MongoDB adding new field to the sub document

I have mongodb document like below.
{
"_id" : ObjectId("57616e718ed5a017089143f2"),
"subitems" : {
"1" : "a",
"2" : "b"
}
}
I was trying to add new fields to "subitems" field.
db.items.update({ "_id" : ObjectId("57616e718ed5a017089143f2") }, { $set: { subitems: { 3: "c" } } })
Instead of updating the field, its overwriting it like
{
"_id" : ObjectId("57616e718ed5a017089143f2"),
"subitems" : {
"3" : "c"
}
}
How do i achieve result
{
"_id" : ObjectId("57616e718ed5a017089143f2"),
"subitems" : {
"1" : "a",
"2" : "b",
"3" : "c"
}
}
Use the dot notation to add the field to an embedded document:
db.items.update(
{ "_id" : ObjectId("57616e718ed5a017089143f2") },
{ "$set": { "subitems.3": "c" } }
)
More from the documentation.

Update a nested array objects in a different collection and position in MongoDB

I have a douments like as follows.
How do I update a skillcluster name. Suppose the other document has name :"c" in 4th position.
{
Job: {
post: { name:"x" }
skill: {
skillcluster: [
{name:"c++",id:"23"},
{name:"c",id:"898"}
]
}
}
}
{
Job: {
post: { name:"x" }
skill: {
skillcluster: [
{name:"c++",id:"23"},
{name:"java"},
{name:"python"},
{name:"c",id:"898"}
]
}
}
}
You need to query to match the "name" field at the embedded level of the document using "dot notation", and then pass that match with the positional $ operator within the update:
db.collection.update(
{ "Job.skill.skillcluster.name": "c" },
{ "$set": { "Job.skill.skillcluster.$.name": "Simple C"}},
{ "multi": true }
)
Also use the "multi" flag to match and update more than one document.
The result will be:
{
"_id" : ObjectId("55dbfd0ed96d655eb0ed2b4f"),
"Job" : {
"post" : {
"name" : "x"
},
"skill" : {
"skillcluster" : [
{
"name" : "c++",
"id" : "23"
},
{
"name" : "Simple C",
"id" : "898"
}
]
}
}
}
{
"_id" : ObjectId("55dbfd0ed96d655eb0ed2b50"),
"Job" : {
"post" : {
"name" : "x"
},
"skill" : {
"skillcluster" : [
{
"name" : "c++",
"id" : "23"
},
{
"name" : "java"
},
{
"name" : "python"
},
{
"name" : "Simple C",
"id" : "898"
}
]
}
}
}

How do I return multiple fields in a MongoDB aggregate query?

I have a collection of MongoDB documents that look like this:
{
"_id" : "123",
"created_by": "bob",
"date_added": ISODate("2014-08-27T17:43:23Z"),
"size": "XL",
"color": "red"
}
The question I'm trying to answer is: What is the color of the item most recently added by each person?
I've gotten this far:
db.stuff.aggregate([
{ $group: { _id: { who: "$created_by"}, added: { $max: "$date_added" } } },
])
{ "_id" : { "who" : "bob" }, "added" : ISODate("2014-09-30T07:06:38.135Z") }
{ "_id" : { "who" : "mike" }, "added" : ISODate("2014-09-30T07:10:03.098Z") }
{ "_id" : { "who" : "mary" }, "added" : ISODate("2014-09-30T07:07:27.787Z") }
{ "_id" : { "who" : "john" }, "added" : ISODate("2014-09-30T07:09:51.418Z") }
However, it only returns the user's name and the date of when the document was added. I can't figure out how to get the query to also return the color. Thank you!
I think this will work for you.
db.stuff.aggregate([ {
$sort : {
date_added : -1
}
}, {
$group : {
_id : {
who : "$created_by"
},
added : {
$first : "$date_added"
},
color : {
$first : "$color"
}
}
} ]);