MongoDB field must be an array - mongodb

Currently I have a collection with the following documents:
[
{
"_id": ObjectId("628e6bd640643f97d6517c75"),
"company": "bau",
"current_version": 0,
"form_name": "don't know",
"history": [],
"id": "23421123-24a9-4a45-a12f-27a330152ax3",
"is_active": True,
"user_id": "999",
},
{
"_id": ObjectId("628eaffe4b8ae2ccdeb9305c"),
"company": "vrau",
"current_version": 0,
"form_name": "exemplo",
"history": [
{
"content": [
{
"field_id": 0,
"label": "insira um texto",
"placeholder": "qualquer texto",
"type": "text",
}
],
"layout": [
{"field_id": 0, "h": 10, "type": "text", "w": 100, "x": 0, "y": 0}
],
"responses": [
{
"client_id": 100,
"response_date": "2020-01-02",
"values": [{"field_id": 0, "value": "um texto"}],
},
{
"client_id": 2,
"response_date": "2020-01-01",
"values": [{"field_id": 0, "value": "roi"}],
},
],
"version": 0,
}
],
"id": "33b66684-24a9-4a45-a12f-27a330152ac8",
"is_active": True,
"user_id": "1",
},
]
I want to change the response fromthe client_id = '2' by I'm receiving the following error:
pymongo.errors.WriteError: The field 'history.0.responses.1' must be an array but is of type object in document {_id: ObjectId('628eaffe4b8ae2ccdeb9305c')}, full error: {'index': 0, 'code': 2, 'errmsg': "The field 'history.0.responses.1' must be an array but is of type object in document {_id: ObjectId('628eaffe4b8ae2ccdeb9305c')}"}
I don't know what I'm doing wrong and this error doesnt make sense to me cuz reponses is an array.
my current query:
collection.update_many(
{"id": "33b66684-24a9-4a45-a12f-27a330152ac8", "history.version": 0},
{
"$push": {
"history.$[h].responses.$[r]": {
"client_id": 2,
"response_date": "2020-01-01",
"values": [{"field_id": 0, "value": "roi"}],
}
}
},
array_filters=[{"h.version": 0}, {"r.client_id": "2"}],
)
Is there another to do it?

It is because you are also performing filter on r, which already resolves to object level in responses array.
You can simply abandon the r arrayFilter if you simply want to push to responses array.
collection.update_many(
{"id": "33b66684-24a9-4a45-a12f-27a330152ac8", "history.version": 0},
{
"$push": {
"history.$[h].responses": {
"client_id": 2,
"response_date": "2020-01-01",
"values": [{"field_id": 0, "value": "roi"}],
}
}
},
array_filters=[{"h.version": 0}],
)
Here is the Mongo playground for your reference. (in native js syntax)
You should use $set instead of $push if you want to update the entry instead of adding an entry. In your given example, the client_id is int while your arrayFilter is string. It could cause problem if it is not intended.
collection.update_many(
{"id": "33b66684-24a9-4a45-a12f-27a330152ac8", "history.version": 0},
{
"$set": {
"history.$[h].responses.$[r]": {
"client_id": 2,
"response_date": "2020-01-01",
"values": [{"field_id": 0, "value": "roi"}],
}
}
},
array_filters=[{"h.version": 0}, {"r.client_id": 2}],
)
Here is the Mongo playground for your reference. (in native js syntax)

Related

How to insert data on last item of an inner array in MongoDB

I am trying to insert data in the last element of an array in MongoDB. insert in the first element works using the 0 operator, but I'm confused about how to insert in the last element. To explain it, consider the following data:
[
{
"_id": "1",
"company": "turivius",
"forms": [
{
"current_version": 1,
"history": [
{
"content": [],
"layout": [],
"responses": [
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"primeiro user": "primeiro form, resposta 1",
},
{
"field_id": 1,
"value": "aaa",
},
],
},
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"primeiro user": "primeiro form, resposta 2",
},
{
"field_id": 1,
"value": "bbb",
},
],
},
],
"version": 0,
},
{
"content": [],
"layout": [],
"responses": [
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"primeiro user": "primeiro form, resposta 1 v1",
},
{
"field_id": 1,
"value": "aaa",
},
],
}
],
"version": 1,
},
],
"id": "33b66684-24a9-4a45-a12f-27a330152ac8",
"is_active": True,
},
{
"current_version": 0,
"history": [],
"id": "fa2eb9a1-c7c4-4b64-b682-7f51658bc4ab",
"is_active": False,
},
],
"user_id": "1",
},
{
"_id": "2",
"company": "turivius",
"forms": [
{
"current_version": 0,
"history": [],
"id": "565656",
"is_active": True,
},
],
"user_id": "9999",
},
]
I want to insert data in the document with user_id = 1, form.id = 33b66684-24a9-4a45-a12f-27a330152ac8 and in the history array where the version = 1 I want to insert a new response:
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"test": "test",
},
{
"field_id": 1,
"test2": "test3",
},
],
}
The history with version 1 is the last element, if I wanted insert in the first element the following code would works:
find_one_and_update(
{"$and": [{"user_id": "1"}, {"forms.id": '33b66684-24a9-4a45-a12f-27a330152ac8'}]},
{
"$push": {"forms.$.history.0.responses": data_here},
},
return_document=ReturnDocument.AFTER,
)
Is there anyway to do it that way or I need a more complex query to do it?
You want to add a new document to the responses array when those conditions are true:
{
user_id: '1',
'forms.id': '33b66684-24a9-4a45-a12f-27a330152ac8',
'forms.history.version': 1
}
forms is an array, so you want to update the array element that matches the condition, so you can use the $[identifier] operator. Inside the array of forms, you only want to update the history with a specific version, so you need to use the $[identifier] operator multiple times, the field you want to update would be like this:
forms.$[f].history.$[h].responses
f is the identifier of each elements in forms that matches the condition and h the elements of the history array that match the other condition. An arrayFilters object is used to define the conditions.
The complete query could be like this:
db.collection.update({
user_id: "1",
'forms.id': '33b66684-24a9-4a45-a12f-27a330152ac8',
'forms.history.version': 1
},
{
$push: {
'forms.$[f].history.$[h].responses': {
"client_id": 100,
"response_date": "datetime(2022, 5, 17, 3, 0)",
"values": [
{
"field_id": 0,
"test": "test",
},
{
"field_id": 1,
"test2": "test3",
},
],
}
}
},
{
arrayFilters: [
{
'h.version': 1
},
{
'f.id': '33b66684-24a9-4a45-a12f-27a330152ac8'
}
]
})
Working example at MongoDB playground

How to update specific field in mongoDB given conditions?

Given the following mongdoDB structure, how can i update the field isAvailable to false given that the shopName is "jamrt" and slug is "67626dae-1537-40d8-837d-483e5759ada0". This is my query but it does not work: Shop.find({ shopName: shopName}).update({products: {$elemMatch: {slug: slug}}}, { $set: { isAvailable: req.body.isAvailable} } Thanks!
"shopName": "jmart",
"products": [{
"id": 1,
"name": "Clean and Clear Deep Clean Cleanser 100g",
"slug": "8d1c895c-6911-4fc8-a34c-89c6948233d7",
"price": 4.5,
"discount_price": 0,
"category": "Health and Beauty",
"sale": false,
"subcategory": "personal care",
"color": "black",
"article": "Clean and Clear",
"quantity": 9,
"img": "https://firebasestorage.googleapis.com/v0/b/swifty-products.appspot.com/o/Jmart%2FBeauty%2FClean%20and%20Clear%20Deep%20Clean%20Cleanser%20100g.jpg?alt=media",
"vendor": {
"id": 1,
"name": "Clean and Clear"
},
"ratings": {
"star_ratings": 0,
"votes": 0
},
"isAvailable": true
}, {
"id": 2,
"name": "Colgate Total Pro Breath Health",
"slug": "67626dae-1537-40d8-837d-483e5759ada0",
"price": 4.5,
"discount_price": 0,
"category": "Health and Beauty",
"sale": false,
"subcategory": "personal care",
"color": "black",
"article": "Colgate",
"quantity": 9,
"img": "https://firebasestorage.googleapis.com/v0/b/swifty-products.appspot.com/o/Jmart%2FBeauty%2FColgate%20Total%20Pro%20Breath%20Health.jpg?alt=media",
"vendor": {
"id": 2,
"name": "Colgate"
},
"ratings": {
"star_ratings": 0,
"votes": 0
},
"isAvailable": true
},
]
In your case, you are trying to update only the matching sub documents.
The $elemMatch operator while using in projection updates only the first matching sub document.
The $elemMatch operator while using in find updates all the fields of the matching document.
This solution might help you.
With your case, the solution might be the below in mongodb query:
db.Shop.update({"shopName":"jmart","products.slug":"67626dae-1537-40d8-837d-483e5759ada0"}, {$set: {“products.$[i].isAvailable”: false}}, {arrayFilters: [{“i.slug”: "67626dae-1537-40d8-837d-483e5759ada0"}]})

Create a discontinued line chart using Vega lib

I am looking to create this kind of chart in Vega:
I carefully read the documentation about marks here:
https://vega.github.io/vega/docs/marks/line/
I read about the Type-Specific Mark Properties and about the defined property and it seemed like what I need. But I have no idea how to use this property.
I have my marks defined like so:
'marks': [
{
'name': 'expected_sales',
'description': 'The sales line',
'type': 'line',
'defined': 'false', // this I added based on the documentation
'from': {
'data': 'SalesData'
},
'zindex': 100,
'encode': { ... }
}
]
But this apparently doesn't work. the line is still continued. I have to mention that the data points that I get don't have null values, but 0.0.
Considering that sales might be $0 at some point, it is better to deferentiate between 0 values and null values.
That said, because null values are defined as 0.0 in the dataset, defined property has to be true for all other points except when value is 0.0
In the below example, "defined": {"signal": "datum.v !== 0.0"} is used to conditionally assign "defined" property to false if value "datum.v" is 0.0
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 400,
"height": 200,
"padding": 5,
"data": [
{
"name": "table",
"values": [
{"u": 1, "v": 28}, {"u": 2, "v": 12.0},
{"u": 3, "v": 0.0}, {"u": 4, "v": 10},
{"u": 5, "v": 36}, {"u": 6, "v": 44}
]
}
],
"scales": [
{
"name": "xscale",
"type": "linear",
"range": "width",
"zero": false,
"domain": {"data": "table", "field": "u"}
},
{
"name": "yscale",
"type": "linear",
"range": "height",
"nice": true,
"zero": false,
"domain": {"data": "table", "field": "v"}
}
],
"axes": [
{"scale": "xscale", "orient": "bottom", "grid": true},
{"scale": "yscale", "orient": "left"}
],
"marks": [
{
"type": "line",
"from": {"data": "table"},
"encode": {
"enter": {
"stroke": {"value": "#652c90"}
},
"update": {
"x": {"scale": "xscale", "field": "u"},
"y": {"scale": "yscale", "field": "v"},
"defined": {"signal": "datum.v !== 0.0"},
"interpolate": {"value": "linear"},
"strokeWidth": {"value": 4},
"strokeDash": {"value": [1,0]},
"strokeCap": {"value": "square"},
"opacity": {"value": 1}
},
"hover": {
"opacity": {"value": 0.5}
}
}
}
]
}

MongoDB Aggregation Error Returning wrong result

I have my json object like this
{
"_id": "5c2e811154855c0012308f00",
"__pclass": "QXRzXFByb2plY3RcTW9kZWxcUHJvamVjdA==",
"id": 44328,
"name": "Test project via postman2//2",
"address": "some random address",
"area": null,
"bidDate": null,
"building": {
"name": "Health Care Facilities",
"type": "Dental Clinic"
},
"collaborators": [],
"createdBy": {
"user": {
"id": 7662036,
"name": "Someone Here"
},
"firm": {
"id": 2520967,
"type": "ATS"
}
},
"createdDate": "2019-01-03T21:39:29Z",
"customers": [],
"doneBy": null,
"file": null,
"firm": {
"id": 1,
"name": "MyFirm"
},
"leadSource": {
"name": "dontknow",
"number": "93794497"
},
"location": {
"id": null,
"city": {
"id": 567,
"name": "Bahamas"
},
"country": {
"id": 38,
"name": "Canada"
},
"province": {
"id": 7,
"name": "British Columbia"
}
},
"modifiedBy": null,
"modifiedDate": null,
"projectPhase": {
"id": 1,
"name": "pre-design"
},
"quotes": [{
"id": 19,
"opportunityValues": {
"Key1": 100,
"Key2 Key2": 100,
"Key3 Key3 Key3": 200,
}
}],
"specForecast": [],
"specIds": [],
"tags": [],
"valuation": "something"
}
I am trying to aggregate using this query in MongoDB. My aggregation key is 4 level deep and also contains spaces. On all online examples shows me the aggregation at the first level. Looking to the online codes, I tried to re-iterate the same with my 4th level deep key.
db.mydata.aggregate([
{$match: {"id": 44328 } } ,
{$group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2"},
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3"}
}
}
]);
This should return
_id totalKey2 totalKey3
0 19 100 300
But it is returning
_id totalKey2 totalKey3
0 19 0 0
What am I doing Wrong?
Although it's not recommended to use space in field names in Mongo, it works as expected.
The problem with your query is that "quotes" is an array and you should first unwind it before grouping it.
This works as expected:
db.mydata.aggregate([
{ $match: { "id": 44328 } } ,
{ $unwind: "$quotes" },
{ $group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2" },
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3" } }
}
]);

How to update a part of an array sub document in MongoDB

I have this document in my mongodb collection:
{
"_id": "YLRM9Wi7f6tp6qNbS",
"sessionId": "hLDkkJKR4Muik6tbe",
"userId": "ZYoG4cH8HcCDPMDGr",
"shopId": "J8Bhq3uTtdgwZx3rz",
"workflow": {
"status": "",
"workflow": ["String"]
},
"billing": [Object],
"discount": 0,
"tax": 0,
"items": [
{
"_id": "JwR233jD2c4HKeYKq",
"shopId": "J8Bhq3uTtdgwZx3rz",
"productId": "BCTMZ6HTxFSppJESk",
"quantity": 1,
"product": {
"_id": "BCTMZ6HTxFSppJESk",
"title": "Product",
"shopId": "J8Bhq3uTtdgwZx3rz",
"ancestors": [],
"createdAt": "2018-01-12T10:22:18.853Z",
"description": "",
"handle": "product",
"hashtags": [
"rpjCvTBGjhBi2xdro",
"cseCBSSrJ3t8HQSNP"
],
"price": {
"range": "12.99 - 19.99",
"min": 12.99,
"max": 19.99
},
"isVisible": true,
"isLowQuantity": false,
"isSoldOut": false,
"isBackorder": false,
"metafields": [
{
"key": "Material",
"value": "Cotton"
},
{
"key": "Quality",
"value": "Excellent"
}
],
"pageTitle": "",
"type": "simple",
"updatedAt": "2018-01-12T10:22:18.854Z",
"vendor": "Vendor_Name",
"originCountry": "country",
"requiresShipping": true,
"isDeleted": false,
"template": "productDetailSimple",
"workflow": {
"status": "new"
}
},
"variants": {},
"title": "Product",
"type": "simple",
"parcel": {
"weight": 25,
"height": 3,
"width": 10,
"length": 10
},
"shippingMethod": {
"shopId": "J8Bhq3uTtdgwZx3rz",
"shipmentQuotes": [Object],
"shipmentQuotesQueryStatus": {
"requestStatus": "success",
"numOfShippingMethodsFound": 11
},
"_id": "s3EJXrLsZe73RbLiD",
"address": {},
"shipmentMethod": {},
"paymentId": "nyybR5BNvDDrJrtwe",
"items": [
{
"_id": "JwR233jD2c4HKeYKq",
"productId": "BCTMZ6HTxFSppJESk",
"shopId": "J8Bhq3uTtdgwZx3rz",
"variantId": "CJoRBm9vRrorc9mxZ"
}
],
"workflow": {
"status": "new",
"workflow": ["String"]
}
},
"workflow": {
"status": "new",
"workflow": ["String"]
}
}
],
"shipping": [Object],
"email": "johndoe#mail.com",
"cartId": "L6sSGv4NR9rpbDbsd",
"createdAt": "2018-01-12T10:22:18.850Z"
}
The field items is an array of objects, I would like to update just a part of the object specifically the workflow field without touching other part of the objects in items array.
I was able to do this using a loop, but it caused some tests to fail. Is there a better of doing this with using a loop?
Thank you.
You can try findAndModify method.
Traverse to the workflow key ad try to set the value.
Hope this would help.