array update in mongoDB - node.js

I've got mongoose schema like this:
var mySchema = new mongoose.Schema({
user: String,
photos: [{
url: String,
thumbnail: String,
time: Date,
mainPhoto: Boolean
}]
});
now, I'd like to make a "setMainPhoto" function - for given id - set mainPhoto flag to true, and set it to false for other photos.
There is a document like this:
{
"_id": {
"$oid": "56269dea07a455920a21bca7"
},
"user": "someUser",
"photos": [
{
"time": {
"$date": "2015-05-25T08:37:56.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e2e07a455920a21bcab"
},
"mainPhoto": false
},
{
"time": {
"$date": "2015-05-25T09:27:17.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e3507a455920a21bcae"
},
"mainPhoto": true
},
{
"time": {
"$date": "2015-05-25T09:27:17.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e3507a455920a21bcaf"
},
"mainPhoto": false
}
]}
I'm trying to do this with mongoose save function:
var myModel = mongoose.model('myCollection', mySchema);
myModel.find({user:params.user,_id:params.documentId}, function(err,data){
var newMyModel = new myModel(data[0]);
for(var i=0; i<newMyModel.photos.length; i++){
if(newMyModel.photos[i]._id == params.photoId){
newMyModel.photos[i].mainPhoto = true;
}else{
newMyModel.photos[i].mainPhoto = false;
}
}
return newTrack.save(function(err,results){
console.log(err,results);
return cb(err,results);
});
});
but I'm getting an error:
'E11000 duplicate key error index: xx.$_id_ dup key: { : ObjectId(\'56269dea07a455920a21bca7\') }',
apart from the fact I feel there is a better way to do this

You may want to try a conditional type of update operation that first updates the documents based on the given criteria using the .update() method with a callback, and then do the other update in the callback for documents that do not satisfy the given criteria. For example, you could restructure this as:
var Model = mongoose.model('myCollection', mySchema);
Model.update(
{
"_id": params.documentId,
"user": params.user,
"photos.mainPhoto": true
},
{
"$set": {
"photos.$.mainPhoto": false
}
},
function(err, numAffected) {
Model.update(
{
"_id": params.documentId,
"user": params.user,
"photos._id": params.photoId /* criteria for setting the updating the documents to true */
},
{
"$set": {
"photos.$.mainPhoto": true
}
},
function(err, numAffected) {
/* Update complete. */
}
)
}
)

Related

Update String inside object inside array mongodb

I have a document that looks something like this
{
"_id":{
"$oid":"id"
},
"side1":[
{
"username":"test1",
"id":"id1",
},
{
"username":"test2",
"id":"id2",
},
{
"username":"test3",
"id":"id3",
}
],
"side2":[
{
"username":"test4",
"id":"id4",
},
{
"username":"test5",
"id":"id5",
},
{
"username":"test6",
"id":"id6",
}
],
}
I want to be able to search and update one of the sides, for example, if I searched with username for side1 and that username would be there then I would be able to $set other fields for the object with this username. Something like: Search side1 username test1: $set result.id: "43242342" this would set the id of the object with the username of test1 to 43242342. I am not sure on how I would go about doing this, I have tried using $elemMatch but that didn't bring any results.
Test.findOne({ id: id },
{ side1: { $elemMatch: {username: username} } }, function (err, result) {
if (err) {
console.log(err);
} else {
console.log(result)
}
});
I'm not exactly sure how you want to update the document, but perhaps this is what you are looking for?
db.collection.update({
"_id": ObjectId("000000000000000000000001"),
"side1.username": "test1"
},
{
"$set": {
"side1.$.id": "43242342"
}
})
Try it on mongoplayground.net.
Example updated document:
[
{
"_id": ObjectId("000000000000000000000001"),
"side1": [
{
"id": "43242342",
"username": "test1"
},
{
"id": "id2",
"username": "test2"
},
{
"id": "id3",
"username": "test3"
}
],
"side2": [
{
"id": "id4",
"username": "test4"
},
{
"id": "id5",
"username": "test5"
},
{
"id": "id6",
"username": "test6"
}
]
}
]
Could you do something like this?
function addProperty(id, side, username, property, value) {
const query = {
_id: {
$oid: id,
},
};
const update = {
$push: {
[side]: {
username: username,
[property]: value,
},
},
};
const options = {
upsert: true,
};
db.collection("Test").updateOne(query, update, options);
}

Create and update field Mongoose NodeJs

{
"_id": {
"$oid": "5a4e5b1d09fb590058bfdf86"
},
"name": "ProjectStore",
"imageURL": "none",
"longitude": 0,
"latitude": 0,
"rating": 5,
"leads": [
{
"customerId": "5a0c57db65a4931768716566",
"customerName": "testuser",
"interested": "testuser",
"_id": {
"$oid": "5a4e5b5409fb590058bfdf88"
}
}
],
"items": [
{
"name": "chat",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5f2235680012bd12cb",
"_id": {
"$oid": "5a4e5b3009fb590058bfdf87"
}
}
],
"__v": 2
}
I added my DB log, I try to write query that will let me update/push information to "LeadStatus".
This varible should be inside each object in the "leads" array.
I have the keys for the main id "5a4e5b1d09fb590058bfdf86"
I have the second key for the specific lead "5a4e5b5409fb590058bfdf88"
I just dont know how to write the query, for now i got this.... and got error.
Store.update(
{ _id: req.body.store._id, 'leads._id': 'req.body.lead._id', },
{ $set: { 'leads.$.LeadStatus': 'open' }, },
(err, result) => {
if (err) {
res.status(500)
.json({ error: 'Unable to update leads.', });
} else {
res.status(200)
.json(result);
}
}
);
Please Help,
Thanks.
I'm assuming you want the updated doc returned (you can choose to not do so as well, switch option new to false). You have to assign an object the value that you're updating and provide all the fields within the subfield you're updating, otherwise it will remove everything else. That's why you have to do a findOne first:
return Store.findOne({ '_id': "5a4e5b1d09fb590058bfdf86" })
.then((store) => {
if (!store) { // patient was not found for some reason
throw new Error(`Store id was not found`);
}
const options = {
new: true,
};
const updatedStore = Object.assign({}, store.toObject(), {
leads: [
{
"leadStatus": "open",
"customerId": "5a0c57db65a4931768716566",
"customerName": "testuser",
"interested": "testuser",
"_id": {
"$oid": "5a4e5b5409fb590058bfdf88"
}
}
],
});
return Store.findOneAndUpdate(
{ '_id': req.body.store._id },
{ $set: updatedStore },
options);
});
In case you're curious, you can build pre/virtual hooks off of findOneAndUpdate (no documentation for this), you won't lose anything from not using update.

Node.js Fetch last week data

I have mongoose schema structured somewhat like this:
{
"_id": {
"$oid": "59d00283893f4500127271f6"
},
"__v": 0,
"bills": [
{
"timestamp": 1513539218509,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36c5979a54980014f06787"
},
"_id": {
"$oid": "5a36c6929a54980014f0678b"
},
"disc_amount": 320,
"amount": 320
},
{
"timestamp": 1513539299486,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36c6c99a54980014f0678c"
},
"_id": {
"$oid": "5a36c6e39a54980014f0678f"
},
"disc_amount": 160,
"amount": 160
},
{
"timestamp": 1513540879109,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36ccfb9a54980014f06790"
},
"_id": {
"$oid": "5a36cd0f9a54980014f06793"
},
"disc_amount": 320,
"amount": 320
},
{
"timestamp": 1513540986507,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36cd639a54980014f06794"
},
"_id": {
"$oid": "5a36cd7a9a54980014f06797"
},
"disc_amount": 240,
"amount": 320
}
]
}
From the subschema bills i want to fetch only those bills which are a week old only.
My first Question is, is it a right way to first find Schema by id, then get its bills and in bills array do processing to fetch only last week data. I guess that won't be efficient because subschema bills can be too large, therefore, it won't be a good idea to fetch whole array. So, what would be the proper way?
And Second question, is there more appropriate way to create schema and do query.(I created subschema bills, so that all bills related to particular id remain at one place, so easy to query. Is this efficient?).
Open to all kind of suggestion. Thanks in advance.
You can create model for your collection in mongoose and use it as following:
var start = new Date(new Date().getTime() - (7 * 60 * 60 * 24 * 1000)); // get date of last week
Bill.find({
'bills.timestamp' : {"$gte": start.getTime()}
}, {
// Only include the subdocument(s) that matched the query.
'bills.$' : 1
}, function(err, data){
console.log(data)
});
Model:
var mongoose = require('mongoose');
/**
* Bill Mongo DB model
* #name billModel
*/
var billModel = function () {
var billSchema = mongoose.Schema({
bills:{ type : Array , "default" : [] }
});
return mongoose.model('Bill', billSchema);
};
module.exports = new billModel();
For single id, below code can be used:
Bill.aggregate([
{
$match: {
'_id': new mongoose.Types.ObjectId('5968dcf965854c7817606014')
}
},
{
$project: {
bills: {
$filter: {
input: "$bills",
as: "bill",
cond: { $gte: [ "$$bill.timestamp", start.getTime() ] }
}
}
}
},
], function (err, result) {
if (err) {
console.log(err);
} else {
res.json(result);
}
});

Elasticsearch full-text query issue from inner array

This is my object. and I want find text inside of task i.e task.name.
{
"_id" : ObjectId("55e2acd77e5e4ddc037b3ef5"),
"userId" : "123",
"username" : "user12",
"address" : "abc",
"number" : 928228828282.0,
"task" : [{
"name" : "metac",
"productCode" : "1234",
"_id" : ObjectId("55e2acd77e5e4ddc037b3ef7")
}, {
"name" : "alfa33",
"productCode" : "1234",
"_id" : ObjectId("55e2acd77e5e4ddc037b3ef6")
}],
"__v" : 0
}
so when I query for that it will return all task.
curl -XPOST 'localhost:9200/userprofiles/_search?pretty' -d '
{
"query": { "match": { "name": "alfa33" } }
}
Output:
{
"took": 51,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.19178301,
"hits": [
{
"_index": "userprofiles",
"_type": "userprofile",
"_id": "55e2acd77e5e4ddc037b3ef5",
"_score": 0.19178301,
"_source": {
"userId": "123",
"username": "user12",
"address": "abc",
"number": 928228828282,
"task": [
{
"name": "metac"
},
{
"name": "alfa33"
}
]
}
}
]
}
}
As you can see task return full array I want only 1 task which is selected.
I am using mongoosastic inside node it will give me problem so i tried to use request directly to Elasticsearch.
mongoosastic config - >elasticsearch search text return full array issue i tried this solution but not working.
Currently I am search my result using curl command in git bush not using search function
EDIT
FILE:- mongoose and mongoosastic.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var medicineSchema = require('./search')
var mongoosastic = require("mongoosastic");
var UserProfileSchema = new Schema({
userId: String,
username: String,
address: String,
number: Number,
task: [{
name: {
type: String,
es_boost: 2.0 // or es_indexed:true
},
taskCode: String,
}]
});
UserProfileSchema.plugin(mongoosastic);
UserProfileSchema.plugin(mongoosastic, {
host: "localhost",
port: 9200,
// ,curlDebug: true
});
UserProfile = module.exports = mongoose.model('UserProfile', UserProfileSchema);
UserProfile.createMapping(function(err, mapping) {
if (err) {
console.log('error creating mapping (you can safely ignore this)');
console.log(err);
} else {
console.log('mapping created!');
console.log(mapping);
}
});
And my search Query:
var UserProfileSchema = require('../../app/models/user');
UserProfileSchema.search({
query_string: {
query: name
}
}, function(err, result) {
if (err) {
callback({
RESULT_CODE: '-1',
MESSAGE: 'System error'
});
} else {
callback({
RESULT_CODE: '1',
DATA: result
});
}
});
In order to treat these as separate objects, you will need to use a nested type
for the "task" field, so setting up your mappings as follows:
{
"mappings": {
"doc": {
"... all the other fields ...": {},
"task": {
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"productCode": {
"type": "string"
}
},
"type": "nested"
}
}
}
}
After reindexing your document. You'll need to use a nested query with an
inner-hits query to return which task matched the query:
{
"query": {
"nested": {
"path": "task",
"query": {
"match": {
"name": "alfa33"
}
},
"inner_hits": {}
}
}
}
The inner_hits portion will return the specific task that matched the query by
itself.
I've posted an example with the
full output here since it's a
little long to post in entirety here.

MongoDB: Update property of subarray just updates the first element

The matching element looks like that:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": false
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
Now I try to set the read property to the current date just of the subelements where the username is not equal to Tester:
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$.read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});
But just the first messages subelement read property updates:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": {
"$date": "2013-01-01T00:00:00.000Z"
}
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
What's wrong with the code?
I'm using node.js v0.10.8 and MongoDB v2.4.3 together with node-mongodb-native.
There's nothing wrong with your code; the $ operator contains the index of the first matching array element. The {multi: true} option only makes the update apply to multiple documents, not array elements. To update multiple array elements in a single update you must specify them by numeric index.
So you'd have to do something like this:
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157")
},
{ $set: {
'messages.0.read': new Date(),
'messages.2.read': new Date()
} },
function (err, result) { ... }
);
This is similar to question: How to Update Multiple Array Elements in mongodb
var set = {}, i, l;
for(i=0,l=messages.length;i<l;i++) {
if(messages[i].username != 'tester') {
set['messages.' + i + '.read'] = new Date();
}
}
.update(objId, {$set:set});
I think ArrayFilters can be used in this case
I know it's a little bit late to answer this question but if you use the $[] operator, it will do the trick.
more details here
https://www.mongodb.com/docs/manual/reference/operator/update/positional-all/
I was able to use it inside an updateMany operation, but it should work also for update
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$[].read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});

Resources