Mongodb how to query result as plain array of one propery - mongodb

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 );
});

Related

Mongoose: search for ObjectID by Array

I want to filter my collection by aggregation for one of many ObjectIDs.
Because of some DocumentDB restrictions I can not build a single pipeline with uncorrelated subqueries. So my fix is to do it in two queries.
for example: I have an aggregation that returns all teamIds, for some conditions as an array of Object with the IDs.
[{_id: ObjectID("abcdef")}, {_id: ObjectID("ghijkl")}, {_id: ObjectID("vwxyz")}, ...]
I now want to have a second aggregation filter another collection using the ObjectIDs.
This would work in Mongo Compass:
{
"team": {
"$in": [ObjectId("60aabcb05c7462f42b3d7zyx"), ObjectId("60aabc7b05c7462f42b3dxyz")]
},
....
}
My issue is that i can not find the correct syntax for JS to generate such a pipeline.
What ever I try, JS always converts my Array of ObjectIDs to something like this:
{
"team": {
"$in": [{
"_id": "60aabcb05c7462f42b3d7zyx"
},{
"_id": "60aabc7b05c7462f42b3dxyz"
}]
},
I fixed it like this. I am not 100% why this syntax works because it is still just an array of objects, formatted like before, but I guess there is some stuff mongoose does, that is opaque to me.
let teams = await TeamMgmt.getTeamsAggregatedByFilter( teamFilter )
// make an array of ObjectIds so we can filter for them.
let idArray = []
Object.keys( teams ).map( function ( key, index ) {
idArray.push( new mongoose.Types.ObjectId( teams[ index ]._id.toString() ) )
} );
const shiftFilter = [
{
'$match': {
'team': {
"$in": idArray
},
....
}

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 refer currently updating records in mongoDB query?

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} }] );

mongodb query to verify embedded array sequence numbers

given a document structure as shown, where the trades array can have thousands of items... how on earth could one do a query that would verify that the sequence always has 'startTradeId' one number higher than the previous items 'endTradeId', all the way through the array? is this even possible?
{
"name": "STOCK",
"trades": [{
"endTradeId": 41306,
"startTradeId": 41302,
...
},
{
"endTradeId": 41301,
"startTradeId": 41297,
...
},
{
"endTradeId": 41296,
"startTradeId": 41240,
...
},
...
]
}
You can use $where operator like below :
db.your_collection.find( { $where : function(){ return "this.trades.startTradeId > this.trades.endTradeId" }});

Select records matching concat value of two fields in mongodb

Is there a way to do something like this on MongoDB?
select * from table where concat(field1, field2) = 'value'
To clarify, I have an array of full names, but the documents have firstname and lastname separate, so I want to do something like:
select * from table where concat(firstname, lastname) in ([ARRAY OF NAMES])
You can only do it with aggregation framework, not with regular find.
db.coll.aggregate({$project:{newField:{$concat:["$field1","$field2"]}}},
{$match:{newField:"value"}}
);
Note that this will not be able to use any indexes, since there is no support for indexes on computed values in MongoDB (yet).
If you have an index on field1 and you know how many characters you expect field1 to contribute to value you can improve performance of this aggregation like this:
db.coll.aggregate({$match:{field1:/^val/}},
{$project:{newField:{$concat:["$field1","$field2"]}}},
{$match:{newField:"value"}}
);
where val is first part of "value" string (you must not compare more characters than the shortest possible value of field1 though.
EDIT as of version 3.6 you can do this in find using the $expr expression:
db.coll.find({$expr:{$eq:["value", {$concat:["$field1", "$field2"]}]}})
If the argument ($field1 or $field2) resolves to a value of null or refers to a field that is missing, $concat returns null. Since mongoDB3.0, a new $ifNull is provided to check if the argument is null and provide an alternative
{ $ifNull: [ <expression>, <replacement-expression-if-null> ] }
db.coll.aggregate({$project:{newField:{$concat:[{$ifNull:["$field1",""]},{$ifNull:["$field2",""]}]}}},
{$match:{newField:"value"}})
The results may not differ if we are using $match, but this results will vary if you want to use $regex.
In Mongoose you can like that
let clause = [
{ $project: { name: { $concat: ["$first_name"," ","$last_name"]}}},
{ $match: { name: new RegExp("ab", 'i')}}
];
Model.aggregate(clause)
OutPut will be like that
[
{
"_id": "5911db2b9235272052fdd2e1",
"name": "Ali Abbas"
},
{
"_id": "5912f7dd8bdbc24aa37b5239",
"name": "Ali Abad"
},
{
"_id": "59229e0322abdb311818e419",
"name": "Syed Ali Abbas Shah"
},
{
"_id": "592837da188fa969cc135ddc",
"name": "Abu Akbar"
}
]