How to refer currently updating records in mongoDB query? - mongodb

Below is my collection
[{documentId: 123, id: uniqueValue }]
Expected result
[{documentId: 123, id: id1,uniqueKey: uniqueValue }]
How do I refer "id" column for currently updating records, also id column can be anything for which my outer query is giving me the column name
db.supplier.updateMany( { documentId : 123}, { $set: { "uniqueKey": id} } );
so in above query "id" is coming like outerObject.mapping.idColumn which I want to substitute in above query.
The whole point of doing this, is to create index on column, and current collection does not have fixed column name on which I want to fire a query
Example
There are two collections collectionOne and collectionTwo
for each document in collectionOne there are multiple document in collectionTwo. The docId is used for lookup.
collectionOne
[{
docId :123,
col1 : lookupColumn
metaData: "some metaData",
extra : "extra columns"
}, ... ]
collectionTwo
[{
docId :123,
lookupColumn:"1",
a:"A",
b:"B" ....
},
{ docId :123,
lookupColumn:"2",
a:"A",
b:"B" ....
}
{ docId :123,
lookupColumn:"3",
a:"A",
b:"B" ....},.....]
lookupColumn in collectionTwo may have different name and mapping of that name is given in collectionOne by col1 field (which is always same), in this example col1 value is lookupColumn so I want to create a column newKey and copy value of lookupColumn into it.
So I came up with below Query
db.collectionOne.find({}).forEach(function(obj) {
if(obj.columns) {
existingColumn =obj.columns.col1;
db.collectionTwo.updateMany( { docId: obj.docId}, { $set: { "newKey": existingColumn} } );
}
}
problem is I am not able to pick an existing column name using variable existingColumn, I have tried using $ as well, which inserts $"existingColumn" as newKey value.
I have updated query with one more loop over collectionTwo but I feel that in optimized and unnecessary.

To go from
{documentId: 123, id: uniqueValue }
to
{documentId: 123, id: id1, uniqueKey: uniqueValue }
Use the pipeline style of update, which lets you use aggregation syntax:
db.collection.update({documentId: 123}, [{$set:{uniqueKey:"$id", id:"id1"}}])
EDIT
The latest edit to the question makes this a lot more clear.
You were almost there.
In MongoDB 4.2, the second argument to updateMany accepts either an update document like you were using:
db.collectionTwo.updateMany( { docId: obj.docId}, { $set: { "newKey": existingColumn} } );
Or it can accept an aggregation-like pipeline, but not all stages are available. For this use, if you make that second argument an array so that it is recognized as a pipeline, you can use the "$variable" structure. Since you already have the field name in a javascript variable, prepend "$" to the fieldname:
db.collectionTwo.updateMany( { docId: obj.docId}, [{ $set: { "newKey": "$" + existingColumn} }] );

Related

Can't remove object in array using Mongoose

This has been extensively covered here, but none of the solutions seems to be working for me. I'm attempting to remove an object from an array using that object's id. Currently, my Schema is:
const scheduleSchema = new Schema({
//unrelated
_id: ObjectId
shifts: [
{
_id: Types.ObjectId,
name: String,
shift_start: Date,
shift_end: Date,
},
],
});
I've tried almost every variation of something like this:
.findOneAndUpdate(
{ _id: req.params.id },
{
$pull: {
shifts: { _id: new Types.ObjectId(req.params.id) },
},
}
);
Database:
Database Format
Within these variations, the usual response I've gotten has been either an empty array or null.
I was able slightly find a way around this and accomplish the deletion by utilizing the main _id of the Schema (instead of the nested one:
.findOneAndUpdate(
{ _id: <main _id> },
{ $pull: { shifts: { _id: new Types.ObjectId(<nested _id>) } } },
{ new: true }
);
But I was hoping to figure out a way to do this by just using the nested _id. Any suggestions?
The problem you are having currently is you are using the same _id.
Using mongo, update method allows three objects: query, update and options.
query object is the object into collection which will be updated.
update is the action to do into the object (add, change value...).
options different options to add.
Then, assuming you have this collection:
[
{
"_id": 1,
"shifts": [
{
"_id": 2
},
{
"_id": 3
}
]
}
]
If you try to look for a document which _id is 2, obviously response will be empty (example).
Then, if none document has been found, none document will be updated.
What happens if we look for a document using shifts._id:2?
This tells mongo "search a document where shifts field has an object with _id equals to 2". This query works ok (example) but be careful, this returns the WHOLE document, not only the array which match the _id.
This not return:
[
{
"_id": 1,
"shifts": [
{
"_id": 2
}
]
}
]
Using this query mongo returns the ENTIRE document where exists a field called shifts that contains an object with an _id with value 2. This also include the whole array.
So, with tat, you know why find object works. Now adding this to an update query you can create the query:
This one to remove all shifts._id which are equal to 2.
db.collection.update({
"shifts._id": 2
},
{
$pull: {
shifts: {
_id: 2
}
}
})
Example
Or this one to remove shifts._id if parent _id is equal to 1
db.collection.update({
"_id": 1
},
{
$pull: {
shifts: {
_id: 2
}
}
})
Example

How to build a MongoDB query that combines two field temporarily?

I have a schema which has one field named ownerId and a field which is an array named participantIds. In the frontend users can select participants. I'm using these ids to filter documents by querying the participantIds with the $all operator and the list of participantsIds from the frontend. This is perfect except that the participantsIds in the document don't include the ownerId. I thought about using aggregate to add a new field which consists of a list like this one: [participantIds, ownerId] and then querying against this new field with $all and after that delete the field again since it isn't need in the frontend.
How would such a query look like or is there any better way to achieve this behavior? I'm really lost right now since I'm trying to implement this with mongo_dart for the last 3 hours.
This is how the schema looks like:
{
_id: ObjectId(),
title: 'Title of the Event',
startDate: '2020-09-09T00:00:00.000',
endDate: '2020-09-09T00:00:00.000',
startHour: 1,
durationHours: 1,
ownerId: '5f57ff55202b0e00065fbd10',
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13'],
classesIds: [],
categoriesIds: [],
roomsIds: [],
creationTime: '2020-09-10T16:42:14.966',
description: 'Some Desc'
}
Tl;dr I want to query documents with the $all operator on the participantsIds field but the ownerId should be included in this query.
What I want is instead of querying against:
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13']
I want to query against:
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13', '5f57ff55202b0e00065fbd10']
Having fun here, by the way, it's better to use Joe answer if you are doing the query frequently, or even better a "All" field on insertion.
Additional Notes: Use projection at the start/end, to get what you need
https://mongoplayground.net/p/UP_-IUGenGp
db.collection.aggregate([
{
"$addFields": {
"all": {
$setUnion: [
"$participantsIds",
[
"$ownerId"
]
]
}
}
},
{
$match: {
all: {
$all: [
"5f57ff55202b0e00065fbd14",
"5f57ff55202b0e00065fbd15",
"5f57ff55202b0e00065fbd13",
"5f57ff55202b0e00065fbd10"
]
}
}
}
])
Didn't fully understand what you want to do but maybe this helps:
db.collection.find({
ownerId: "5f57ff55202b0e00065fbd10",
participantsIds: {
$all: ['5f57ff55202b0e00065fbd14',
'5f57ff55202b0e00065fbd15',
'5f57ff55202b0e00065fbd13']
})
You could use the pipeline form of update to either add the owner to the participant list or add a new consolidated field:
db.collection.update({},[{$set:{
allParticipantsIds: {$setUnion: [
"$participantsIds",
["$ownerId"]
]}
}}])

Mongodb how to query result as plain array of one propery

I I had in mongodb colleciton documents like:
Documents in collection (each has name property):
[{ name: 'Alex', .. }, { name: 'Jane', .. } ... ]
How can I query db to get the result simply mapping the name property:
['Ale', 'Jane', ...]
?
What you would want to use is query projections.
Queries in MongoDB return all fields in all matching documents by default. To limit the amount of data that MongoDB sends to applications, include a projection in the queries.
For your example, your query would look something like this:
db.people.find( {}, { "name": 1 } )
This will extract all documents but only return the name attribute (and _id) for each one. To remove the _id attribute as well, you would have to explicitly specify that you don't want it:
db.people.find( {}, { "name": 1, "_id": 0 } )
In order to retrieve all of the names in one array, you could use a forEach function to assemble the data:
var names = [];
db.people.find( {}, { "name": 1, "_id": 0 } ).forEach( function( doc ){
names.push( doc.name );
});

upsert a field in a subdocument in an array by index in MongoDB

I have the following MongoDB structure
{
_id : ...,
other_stuff ... ,
my_array : [
{ title: ...., body: ...., email: .... },
{ title: ...., body: ...., email: .... },
{ title: ...., body: ...., email: .... }
]
}
I need to update/or insert (if not existent) a field called "click_number" in the subdocument within the "my_array" field. If the "click_number" field is not existent, insert the field and set it to 1; if existent, increment it by 1.
First of all, I don't know how to update an array element by its index, and secondly, I do not know how to do update or insert depending on the existence of the field. I appreciate your help
You want to use the update command on your collection as follows (example):
db.collection.update(
{ "my_array.title" : "title_one" },
{ $inc : { "my_array.$.click_number" : 1 } }
);
What just happened?
On the first parameter of update you define a query to match documents you wish to update. We search for a property named title inside the array called my_array. You could match against body or email of course by modifying the dot notation to: "my_array.email".
The second parameter defines the update, the modification to apply. We have a $inc operator to increment fields, which we use in this statement. The query selects a document with the matched array element. You can reach for this matched array item with the $ notation. The "my_array.$" selects the matched array element, which has a title, email and body. If you try to give value to a non existing field, MongoDB will do it for you. If the field does not exist, $inc sets the field to the specified amount. The $inc operator accepts positive and negative incremental amounts.
Learn more about the update command.
Another example:
db.collection.update(
{ _id : "john", "my_array.email" : "email" },
{ $inc : { "my_array.$.click_number" : 1 } }
);

mongodb query without field name

I would like query all objects that have a field containing a specific value. For example, I have two documents:
{"123": "apple", "217": "pear", "179": "orange"}
{"831": "pear", "189": "grapes"}
and would like to get all objects that has a field whose value is "apple", but I do not know the name of the field. Is it possible to specify a query in MongoDB to achieve this? (The numerical keys in the objects are children ids, and the values in the objects are long GUIDs)
Unfortunately, MongoDB does not support any method of querying all fields with a particular value. There is an existing Jira ticket requesting this enhancement: https://jira.mongodb.org/browse/SERVER-1248 . Feel free to comment, vote, or follow that ticket.
In the meantime, the usual way that this is handled is to change the MongoDB schema. For your example, you would change your existing schema:
{"123": "apple", "217": "pear", "179": "orange"}
{"831": "pear", "189": "grapes"}
And you might structure it something like this:
{ tags: [
{ cid: "123", value: "apple" },
{ cid: "217", value: "pear" },
{ cid: "179", value: "orange" },
]
}
{ tags: [
{ cid: "831", value: "pear" },
{ cid: "189", value: "grapes" },
]
}
Once you've done this, you can perform the follwing query to find all of the desired documents:
db.docs.find( {'tags.value': "apple" } )
Note that this schema allows you to index the 'tags.cid' and 'tags.value' fields, which your original schema does not.
I hope this helps.
-William
You can use Full-text Index on all fields.
use dbname
db.collectionname.ensureIndex(
{ "$**": "text" },
{ name: "TextIndex" }
)
Then Search Using :
DB db = mongoClient.getDB("dbname");
DBCollection coll = db.getCollection("collectionname");
BasicDBObject query = new BasicDBObject();
query.append("$text", new BasicDBObject("$search", searchstring));
DBCursor cursor = coll.find(query);