I went through this link: How to rename a document field in a MongoDB?, but its not working fine for me. I've Mongo Document like below. I am using MongoDB server version: 4.0.3.
{
"_id" : ObjectId("5cb825e566135255e0bf38a4"),
"firstName" : "John",
"lastName" : "svc_user",
.....
.....
"status" : "A",
"effDate" : ISODate("2012-08-24T01:46:33.000Z"),
"department" : [
{
"deptName" : "KG",
....
....
},
...
....
.....
],
...
...
...
}
I executed the below query:
db.employee.update({}, {$rename:{"department.deptName":"department.departmentName"}}, false, true);
Error:
cannot use the part (department of department.deptName) to traverse the element
In Mongo 4.2 you could run this one:
db.collection.updateMany(
{
department: { $exists: true },
"department.deptName": { $exists: true }
},
[{
$set: {
department: {
$map: {
input: "$department",
in: { departmentName: "$$this.deptName" }
}
}
}
}]
)
I assume in 4.0.3. you have to modify the field name one-by-one with a loop in JavaScript
Related
I'm working on Django which uses MongoDB.
One of collections has the following structure:
{
"inspectionType" : {
"id" : "59a79e44d12b52042104c1e8",
"name" : "Example Name",
"inspMngrsRole" : [
{
"id" : "55937af6f3de6c004bc42862",
"type" : "inspectorManager",
"is_secret_shoper" : false
}
],
"scopes" : {
"56fcf6736389b9007a114b10" : {
"_cls" : "SomeClass",
"id" : "56fcf6736389b9007a114b10",
"name" : "Example Name",
"category" : "Example Category"
},
}
}
}
I need to update field "_cls" ("inspectionType.scopes.._cls") for all documents in the collection.
The problem is the scope_id is dynamic and unique for each scope.
Is it possible to use db.collection.update for that?
And how should the path to the field look like?
Update:
MongoDB version: 3.6.7
You can update using an aggregation (if you are using MongoDB version lesser than 4.2) plus an update operation or the updateMany method (if using version 4.2 or later) as follows:
# 1
var NEW_VALUE = "some new value" // the value to be updated
db.collection.aggregate( [
{
$addFields: {
"inspectionType.scopes": { $objectToArray: "$inspectionType.scopes" }
}
},
{
$addFields: {
"inspectionType.scopes.v._cls": NEW_VALUE
}
},
{
$addFields: {
"inspectionType.scopes": { $arrayToObject: "$inspectionType.scopes" }
}
}
] ).forEach( doc => db.scopes.updateOne( { _id: doc._id }, { $set: { "inspectionType.scopes": doc.inspectionType.scopes } } ) )
Starting MongoDB version 4.2 the updateMany can use an aggregation pipeline for the update operation; see Update with Aggregation Pipeline.
# 2
db.collection.updateMany(
{ },
[
{
$set: {
"inspectionType.scopes": { $objectToArray: "$inspectionType.scopes" }
}
},
{
$set: {
"inspectionType.scopes.v._cls": NEW_VALUE
}
},
{
$set: {
"inspectionType.scopes": { $arrayToObject: "$inspectionType.scopes" }
}
}
]
)
I've tried many answers to similar problems using $lookup, $unwind, and $match, but I can't get this to work for my sub-sub-subdocument situation.
I have this collection, Things:
{
"_id" : ObjectId("5a7241f7912cfc256468cb27"),
"name" : "Fortress of Solitude",
"alias" : "fortress_of_solitude",
},
{
"_id" : ObjectId("5a7247ec548c9ad042f579e2"),
"name" : "Batcave",
"alias" : "batcave",
},
{
"_id" : ObjectId("6a7247bc548c9ad042f579e8"),
"name" : "Oz",
"alias" : "oz",
},
and this one-document collection, Venues:
{
"_id" : ObjectId("5b9acabbbf71f39223f8de6e"),
"name" : "The Office",
"floors" : [
{
"name" : "1st Floor",
"places" : [
{
"name" : "Front Entrance",
"alias" : "front_entrance"
}
]
},
{
"name" : "2nd Floor",
"places" : [
{
"name" : "Batcave",
"alias" : "batcave"
},
{
"name" : "Oz",
"alias" : "oz"
}
]
}
]
}
I want to return all the Things, but with the Venue's floors.places.name aggregated with each Thing if it exists if the aliases match between Things and Venues. So, I want to return:
{
"_id" : ObjectId("5a7241f7912cfc256468cb27"),
"name" : "Fortress of Solitude",
"alias" : "fortress_of_solitude",
<-- nothing added here because
<-- it's not found in Venues
},
{
"_id" : ObjectId("5a7247ec548c9ad042f579e2"),
"name" : "Batcave",
"alias" : "batcave",
"floors" : [ <-- this should be
{ <-- returned
"places" : [ <-- because
{ <-- the alias
name" : "Batcave" <-- matches
} <-- in Venues
] <--
} <--
] <--
},
{
"_id" : ObjectId("6a7247bc548c9ad042f579e8"),
"name" : "Oz",
"alias" : "oz",
"floors" : [ <-- this should be
{ <-- returned
"places" : [ <-- because
{ <-- the alias
name" : "Oz" <-- matches
} <-- in Venues
] <--
} <--
] <--
}
I've gotten as far as the following query, but it only returns the entire Venues.floors array as an aggregate onto each Thing, which is way too much extraneous data aggregated. I just want to merge each relevant floor.place sub-subsubdocument from Venues into its corresponding Thing if it exists in Venues.
db.getCollection('things').aggregate([
{$lookup: {from: "venues",localField: "alias",foreignField: "floors.places.alias",as: "matches"}},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$matches", 0 ] }, "$$ROOT" ] } }
},
{ $project: { matches: 0 } }
])
I'm struggling with existing answers, which seem to change at MongoDB version 3.2, 3.4, 3.6, or 4.2 to include or not include $unwind, $pipeline, and other terms. Can someone explain how to get a sub-sub-subdocument aggregated like this? Thanks!
You can try this :
db.things.aggregate([
{
$lookup:
{
from: "venues",
let: { alias: "$alias" },
pipeline: [
{ $unwind: { path: "$floors", preserveNullAndEmptyArrays: true } },
{ $match: { $expr: { $in: ['$$alias', '$floors.places.alias'] } } },
/** Below stages are only if you've docs like doc 2 in Venues */
{ $addFields: { 'floors.places': { $filter: { input: '$floors.places', cond: { $eq: ['$$this.alias', '$$alias'] } } } } },
{ $group: { _id: '$_id', name: { $first: '$name' }, floors: { $push: '$floors' } } },
{$project : {'floors.places.alias': 1, _id :0}} // Optional
],
as: "matches"
}
}
])
Test : MongoDB-Playground
Since MongoDB v3.6, we may perform uncorrelated sub-queries which gives us more flexibility to join two collections.
Try this:
db.things.aggregate([
{
$lookup: {
from: "venues",
let: {
"alias": "$alias"
},
pipeline: [
{
$unwind: "$floors"
},
{
$project: {
_id: 0,
places: {
$filter: {
input: "$floors.places",
cond: {
$eq: [
"$$alias",
"$$this.alias"
]
}
}
}
}
},
{
$match: {
"places.0": {
$exists: true
}
}
},
{
$unset: "places.name"
}
],
as: "floors"
}
}
])
MongoPlayground
I want to know how to write the mongodb script for updating the field in my db
my collection is something like this
{
_id: 1,
name: xyz,
answer: [
{ type: 'C',
from: 0
},
{ type: 'M',
from: 0
},
{ type: 'P',
from: 0
}
........ so on
]
}
.
.
.
and other objects
I want to add a field called "test" in each object of answer array whose "type" value is not 'C'
I am new to this. Can someone help me how should i do this.
Are you looking for this:
db.col.updateOne(
{ name: "xyz" },
{ "$set": { 'answer.$[i].test': null } },
{ arrayFilters: [{ "i.type": { $ne: "C" } }] }
)
Result:
{
"name" : "xyz",
"answer" : [
{
"type" : "C",
"from" : 0.0
},
{
"type" : "M",
"from" : 0.0,
"test" : null
},
{
"type" : "P",
"from" : 0.0,
"test" : null
}
]
}
If you like to update all documents in your collection use updateMany() and skip filter { name: "xyz" }
Of course you can run the update manually like this:
db.col.find().forEach(function (doc) {
doc.answer.forEach(function (i) {
if (i.type != "C")
i.test = null;
})
db.col.updateOne(
{ _id: doc._id },
{ $set: doc }
)
})
but you have to admit, arrayFilters is shorter and more convenient.
How can I query a MongoDB collection to find documents with a structure as below? The documents have a field called thing which is a subdocument, and the keys for this field are a form of ID number which will generally not be known by the person writing the query (making dot notation difficult and I assume impossible).
{
"_id" : 3,
"_id2" : 234,
"thing":
{
"2340945683":
{"attribute1": "typeA",
"attribute2": "typeB",
"attribute3": "typeA"
},
"349687346":
{"attribute1": "typeC",
"attribute2": "typeB",
"attribute3": "typeA"
}
},
"username": "user1"
}
Say I want to set a filter which will return the document only if some one or more of the fields within thing have the condition "attribute1" : "typeC"?
I need something like
db.collection.find( {thing.ANY_FIELD: $elemMatch:{"attribute1":"typeC"}})
You need to start with $objectToArray to read your keys dynamically. Then you can $map properties along with $anyElementTrue to detect if there's any nested field in thing containing {"attribute1":"typeC"}:
db.collection.aggregate([
{
$match: {
$expr: {
$anyElementTrue: {
$map: {
input: { $objectToArray: "$thing" },
in: { $eq: [ "$$this.v.attribute1", "typeC" ] }
}
}
}
}
}
])
Mongo Playground
My solution to this was to use two aggregate operations, the first one is called objectToArray and it's purpose is to convert a object into a list of objects with keys and values (see the documentation examples), and the reduce to search in this array of key-values, at the end we end up with a boolean "hasAttribute" indicating that the one field matched the value wee are looking for.
Here is the solution:
db.getCollection("thing").aggregate([
{
$addFields: {
hasAttribute: {
$reduce: {
input: {
$objectToArray: "$thing"
},
initialValue: false,
in: {$or: ["$$value", {$eq: ["typeC", "$$this.v.attribute1"]}]}
}
}
}
},
{
$match: {
hasAttribute: true
}
}
])
Here is the sample output and how the boolean value behaves:
{
"_id" : ObjectId("5ddd63c02e5c579c5076c76f"),
"thing" : {
"349687346" : {
"attribute1" : "typeC",
"attribute2" : "typeB",
"attribute3" : "typeA"
},
"2340945683" : {
"attribute1" : "typeA",
"attribute2" : "typeB",
"attribute3" : "typeA"
}
},
"hasAttribute" : true
}
// ----------------------------------------------
{
"_id" : ObjectId("5ddd63d12e5c579c5076c770"),
"thing" : {
"2340945683" : {
"attribute1" : "typeA",
"attribute2" : "typeB",
"attribute3" : "typeA"
}
},
"hasAttribute" : false
}
// ----------------------------------------------
{
"_id" : ObjectId("5ddd63d12e5c579c5076c771"),
"thing" : {
"349687346" : {
"attribute1" : "typeC",
"attribute2" : "typeB",
"attribute3" : "typeA"
}
},
"hasAttribute" : true
}
Ask for clarifications if you need!
I have a document like this:
{
"_id": ObjectId("5d779541bd4e75c58d598212")
"client": ObjectId("5d779558bd4e75c58d598213")
}
When I do $lookup like this:
{
from: 'client',
localField: 'client',
foreignField: 'id',
as: 'client',
}
I get:
{
"_id": ObjectId("5d779541bd4e75c58d598212")
"client":[
{
... client info wrapped in array
}
]
}
This forces me to add $unwind after the lookup stage.
This would work fine in this example because I know that it is a regular field (not array). But on other collections I have arrays of ObjectId's and I don't want to unwind them.
How should I tell mongo to unwind only if it's not an array?
Add $project stage with $arrayElemAt
{ $lookup ..... },
{ $project: { client: { $arrayElemAt: [ "$client" , 0 ]}} // Add other filed
The lookup always returns an array as it doesn't know if its a one-to-one or one-to-many mapping. But we can ensure that the lookup returns a single document and that document would hold all documents which were supposed to come as an array in the general lookup.
Following is the way:
db.collection.aggregate([
{
$lookup:{
"from":"client",
"let":{
"client":"$client"
},
"pipeline":[
{
$match:{
$expr:{
$eq:["$id","$$client"]
}
}
},
{
$group:{
"_id":null,
"data":{
$push:"$$ROOT"
}
}
},
{
$project:{
"_id":0
}
}
],
"as":"clientLookup"
}
},
{
$unwind:"$clientLookup"
}
]).pretty()
Query analysis: We are looking up into client collection and executing a pipeline inside that. The output of that pipeline would hold every matched document inside data field.
Data set:
Collection: collection
{
"client":1
}
{
"client":2
}
Collection: client
{
"id":1,
"name":"Tony"
}
{
"id":1,
"name":"Thor"
}
{
"id":1,
"name":"Natasha"
}
{
"id":2,
"name":"Banner"
}
Output:
{
"_id" : ObjectId("5d7792c6bd4e75c58d59820c"),
"client" : 1,
"clientLookup" : {
"data" : [
{
"_id" : ObjectId("5d779322bd4e75c58d59820e"),
"id" : 1,
"name" : "Tony"
},
{
"_id" : ObjectId("5d779322bd4e75c58d59820f"),
"id" : 1,
"name" : "Thor"
},
{
"_id" : ObjectId("5d779322bd4e75c58d598210"),
"id" : 1,
"name" : "Natasha"
}
]
}
}
{
"_id" : ObjectId("5d7792c6bd4e75c58d59820d"),
"client" : 2,
"clientLookup" : {
"data" : [
{
"_id" : ObjectId("5d779322bd4e75c58d598211"),
"id" : 2,
"name" : "Banner"
}
]
}
}