how to write projection query in mongoDB - mongodb

I have a json object like this.
[{
A:"XXX",
B:"ss",
C:"qee",
Z:[ {e:"p",f:"q",g:"r"},
{e:"w",f:"x",g:"y"},
{e:"s",f:"t",g:"u"}]
},
{
A:"XYX",
B:"ss",
C:"qee",
Z:[ {e:"p",f:"q",g:"r"},
{e:"w",f:"x",g:"y"},
{e:"s",f:"t",g:"u"}]
},
{
A:"YYY",
B:"ss",
C:"qee",
Z:[ {e:"p",f:"q",g:"r"},
{e:"m",f:"n",g:"o"},
{e:"s",f:"t",g:"u"}]
}]
now i want to run a query on basis of A,e,f. and i want resultant object to have A,B,C,Z{} (but for Z, only object that got matched.
so if i give value as {A: "XXX", Z.e:"s", Z.f:"t"} i get
{
A:"XXX",
B:"ss",
C:"qee",
Z:
{e:"s",f:"t",g:"u"}
}
I am not sure how to write projection for this?

Related

mongoDB collection.findOne() update the query with its own results

I have a query like this one :
.findOne( {
_id: myObjectId,
}, {
fields: {
smallSingleValue: 1,
HUDGE_OBJECT: 1,
}
} );
I do not need the entire HUDGE_OBJECT (it is roughly 1 Mo), I need HUDGE_OBJECT[ smallSingleValue ] ( less than a Ko ).
Right now, I can make that request and get the entire HUDGE_OBJECT, or make two requests ; one the guet the smallSingleValue and the other one to get the HUDGE_OBJECT[ smallSingleValue ].
Both solution are crapy.
Is there a way to do something like that :
fields: {
smallSingleValue: 1,
`HUDGE_OBJECT.${ $smallSingleValue }`: 1,
}
Obviously not with that syntax, but you get the idea.
I tried aggregation, but it’s probably not the solution.
Does the projection operator allows it ?
https://docs.mongodb.com/manual/reference/operator/projection/positional/
EDIT FOR THE COMMENTS :
Example of data :
{
_id: xxx,
firstName: xxx,
lastName: xxx,
currentLevel: bar, // <- that one is important
progress: {
foo: { big object },
bar: { big object },
baz: { big object },
...
}
}
What I need :
{
firstName: xxx,
lastName: xxx,
currentLevel: bar,
progress: {
bar: { big object },
}
}
But the question is : performance wise, is it better to just get the entire object (easy query) or to get a truncated object (more complex query but passing a bigger object) ? There are 50 «levels».
I don’ remember what I tried with the aggregation :x but it seemed to be a bad idea.

What is wrong with this mongo $or query

This query works perfectly
{
$or:[{author:this.userId} , {somethingelse:true} ]
}
But when I try:
{
$or:[{author:this.userId} , {sharedwith[this.userId]:true} ]
}
I receive the message
Errors prevented startup:
While processing files with ecmascript (for target os.linux.x86_64): server/main.js:113:43: Unexpected token, expected
, (113:43)
=> Your application has errors. Waiting for file change.
And thats where the comma , in the $or statement is
Help
I guess that you are trying to retrieve all documents for which the current user is the author, or which have been shared with him/her? And therefore that you have structured your documents with a sharedWith field which is a hash map of userId as keys and boolean as value?
Document structure:
{
author: string,
sharedWith: {
<userId1>: boolean
// <userId2>…
}
}
In that case, your MongoDB query should use the dot notation to specify the value of a nested field within sharedWith field:
{
$or: [{
author: string
}, {
"sharedWith.<userId>": boolean
}]
}
To easily create the query with the interpolated value of <userId>, you can use a computed key in your object (ES6 syntax):
{
$or:[{
author: this.userId
} , {
// The query computed key must be in square brackets.
// Specify the field child key using the dot notation within your query.
["sharedwith." + this.userId]: true
}]
}
Or with good old ES5 syntax, similarly to #MichelFloyd's answer:
var query = {
$or: [{
author: this.userId
}]
};
var newCondition = {};
newCondition["sharedWith." + this.userId] = true;
query.$or.push(newCondition);
Note: the above described document structure could conveniently replace the sharedWith hash map by an array (since having a false value for the boolean could simply be replaced by removing the corresponding userId from the array):
{
author: string,
sharedWith: [
<userId1>
// <userId2>…
]
}
In which case the query would simply become:
{
$or:[{
author: this.userId
} , {
// In MongoDB query, the below selector matches either:
// - documents where `sharedWith` value is a string equal to
// the value of `this.userId`, or
// - documents where `sharedWith` value is an array which contains
// an element with the same value as `this.userId`.
sharedwith: this.userId
}]
}
Try building the query as a variable before running it.
let query = { $or: [{ author: this.userId }]};
const sw = sharedwith[this.userId];
query.$or.push({sw: true});
MyCollection.find(query);

Meteor/Mongo DB $gte operator not working on find();

I am trying to make a query so that I only return the 'productos' that are on the current 'categoria', they also need to have the 'stock.cantidad' field greater or equal than 1 and the 'stock.idCedis' equal to a specific value, and this is how I am trying to do it:
return Productos.find(
{
idCategoria: Router.current().params._id,
"stock.cantidad":{$gte: 1},
"stock.idCedis":idCedis
});
I checked and the "stock.idCedis":idCedis is working just fine displaying the 'productos' that have that specific 'idCedis', but what I am having problems with is the "stock.cantidad":{$gte: 1}, part because I don't know why Meteor or Mongo DB for that matter are just ignoring it.
The schema for the stock part of 'productos' that I am currently using is this:
stock: {
type: [Object]
},
"stock.$.cantidad": {
type: Number,
label: "Cantidad de Stock",
min: 0
},
"stock.$.idCedis": {
type: String,
label: "Centro de Distribución"
},
So I would like to know if I am doing something wrong or any other way I could make this work.
Since stock.$.cantitad is an array try $elemMatch:
return Productos.find(
{
idCategoria: Router.current().params._id,
"stock.cantidad":{$elemMatch {$gte: 1}},
"stock.idCedis":idCedis
});
The mongo docs indicate that you shouldn't need to do this when there's only a single query condition but given how Meteor interacts with mongo I'd give it a try.
Muchas gracias Michael Floyd, what I needed was that $elemMatch query operator, but for some reason your code did not worked form me, what worked was this:
Productos.find(
{
idCategoria: Router.current().params._id,
stock: { $elemMatch: { idCedis: idCedis, cantidad: { $gte: 0 } } }
});

Update nested array object (put request)

I have an array inside a document of a collection called pown.
{
_id: 123..,
name: pupies,
pups:[ {name: pup1, location: somewhere}, {name: pup2, ...}]
}
Now a user using my rest-service sends the entire first entry as put request:
{name: pup1, location: inTown}
After that I want to update this element in my database.
Therefore I tried this:
var updatedPup = req.body;
var searchQuery = {
_id : 123...,
pups : { name : req.body.name }
}
var updateQuery = {
$set: {'pups': updatedPup }
}
db.pown.update(searchQuery, updateQuery, function(err, data){ ... }
Unfortunately it is not updating anythig.
Does anyone know how to update an entire array-element?
As Neil pointed, you need to be acquainted with the dot notation(used to select the fields) and the positional operator $ (used to select a particular element in an array i.e the element matched in the original search query). If you want to replace the whole element in the array
var updateQuery= {
"$set":{"pups.$": updatedPup}
}
If you only need to change the location,
var updateQuery= {
"$set":{"pups.$.location": updatedPup.location}
}
The problem here is that the selection in your query actually wants to update an embedded array element in your document. The first thing is that you want to use "dot notation" instead, and then you also want the positional $ modifier to select the correct element:
db.pown.update(
{ "pups.name": req.body.name },
{ "$set": { "pups.$.locatation": req.body.location }
)
That would be the nice way to do things. Mostly because you really only want to modify the "location" property of the sub-document. So that is how you express that.

convert array of objects to mongoose query?

I have an array of mongoose queries like so
var q = [{"_id":'5324b341a3a9d30000ee310c'},{$addToSet:{"Campaigns":'532365acfc07f60000200ae9'}}]
and I would like to apply them to a mongoose method like so
var q = [{"_id":'5324b341a3a9d30000ee310c'},{$addToSet:{"Campaigns":'532365acfc07f60000200ae9'}}]
Account.update.apply(this, q);
How can I do this ? How can I convert an array of mongoose query objects to mongoose parameters?
I tried the following but it doesnt work.
var q = [{
"_id": '5324b341a3a9d30000ee310c'
}, {
$addToSet: {
"Campaigns": '532365acfc07f60000200ae9'
}
}]
Account.update(q).exec(function (e, r) {
console.log(r)
console.log('ERROR')
console.log(e)
console.log('ERROR')
cb(e, r);
});
All you need to do is pass in the correct object context via apply to the update method:
Account.update.apply(Account, q)
.exec(function(e, r) {
// your code here ...
});
The this needs to be the Model instance, not the global scope context (or whatever else this may have been at the time it was called).
Basically seems to be just the way the prototype of the function seems to be passed in. So if you want to build your arguments dynamically then you need to do something like this:
var q = [
{ "_id": account._id },
{ "$addToSet": { "Campaigns": campaign._id } },
];
Account.update( q[0], q[1], (q[2]) ? q[2] : {}, function(err, num) {
Essentially then you are always passing in the query, update and options documents as arguments. This uses the ternary condition to put an empty {} document in as the options parameter, where it does not exist in your dynamic entry.