Defining a Mongoose Schema on the fly - mongodb

I have a model file that gathers together all my Mongoose models. One of the models I would like to initialize with a variable number of fields. Currently I'm defining more fields than I think I am going to require:
TallySchema = new mongoose.Schema
0: Number
1: Number
...
20: Number
Obviously this is not ideal. I see Mongoose will let you specify options outside the Schema definition but can't see how to add new fields (or paths, I guess, in Mongoose).

Based on the mongoose plugin documentation it looks like you can just do:
schema.add({ field: Number })

This would need to be verified, but looking at the source it should be possible:
In the Schema constructor, it simply passes the definition object to this.add() (source).
The actual paths then get created within Schema.prototype.add (source).
So, seems like all you would need to do is something like:
// not sure what this looks like in CoffeeScript
TallySchema.add({ /* new property definition here */ });

I found this in the Mongoose documentation page:
var ToySchema = new Schema;
ToySchema.add({ name: 'string', color: 'string', price: 'number' });

You can use the 'mixed' type to encapsulate your values. It wouldn't be possible to have them be at the top level though but it works great otherwise.
new mongoose.Schema({
average: Number,
countPerRating: mongoose.Schema.Types.Mixed,
});
This is an excerpt from a mapreduce schema. I use the mixed type to store the number of times someone gives a certain rating, so we can say things like "10 1 star ratings, 45 4 star ratings", etc.
mixed type worked great for that.

Related

Why doesn't enabling timestamps work with Mongoose's set option?

I'm diligently trying not to have a conniption while dealing with this issue. I'm simply trying to add timestamps to new and existing documents by using set() per Mongoose's documentation here, which looks like this:
new Schema({..}, options);
// or
const schema = new Schema({..});
schema.set(option, value); // <----- THIS PART
My code:
Object.entries(sortedSchemas.lore).forEach(([name, schema]) => {
schema.set("timestamps", true).add(contentLock(schema));
console.log(name + ":", { schema }); // FOR TESTING
});
Using add() works fine (where I'm using contentLock(schema)) as far as adding fields to all the schema in sortedSchema.lore. And when I console log the modified schema it shows that the timestamps option is true. And yet, when I create a new document or update one, I don't see createdAt or updatedAt. Why is that?
Also, if I manually set timestamps: true directly in the options parameter of each schema it seems to work almost as expected (createdAt seems to disappear after updating a document, but that's a separate issue).
Am I misunderstanding set()?
Any help with this is much appreciated.

How to create a odata model for an entity during new instantiation

In CRUD when new button is clicked, how to create a JSON model, based of a entity type.
This empty entity based JSON model can be derived from the metadata entity type and assigned to a view.
I tried to use Model.onOnMetaDataLoaded to the get the entity type and use that the default JSON Model with initial values. But I couldn't get the entity type from the metadata though I can see the entity type in the object.
var oModel = new sap.ui.model.odata.v2.ODataModel(<ServiceURL>);
oModel.attachMetadataLoaded(null, function(){
var oMetadata = oModel.getServiceMetadata();
console.log(oMetadata);
var metaModel = new sap.ui.model.odata.ODataMetaModel(oMetadata);
metaModel.getEntityType('XXX') // DOES NOT GIVE VALUE
},null);
metaModel.getEntityType('XXX') // DOES NOT GIVE VALUE
And do we need to base our JSON model based off of the entity type when creating new. I am assuming this would be of help in validating data type instead of doing manually.
Since you asked, no I don't do it this way... I use the oData model's capabilities to track changes. Any element that supports a binding context can work like this, such as a dialog, a view or a simple form.
In its simplest way, it would look like:
myView.setBindingContext(this.getModel().createEntry("/MyEntitySet"));
If you want more control, like specify the success- and error handler (and you probably will), it's something like:
myView.setBindingContext(this.getModel().createEntry("/MyEntitySet", {
changeSetId: 'myChanges',
properties: {
myField: 'DefaultValue',
myDate: new Date()
},
success: _ => myView.setBusy(false),
error: _ => myView.setBusy(false)
}));
Regarding your question if you need to specify the fields yourself: No you don't, you simply bind any of the values from the entity straight to a field. If the user fills them out, they will appear in the object used in the creation. You can retrieve the object at any time using
const filledOutEntityFields = myView.getBindingContext().getObject();
In your views you can use a relative binding like you always would:
<Input value="{myField}" />
Or to be more specific with your types and type checking:
<Input value="{
path: 'myField',
type: 'sap.ui.model.type.String',
constraints: {
minLength: 1,
maxLength: 20
}
}" />
And later on you trigger the create. If you used a changeSet, you should pass its name in there.
this.getModel().submitChanges('myChanges');
One of the advantages is, besides not having to use a JSON model, if you retrieve this data and bind the element with existing results, the code is the same. You can use submitChanges on that, too. Except, it sends an update and not a create.
More info: https://ui5.sap.com/sdk#/api/sap.ui.model.odata.v2.ODataModel/methods/createEntry

Add rows in smartsheets using python

How do I take a list of values, iterate through it to create the needed objects then pass that "list" of objects to the API to create multiple rows?
I have been successful in adding a new row with a value using the API example. In that example, two objects are created.
row_a = ss_client.models.Row()
row_b = ss_client.models.Row()
These two objects are passed in the add row function. (Forgive me if I use the wrong terms. Still new to this)
response = ss_client.Sheets.add_rows(
2331373580117892, # sheet_id
[row_a, row_b])
I have not been successful in passing an unknown amount of objects with something like this.
newRowsToCreate = []
for row in new_rows:
rowObject = ss.models.Row()
rowObject.cells.append({
'column_id': PM_columns['Row ID Master'],
'value': row
})
newRowsToCreate.append(rowObject)
# Add rows to sheet
response = ss.Sheets.add_rows(
OH_MkrSheetId, # sheet_id
newRowsToCreate)
This returns this error:
{"code": 1062, "errorCode": 1062, "message": "Invalid row location: You must
use at least 1 location specifier.",
Thank you for any help.
From the error message, it looks like you're missing the location specification for the new rows.
Each row object that you create needs to have a location value set. For example, if you want your new rows to be added to the bottom of your sheet, then you would add this attribute to your rowObject.
rowObject.toBottom=True
You can read about this location specific attribute and how it relates to the Python SDK here.
To be 100% precise here I had to set the attribute differently to make it work:
rowObject.to_bottom = True
I've found the name of the property below:
https://smartsheet-platform.github.io/smartsheet-python-sdk/smartsheet.models.html#module-smartsheet.models.row
To be 100% precise here I had to set the attribute differently to make it work:
Yep, the documentation isn't super clear about this other than in the examples, but the API uses camelCase in Javascript, but the same terms are always in snake_case in the Python API (which is, after all, the Pythonic way to do it!)

Autocomplete function not working in meteor

I have an autocomplete textbox function which I am using in meteor.
It works fine for the following hardcoded data like :
$(document).ready(function() {
$("#demo-input-facebook-theme").tokenInput(
[{id: 7, name: "Ruby"},{id: 11, name: "Python"},{id: 13, name: "JavaScript"}],
{theme: "facebook"}
);
});
Now , I had fetched data from database MongoDB, when I pass this data as parameter to the autocomplete function it does not work... I have also used the method JSON.stringify().
The returned data looks like :
[{"_id":"ab170916-a44b-49f9-85ef-a34c90fb815d","Namelist_name":"Badminton"},
{"_id":"f768e4ba-b628-4d3f-8da6-0bad31346dcc","Namelist_name":"Biking"},
{"_id":"0bee086b-1785-40c9-9c5d-a39331c875e1","Namelist_name":"Chess"},
{"_id":"4eae1e54-ec60-4578-8052-0bf1bccf13b1","Namelist_name":"Golf"},
{"_id":"a0d2b89e-a2d6-4b30-8e38-779c5a886d49","Namelist_name":"Hiking"},
{"_id":"f3a05456-38d4-40f2-86b1-eddea061fdf0","Namelist_name":"Tennis"},
{"_id":"3669b9a2-3f87-4579-8064-82d627196fcb","Namelist_name":"Walking"},
{"_id":"6ac6497e-82b2-40fe-8b24-152e9f42750d","Namelist_name":"Wine Tasting"},
{"_id":"15a7ca87-aef7-43ab-945b-168647bb59aa","Namelist_name":"Yoga"},
{"_id":"bc40d166-64ef-4e61-85cd-60064dc037cd","Namelist_name":"Zumba"}]
Just change the Namelist_name with only name keyword. Since jquery tokeninput uses name as key
as mentioned in your hardcoded data. And if we download jquery tokeninput from http://loopj.com/jquery-tokeninput/. we come to know that the keyword is 'name'.
Hope this helps....
If you compare your returned JSON data with the test data that works there is one essential difference: you are missing the id field in your MongoDB JSON results and instead providing _id. The id field is currently a hardcoded default for the jQuery tokenInput plugin you are using for autocomplete.
Several folks have submitted patches to allow setting a different key using the tokenValue parameter.
Example (untested) patch: tokenValue cannot be changed.
If you're autocompleting multiple items with free text, you may want to check out this package I created:
https://github.com/mizzao/meteor-autocomplete

Creating short, unique object id's in MongoDB

I'm making an app similar to instagram using Rails/Mongoid. I want a unique ID that I can use in a url like http://instagr.am/p/DJmU8/
What's the easiest way to do that? Can I derive such an ID from the default BSON ObjectID Mongo creates?
You may try to use first 4 bytes of ObjectID (they will represent timestamp).
But, to be 100% safe, it's better to produce really unique short id, by implementing a counter. You can use separate collection to maintain current value of your counter.
More details on mongo's ObjectID structure can be found here: http://www.mongodb.org/display/DOCS/Object+IDs
As an alternative you can convert convert hex string id representation to a representation based on 36 symbols (26 latin letters + 10 digits). It will obviously be shorter.
It seems, that there is a ruby library, that can do such conversions http://rubyworks.github.com/radix/
Why not use dylang/shortid?
Install using npm npmjs.com/package/shortid:
npm i shortid
Then require:
const shortid = require('shortid');
In mongoose schema:
new Schema {
_id: {
type: String,
default: shortid.generate
}
}
or just insert directly:
users.insert({
_id: shortid.generate()
name: ...
email: ...
});
You could try Mongoid::Token
https://github.com/thetron/mongoid_token
From the docs:
This library is a quick and simple way to generate unique, random
tokens for your mongoid documents, in the cases where you can't, or
don't want to use slugs, or the default MongoDB IDs.
Mongoid::Token can help turn this:
http://myawesomewebapp.com/video/4dcfbb3c6a4f1d4c4a000012/edit
Into something more like this:
http://myawesomewebapp.com/video/83xQ3r/edit
#aav was mention that you can use first 4 bytes, but this value are in seconds and you can get even 10.000 or more insert per seconds. Other thing objectID is Uniq and you need check "when" you get error from duplicate value "Write Concerns"?
new Date().getTime() - is in milliseconds => 1557702577900 why not use last 4 bytes ? Timestamp in base62 is rqiJgPq
This code look interesting:
https://github.com/treygriffith/short-mongo-id/blob/master/lib/objectIdToShortId.js
Check also ObjectID timestamp parser:
https://steveridout.github.io/mongo-object-time/
Or you can execute ObjectId().toString() and base of this string create new by hashids [nodejs,php, andmanymore]
Maybe best options it to use 4-5 bytes from js timestamp and INC from
bson then hash this value by hids
var id = ObjectID('61e33b8467a45920f80eba52').toString();
console.log("id:", id);
console.log("timestamp:", parseInt(id.substring(0, 8),16).toString());
console.log("machineID:", parseInt(id.substring(8, 14),16) );
console.log("processID:", parseInt(id.substring(14, 18),16) );
console.log("counter:", parseInt(id.slice(-6),16) );
var ObjTimestamp = parseInt(ObjectID2.substring(0, 8),16).toString().slice(-5);
var Counter = parseInt(ObjectID2.slice(-6),16);
//https://github.com/base62/base62.js/
console.log('Final:',base62.encode(parseInt(Counter) + parseInt(ObjTimestamp) ));
Output:
vI7o
15V9L
5t4l
You can get collision if: more process are running, then consider add PID to unique and when you run multiple instance on different computer
Try gem https://github.com/jffjs/mongoid_auto_inc
The Hashids library is meant for generating IDs like this. Check it out here ☞ https://github.com/peterhellberg/hashids.rb