I 'm facing an issue while trying to get some results from a mongoDB aggregation pipeline.
Here's what my DB look like:
var dbSchema = mongoose.Schema({
identity: Number,
parametres: {
style: {
fuel: [styleSchema],
gasoline: [styleSchema],
},
},
And here's what the styleSchema looks like:
var styleSchema = new mongoose.Schema({
date: Date,
value: Number,
type: String,
});
I'm trying to extract ALL the objects in 'fuel' and 'gasoline' which are of some kind of 'type'.
I've tried to group both in a unique array with concatArray and then match the 'type' I want by:
db.aggregate([
{$match:
{'identity':3,
}},
{$project: {all: {$concatArrays: ['$parametres.style.fuel','$parametres.style.gasoline']} }},
{$match: {'$all.type': 'example'}},
Before trying to match the second time, I've got a unique array ('all') and I try to match some things on it, but nothing works (I've tried 'all.type' also)...
I've probably misunderstood the way I have to use the 'match' query as I am a beginner, so thanks for your time and your answers,
Arthur
db.aggregate([
{
$match:{identity:3}
},
{
$project: {all: {$concatArrays: ['$parametres.style.fuel','$parametres.style.gasoline']} }
},
{$unwind: "$all"},
{$match: {"all.type": 'example'}},
{$group : {_id: null, all:{$push:"$all"}}}
])
Probably you are trying to do something like this.
In aggregate operation, the $ is used in right side of : to mention field name and in left side to mention operator.
So when you are using "$all.type" in left-hand side MongoDB is treating it as an operator which is not available in the Mongodb operator list.
Another thing is that when you do any query over an array. Mongodb sends back the full array if atleast one of the element matches the condition. So we need to use $unwind operator to deconstruct the array before doing any query.
Related
I've been trying to update a particular object in a Mongodb document without any luck using the findOneAndUpdate() method. This is what my collectiom looks like
{
_id: new ObjectId("61da0ab855483312e8f4483b"),
products: [
{
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483c"),
productCode: 'otf',
productName: 'facebookmeta',
claims: [Array],
permissions: []
},
{
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483f"),
productCode: '4pf',
productName: 'twitteroauth',
claims: [Array],
permissions: [Array]
}
],
__v: 0
}
When i try something like this. i am trying to find a singular object based on its product code and update that object
ProductModel.findOneAndUpdate({productCode: userData.productCode}, dataToBeUpdated, {new: true})
it returns a
Performing an update on the path '_id' would modify the immutable field '_id
i am suspecting that MongoDB is applying query conditions on collection and returns the result with the matching documents so its probably returning both objects which is probably the cause of the error i am encountering but i could be wrong.
How can i efficiently point it to the right object and perform an update
MongoDB supports the single-level deep update of subdocument using positional operator ($).
db.collection_name.update({
_id: ObjectId("61da0ab855483312e8f4483b"),
"products.productCode": "4pf"
},
{
"$set": {
"products.$.productName": "twitteroauth name updated"
}
})
In the above update clause, $ will be replaced by the matching index of a subdocument at a runtime.
For example $ will be replaced by value 1 at a runtime because of "products.productCode": "4pf" condition
Please note the below points:
You must include the array field as part of the query document.
The positional operator cannot be used for updates inside of a nested array.
Additional Reference: https://docs.mongodb.com/manual/reference/operator/update/positional/
Let's say I have this schema and it has two fields
someDate: Date
isPriority: boolean
and I want to make a sort query by someDate and records with isPriority: true should be always first.
So how should it look like? Right now I know how to sort by date like this:
Record.find().sort({ someDate: -1 });
I think at some point MongoDB started supporting sort (by means of other than which index(s) it uses), and the key order works to support sorts.
Per MongoDB documentation, to achieve a "stable" sort, ensure that you have a unique column as the last key.
db.restaurants.find().sort( { "borough": 1, "_id": 1 } )
So, for your particular query, use:
{isPriority: 1, someDate: -1, _id: 1}.
The wrong way, meaning, would be
{someDate: -1, isPriority: 1, _id: 1}
I have a program where I'm requesting weather data from a server, processing the data, and then saving it to an mlab account using mongoose. I'm gathering 10 years of data, but the API that I'm requesting the data from only allows about a year at a time to be requested.
I'm using findOndAndUpdate to create/update the document for each weather station, but am having trouble updating the arrays within the data object. (Probably not the best way to describe it...)
For example, here's the model:
const stnDataSchema = new Schema(
{
station: { type: String, default: null },
elevation: { type: String, default: null },
timeZone: { type: String, default: null },
dates: {},
data: {}
},
{ collection: 'stndata' },
{ runSettersOnQuery: true }
)
where the dates object looks like this:
dates: ["2007-01-01",
"2007-01-02",
"2007-01-03",
"2007-01-04",
"2007-01-05",
"2007-01-06",
"2007-01-07",
"2007-01-08",
"2007-01-09"]
and the data object like this:
"data": [
{
"maxT": [
0,
null,
4.4,
0,
-2.7,
etc.....
what I want to have happen is when I run findOneAndUpdate I want to find the document based on the station, and then append new maxT values and dates to the respective arrays. I have it working for the date array, but am running into trouble with the data array as the elements I'm updated are nested.
I tried this:
const update = {
$set: {'station': station, 'elevation': elevation, 'timeZone': timeZone},
$push: {'dates': datesTest, 'data.0.maxT': testMaxT}};
StnData.findOneAndUpdate( query, update, {upsert: true} ,
function(err, doc) {
if (err) {
console.log("error in updateStation", err)
throw new Error('error in updateStation')
}
else {
console.log('saved')
but got an output into mlab like this:
"data": {
"0": {
"maxT": [
"a",
"b",
the issue is that I get a "0" instead of an array of one element. I tried 'data[0].maxT' but nothing happens when I do that.
The issue is that the first time I run the data for a station, I want to create a new document with data object of the format in my third code block, and then on subsequent runs, once that document already exists, update the maxT array with new values. Any ideas?
You are getting this output:
"data": {
"0": {
"maxT": [
"a",
"b",
because you are upserting the document. Upserting gets a bit complicated when dealing with arrays of documents.
When updating an array, MongoDB knows that data.0 refers to the first element in the array. However, when inserting, MongoDB can't tell if it's meant to be an array or an object. So it assumes it's an object. So rather than inserting ["val"], it inserts {"0": "val"}.
Simplest Solution
Don't use an upsert. Insert a document for each new weather station then use findOndAndUpdate to push values into the arrays in the documents. As long as you insert the arrays correctly the first time, you will be able to push to them without them turning into objects.
Alternative Simple Solution if data just Contains one Object
From your question, it looks like you only have one object in data. If that is the case, you could just make the maxT array top-level, instead of being a property of a single document in an array. Then it would act just like dates.
More Complicated MongoDB 3.6 Solution
If you truly cannot do without upserts, MongoDB 3.6 introduced the filtered positional operator $[<identifier>]. You can use this operator to update specific elements in an array which match a query. Unlike the simple positional operator $, the new $[<identifier>] operator can be used to upsert as long as an exact match is used.
You can read more about this operator here: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
So your data objects will need to have a field which can be matched exactly on (say name). An example query would look something like this:
let query = {
_id: 'idOfDocument',
data: [{name: 'subobjectName'}] // Need this for an exact match
}
let update = {$push: {'data.$[el].maxT': testMaxT}}
let options = {upsert: true, arrayFilters: [{'el.name': 'subobjectName'}]}
StnData.findOneAndUpdate(query, update, options, callbackFn)
As you can see this adds much more complexity. It would be much easier to forget about trying to do upserts. Just do one insert then update.
Moreover mLab currently does not support MongoDB 3.6. So this method won't be viable when using mLab until 3.6 is supported.
I have a problem with Mongoose and I want to update a document in MongoDB using the module mongoose, this is my schema:
var User = new mongoose.Schema({
name: String,
email: String,
list_houses: [{
id_house: String,
price: Double
}],
...
});
When I want to add a value 'id_house' without repeating using the code below, this it does not work
User.update({'name': Name},
{$addToSet: {'list_houses': { 'id_house': new_houses , 'price': new_prices } } } ,
if(err)console.log(err);
console.log(result);
});
When I execute this, the result in BD is:
>db.users.find().pretty()
{
"__v" : 0,
"_id" : qweqweqweqweqweqwe,
"email" : adadasdweqwe#gmail.com,
"name" : Richard Smith,
"list_houses" : [ ],
.....
}
I used $push too, but neither it works, I don't know what is the problem with my code now.
Sorry to tell you but $addToSet doesn`t work with Object values in the array.
You ask me why?
Logic is simple, In JS when you compare strings/number etc with same values you do get a true but not with Object
So {a:1} is not equal to {a:1}. Of course because Objects are compared with reference.
Now the solution to your problem is you'll have to do it manually
get the list using find. Check in the list if your object exists. If not add to the list.
You can also specify your object details within find query and just find may be the documents not with this data(to be inserted) in it
I have the following model:
var PersonSchema = new Schema({
name: String,
groups: [
{type: Schema.Types.ObjectId, ref: 'Group'}
],
});
I am looking for a query that retrieves all the Persons that are not part of a certain Group (i.e the persons' group array doesn't contain the id of the specified group).
I was thinking about something like this, but I'm not sure it is correct:
Person.find({groups: {$nin: [group._id]})
Nothing wrong with what you are basically attempting, but perhaps the only clarification here is the common misconception that you need operators like $nin or $in when querying an array.
Also you really need to do here is a basic inequality match with $ne:
Person.find({ "groups": { "$ne": group._id } })
The "array" operators are not for "array targets" but for providing a "list" of conditions to test in a convenient form.
Person.find({ "groups": { "$nin": [oneId, twoId,threeId] } })
So just use normal operators for single conditions, and save $in and $nin for where you want to test more than one condition against either a single value or a list. So it's just the other way around.
If you do need to pass a "list" of arguments where "none" of those in the provided list match the contents of the array then you reverse the logic with the $not operator and the $all operator:
Person.find({ "groups": { "$not": { "$all": [oneId,twoId,threeId] } } })
So that means that "none of the list" provided are present in the array.
This is a better way to do this in Mongoose v5.11:
Person.find({ occupation: /host/ }).where('groups').nin(['group1', 'group2']);
The code becomes clearer and has more readability.