Suppose I have a JSON Object which needs to be updated in Mongodb like
{
"_id": 12345,
"Attribute": { "Property1": "X", "Property2": true, "Property3": 123 }
}
Suppose I have a record in mongoDb
{
"_id": 12345,
"Attribute1": "abc",
"Attribute2": "xyz",
"Attribute": { "Property4": "X", "Property2": false, "Property3": 456 }
}
The result should update Attribute JSON while updating only the fields that are changed and keeping rest of the values intact.
Resultant record in db should be like this
{
"_id": 12345,
"Attribute1": "abc",
"Attribute2": "xyz",
"Attribute": { "Property4": "X", "Property1": "X", "Property2": true, "Property3": 123 }
}
I really don't know how to achieve this in single Pass in Mongodb using JAVA spring boot. Can Anyone please help? Any help is appreciated.
You can use Update class from org.springframework.data.mongodb.core.query package. You can write a code snippet like below.
Update updateAttribute = new Update();
updateAttribute.push("Attribute", "Your Value");
mongoOperations.updateFirst(new Query(Criteria.where("id").is(12345)), updateAttribute, "yourCollection");
Also, you need to inject constructor for MongoOperations from org.springframework.data.mongodb.core package.
You can do it in 2 ways,
For Mongo version 4.4+ you can use pipelined updates, this allows the use of aggregation operators in the update body, specifically we'll want to use $mergeObjects, like so:
db.collection.update(
{
_id: 12345
},
[
{
$set: {
Attribute: {
$mergeObjects: [
"$Attribute",
{
"Property1": "X",
"Property2": true,
"Property3": 123
}
]
}
}
}
])
Mongo Playground
For lesser Mongo versions you'll have to construct the update body in code, here is a javascript example ( might be slightly more annoying in spring )
const input = {
'_id': 12345,
'Attribute': { 'Property1': 'X', 'Property2': true, 'Property3': 123 },
};
const updateBody = {};
Object.keys(input.Attribute).forEach((key) => {
const updateKey = `Attribute.${key}`;
updateBody[updateKey] = input.Attribute[key];
});
db.collection.updateOne({ _id: 12345 }, { $set: updateBody });
By using the dot notation in the update body we ensure we don't overwrite existing fields in the Attribute.
I achieved this using this query.
db.collection.update(
{
_id: 12345
},
{
$set: {
"Property1": "X",
"Property2": true,
"Property3": 123
}
},
{upsert: true}
)
Related
This is how my data looks like
{
"_id" : "2011250546437843117",
"name" : "Book",
"textbook" : [
"Maths",
"Science"
],
"language" : [
"English"
],
"isRead" : true,
"isAvailable" : true
}
I have to filter documents based on textbook,and based on that isRead field should be true or false.
my mongo query is
db.user.aggregate([
{
$match: {
"isAvailable": true
}
},
{
$project: {
"textbook": 1,
"name": 1,
"isread": {
$in: [
"Maths",
"$textbook"
]
}
}
}
]);
I have tried to write this using mongo-template
Aggregation aggregation = newAggregation(match(Criteria.where("isAvailable").is(true)),
project("textbook","name"));
I dont understand how to write the $in operator in project stage.
Thankyou in advance.
I have a very strange situation with the following N1QL update statement:
update testBucket set A1='TESTVALUE' where TONUMBER(`Respondent.Serial`) = 8
{"results": []}
select А1 from testBucket where TONUMBER(`Respondent.Serial`) = 8
I get tthe following (notice that not all is updated), having:
{
"A1": "ONE"
},
{
"A1": "TESTVALUE"
},
{
"A1": "TESTVALUE"
},
{
"A1": "TESTVALUE"
},
{
"A1": "TESTVALUE"
},
{
"A1": "TESTVALUE"
},
{
"A1": "TEST2"
}
The problem may be that you are using backticks around Respondent.Serial.
If your document structure is like this, that isn't right.
{
"Respondent": {
"Serial": "8"
}
}
You should only be using the backticks if you have a single field name that includes the period, like this:
{
"Respondent.Serial": "8"
}
Assuming you have the former document structure, try your queries without the backticks.
I'm new to mongoDb and created a query that fulfills my functional requirements:
db.collection.find(
{
"typeD": "ABC"
, "typeT": {$size: 2}
, "typeT": { $all: ["def", "abc"] }
, "typeC": { $size: 3}
, "typeC": { $all: ["pdf", "video", "png"] }
, "properties": {$size: 3}
,"properties":
{
$all:
[
{"$elemMatch": {"name": "propName1", "value": "proName1_value"} }
, {"$elemMatch": {"name": "propName2", "value": "proName2_value"} }
, {"$elemMatch": {"name": "propName3", "value": "proName3_value"} }
]
}
);
I want to find the documents that exactly contains the elements provided by the arrays - as a fixed order of the elements inside arrays cannot be assumed, I've chosen the $all operator and to ensure exact matching I added to additional restriction with the $size.
Above query can be executed on mongo shell without any problems.
While trying to execute this statement with java by using mongoTemplate, I get some problems:
BasicQuery query = new BasicQuery(queryString);
CollectionEntity existingCmc = this.mongoTemplate.find(query, CollectionEntity.class);
After the first java line, query.toString() provides:
db.collection.find(
{
"typeD": "ABC"
, "typeT": { $all: ["def", "abc"] }
, "typeC": { $all: ["pdf", "video", "png"] }
,"properties":
{
$all:
[
{"$elemMatch": {"name": "propName1", "value": "proName1_value"} }
, {"$elemMatch": {"name": "propName2", "value": "proName2_value"} }
, {"$elemMatch": {"name": "propName3", "value": "proName3_value"} }
]
}
);
How can I execute the query that fulfills all of my requirements?
Can I rewrite the query so that one "single condition per attribute" is in the query?
How can I tell mongoTemplate, not to "overwrite" the previous condition for this attribute?
Thanks in advance
MongoDB queries are actually Maps (Key-Value pairs).
Since you are defining 'properties' key two times which is why it is getting overridden.
Your desired query can be done using '$and' operator.
{ "$and": [
{ "properties": { "$size": 3 } },
{ "properties": { $all:
[
{"$elemMatch": {"name": "propName1", "value": "proName1_value"} },
{"$elemMatch": {"name": "propName2", "value": "proName2_value"} },
{"$elemMatch": {"name": "propName3", "value": "proName3_value"} }
]
} }
] }
How to project embedded array element field in Spring Data MongoDB Aggregation with the document sample below, I tried:
project("customers.id")
project("customers.[].id")
project("customers.?.id")
project("$customers.id")
but didn't work.
Result document without projection:
{
"id": "group1",
"name": "Default Identity Management",
"warningThreshold": 900000,
"tariffId": "TR_0001",
"active": false,
"customers": [
{
"id": "1",
"name": "David",
"properties": [
{
"name": "phone",
"value": "678"
}
],
"roles": [
"dev"
]
},
{
"id": "2",
"name": "Peter",
"properties": [
{
"name": "phone",
"value": "770"
}
],
"roles": [
"techsales",
"dev"
]
}
]
}
Expected document like this:
{
"id" : "group1",
"name" : "Group1",
"tariffId" : "TR_0001",
"warningThreshold" : 900000,
"customers" : [
{
"id" : "1",
"name" : "David",
"properties" : [
{
"name" : "phone",
"value" : "678"
}
]
},
{
"id" : "2",
"name" : "Peter",
"properties" : [
{
"name" : "phone",
"value" : "770"
}
]
}
]
}
I would like to include customers[].id, customers[].name, customers[].properties.
I'd been trying to figure this out for a while now, but couldn't. And the other posts here on stackoverflow, and other places on the internet, didn't provide the solution I was looking for.
My problem was similar to the original author's: There's a document, which has a field which is an array of documents. I wanted to query all the top level fields in the document, and exclude a single field from the documents within the array.
s7vr's answer in the comments for the question did the job for me! Just re-posting that here since most people don't go through all the comments, and it is a really useful answer, that saved me from writing a lot of crappy code! :D
AggregationOperation project = new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return new Document("$project", new Document("arrayField.nestedFieldToExclude", 0));
}
};
With Lambda:
AggregationOperation project = aggregationOperationContext -> new Document("$project", new Document("arrayField.nestedFieldToExclude", 0));
Overall pipeline:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.sort(Sort.Direction.DESC, "createdOn"),
project);
I just wish there was a cleaner way to do this with the Spring MongoDB Data API directly, rather than using it this way with lambda functions.
Also, please note that the method AggregationOperation.toDocument(AggregationOperationContext aggregationOperationContext) has been deprecated as of spring-data-mongodb version 2.2.
I use Vaadin 7. I want to save a Chart configuration and restore it later. I found an interesting thing in com.vaadin.addon.charts.model.Configuration is that you can serialize the configuration into JSON object.
Code :
chart.getConfiguration().toString();
Result:
{
"type": "column"
},
"title": {
"text": "Chart"
},
"xAxis": {
"categories": [
"f",
"e"
],
"axisIndex": 0
},
"yAxis": {
"min": 0,
"title": {
"text": "Quantity"
},
"axisIndex": 0
},
"tooltip": {
"_fn_formatter": "this.series.name +\u0027: \u0027+ this.y +\u0027 (\u0027+ Math.round(this.percentage) +\u0027%)\u0027"
},
"plotOptions": {
"column": {
"stacking": "normal"
}
},
"series": [
{
"data": [
1,
2
],
"name": "d",
"visible": true
}
],
"exporting": {
"enabled": false
}
}
What I want now is build Configuration from that JSON object. Is there a way to that ?
Found it, pretty simple actually :
Chart chart = new Chart();
//json is the string containing your JSON object
chart.setJsonConfig(json);
//you'll have to draw the chart to update it if needed
//chart.drawChart();