using update with upsert and $all - mongodb

I cannot manage to get this to work:
db.library.update({
categories: {
$all: ['/movie/action', '/movie/comedy'],
$nin: ['/movie/cartoon']
},
location: {
$geoWithin: {
$centerSphere: [[48.8574946, 2.3476296000000048], 50/6378.1]
}
}
},
{
$setOnInsert: {
categories: ['/movie/action', '/movie/comedy'],
location: {
type: 'Point',
coordinates: [48.8574946, 2.3476296000000048]
}
},
$addToSet: {users: {_id: '1', date: '2018-04-06'}}
},
{ upsert: true })
It returns the following error:
cannot infer query fields to set, path 'categories' is matched twice
I understand that query part is moved to update part when upsert happens, but I'm not sure how to keep $all from having this effect
It does work when $all array is not set to more than 1 element.

I've found this solution, even though it's painful to be forced to list $all elements under { $elemMatch: { $eq:... :
db.library.update({
categories: {
$all: [
{ $elemMatch: { $eq: '/movie/action' } },
{ $elemMatch: { $eq: '/movie/comedy' } }
],
$nin: ['/movie/cartoon']
},
location: {
$geoWithin: {
$centerSphere: [[48.8574946, 2.3476296000000048], 50/6378.1]
}
}
},
{
$setOnInsert: {
categories: ['/movie/action', '/movie/comedy'],
location: {
type: 'Point',
coordinates: [48.8574946, 2.3476296000000048]
}
},
$addToSet: {users: {_id: '1', date: '2018-04-06'}}
},
{ upsert: true })
any simpler solution is welcome

Related

mongoDB: update nested array element

I have the following datastructure
{
_id: ObjectId('61ae12bfb8047effd0ac2a01'),
data: [
{
xml: {
messageId: 1638798015073,
xmlString: 'someXML'
},
data: [
{
customerId: 123456,
validation: {
isValid: true,
message: ''
},
docs: [
{
objectId: 'PA1106:zt:bb302216879669b58c141b12dcdd5eb0',
writtenBack: false
}
]
},
{
customerId: 55555,
validation: {
isValid: true,
message: ''
},
docs: [
{
objectId: 'PA1106:zt:bb302216879669b58b143ef38c016217',
writtenBack: true
}
]
}
]
},
{
xml: {
messageId: 1638798015094,
xmlString: 'someXML'
},
data: [
{
customerId: 55555,
validation: {
isValid: true,
message: ''
},
docs: [
{
objectId: 'PA1106:zt:bb302216879669b58c1416129062c2d2',
writtenBack: false
},
{
objectId: 'PA1106:zt:b8be9ea04011c2a18c148a0d4c9d6aab',
writtenBack: true
}
]
},
]
},
],
createdAt: '2021-12-06T13:40:15.096Z',
createdBy: 'Test'
}
Now I want to update the writtenBack property of for a given Document and objectId. How would I write a query for this?
my updateOne looked like this
{
_id: '61ae12bfb8047effd0ac2a01',
'data.data.docs.objectId': 'PA1106:zt:bb302216879669b58b143ef38c016217'
},
{
$set: { 'data.data.docs.$.writtenBack': true }
}
I know there is arrayFilters for nested arrays, but as far as I know, I need a unique identifier for each array-Level. But I only have the objectId which is unique for a Document. Any Ideas?
Yes, you can achieve the update for the document in the nested array with positional operator $[] and arrayFilters.
db.collection.update({
_id: "61ae12bfb8047effd0ac2a01",
},
{
$set: {
"data.$.data.docs.$[doc].writtenBack": true
}
},
{
arrayFilters: [
{
"doc.objectId": "PA1106:zt:bb302216879669b58b143ef38c016217"
}
]
})
Sample Mongo Playground
I made it work with this query
{
_id: '61ae12bfb8047effd0ac2a01',
},
{
$set: { 'data.$[elem1].data.$[elem2].docs.$[elem3].writtenBack': true }
},
{
arrayFilters: [
{ 'elem1.xml.messageId': 1638798015094 },
{ 'elem2.customerId': 55555},
{ 'elem3.objectId': 'PA1106:zt:bb302216879669b58c1416129062c2d2'}]
}

update a nested array of strings inside an array of objects in a schema in mongo

I´m trying to update my database schema in its user topic .
Its schema would look like this:
name: [Object],
nickname: [Object],
email: [Object],
password: [Object],
image: [Object],
googleFlag: [Object],
groupOfChats: [Array],
role: [Object],
messages: [Array],
userState: [Object],
friends: [Array]
Where the item to modify would be the groupOfChats that is an array that contains several objects and on the objects there is an item 'memberId' which is a array of string ,being this last one the one i want to access to modify:
groupOfChats: [
{
idGroup: { type: String, required: true },
nameGroup: { type: String, required: true },
membersId: [{ type: String, required: false }],
groupCreatorId: { type: String, required: true },
messages: [{ type: String, required: false }],
groupImage: { type: String, required: false },
},
],
Traying to access that membersId item in a specific group i just tried to set this:
let friendsAddedIdOnly =["des","pa","cito"];
let userChatGroupUpdate = User.updateOne(
{
_id: payload.idUserCreatorGroup,
"groupOfChats.idGroup": payload.groupId,
},
{ $push: { "membersId.$[]": friendsAddedIdOnly} },
{ new: true }
);
(await userChatGroupUpdate).save();
a view of my mongo database would be like this:
Edit:
Old asnwer wasn't working you're right. But you can use below aggregation
db.collection.aggregate([
{
"$match": {
_id: payload.idUserCreatorGroup
}
},
{
"$set": {
"groupOfChats.0.membersId": {
"$reduce": {
"input": "$groupOfChats.membersId",
"initialValue": friendsAddedIdOnly,
"in": {
"$concatArrays": [
"$$this",
"$$value"
]
}
}
}
}
},
{
"$set": {
"groupOfChats.0": {
$concatArrays: [
{
$slice: [
"$groupOfChats.0",
1
]
},
{
$slice: [
"$groupOfChats.0",
{
$add: [
1,
1
]
},
{
$size: "$groupOfChats.0"
}
]
}
]
}
}
}
])
Playground
Gues this is the right approach. By the way thanks to #Murat Colyaran for being so helpful:
User.updateOne(
{
_id: payload.idUserCreatorGroup,
"groupOfChats.idGroup": payload.groupId,
},
{
$push: {
"groupOfChats.$.membersId": { $each: friendsAddedIdOnly },
},
}
);
(await userChatGroupUpdate).save();

Two $Geonear in aggrigate Mongo

The collection has two geo fields: fromLocation and toLocation. But only one Geonear is allowed.
The collection looks like:
...............
fromLocation: {
type: { type: String, default: "Point" },
coordinates: [Number],
},
toLocation: {
type: { type: String, default: "Point" },
coordinates: [Number],
},
.........................................
plese give example code, how to use Geonear for search by two fields.
My code for one field search:
[
{
'$geoNear': {
near: [Object],
key: 'fromLocation',
distanceField: 'fromDistance',
spherical: true
}
},
{
'$match': {
status: 2,
'from.data.city_fias_id': '27c5bc66-61bf-4a17-b0cd-ca0eb64192d6',
'to.data.city_fias_id': '27c5bc66-61bf-4a17-b0cd-ca0eb64192d6',
'car.paymentInfo.id': [Object],
budget: [Object]
}
},
{
'$lookup': {
from: 'users',
localField: 'autor',
foreignField: '_id',
as: 'autor'
}
},
{ '$unwind': '$autor' },
{ '$addFields': { sortBudget: [Object] } },
{ '$sort': { sortBudget: 1 } },
{ '$group': { _id: null, total: [Object], results: [Object] } },
{ '$project': { total: 1, results: [Object] } }
]
Distance calculation is rather straightforward. Use $geoNear for the more selective condition to take advantage of the geo index, use $match and $expr for the second condition.

How I can compile $cond with $ne on mongoose aggregate

This is the sample about my data:
[{
_id: ObjectId('5e982040227ddfb12bf43e39'),
classId: 'class-1',
state: 'active',
logs: []
},
{
_id: ObjectId('5e982040227ddfb12bf43e38'),
classId: 'class-1',
state: 'unactive',
logs: []
},
{
_id: ObjectId('5e982040227ddfb12bf43e40'),
classId: 'class-1',
state: 'graduated',
logs: []
}]
I want to change state for all students in class class-1 and state not equal unactive.
This is my query:
db.getCollection('student').aggregate([{
$addFields: {
state: {
$cond: [{ classId: 'class-1', $ne: ['$state', 'unactive']}, 'unactive', '$state']
},
logs: {
$cond: [{
{ classId: 'class-1', $ne: ['$state', 'unactive']},
{
$concatArrays: ['$logs', [{ from: '$state', to: 'unactive', at: new Date() }],
'$logs'
]}
}]
}
}
}, {
$out: 'student'
}]);
But it's throw an error: FieldPath field names may not start with '$'.
Here is document I'm following: https://docs.mongodb.com/manual/reference/operator/aggregation/cond/
If I remove $ne condition and keep only filter by classId, It's working well, but I don't want to unactive users who guys unactived.
Here my expected resuult:
[{
_id: ObjectId('5e982040227ddfb12bf43e39'),
classId: 'class-1',
state: 'unactive',
logs: [{ from: 'active', to: 'unactive', at: ... }]
},
{
_id: ObjectId('5e982040227ddfb12bf43e38'),
classId: 'class-1',
state: 'unactive',
logs: []
},
{
_id: ObjectId('5e982040227ddfb12bf43e40'),
classId: 'class-1',
state: 'unactive',
logs: [{ from: 'graduated', to: 'unactive', at: ...}]
}]
Please give me an idea if you know the way to resolve this problem
I believe you are trying to update. If that's not the case then, let me know.
You are using the $cond operator in a wrong way. The first argument in it, expects some logical condition like $and, $or, or any combination of logical operators.
The below query will be helpful:
db.student.aggregate([
{ // Unwind the 'logs' array field, along with preserving empty arrays.
$unwind: {
path: "$logs",
preserveNullAndEmptyArrays: true
}
},
{ // Group the documents by '_id' field.
$group: {
_id: "$_id",
classId: {
$first: "$classId"
},
oldState: {
$first: "$state"
},
logs: { // Conditionally push the object.
$push: {
$cond: [
{
$and: [
{
$eq: [
"$classId",
"class-1"
]
},
{
$ne: [
"$state",
"unactive"
]
}
]
},
{
"from": "$state",
"to": "unactive",
"at": "20200522"
},
"$$REMOVE" // If the above condition results false, then don't push.
]
}
}
}
},
{ // Project the required fields.
$project: {
classId: 1,
logs: 1,
state: { // conditionally set the 'state' field.
$cond: [
{
$and: [
{
$eq: [
"$classId",
"class-1"
]
},
{
$ne: [
"$oldState",
"unactive"
]
}
]
},
"unactive",
"$oldState"
]
}
}
},
{ // overwrite the output of this aggregation to 'student' collection.
$out : 'student'
}
])

How to Combine GeoQuery with another separate query in MongoDB and paginate result?

let params = {
$or: [
{//STATUS1 PERSON
status: { '$in': ['Status1'] }, object: { '$in': ['Person'] },
geometry: {
$near: {
$maxDistance: 160000,
$geometry: {
type: "Point",
coordinates: [geoCoordinates[1], geoCoordinates[0]]
}
}
},
name: { '$in': req.user.names },
sessionStartTime: { "$gte": new Date() },
$and: [
{
user_id: '',
},
{
userID: { $exists: false }
}
]
},
{ //STATUS1 ALL OTHER ITEM TYPES
status: { '$in': ['Status1'] }, object: { '$in': ['Animal', 'Planet', 'Star'] },
name: { '$in': req.user.names },
sessionStartTime: { "$gte": new Date() },
$and: [
{
user_id: '',
},
{
userID: { $exists: false }
}
]
},
{ //ALL ITEM TYPES WITH STATUS2 AND STATUS3
status: { '$in': ['Status2', 'Status3'] }, object: { '$in': ['Person', 'Animal', 'Planet', 'Star'] },
user_id: req.user._id
}
]
}
var options = {
page: 1,
limit: 5
};
let foundItems = await User.paginate(params, options).then(function (result) {
});
With this query, I'm getting the following error:
MongoError: geoNear must be top-level expr
How do I include geometry query as one query among others and return the combined result with pagination ? Or is there a different way to achieve the same result?