In-/upserting a nested document in MongoDB - javascript

I'm trying to upsert a nested document (activities) in a structure similar to
{
"_id" : "123",
"modules" : {
"x" : {
},
"y" : {
"activities" : {
"preview": {
"createdAt": "2014-10-13 15:21:22.113",
"data": {}
}
}
},
"z" : {
"activities" : {
"render": {
"createdAt": "2014-10-15 04:22:25.171",
"data": {}
},
"render": {
"createdAt": "2014-10-14 02:42:24.132",
"data": {}
}
}
}
}
}
I then try
selector = { "_id": id, "modules": module, "activities" }
activity = { "preview": { "data": data }}
Meteor.users.upsert(selector, { $set: activity, $setOnInsert: { "createdAt: new Date()" }})
Which yields
MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index: meteor.users.$_id_ dup key: { : "123" }
_id is a unique index key. There are additional fields/documents on the _id 'level' of each user and on the activities 'level' of each module, but none concerning the upsert. Each module is unique. The activities document doesn't exist before the first record is inserted.
With that, I hope someone gets the gist of what I'm trying to accomplish and can help me in the right direction.

Upsert is for insert or update(if exist) a new document. It does not apply for a sub-document.
You should just update your document:
selector = { "_id": id, "modules": module, preview : null }
activity = { "preview": { "data": data , "createdAt" : new Date() }}
Meteor.users.update(selector, { $set: 'activity' : activity }})

Related

how to find x number of documents where one property matches but other are distinct

Ok, imagine a collection like this:
[
{
"_id" : ObjectId("5b5f76eb2bfe4a1e9c473bd2"),
"machine" : "NY-D800",
"level" : "Fatal",
},
{
"_id" : ObjectId("5b5f76eb2bfe4a1e9c473bd2"),
"machine" : "NY-D889",
"level" : "Fatal",
},
{
"_id" : ObjectId("5b5f76eb2bfe4a1e9c473bd2"),
"machine" : "NY-D889",
"level" : "Info",
},
{
"_id" : ObjectId("5b5f76eb2bfe4a1e9c473bd2"),
"machine" : "NY-D800",
"level" : "Fatal",
},
...
]
I want to find documents that have level set to 'Fatal' but I don't want to return duplicates (duplicate machine). So for example 'NY-D800' is listed twice with 'Fatal' so I would want it to be returned only once. Finally I would like to limit the values returned to 10 items.
Recapping:
level = 'Fatal'
only unique values determined by machine
limit to 10 docs
Is this possible with MongoDB, Mongoose?
I tried this:
Logs
.distinct({'level': 'Fatal'})
.limit(10)
.exec(function (err, response){
var result = {
status: 201,
message: response
};
if (err){
result.status = 500;
result.message = err;
} else if (!response){
result.status = 404;
result.message = err;
}
res.status(result.status).json(result.message);
});
You can first $match with the level "Fatal" and then apply $group with machine
Logs.aggregate([
{ "$match": { "level": "Fatal" }},
{ "$group": {
"_id": "$machine",
"level": { "$first": "$level" },
}},
{ "$limit": 10 },
{ "$project": { "machine": "$_id", "level": 1, "_id": 0 }}
])

mongoDB: Can't add an field to an array object

This is how my db document looks like:
{
"_id" : "aN2jGuR2rSzDx87LB",
"content" : {
"en" : [
{
"content" : "Item 1",
"timestamp" : 1518811796
}
]
}
}
Now I need to add another field in the first object of the content.en array.
The document itself gets selected by an ID.
The result should be:
{
"_id" : "aN2jGuR2rSzDx87LB",
"content" : {
"en" : [
{
"content" : "Item 1",
"timestamp" : 1518811796,
"user" : {
"id" : 'userId'
}
}
]
}
}
I tried to do it like this, but nothing is happening. I don't even get an error message.
Content.update(
{ _id: id },
{
$addToSet: {
'content.en.0.user': {
id: 'userId',
}
}
}
)
Also I would like to use an variable for the language. How do I do that? Something like 'content.' + language + '.0.user'...
$addToSet is useful when you want to add someting to an array. In your case you want to modify first element of your array (at index 0) so you should simply use $set (which is field update operator):
Content.update(
{ _id: "aN2jGuR2rSzDx87LB" },
{
$set: {
"content.en.0.user": {
id: "userId",
}
}
}
)

Find existing object into an array in nodejs

Hi this is my collection i just want to look userId into subcriber array if both userId exist into the subcriber array than not create new collection return same channelId. If both userId not exist into subcriber array then create new document how can i search userids in an array.
this is my collection.
{
"_id" : ObjectId("58dd1013e973fc0004743443"),
"createdAt" : ISODate("2017-03-30T14:02:59.175Z"),
"updatedAt" : ISODate("2017-03-30T14:02:59.175Z"),
"timestamp" : "2017-03-30",
"channelId" : "CH-EU7D",
"createdById" : "58dcc3cd9a7a301308b62857",
"message" : "",
"subcriber" : [
{
"userId" : "58dcc3cd9a7a301308b62857",
"channelId" : "CH-EU7D",
"_id" : ObjectId("58dd1013e973fc0004743444"),
"status" : "accepted"
},
{
"userId" : "58dcc3ec9a7a301308b62859",
"channelId" : "CH-EU7D",
"_id" : ObjectId("58dd1013e973fc0004743445"),
"status" : "pending"
}
],
"__v" : 0
}
I have tried this but not working.
Channel.find({ 'subcriber.userId': b.userId }, { $and: [{ 'subcriber.userId': b.friendId }] }
In nodejs you can use JavaScript Array.prototype.some() method with your json array to test if there's any matching useId, you will need to write something like:
function checkUserId(array, userId) {
return array.some(function(element) {
return userId === element.userId;
});
}
This is a Demo snippet:
function checkUserId(array, userId) {
return array.some(function(element) {
return userId === element.userId;
});
}
var json = {
"_id": "58dd1013e973fc0004743443",
"createdAt": "2017-03-30T14:02:59.175Z",
"updatedAt": "2017-03-30T14:02:59.175Z",
"timestamp": "2017-03-30",
"channelId": "CH-EU7D",
"createdById": "58dcc3cd9a7a301308b62857",
"message": "",
"subcriber": [{
"userId": "58dcc3cd9a7a301308b62857",
"channelId": "CH-EU7D",
"_id": "58dd1013e973fc0004743444",
"status": "accepted"
},
{
"userId": "58dcc3ec9a7a301308b62859",
"channelId": "CH-EU7D",
"_id": "58dd1013e973fc0004743445",
"status": "pending"
}
]
};
//Check an existing userId
console.log(checkUserId(json.subcriber, "58dcc3ec9a7a301308b62859"));
//Check a non existing userId
console.log(checkUserId(json.subcriber, "58dcc3ec9a7a3000000000859"));
for this type of problem can use $elemMatch of mongodb.
db.getCollection('channels').find({subcriber:{"$elemMatch":{userId:"58dcc3cd9a7a301308b62857",userId:'58dcc3ec9a7a301308b62859'}}})

Editing an inner array that is only present some docs

I have a JSON entry within mongo:
{
_id: "fe50fdee-4ea3-4824-94af-f369633c0c7a",
_class: "com.tracking.daoservice.model.TrackingData",
modified: ISODate("2014-09-10T23:38:48.407Z"),
eventtype: "William-Test",
eventdata: {
QueryDate: "01-APR-2014",
SearchQuery: {
keyword: "Java",
location: "Santa Clara, CA",
Facet: "skill~java~perl|workAuth~USC",
SearchAgentId: "4299"
},
Viewed: [
{
ViewedID: "8992ade400a",
Dockey: "3323aba3233",
PID: "32399a",
actionsTaken: "email|direct message|report seeker",
viewDate: "01-APR-2014",
MessageSent: "true",
Message: [
{
MessageID: "123aca323",
Delivered: "True",
Opened: "True",
ClickThroughRate: "NotBad",
MessageDate: "02-APR-2014",
Response: [
{
ResponseId: "a323a9da",
ResponseDate: "23-APR-2014"
}
]
}
]
}
]
},
eventsource: "API-Dev Test - JMachine",
sourceip: "myIp",
entityid: "TmoneyBunnyWunny",
groupid: "Dice",
datecreated: ISODate("2014-09-10T23:38:48.405Z")
}
I have a script to change the inner date attributes,
db.TRACKING_DATA.find().forEach(function(doc) {
db.TRACKING_DATA.update({
"_id": doc._id,
"eventdata.Viewed.viewDate":doc.eventdata.Viewed[0].viewDate
}, {
"$set": { "eventdata.Viewed.$.Message.0.MessageDate": new Date( doc.eventdata.Viewed[0].Message[0].MessageDate) }
}
)
});
The problem is not all of the docs contain the inner object:
Message: [
{
MessageID: "123aca323",
Delivered: "True",
Opened: "True",
ClickThroughRate: "NotBad",
MessageDate: "02-APR-2014",
Response: [
{
ResponseId: "a323a9da",
ResponseDate: "23-APR-2014"
}
]
}
]
So when I run it, it returns:
TypeError: Cannot read property '0' of undefined
I need a way to basically say:
if(Message exists change these entries within it)
I have tried a number of variations on:
db.TRACKING_DATA.find().forEach(function(doc) {
db.TRACKING_DATA.update(
{
"_id": doc._id,
"eventdata.Viewed.viewDate":doc.eventdata.Viewed[0].viewDate, doc.eventdata.Viewed[0].Message[0]: {$exists}
},
{
"$set": {"eventdata.Viewed.$.Message.0.MessageDate": new Date( doc.eventdata.Viewed[0].Message[0].MessageDate) }
}
)
});
but unable to find a viable solution.
Cheers,
Add some judgement will help.
db.TRACKING_DATA.find().forEach(
function(doc) {
var Viewed = doc.eventdata.Viewed;
if (Viewed instanceof Array && Viewed[0]) {
var Message = Viewed[0].Message;
if (Message instanceof Array && Message[0]) {
db.TRACKING_DATA.update({
"_id" : doc._id,
"eventdata.Viewed.viewDate" : Viewed[0].viewDate
}, {
"$set" : {
"eventdata.Viewed.$.Message.0.MessageDate" : new Date(Message[0].MessageDate)
}
});
}
}
});
I guess you want to change field type from String to Date on every MessageDate of Message. If true you need to change these codes. Above only change the first element according to your original codes.

MongoDB Aggregate - Unwind, group and project

Using the collection Users, is it possible to retrieve the below unique list of organisations/owners? If this current set up isn't possible, is it possible to get the same results from two ID-linked collections with one query?
Currently, using Mongoose I can only retrieve the group of organisation names:
Current query
userModel.aggregate([
{ $unwind:'$organisations' }
, { $group: { name: '$organisations.name' } }
])
Users
{ "_id" : ObjectId("53f4a94e7c88310000000001"),
"email" : "bob#example.com",
"organisations" : [
{
"name" : "OrgOne",
"isOwner" : true
}
]
},
{ "_id" : ObjectId("53f4a94e7c88310000000002"),
"email" : "ash#something.com",
"organisations" : [
{
"name" : "OrgOne"
}
]
},
{ "_id" : ObjectId("53f4a94e7c88310000000003"),
"email" : "george#hello.com",
"organisations" : [
{
"name" : "OrgTwo",
"isOwner" : true
}
]
}
Results
{ "orgName" : "OrgOne",
"owner" : 53f4a94e7c88310000000001
},
{ "orgName" : "OrgTwo",
"owner" : 53f4a94e7c88310000000003
}
Thanks in advance, Nick
Seems like an odd use of aggregation to me, but possibly there are several "organisations" per user here, so I guess I'll continue:
userModel.aggregate(
[
{ "$match": { "organisations.isOwner": true } },
{ "$unwind": "$organisations" },
{ "$match": { "organisations.isOwner": true } },
{ "$group": {
"_id": "$organisations.name",
"owner": { "$first": "$_id" }
}}
],
function(err,result) {
}
);
If there is more than one owner and you need some precedence then you can implement a $sort before the group. Or otherwise just $project rather than group in order to get everyone.

Categories

Resources