Rename key in a mongoDB array of object - mongodb

I need to rename the key "street" to "block" in every object of the "address" array in a mongo document. The structure of the document is as follows,
{
"_id": 1234,
"name": "Jack",
"address": [
{
"no": 1,
"street": "streetx",
"country": "countryx"
},
{
"no": 1,
"street": "streety",
"country": "countryy"
}
]
}
Note : The mongoDB version is '4.0.0'

You can use $map to assign the new field to the address objects. For MongoDB v4.0, use javascript to iterate and update back to the collection.
db.collection.aggregate([
{
"$addFields": {
"address": {
"$map": {
"input": "$address",
"as": "a",
"in": {
"$mergeObjects": [
"$$a",
{
"no": "$$a.no",
"country": "$$a.country",
"block": "$$a.street"
}
]
}
}
}
}
}
]).forEach(function(doc){
db.collection.save(doc);
})
Here is the Mongo playground for your reference.

Related

How to use an existing field value with $push in MongoDb?

I have the following Mongo collection
{
"_id": ObjectId("5524d12d2702a21830bdb8e5"),
"code": "Apple",
"name": "iPhone",
"parameters": [
{
"code": "xxx",
"name": "Andrew",
"value": "9",
},
{
"code": "yyy",
"name": "Joy",
"value": "7",
},
]
}
I am using the following query to push into the parameters array object
db.coll.update({
"parameters.name": "Andrew"
},
{
$push: {
"parameters": {
"code": "$code",
"name": "bar",
"value": "10",
}
}
},
{
multi: true
})
However, for the value of code, I want to use the value of the object that matched (i.e. the object with parameters.name == "Andrew", which here is xxx.
Here's a playground link to the problem https://mongoplayground.net/p/v-j1tCCjiWq
Also, I am using a really old version (3.2) of MongoDb. It would be preferable if the solution worked with that.
With MongoDB v4.4+, you can first $match with your criteria. Chain up $first with $filter to extract the array element you want. Use $concatArrays to append a new element(i.e. same as $push) to the array and $merge to update back into the collection.
db.coll.aggregate([
{
$match: {
"parameters.name": "Andrew"
}
},
{
$set: {
parameters: {
"$concatArrays": [
"$parameters",
[
{
"$mergeObjects": [
// get the object matched
{
"$first": {
"$filter": {
"input": "$parameters",
"as": "p",
"cond": {
$eq: [
"Andrew",
"$$p.name"
]
}
}
}
},
// update the object matched with other fields with constant value
{
"name": "bar",
"value": "10"
}
]
}
]
]
}
}
},
{
"$merge": {
"into": "coll1",
"on": "_id"
}
}
])
Mongo Playground

Find specific field in MongoDB document based on condition

I have the following MongoDB documents like this one:
{
"_id": "ABC",
"properties":
[
{
"_id": "123",
"weight":
{
"$numberInt": "0"
},
"name": "Alice"
},
{
"_id": "456",
"weight":
{
"$numberInt": "1"
},
"name": "Bob"
},
{
"_id": "789",
"weight":
{
"$numberInt": "1"
},
"name": "Charlie"
}
]
}
And I would like to find the _id of the property with name "Alice", or the _id of the property with "$numberInt": "0".
I'm using pymongo.
The following approach:
from pymongo import MongoClient
mongo_client = MongoClient("mymongourl")
mongo_collection = mongo_client.mongo_database.mongo_collection
mongo_collection.find({'properties.name': 'Alice'}, {'properties': 1})[0]['_id']
Gives the very first _id ("123")
But since I filtered for the document, if Alice was in the second element of the properties array (_id: "456") I would have missed her.
Which is the best method to find for the specific _id associated with the element with the specified name?
You can simply use $reduce to iterate through the properties array. Conditionally store the _id field if it matches your conditions.
db.collection.aggregate([
{
"$addFields": {
"answer": {
"$reduce": {
"input": "$properties",
"initialValue": null,
"in": {
"$cond": {
"if": {
$or: [
{
$eq: [
"$$this.name",
"Alice"
]
},
{
$eq: [
"$$this.weight",
0
]
}
]
},
"then": "$$this._id",
"else": "$$value"
}
}
}
}
}
}
])
Mongo Playground

mongodb query documents which have subset of all key value pairs mentioned in the query

Let's assume we have the following documents in the Mongo db
[
{
"Name": "Jack",
"info": {
"eyes": "brown",
"city": "paris",
"accomodation": "house"
}
},
{
"Name": "Mathew",
"info": {
"eyes": "yellow",
"city": "rome",
"accomodation": "apartment"
}
},
{
"Name": "Peter",
"info": {
"eyes": "brown",
"city": "barcelona",
"accomodation": "house",
"hair_color": "black"
}
}
]
Let's say the input JSON is
{
"eyes": "brown",
"city": "paris",
"accomodation": "house",
"hair_color": "black"
}
I would like to construct a query such that all the key-value pairs of the document should be present in the input json document but not necessarily the other way round.
For the above example - the output should be
{
"Name": "Jack",
"info": {
"eyes": "brown",
"city": "paris",
"accomodation": "house"
}
}
Since all the key value pairs of Jack were met by the input json document even though the input JSON document had hair_color which wasn't present in Jack document
Mongo playground with input : https://mongoplayground.net/p/1T0ZL8yGhPW
All you have to do is convert both your input and info field to an array using $objectToArray and check if their if there's any none matches, like so:
db.collection.aggregate([
{
$addFields: {
rawInput: { // better if you do this in code once and drop this stage.
"$objectToArray": {
"eyes": "brown",
"city": "paris",
"accomodation": "house",
"hair_color": "black"
}
}
}
},
{
$match: {
$expr: {
$eq: [
{
$size: {
$filter: {
input: {
"$objectToArray": "$info"
},
as: "elem",
cond: {
$not: {
"$in": [
"$$elem",
"$rawInput"
]
}
}
}
}
},
0
]
}
}
},
{
"$project": {
Name: 1,
info: 1
}
}
])
Mongo Playground

Select data from two MongoDB tables and update the results

We have the following problem
Given are the tables and fields
Offer
OfferId
State
Article
OfferId
ArticleId
NetPrice
GrossPrice
VatRate
Example-data:
Offer-Collection
{
"_id": "1",
"State": "INITIAL",
"_class": "com.example.dto.OfferData"
}
{
"_id": "2",
"State": "COMPLETED",
"_class": "com.example.dto.OfferData"
}
Article-Collection
{
"_id": {
"$oid": "a"
},
"Description": "asdf",
"NetPrice": "100",
"GrossPrice": "116",
"VatRate": "16",
"OfferId": "1",
"_class": "com.example.dto.Article"
}
{
"_id": {
"$oid": "b"
},
"Description": "my description",
"NetPrice": "100",
"GrossPrice": "119",
"VatRate": "19",
"OfferId": "1",
"_class": "com.example.dto.Article"
}
{
"_id": {
"$oid": "c"
},
"Description": "my description",
"NetPrice": "100",
"GrossPrice": "116",
"VatRate": "16",
"OfferId": "2",
"_class": "com.example.dto.Article"
}
Now we have to update all articles belonging to an offer with the state "initial" in the following way: if the VatRate is equal to 16 than it must be updated to 19 AND the GrossPrice must be recalculated from the existing NetPrice.
The result should be: the article with _id = "a" and VatRate = 16 for OfferId = 1 (State = INITIAL) should have VatRate = 19 and GrossPrice = 119. The fields should be updated and persisted in the original MongoDB collection.
Can we do this only with Mongo-shell? Our Version is 3.6.
Our tries:
We have played around with .aggregate, $lookup, $match and $project but without much luck. It's the first time we are using the Mongo-shell.
db.getCollection("Offers").aggregate([{
$lookup:{
from:"Articles",
localField:"OfferId",
foreignField:"OfferId",
as:"selected-articles"
}
},
{
$match: { "state": { "$eq": "INITIAL" } }
},
{
$project: { "articles": 1 }
}
]).forEach(...?)
$match your State condition
$lookup with Articles collection
$map to iterate loop of selected-articles array, check condition using $cond if VatRate is "16" then updated to 19 and recalculate GrossPrice as per NetPrice using $multiply before it convert NetPrice to integer because its in string type, back to merge objects with current objects using $mergeObjects
db.getCollection("Offers").aggregate([
{ $match: { State: { $eq: "INITIAL" } } },
{
$lookup: {
from: "Articles",
localField: "_id",
foreignField: "OfferId",
as: "selected-articles"
}
},
{
$addFields: {
"selected-articles": {
$map: {
input: "$selected-articles",
in: {
$mergeObjects: [
"$$this",
{
$cond: [
{ $eq: ["$$this.VatRate", "16"] },
{
VatRate: 19,
GrossPrice: {
$multiply: [{ $toInt: "$$this.NetPrice" }, 19]
}
},
{}
]
}
]
}
}
}
}
}
])
Playground

MongoDB aggregate merging fields

I have a mongo Database I'll like to "join" two of them and then merge some other fields:
Let's see the schemas:
Students Schema (and data):
{
"_id": ObjectId("5fbd564981b1313de790b580"),
"name": "John Doe",
"age": "21",
"image": "https://XXXX/481.png",
"subjects": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"passed": true,
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
}
],
"__v": NumberInt("1"),
}
and Subject schema:
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"course": 3,
"teacher": "John Smith",
"name": "Math",
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
"name": "IT",
"course": 8,
"teacher": "John Peter",
}
What I'll like to make a query with the subjects (all info) of a student, also if the student have additional fields in subject like passed add it to the subject subdocument.
Here is my query till now:
db.students.aggregate([
{
$match:
{
_id : ObjectId('5fbd564981b1313de790b580')
}
},
{
$lookup :
{
from : "subjects",
localField : "subjects._id",
foreignField : "_id",
as : "FoundSubject"
}
}
]);
which correctly make the "join" but the merge is still missing, I got as result:
{
"_id": ObjectId("5fbd564981b1313de790b580"),
"name": "John Doe",
"age": "21",
"image": "https://XXXX/481.png",
"subjects": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"passed": true,
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
}
],
"__v": NumberInt("1"),
"FoundSubject": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"course": 3,
"teacher": "John Smith",
"name": "Math"
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
"name": "IT",
"course": 8,
"teacher": "John Peter"
}
]
}
but I'll like to have:
{
"_id": ObjectId("5fbd564981b1313de790b580"),
"name": "John Doe",
"age": "21",
"image": "https://XXXX/481.png",
"subjects": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"course": 3,
"teacher": "John Smith",
"name": "Math",
"passed": true,
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
"name": "IT",
"course": 8,
"teacher": "John Peter"
}
],
"__v": NumberInt("1"),
}
with merged data and field "passed" added. How can accomplish that?
I'm new to MongoDB coming from MySQL.
Thanks
You need to merge both objects, add below stage after $lookup,
MongoDB Version From 3.4
$map to iterate loop of students array
$reduce to iterate loop of FoundSubject array, check condition if condition match then return required fields otherwise return initial value
$project to remove FoundSubject from result
{
$addFields: {
subjects: {
$map: {
input: "$subjects",
as: "s",
in: {
$reduce: {
input: "$FoundSubject",
initialValue: {},
in: {
$cond: [
{ $eq: ["$$s._id", "$$this._id"] },
{
_id: "$$this._id",
course: "$$this.course",
name: "$$this.name",
teacher: "$$this.teacher",
passed: "$$s.passed"
},
"$$value"
]
}
}
}
}
}
}
},
{ $project: { FoundSubject: 0 } }
Playground
MongoDB Version From 4.4
$map to iterate loop of students array,
$filter to get matching document from FoundSubject array and $first to get first object from array returned by filter
$mergeObjects to merge current objects with found result object from filter
remove FoundSubject using $$REMOVE
// skipping your stages
{
$addFields: {
FoundSubject: "$$REMOVE",
subjects: {
$map: {
input: "$subjects",
as: "s",
in: {
$mergeObjects: [
"$$s",
{
$first: {
$filter: {
input: "$FoundSubject",
cond: { $eq: ["$$s._id", "$$this._id"] }
}
}
}
]
}
}
}
}
}
Playground