MongoDB Transform element fields into array of values only - arrays

I need help with following thing in MongoDB, what I'm trying to do is to get only marks in reviews into it's own array of values only.
Code i got so far:
db.lodging.aggregate([
{$project:{
reviews:"$host.reviews",host:"$host"
}
},
{$unwind: "$reviews"},
])
JSON Example:
"host" : {
"name" : "Grimes",
"surname" : "Terrell",
"gender" : "male",
"age" : NumberInt(55),
"picture" : "https://api.adorable.io/avatars/285/GrimesTerrell.png",
"reviews" : [
{
"reviewer" : "Mae Ryan",
"date" : "2015-06-01T02:41:46 -02:00",
"helpful" : NumberInt(8),
"kind" : NumberInt(1),
"responsive" : NumberInt(5)
},
{
"reviewer" : "Nixon Johnson",
"date" : "2016-02-08T10:35:12 -01:00",
"helpful" : NumberInt(1),
"kind" : NumberInt(1),
"responsive" : NumberInt(9)
},
]
}
This is what im trying to achieve:
{
"host" : {
"name" : "Grimes",
"surname" : "Terrell",
"gender" : "male",
"age" : NumberInt(55),
"picture" : "https://api.adorable.io/avatars/285/GrimesTerrell.png",
"reviews" : [
{
"marks" : [8,1,5],
},
{
"marks" : [1,1,9],
},
]
}

Assuming your marks' field names are always helpful, kind, responsive, you can use $map.
Here is a mongo playground for you reference

Related

MongoDb sort on list of objects

I have some dummy data in my database. I'm trying to perform mongodb aggregation functions in order to sort based on a given title
{ "_id" : 1, "item" : "ABC1", "fields" : [ { "title" : "firstName", "value" : "Trish" }, { "title" : "zipcode", "value" : "01001" } ] }
{ "_id" : 2, "item" : "ABC2", "fields" : [ { "title" : "firstName", "value" : "Peter" }, { "title" : "zipcode", "value" : "00011" } ] }
The query that i'm able to come up with so far is this and the following is what mongodb returns.
db.test.aggregate([ {$unwind: "$fields"}, {$match: {"fields.title" : "firstName"}}, {$sort: {"fields.value" : 1} }])
{ "_id" : 2, "item" : "ABC2", "fields" : { "title" : "firstName", "value" : "Peter" } }
{ "_id" : 1, "item" : "ABC1", "fields" : { "title" : "firstName", "value" : "Trish" } }
However the result I would the returned dataset should include the other object in the fields list like so. The main thing that I am trying to accomplish is being able to sort the documents based on a specific field title
{ "_id" : 2, "item" : "ABC2", "fields" : [ { "title" : "firstName", "value" : "Peter" }, { "title" : "zipcode", "value" : "00011" } ] }
{ "_id" : 1, "item" : "ABC1", "fields" : [ { "title" : "firstName", "value" : "Trish" }, { "title" : "zipcode", "value" : "01001" } ] }
If you can guarantee the position of the fields, firstName always the first, and so on. You can sort by that. like this:
db.test.find().sort({'fields.0.value': -1})
//or
db.test.find().sort({'fields.0.value': 1})

How to modify fields in specific sub-documents in an array?

I have a collection where each document has an array of sub-documents, containing several fields. I need to update specific sub-documents based on their fields, but every attempt I've done so far changes the field in all of the arrayed sub-documents
Here is some sample data:
{
'_id' : ObjectId('0001'),
'Region' : 'Northern',
"Items" : [
{
"ItemId" : NumberInt(25),
"Name" : "Widget",
"ProductType" : "15",
"ItemIdLegacy" : "ca-000037"
},
{
"ItemId" : NumberInt(30),
"Name" : "Gizmo",
"ProductType" : "15",
"ItemIdLegacy" : "ca-000038"
},
{
"ItemId" : NumberInt(35),
"Name" : "Thingy",
"ProductType" : "15",
"ItemIdLegacy" : "ca-000039"
}
]
}
When I try to use update() with the following query, it updates the ProductType on all array items, not just the one I'm trying to change.
To clarify, I want to change the array item with ItemIdLegacy: "ca-000038" to have ProductType: 20. All other Array items should remain unchanged. Query that I have tried:
db.Collection.update({"Items.ItemIdLegacy" : "ca-000038"},[{$set: { "Items.ProductType" : "20"} }],{multi: false});
This is the desired output:
{
'_id' : ObjectId('0001'),
'Region' : 'Northern',
"Items" : [
{
"ItemId" : NumberInt(25),
"Name" : "Widget",
"ProductType" : "15",
"ItemIdLegacy" : "ca-000037"
},
{
"ItemId" : NumberInt(30),
"Name" : "Gizmo",
"ProductType" : "20",
"ItemIdLegacy" : "ca-000038"
},
{
"ItemId" : NumberInt(35),
"Name" : "Thingy",
"ProductType" : "15",
"ItemIdLegacy" : "ca-000039"
}
]
}
But this is the actual output of running that query:
{
'_id' : ObjectId('0001'),
'Region' : 'Northern',
"Items" : [
{
"ItemId" : NumberInt(25),
"Name" : "Widget",
"ProductType" : "20",
"ItemIdLegacy" : "ca-000037"
},
{
"ItemId" : NumberInt(30),
"Name" : "Gizmo",
"ProductType" : "20",
"ItemIdLegacy" : "ca-000038"
},
{
"ItemId" : NumberInt(35),
"Name" : "Thingy",
"ProductType" : "20",
"ItemIdLegacy" : "ca-000039"
}
]
}
I feel like I'm missing something simple...
EDIT: I have q uery that will allow me to update a single matching element in the array, but I have thousands that would need to be done so one offs don't necessarily work (sure I could parse them all out and spam them to the shell, but I want something more elegant)
Single Element Update:
db.Collection.updateOne({"Items.ItemIdLegacy" : "ca-000038"},{ $set: { "Items.$.ProductType" : "20" } } );
Try this :-
db.collection.update({
"Items.ItemIdLegacy": "ca-000038"
},
{
$set: {
"Items.$.ProductType": 20
}
},
{
multi: true
})
Here is Working Example

Remove objects within array in MongoDB

I need to remove all the objects within array who meet the conditions i will show down below. I'll let here the documents and an example of what i've done.
//document 1
{
"_id" : ObjectId("5ec73abebd7e4d618a057350"),
"code" : "X20",
"title" : "Full stack developer",
"location" : "Paris",
"date" : ISODate("2020-05-22T02:36:46.272Z"),
"candidates" : [
{
"name" : "David",
"last_name" : "Broncano",
"telephone" : "642025552",
"email" : "david#gmail.com"
},
{
"name" : "Pablo",
"last_name" : "Claros",
"telephone" : "628721784",
"email" : "pablo#gmail.com"
}
]
}
// document 2
{
"_id" : ObjectId("4ec73abebd7e4d618a057350"),
"code" : "X50",
"title" : "Full stack developer",
"location" : "Madrid",
"date" : ISODate("2020-05-22T02:36:46.272Z"),
"candidates" : [
{
"name" : "Maria",
"last_name" : "Mars",
"telephone" : "642024582",
"email" : "dasd#gmail.com"
},
{
"name" : "Pablo",
"last_name" : "Claros",
"telephone" : "628721784",
"email" : "pablo#gmail.com"
}
]
}
So i need to remove all the candidates where location is Madrid.I have done this but it removes the field. Is it possible to just remove the content of it using $pull or something?
db.offers.update(
{ location : "Madrid"},
{
$unset:{
"candidates":""
} } ,
{
multi : true
}
)
According to my understanding, you need to just clear the candidates array and maintain that as candidates: []. For this, you can use use $set operator to set candidates to [] based on your condition
db.offers.update({ location : "Madrid"}, { $set:{ "candidates": [] } } , { multi : true })

Aggregate from one document array to another

I am trying to do a transformation from one data structure to another and am using NoSQL Booster as I feel more comfortable using SQL queries than I do using Mongo script, a little lazy but its worked for me thus far.
I have a document which has a an array of line items which represents a sales receipt. The line items are the items sold.
I have used the following SQL query to generate Mongo script to select and then rename values from one document to create another. Where I'm stuck is in selecting from an array some fields and renaming them into a new document within a new array.
mb.runSQLQuery(`
SELECT object_origin,
party_uuid AS company,
connection_uuid AS connection,
object_created AS object_creation_date,
"object_raw_origin_data.register_sales.sale_date" AS transaction_date,
"object_raw_origin_data.register_sales.total_price"
+ "object_raw_origin_data.register_sales.total_tax" AS transaction_gross_value,
"object_raw_origin_data.register_sales.total_price" AS transaction_net_value,
'goods-service-transaction' AS object_class,
'point-of-sale' AS object_origin_category,
'offline' AS object_origin_type,
object_origin AS object_origin,
"object_raw_origin_data.register_sales.invoice_number" AS transaction_reference,
"object_raw_origin_data.register_sales.status" AS transaction_status,
'GBP' AS transaction_currency,
"object_raw_origin_data.register_sales.sale_products.name" AS 'line_items.item_name'
FROM Vend_raw_transactions
`)
The query above works all except the last line which creates an object in the document called line_items but it isn't an array. The source is also an array.
Below is the Mongo script it creates for me:
db.Vend_raw_transactions.aggregate(
[{
"$project": {
"object_origin": "$object_origin",
"company": "$party_uuid",
"connection": "$connection_uuid",
"object_creation_date": "$object_created",
"transaction_date": "$object_raw_origin_data.register_sales.sale_date",
"transaction_gross_value": {
"$add": [
"$object_raw_origin_data.register_sales.total_price",
"$object_raw_origin_data.register_sales.total_tax"
]
},
"transaction_net_value":
"$object_raw_origin_data.register_sales.total_price",
"object_class": "goods-service-transaction",
"object_origin_category": "point-of-sale",
"object_origin_type": "offline",
"transaction_reference":
"$object_raw_origin_data.register_sales.invoice_number",
"transaction_status": "$object_raw_origin_data.register_sales.status",
"transaction_currency": "GBP",
"line_items.item_name":
"$object_raw_origin_data.register_sales.sale_products.name"
}
}])
Does anyone know why the last line isn't working for me?
"line_items.item_name": "$object_raw_origin_data.register_sales.sale_products.name"
I'm pretty sure I'm missing an [*] somewhere. Thanks, Matt
EDITED TO ADD example of Vend Transaction
{
"object_category" : "application",
"object_type" : "register-sales-24-months",
"object_origin" : "vend",
"tenant_uuid" : "00000000-0000-0009-9999-999999999999",
"party_uuid" : "8d519765-05d2-469f-ad35-d7a22fa9df2f",
"subscription_uuid" : "0",
"connection_uuid" : "6ed9bd79-d9c5-4296-a821-7e15b1c69e6c",
"status" : "",
"object_created" : ISODate("2018-03-15T21:40:57.158+0000"),
"object_raw_origin_data" : {
"pagination" : {
"results" : NumberInt(75964),
"page" : NumberInt(1),
"page_size" : NumberInt(200),
"pages" : NumberInt(380)
},
"register_sales" : {
"id" : "776a66f2-993c-b372-11e8-26f9bbe253f4",
"source" : "USER",
"source_id" : "",
"register_id" : "02dcd191-ae55-11e6-edd8-ec8dce1d9e1c",
"market_id" : "3",
"customer_id" : "02d59481-b67d-11e5-f667-b08185e8f6d5",
"customer_name" : "",
"customer" : {
"id" : "02d59481-b67d-11e5-f667-b08185e8f6d5",
"name" : "",
"customer_code" : "WALKIN",
"customer_group_id" : "02d59481-b67d-11e5-f667-b08185e893f8",
"customer_group_name" : "All Customers",
"updated_at" : "2016-01-01 12:16:44",
"deleted_at" : "",
"balance" : "0",
"year_to_date" : "0",
"date_of_birth" : "",
"sex" : "",
"custom_field_1" : "",
"custom_field_2" : "",
"custom_field_3" : "",
"custom_field_4" : "",
"note" : "",
"contact" : {
}
},
"user_id" : "02d59481-b655-11e5-f667-dca974edc4ea",
"user_name" : "Alvaro Velosa",
"sale_date" : "2018-03-13 20:04:57",
"created_at" : "2018-03-13 20:05:08",
"updated_at" : "2018-03-13 20:05:08",
"total_price" : 3.5,
"total_cost" : 2.74,
"total_tax" : NumberInt(0),
"tax_name" : "No Tax",
"note" : "",
"status" : "CLOSED",
"short_code" : "newa6f",
"invoice_number" : "Masonic2107Temple",
"accounts_transaction_id" : "",
"return_for" : "",
"register_sale_products" : [
{
"id" : "776a66f2-993c-b372-11e8-26f9cbf10f10",
"product_id" : "02dcd191-ae55-11e7-f130-9d4f4bcd91b1",
"register_id" : "02dcd191-ae55-11e6-edd8-ec8dce1d9e1c",
"sequence" : "0",
"handle" : "LAGERDRAUGHT300",
"sku" : "10287",
"name" : "LAGER DRAUGHT £3.00",
"quantity" : NumberInt(1),
"price" : 3.5,
"cost" : 2.74,
"price_set" : NumberInt(0),
"discount" : NumberInt(0),
"loyalty_value" : NumberInt(0),
"tax" : NumberInt(0),
"tax_id" : "02d59481-b67d-11e5-f667-b08185ec2871",
"tax_name" : "No Tax",
"tax_rate" : NumberInt(0),
"tax_total" : NumberInt(0),
"price_total" : 3.5,
"display_retail_price_tax_inclusive" : "0",
"status" : "CONFIRMED",
"attributes" : [
{
"name" : "line_note",
"value" : ""
}
]
}
],
"totals" : {
"total_tax" : NumberInt(0),
"total_price" : 3.5,
"total_payment" : 3.5,
"total_to_pay" : NumberInt(0)
},
"register_sale_payments" : [
{
"id" : "776a66f2-993c-b372-11e8-26f9cd75e9aa",
"payment_type_id" : "1",
"register_id" : "02dcd191-ae55-11e6-edd8-ec8dce1d9e1c",
"retailer_payment_type_id" : "02d59481-b655-11e5-f667-b0a23bc0e7bc",
"name" : "Cash",
"label" : "Account Customer",
"payment_date" : "2018-03-13 20:04:57",
"amount" : NumberInt(10)
},
{
"id" : "776a66f2-993c-b372-11e8-26f9cd7a3096",
"payment_type_id" : "1",
"register_id" : "02dcd191-ae55-11e6-edd8-ec8dce1d9e1c",
"retailer_payment_type_id" : "02d59481-b655-11e5-f667-b0a23bc0e7bc",
"name" : "Cash",
"label" : "Account Customer",
"payment_date" : "2018-03-13 20:04:57",
"amount" : -6.5
}
]
}
}
}
Use the $map operator to shape the field to an array by mapping the array elements to the item_name key:
"line_items": {
"$map": {
"input": "$object_raw_origin_data.register_sales.register_sale_products",
"as": "product",
"in": { "item_name": "$$product.name" }
}
}
Output
"line_items" : [
{
"item_name" : "LAGER DRAUGHT £3.00"
}
]

Reformat MongoDB results from aggregate

I'm coming from SQL world and starting with MongoDB, I'm still a bit confused...
I have a collection with this structure
{
"_id" : ObjectId("5769b51f675e6190119935ec"),
"city" : "City1",
"company" : "Company1",
"country" : "Country1",
"zip" : "23345",
},{
"_id" : ObjectId("5769b51f675e6190119935ed"),
"city" : "City1",
"company" : "Company2",
"country" : "Country1",
"zip" : "1245",
},{
"_id" : ObjectId("5769b51f675e6190119935ee"),
"city" : "City2",
"company" : "Company1",
"country" : "Country1",
"zip" : "123445",
},{
"_id" : ObjectId("5769b51f675e6190119935ef"),
"city" : "City1",
"company" : "Company2",
"country" : "Country1",
"zip" : "1235445",
}
and my query,
db.getCollection('stores').aggregate([{"$group":{"_id" : {city :"$city", company : "$company"}}}])
I'm using angular and NodejS+Express to get the data from de database and I get the data with this format
[
{
_id:{
city:"City1",
company:"Compnay1"
}
},
{
_id:{
city:"City1",
company:"Company2"
}
},
{
_id:{
city:"City2",
company:"Compan1"
}
}
]
So I'm wondering if there's a way to get this query result without the _id key,
like this:
[
{
city:"City1",
company:"Compnay1"
},
{
city:"City1",
company:"Company2"
},
{
city:"City2",
company:"Compan1"
}
]
Just use $project as aggregation pipeline operator in the next pipeline stage:
db.getCollection('stores').aggregate([
{"$group":{"_id" : {city :"$city", company : "$company"}}},
{"$project": {"city": "$_id.city", "company": "$_id.company", "_id": 0}}
])

Resources