Unable to update mongoose model - javascript

I have a weird issue that is baffling me. I have a model:
var Model = new Schema({
name: String,
variations: Array
});
The variations entry looks like this:
[ {code: '', price: '' }, {code: '', price: '' }]
I need to add a new field - say "color". So I am doing this to batch update:
Model.find().exec(function(err, products) {
if (!err) {
products.forEach(function(p) {
for(var i = p.variations.length - 1; i >= 0; i--) {
p.variations[i]['color'] = 'red';
// This shows all existing variations
// with the new color feed - correct
console.log(p.variations[i]);
}
p.save(function(err) {
if (!err) {
console.log("Success");
} else {
console.log(err);
}
});
});
}
});
However the "color" field is not set - if I go through again and comment out the p.variations[i]['color'] = 'red'; line then it does not show. I can't seem to figure out why it's doing this. I have an onSave event that is triggered correctly so it's saving. I also do not have any check on the variations structure - i.e. there is no code that only allows code and price. I'm obviously missing something but after a couple of hours I ran out of ideas.

When you modify the contents of an untyped Array field like variations, you need to notify Mongoose that you've changed its value by calling markModified(path) on the modified document or a subsequent save() call won't save it. See docs.
for(var i = p.variations.length - 1; i >=0; i--) {
p.variations[i]['color'] = 'red';
}
p.markModified('variations');
p.save(function(err) { ...

You have to use the set function to change a property. The reasoning behind that is that mongoose has to mark the field as modified in order to be saved to the database.
for(var i = p.variations.length - 1; i >=0; i--) {
p.variations[i].set({"color":"red", "code":"herr"});
// or
p.variations[i].set("color":"red");
p.variations[i].set("code":"herr");
}
An alternative would be to change the field's value the old way, without going trought the setter, then manually mark it as modified: p.markModified('variations');
In my opinion you should always use the setter since this is more readable. You can just pass a json object containing all your changes in parameter and it will safely update the fields that you really want to change.

Related

Post Object Property Any on Post

DataType property Raw suddenly Not working for the New PHP8.1 update. Is there a better way to post this data?
We are passing Website data to FormStack for a PDF print. This has been working for 5 years until last week an new PHP8.1 update has suddenly broke our system.
"Due to our new PHP 8 update, any time you have a field that looks like {$TestField.value}
where the object has a key attached to it, it's going to need a value for that
key.TestField = Object
value = key
value will always need a value assigned to it, even if it is an empty value, or PHP 8 will cause an error.
We've noticed many customers sending over an array, or nothing at all, and the key that field is expecting is not in the data. This would be a silent error in the previous PHP version, and it would have removed that field completely. In the new version, rather than removing the field, it shows an error and prevents the Document from merging.There are two fixes for this currently -
You'll need to add an{if is_array($TestField)}{$TestField.Name}{/if}for every field. This will check if the key is present in the object coming over. If it isn't, it will remove that field completely. This is more of a bandaid fix and will require you to do it for every field experiencing this issue.
Making sure that the object you're sending over has a key present for every key used in merge fields, even if that value is blank. As long as Documents can map that key to something, the Document should merge just fine. The issue arises when there isn't a value for the key to map to.
https://www.webmerge.me/manage/documents?page=overview&document_
type here
$(document).on('knack-scene-render.scene_316', function(event, scene) {
// link hander: send to webmerge
$('#view_684') .submit(function( event) {
event.preventDefault();
// get data
var data = Knack.models['view_684'].toJSON();
var data_703 = Knack.models['view_703'].toJSON();
var data_704 = Knack.models['view_704'].toJSON();
var data_705 = Knack.models['view_705'].toJSON();
var data_875 = Knack.models['view_875'].toJSON();
var data_1161 = Knack.models['view_1161'].toJSON();
var models = Knack.models['view_807'].data.models; //tableview
var items =[]; //table items
for(var i = 0; i < models.length; i++){
items.push({
product: models[i].attributes.field_1594_raw, //qty
price: models[i].attributes.field_1593_raw, //packaging
total: models[i].attributes.field_1595_raw, //desc
}); //
}//
log('data!');log(data);log(items);
Knack.showSpinner();
$.ajax({
url: 'https://www.webmerge.me/route/XXXX',
data: {
items: items,
AZPONUM: data_703.field_1677_raw,
OldPO: data_703.field_1477_raw,
PickupDate: data_703.field_1407_raw,
OriginAddress: data_703.field_1375_raw,
LocationAddress: data_703.field_1044_raw,
Packaging: data_703.field_1314_raw,
CustomerPOnum: data_703.field_1409_raw,
CarrierName: data_703.field_1635_raw,
ProductType: data_703.field_1046_raw,
DeliveryDate: data_703.field_1408_raw,
CustomerName: data_703.field_1424_raw,
SKUNum: data_703.field_1426_raw,
UnitCount: data_703.field_1315_raw,
OrderContact: data_703.field_1483_ra,
TrailerType: data_703.field_1967_raw,
EmployeeEmail: data_703.field_1484_raw,
CarrierContactName: data_703.field_1485_raw,
CarrierPhone: data_703.field_1486_raw,
MainContact: data_703.field_1489_raw,
MainPhone: data_703.field_1490_raw,
MainCell: data_703.field_1491_raw,
AltContact: data_703.field_1492_raw,
AltPhone: data_703.field_1493_raw,
OrderCommentsDE: data_703.field_1494_raw,
BulkorBags: data_703.field_1503_raw,
producttypename: data_703.field_1510_raw,
ProductDesc: data_703.field_1508_raw,
SalesmanEmail: data_703.field_1500_raw,
ShipmentType: data_703.field_1342_raw,
ExpireDate: data_703.field_1509_raw,
OrderDate: data_703.field_1428_raw,
QTY: data_703.field_1047_raw,
ShippingMarks: data_703.field_1675_raw,
CurrentUserEmail: data_703.field_1668_raw,
CustomerEmail: data_703.field_1684_raw,
CarrierContactEmail: data_703.field_1683_raw,
LocationContactEmail: data_703.field_1685_raw,
CancelStatus: data_703.field_1507_raw,
Transloaded: data_703.field_1686_raw,
Revision: data_703.field_1688_raw,
PrintCounter: data_703.field_1717_raw,
COFA: data_703.field_1761_raw,
MSDS: data_703.field_1887_raw,
CustomerType: data_703.field_1431_raw,
FreightRateShow: data_703.field_1974_raw,
FSTrigger: data_703.field_1983_raw,
OceanCarrier: data_703.field_2077_raw,
FFnum: data_703.field_2079_raw,
OceanBookNum: data_703.field_2078_raw,
DistroContact: data_703.field_2244_raw,
Distributor: data_703.field_2243_raw,
},
type: 'POST',
success: function() {
alert('Successfully Sent!');
Knack.hideSpinner();
},
error: function() {
alert('There was an error creating the Delivery Coordination Form');
}
});
});
});

mongodb / javascript variable in query

i have:
var optiontable = {1: 'attack', 2: 'defence', 3: 'intelligence'};
var option = 1;
var minion = minions[thisminion]; // get correct, works.
console.log(optiontable[option]); // would output "attack"
var nameoption = optiontable[option];
var increasement = 8;
how would i do to get the minion.attack based on:
thisminion.nameoption // this wont work when it should display the result of thisminion.attack
and get the nameoption to use in:
minions.update({
_id: thisminion._id,
userid: playerid
}, {$inc: {nameoption: increasement}})
Hard to tell without looking at the rest of the code, but looks like you just need to change
thisminion.nameoption
to
thisminion[nameoption]
Since your original line is trying to access thisminion's property called 'nameoption'. Using square brackets would access the property named the same as the value of nameoption.
As for the mongo part: since you can't use a variable as left-hand value, you need to do a little bit of extra work:
var updateObj = {};
updateObj[nameoption] = increasement;
then you can do this:
minions.update({
_id: thisminion._id,
userid: playerid
}, {$inc: updateObj})
Similar questions:
How to set mongo field from variable
Using variables in MongoDB update statement

Turn Observable Array into nested JSON

I'm having a problem getting an array of information stored properly as JSON.
I made a fiddle to illustrate the problem. Enter a set of tags and take a look at the console to see the output.
More explanation:
So I have an input that takes in a comma-separated list of tags, which I then format.
function createTagArray() {
// given an input value of 'tag1, tag2, tag3'
// returns array = ['tag1', 'tag2', 'tag3']
}
I thought what I needed to do next was the following:
loop over the array and create a 'tag' object for each item which also includes an id for the tag and the id of the contact the tag is associated with.
Each object is pushed to tags, an observable array.
function single_tag(id, contactId, tagLabel) {
var self = this;
self.id = id;
self.contactId = contactId;
self.tagLabel = tagLabel;
}
function createTags() {
var array = createTagArray();
for (var i = 0; i < array.length; i++) {
self.tags().push(new single_tag(uuid.generate(), self.contactId, array[i]));
}
}
Then, I converted it into JSON
self.contactInformation = function() {
return ko.toJS({
"id": self.contactId,
"firstname": self.firstname(),
"lastname": self.lastname(),
... other fields ...
"tags": self.tags(),
})
}
But, when I inspect the console output of calling this function, tags is a collection of arrays, not a nice json object.
How do I get it formatted correctly?
I tried this suggestion, and the tag json is structured correctly, but it is stored with escaped quotes, so that seems wrong.
Thanks for all the help!
I would recommend you knockout.mapping plugin for KO, it allow map complicated JSON structure to view model, even without declarations.
From the documentation
Let’s say you have a JavaScript object that looks like this:
var data = {
name: 'Scot',
children: [
{ id : 1, name : 'Alicw' }
]
}
You can map this to a view model without any problems:
var viewModel = ko.mapping.fromJS(data);
Now, let’s say the data is updated to be without any typos:
var data = {
name: 'Scott',
children: [
{ id : 1, name : 'Alice' }
]
}
Two things have happened here: name was changed from Scot to Scott and children[0].name was changed from Alicw to the typo-free Alice. You can update viewModel based on this new data:
ko.mapping.fromJS(data, viewModel);
And name would have changed as expected. However, in the children array, the child (Alicw) would have been completely removed and a new one (Alice) added. This is not completely what you would have expected. Instead, you would have expected that only the name property of the child was updated from Alicw to Alice, not that the entire child was replaced!
...
To solve this, you can specify which key the mapping plugin should use to determine if an object is new or old. You would set it up like this:
var mapping = {
'children': {
key: function(data) {
return ko.utils.unwrapObservable(data.id);
}
}
}
var viewModel = ko.mapping.fromJS(data, mapping);
In the jsfiddle you were using Knockout 3.0 which doesn't have support for textInput. This was added in 3.2. To use version 3.2 you need to use a cdn such as this: http://cdnjs.com/libraries/knockout
There was typeo in your binding. sumbit should be submit.
There was a problem with your constructor for single_tag. id was not used so I removed it:
function single_tag(contactId, tagLabel) {
var self = this;
self.contactId = contactId;
self.tagLabel = tagLabel;
}
Currently also contactId is not set because the observable has not been set to a value.
To convert to JSON you need to use ko.toJSON instead of ko.toJS:
self.contactInformation = function() {
return ko.toJSON({
"firstname": self.firstname(),
"tags": self.tags(),
})
}
Now when the console writes out an array appears:
{
"firstname":"test",
"tags":[
{"tagLabel":"test1"},
{"tagLabel":"test2"},
{"tagLabel":"test3"}
]
}
JsFiddle
So my problem was more basic than I was realizing. I'm using JSON Server to serve up my data, and I was pulling information from two parts of the database (contacts & tags).
When I tried to update my tags, I was trying to apply them to a property that didn't exist on the contact JSON in my database. Posting the tags separately worked though.

Implementing MongoDB 2.4's full text search in a Meteor app

I'm looking into adding full text search to a Meteor app. I know MongoDB now supports this feature, but I have a few questions about the implementation:
What's the best way to enable the text search feature (textSearchEnabled=true) in a Meteor app?
Is there a way to add an index (db.collection.ensureIndex()) from within your app?
How can you run a Mongo command (i.e. db.quotes.runCommand( "text", { search: "TOMORROW" } )) from within a Meteor app?
Since my goal is to add search to Telescope, I'm searching for a "plug-and-play" implementation that requires minimal command line magic and could even work on Heroku or *.meteor.com.
The simplest way without editing any Meteor code is to use your own mongodb. Your mongodb.conf should look something like this (on Arch Linux it is found at /etc/mongodb.conf)
bind_ip = 127.0.0.1
quiet = true
dbpath = /var/lib/mongodb
logpath = /var/log/mongodb/mongod.log
logappend = true
setParameter = textSearchEnabled=true
The key line is setParameter = textSearchEnabled=true, which, as it states, enables text search.
Start mongod up
Tell meteor to use your mongod not its own by specifying the MONGO_URL environmental variable.
MONGO_URL="mongodb://localhost:27017/meteor" meteor
Now say you have collection called Dinosaurs declared say in collections/dinosaurs.js
Dinosaurs = new Meteor.Collection('dinosaurs');
To create an text index for the collection create a file server/indexes.js
Meteor.startUp(function () {
search_index_name = 'whatever_you_want_to_call_it_less_than_128_characters'
// Remove old indexes as you can only have one text index and if you add
// more fields to your index then you will need to recreate it.
Dinosaurs._dropIndex(search_index_name);
Dinosaurs._ensureIndex({
species: 'text',
favouriteFood: 'text'
}, {
name: search_index_name
});
});
Then you can expose the search through a Meteor.method, for example in the file server/lib/search_dinosaurs.js.
// Actual text search function
_searchDinosaurs = function (searchText) {
var Future = Npm.require('fibers/future');
var future = new Future();
Meteor._RemoteCollectionDriver.mongo.db.executeDbCommand({
text: 'dinosaurs',
search: searchText,
project: {
id: 1 // Only take the ids
}
}
, function(error, results) {
if (results && results.documents[0].ok === 1) {
future.ret(results.documents[0].results);
}
else {
future.ret('');
}
});
return future.wait();
};
// Helper that extracts the ids from the search results
searchDinosaurs = function (searchText) {
if (searchText && searchText !== '') {
var searchResults = _searchEnquiries(searchText);
var ids = [];
for (var i = 0; i < searchResults.length; i++) {
ids.push(searchResults[i].obj._id);
}
return ids;
}
};
Then you can publish only documents that have been searched for in 'server/publications.js'
Meteor.publish('dinosaurs', function(searchText) {
var doc = {};
var dinosaurIds = searchDinosaurs(searchText);
if (dinosaurIds) {
doc._id = {
$in: dinosaurIds
};
}
return Dinosaurs.find(doc);
});
And the client side subscription would look something like this in client/main.js
Meteor.subscribe('dinosaurs', Session.get('searchQuery'));
Props to Timo Brinkmann whose musiccrawler project was the source of most this knowledge.
To create a text index and try to add like this I hope so it will be useful if there is still problem comment
From docs.mongodb.org:
Append scalar index fields to a text index, as in the following
example which specifies an ascending index key on username:
db.collection.ensureIndex( { comments: "text",
username: 1 } )
Warning You cannot include multi-key index field or geospatial index
field.
Use the project option in the text to return only the fields in the
index, as in the following:
db.quotes.runCommand( "text", { search: "tomorrow",
project: { username: 1,
_id: 0
}
}
)
Note: By default, the _id field is included in the result set. Since the example index did not include the _id field, you must
explicitly exclude the field in the project document.

In Firebase when using push() How do I pull the unique ID

I'm attempting to add/remove entries from a Firebase database. I want to list them in a table to be added/modified/removed (front end) but I need a way to uniquely identify each entry in order to modify/remove. Firebase adds a unique identifier by default when using push(), but I didn't see anything referencing how to select this unique identifier in the API documentation. Can this even be done? Should I be using set() instead so I'm creating the unique ID?
I've put this quick example together using their tutorial:
<div id='messagesDiv'></div>
<input type='text' class="td-field" id='nameInput' placeholder='Name'>
<input type='text' class="td-field" id='messageInput' placeholder='Message'>
<input type='text' class="td-field" id='categoryInput' placeholder='Category'>
<input type='text' class="td-field" id='enabledInput' placeholder='Enabled'>
<input type='text' class="td-field" id='approvedInput' placeholder='Approved'>
<input type='Button' class="td-field" id='Submit' Value="Revove" onclick="msgRef.remove()">
<script>
var myDataRef = new Firebase('https://unique.firebase.com/');
$('.td-field').keypress(function (e) {
if (e.keyCode == 13) {
var name = $('#nameInput').val();
var text = $('#messageInput').val();
var category = $('#categoryInput').val();
var enabled = $('#enabledInput').val();
var approved = $('#approvedInput').val();
myDataRef.push({name: name, text: text, category: category, enabled: enabled, approved: approved });
$('#messageInput').val('');
}
});
myDataRef.on('child_added', function(snapshot) {
var message = snapshot.val();
displayChatMessage(message.name, message.text, message.category, message.enabled, message.approved);
});
function displayChatMessage(name, text, category, enabled, approved, ) {
$('<div/>').text(text).prepend($('<em/>').text(name+' : '+category +' : '+enabled +' : '+approved+ ' : ' )).appendTo($('#messagesDiv'));
$('#messagesDiv')[0].scrollTop = $('#messagesDiv')[0].scrollHeight;
};
</script>
Now lets assume I have three rows of data:
fred : 1 : 1 : 1 : test message 1
fred : 1 : 1 : 1 : test message 2
fred : 1 : 1 : 1 : test message 3
How do I go about uniquely identifying row 2?
in the Firebase Database they look like this:
-DatabaseName
-IuxeSuSiNy6xiahCXa0
approved: "1"
category: "1"
enabled: "1"
name: "Fred"
text: "test message 1"
-IuxeTjwWOhV0lyEP5hf
approved: "1"
category: "1"
enabled: "1"
name: "Fred"
text: "test message 2"
-IuxeUWgBMTH4Xk9QADM
approved: "1"
category: "1"
enabled: "1"
name: "Fred"
text: "test message 3"
To anybody finding this question & using Firebase 3+, the way you get auto generated object unique ids after push is by using the key property (not method) on the promise snapshot:
firebase
.ref('item')
.push({...})
.then((snap) => {
const key = snap.key
})
Read more about it in the Firebase docs.
As a side note, those that consider generating their own unique ID should think twice about it. It may have security and performance implications. If you're not sure about it, use Firebase's ID. It contains a timestamp and has some neat security features out of the box.
More about it here:
The unique key generated by push() are ordered by the current time, so the resulting list of items will be chronologically sorted. The keys are also designed to be unguessable (they contain 72 random bits of entropy).
To get the "name" of any snapshot (in this case, the ID created by push()) just call name() like this:
var name = snapshot.name();
If you want to get the name that has been auto-generated by push(), you can just call name() on the returned reference, like so:
var newRef = myDataRef.push(...);
var newID = newRef.name();
NOTE:
snapshot.name() has been deprecated. See other answers.
snapshot.name() has been deprecated. use key instead. The key property on any DataSnapshot (except for one which represents the root of a Firebase) will return the key name of the location that generated it. In your example:
myDataRef.on('child_added', function(snapshot) {
var message = snapshot.val();
var id = snapshot.key;
displayChatMessage(message.name, message.text, message.category, message.enabled, message.approved);
});
To get uniqueID after push() you must use this variant:
// Generate a reference to a new location and add some data using push()
var newPostRef = postsRef.push();
// Get the unique key generated by push()
var postId = newPostRef.key;
You generate a new Ref when you push() and using .key of this ref you can get uniqueID.
As #Rima pointed out, key() is the most straightforward way of getting the ID firebase assigned to your push().
If, however, you wish to cut-out the middle-man, Firebase released a gist with their ID generation code. It's simply a function of the current time, which is how they guarantee uniqueness, even w/o communicating w/ the server.
With that, you can use generateId(obj) and set(obj) to replicate the functionality of push()
Here's the ID function:
/**
* Fancy ID generator that creates 20-character string identifiers with the following properties:
*
* 1. They're based on timestamp so that they sort *after* any existing ids.
* 2. They contain 72-bits of random data after the timestamp so that IDs won't collide with other clients' IDs.
* 3. They sort *lexicographically* (so the timestamp is converted to characters that will sort properly).
* 4. They're monotonically increasing. Even if you generate more than one in the same timestamp, the
* latter ones will sort after the former ones. We do this by using the previous random bits
* but "incrementing" them by 1 (only in the case of a timestamp collision).
*/
generatePushID = (function() {
// Modeled after base64 web-safe chars, but ordered by ASCII.
var PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
var lastPushTime = 0;
// We generate 72-bits of randomness which get turned into 12 characters and appended to the
// timestamp to prevent collisions with other clients. We store the last characters we
// generated because in the event of a collision, we'll use those same characters except
// "incremented" by one.
var lastRandChars = [];
return function() {
var now = new Date().getTime();
var duplicateTime = (now === lastPushTime);
lastPushTime = now;
var timeStampChars = new Array(8);
for (var i = 7; i >= 0; i--) {
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
// NOTE: Can't use << here because javascript will convert to int and lose the upper bits.
now = Math.floor(now / 64);
}
if (now !== 0) throw new Error('We should have converted the entire timestamp.');
var id = timeStampChars.join('');
if (!duplicateTime) {
for (i = 0; i < 12; i++) {
lastRandChars[i] = Math.floor(Math.random() * 64);
}
} else {
// If the timestamp hasn't changed since last push, use the same random number, except incremented by 1.
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
lastRandChars[i] = 0;
}
lastRandChars[i]++;
}
for (i = 0; i < 12; i++) {
id += PUSH_CHARS.charAt(lastRandChars[i]);
}
if(id.length != 20) throw new Error('Length should be 20.');
return id;
};
})();
You can update record adding the ObjectID using a promise returned by .then() after the .push() with snapshot.key:
const ref = Firebase.database().ref(`/posts`);
ref.push({ title, categories, content, timestamp})
.then((snapshot) => {
ref.child(snapshot.key).update({"id": snapshot.key})
});
If you want to get the unique key generated by the firebase push() method while or after writing to the database without the need to make another call, here's how you do it:
var reference = firebaseDatabase.ref('your/reference').push()
var uniqueKey = reference.key
reference.set("helllooooo")
.then(() => {
console.log(uniqueKey)
// this uniqueKey will be the same key that was just add/saved to your database
// can check your local console and your database, you will see the same key in both firebase and your local console
})
.catch(err =>
console.log(err)
});
The push() method has a key property which provides the key that was just generated which you can use before, after, or while you write to the database.
Use push() to get a new reference and key to get the the unique id of the it.
var ref = FirebaseDatabase.instance.ref();
var newRef = ref.push(); // Get new key
print(newRef.key); // This is the new key i.e IqpDfbI8f7EXABCma1t
newRef.set({"Demo": "Data"}) // Will be set under the above key
How i did it like:
FirebaseDatabase mFirebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference ref = mFirebaseDatabase.getReference().child("users").child(uid);
String key = ref.push().getKey(); // this will fetch unique key in advance
ref.child(key).setValue(classObject);
Now you can retain key for further use..

Categories

Resources