How to update/insert into a mongodb collection with the result of aggregate query on the another collection - mongodb

I have a below aggregate query and I wanted to insert/update this result into another collection.
db.coll1.aggregate([
{
$group:
{
_id: "$collId",
name: {$first:"$name"} ,
type: {$first:"$Type"} ,
startDate: {$first:"$startDate"} ,
endDate: {$first:"$endDate"}
}
}
])
I have another collection coll2, which has fields in the above query and some additional fields too.
I may already have the document created in Coll2 matching the _id:collId in the above query. If this Id matches, I want to update the document with the field values of the result and keep the values of other fields in the document.
If the Id does not exists, it should just create a new document in Coll2.
Is there a way to do it in the MongoDB query. I want to implement this in my Spring application.

We can use $merge to do that. It's supported from Mongo v4.2
db.collection.aggregate([
{
$group:{
"_id":"$collId",
"name": {
$first:"$name"
},
"type":{
$first:"$Type"
},
"startDate":{
$first:"$startDate"
},
"endDate":{
$first:"$endDate"
}
}
},
{
$merge:{
"into":"collection2",
"on":"_id",
"whenMatched":"replace",
"whenNotMatched":"insert"
}
}
])

Related

MongoDB querying aggregation in one single document

I have a short but important question. I am new to MongoDB and querying.
My database looks like the following: I only have one document stored in my database (sorry for blurring).
The document consists of different fields:
two are blurred and not important
datum -> date
instance -> Array with an Embedded Document Object; Our instance has an id, two not important fields and a code.
Now I want to query how many times an object in my instance array has the group "a" and a text "sample"?
Is this even possible?
I only found methods to count how many documents have something...
I am using Mongo Compass, but i can also use Pymongo, Mongoengine or every other different tool for querying the mongodb.
Thank you in advance and if you have more questions please leave a comment!
You can try this
db.collection.aggregate([
{
$unwind: "$instance"
},
{
$unwind: "$instance.label"
},
{
$match: {
"instance.label.group": "a",
"instance.label.text": "sample",
}
},
{
$group: {
_id: {
group: "$instance.label.group",
text: "$instance.label.text"
},
count: {
$sum: 1
}
}
}
])

MongoDb - add index on 'calculated' fields

I have a query that includes an $expr-operator with a $cond in it.
Basically, I want to have objects with a timestamp from a certain year. If the timestamp is not set, I'll use the creation date instead.
{
$expr: {
$eq: [
{
$cond: {
'if': {
TimeStamp: {
$type: 'null'
}
},
then: {
$year: '$Created'
},
'else': {
$year: '$TimeStamp'
}
}
},
<wanted-year>
]
}
}
It would be nice to have this query using a index. But is it possible to do so? Should I just add index to both TimeStamp and Created-fields? Or is it possible to create an index for a Year-field that doesn't really exist on the document itself...?
Not possible
Indexes are stored on disk before executing the query.
Workaround: On-Demand Materialized Views
You store in separate collection your calculated data (with indexes)
This can't be done today without precomputing that information and storing it in a field on the document. The closest alternative would probably be to use MongoDB 4.2's aggregation pipeline-powered updates to precompute and store a createdOrTimestamp field whenever your documents are updated. You could then create an index on createdOrTimestamp that would be used when querying for documents that match a certain year.
What this would look like when updating or after inserting your document:
db.collection.update({ _id: ObjectId("5e8523e7ea740b14fb16b5c3") }, [
{
$set: {
createdOrTimestamp: {
$cond: {
if: {$gt: ['$TimeStamp', null]},
then: '$TimeStamp',
else: '$Created'
}
}
}
}
])
If documents already exist, you could also send off an updateMany operation with that aggregation to get that computed field into all your existing documents.
It would be really nice to be able to define computed fields declaratively on a collection just like indexes, so that they take care of keeping themselves up to date!

MongoDB Update from Aggregate

I am extremely new to Mongo and need some up creating an update statement. I have two different collections. I need to update the one collection's values with the results from my aggregate query where the id's match.
Here is my aggregate query that gives me the id for the other collection and the value I need to set it to:
db.ResultsCollection.aggregate(
{$group:{_id:"$SystemId", "maxValue": {$max:"$LastModified"}}}
);
How do I loop through the other collection with this data and update where the _id matches the SystemId from my aggreagate?
UPDATED CODE:
db.ResultsCollection.aggregate(
{$group:{_id:"$SystemId", "maxValue": {$max:"$LastModified"}}}
).forEach(function(
db.CollectionToUpdate.updateOne(
{ _id : doc._id },
{ $set: {UpdateDate: doc.maxValue } },
{ upsert: false }
);
});
My updated code does not generate a syntax error, but does not update the results when I refresh.

MongoDB update collection's data

I try to update an MongoDB data by using this code:
db.medicines.update({"_id":"586a048e34e5c12614a7424a"}, {$set: {amount:'3'}})
but unfortantly the query does not recognize the selector "_id":"586a048e34e5c12614a7424a", even if its exists.
Its succsed when I change the key to another like: name,rate and etc..
there is a special way to use update with _id parameter?
Thanks a head.
_id will be the unique ObjectId that mongodb generates for every document before inserting it. The query dint work because _id is an ObjectId and "586a048e34e5c12614a7424a" is a String. You need to wrap _id with ObjectId().
If you're using mongodb query
db.medicines.update({
"_id": ObjectId("586a048e34e5c12614a7424a")
}, {
$set: {
amount: '3'
}
});
If you are using mongoose. You can use findByIdAndUpdate
db.medicines.findByIdAndUpdate({
"_id": "586a048e34e5c12614a7424a"
}, {
$set: {
amount: '3'
}
});

Include all existing fields and add new fields to document

I would like to define a $project aggregation stage where I can instruct it to add a new field and include all existing fields, without having to list all the existing fields.
My document looks like this, with many fields:
{
obj: {
obj_field1: "hi",
obj_field2: "hi2"
},
field1: "a",
field2: "b",
...
field26: "z"
}
I want to make an aggregation operation like this:
[
{
$project: {
custom_field: "$obj.obj_field1",
//the next part is that I don't want to do
field1: 1,
field2: 1,
...
field26: 1
}
},
... //group, match, and whatever...
]
Is there something like an "include all fields" keyword that I can use in this case, or some other way to avoid having to list every field separately?
In 4.2+, you can use the $set aggregation pipeline operator which is nothing other than an alias to $addFieldsadded in 3.4
The $addFields stage is equivalent to a $project stage that explicitly specifies all existing fields in the input documents and adds the new fields.
db.collection.aggregate([
{ "$addFields": { "custom_field": "$obj.obj_field1" } }
])
You can use $$ROOT to references the root document. Keep all fields of this document in a field and try to get it after that (depending on your client system: Java, C++, ...)
[
{
$project: {
custom_field: "$obj.obj_field1",
document: "$$ROOT"
}
},
... //group, match, and whatever...
]
>>> There's something like "include all fields" keyword that I can use in this case or some another solution?
Unfortunaly, there is no operator to "include all fields" in aggregation operation. The only reason, why, because aggregation is mostly created to group/calculate data from collection fields (sum, avg, etc.) and return all the collection's fields is not direct purpose.
To add new fields to your document you can use $addFields
from docs
and to all the fields in your document, you can use $$ROOT
db.collection.aggregate([
{ "$addFields": { "custom_field": "$obj.obj_field1" } },
{ "$group": {
_id : "$field1",
data: { $push : "$$ROOT" }
}}
])
As of version 2.6.4, Mongo DB does not have such a feature for the $project aggregation pipeline. From the docs for $project:
Passes along the documents with only the specified fields to the next stage in the pipeline. The specified fields can be existing fields from the input documents or newly computed fields.
and
The _id field is, by default, included in the output documents. To include the other fields from the input documents in the output documents, you must explicitly specify the inclusion in $project.
according to #Deka reply, for c# mongodb driver 2.5 you can get the grouped document with all keys like below;
var group = new BsonDocument
{
{ "_id", "$groupField" },
{ "_document", new BsonDocument { { "$first", "$$ROOT" } } }
};
ProjectionDefinition<BsonDocument> projection = new BsonDocument{{ "document", "$_document"}};
var result = await col.Aggregate().Group(group).Project(projection).ToListAsync();
// For demo first record
var fistItemAsT = BsonSerializer.Deserialize<T>(result.ToArray()[0]["document"].AsBsonDocument);