Hi I created a SimpleSchema for a Mongo collection which has a variable number of sub-documents called measurables. Unfortunately it's been a while since I've done this and I can't remember how to insert into this type of schema! Can someone help me out?
The schema is as follows:
const ExerciseTemplates = new Mongo.Collection('ExerciseTemplates');
const ExerciseTemplateSchema = new SimpleSchema({
name: {
type: String,
label: 'name',
},
description: {
type: String,
label: 'description',
},
createdAt: {
type: Date,
label: 'date',
},
measurables: {
type: Array,
minCount: 1,
},
'measurables.$': Object,
'measurables.$.name': String,
'measurables.$.unit': String,
});
ExerciseTemplates.attachSchema(ExerciseTemplateSchema);
The method is:
Meteor.methods({
addNewExerciseTemplate(name, description, measurables) {
ExerciseTemplates.insert({
name,
description,
createdAt: new Date(),
measurables,
});
},
});
The data sent by my form for measurables is an array of objects.
The SimpleSchema docs seem to be out of date. If I use the example they show with measurables: type: [Object] for an array of objects. I get an error that the the type can't be an array and I should set it to Array.
Any suggestions would be awesome!!
Many thanks in advance!
edit:
The measurable variable contains the following data:
[{name: weight, unit: kg}]
With the schema above I get no error at all, it is silent as if it was successful, but when I check the db via CLI I have no collections. Am I doing something really stupid? When I create a new meteor app, it creates a Mongo db for me I assume - I'm not forgetting to actually create a db or something dumb?
Turns out I was stupid. The schema I posted was correct and works exactly as intended. The problem was that I defined my schema and method in a file in my imports directory, outside both client and server directories. This methods file was imported into the file with the form that calls the method, and therefore available on the client, but not imported into the server.
I guess that the method was being called on the client as a stub so I saw the console.log firing, but the method was not being called on the server therefore not hitting the db.
Good lesson for me regarding the new recommended file structure. Always import server side code in server/main.js!!! :D
Thanks for your help, thought I was going to go mad!
Related
It seems, that Keystone.js does't provides a solution of "N+1" problem.
Maybe, they are some plugins for that?
You can try to check https://www.keystonejs.com/guides/cache-hints/
This is a typical pattern for solving this problem.
You can try:
const app = new GraphQLApp({
apollo: {
cacheControl: {
defaultMaxAge: 3600,
},
},
});
to cache all resolver results.
What version are you on? The current version of KeystoneJS (Keystone Next) is build on top of Prisma which should be building fairly performant DB queries. If there's a specific GraphQL query you're preforming that's resulting in a suboptimal SQL it may represent a bug in either Keystone or Prisma code, in which case it'd be great if you could isolate the problem and log an issue.
If you're adding hooks, access control or virtual fields that query the DB it is possible to encounter the N+1 problem as those functions can be called for each item returned in a query. For example, this code, taken from the virtual-fields example causes N+1 queries if used as written:
Post: list({
fields: {
// [... various fields ...]
author: relationship({ ref: 'Author.posts', many: false }),
// A virtual field which uses `item` and `context` to query data.
authorName: virtual({
field: schema.field({
type: schema.String,
async resolve(item, args, context) {
const { author } = await context.lists.Post.findOne({
where: { id: item.id },
query: 'author { name }',
});
return author && author.name;
},
}),
}),
},
}),
Here, the resolver function for the authorName field will be called for each item loaded (assuming the field is queried). In these cases I'd suggest using something like the GraphQL dataloader (or similar) over the top of the Keystone CRUD API. If used correctly, dataloader can combine multiple queries and resolve the N+1 behaviour.
I'm experiencing little trouble getting Orionjs working within Angular-Meteor especially with the collections.
I had my old mongodb declarations, for instance :
Gallery = new Mongo.Collection('gallery') and so one.
As the documentation told , I wrote
Gallery = new orion.collection('gallery') but what I get is
Error: Match error: Expected object, got undefined
at exports.check (packages/check/match.js:34:1)
at new orion.collection (packages/orionjs:collections/new.js:8:3)
at meteorInstall.shared.collections.js (shared/collections.js:1:11)
So I tried to start a project from scratch with this framework.
Fact is, it doesn't work neither with Iron Router nor Flow Router.
Can anyone hit me with any hint about it?
Thank you guys.
Ideally OrionJS expect a schema detail like the label for singular and plural names, navigation detail, table layout for displaying data and so on.Here's a typical company collection shown below:
Company = new orion.collection('company', {
singularName: orion.helpers.getTranslation('company.singularName'),
pluralName: orion.helpers.getTranslation('company.pluralName'),
title: orion.helpers.getTranslation('company.title'),
link: {
title: orion.helpers.getTranslation('company.title'),
parent: 'collections-abc'
},
tabular: {
columns: [
{ data: '_id', title: orion.helpers.getTranslation('company.schema.id') },
{ data: 'name', title: orion.helpers.getTranslation('company.schema.name') }
]
}
});
You can also pass a null JSON if you do not wish to show page directly. Usually it expects a JSON like the one shown above.
I need to use mongoose with dbref but I don't know which design is better for me.
First design:
var user = mongoose.Schema({
name: 'string'
});
var eventSchema = mongoose.Schema({
title: 'string',
propietary_id: 'String',
comments : [{
text: 'string',
user: { type : mongoose.Schema.Types.ObjectId, ref : 'users' },
createdAt: {type: Date, default: Date.now }
}]
});
Second design:
var user = mongoose.Schema({
name: 'string'
});
var eventSchema = mongoose.Schema({
title: 'string',
propietary_id: 'String'
});
var commentSchema = mongoose.Schema({
text: 'string',
event_id : { type : mongoose.Schema.Types.ObjectId, ref : 'events' },
user_id : { type : mongoose.Schema.Types.ObjectId, ref : 'users' },
createdAt: {type: Date, default: Date.now }
});
How it works? On my website there is an event list and if you want to see comments you have to click every event, then angularjs gets all comments (text, user name and user photo) of the selected events.
There are pros and cons with both solutions and the best one for you depends on your usage. Remember that you can produce exactly the same API independent of your design it only comes down to how quickly and easily you can maintain the backend. First some thoughts on both designs:
First design:
First a comment, I wouldn't save comments as a nested document but as an array instead. Otherwise you are limited to one comment per event. Use this schema instead:
comments: [
{
text: { type: String },
user: { type: mongoose.Schema.Types.ObjectId, ref : 'users' },
createdAt: { type: Date, default: Date.now },
}
]
Pros:
No need for multiple collections
You will have the comments returned with the event in the get request which will mean less requests to your backend
No need to map comments to events
Cons:
You will have the comments returned to you with the event, even if you don't want them displayed
If there are a lot of comments to an event, the request response will be pretty large
If you want to remove or edit comments in your array it will be trickier (not impossible though)
Second design:
Pros:
You will have the events and comments separated which means leaner objects
You can much easier extract one comment for edit or delete
You can more easily get events without comments and then request comments at another point
Cons:
You will need to always map comments to events which will mean more code
Two collections will mean two requests usually
Maintenance of another collection
Verdict:
All the pros and cons are judged by how much extra code you need to write. Of course you can always have comments returned with your events in the second design as well but then you will have the extract the comments first and returned them with the event object which will mean extra code to maintain.
I think the second design would work better for you. I'm judging this by your comment that you will only need comments if the user click on an event. I would then be requesting the events first and do another request for comments as soon as the user click on the event, however, having the comments always be returned with the events should make the ui more snappy as the comments will already have been loaded.
It all depends in the end what is more important for you to do with the data. Please let me know if you have any questions on any of the points.
Let's say I have the following document schema in a collection called 'users':
{
name: 'John',
items: [ {}, {}, {}, ... ]
}
The 'items' array contains objects in the following format:
{
item_id: "1234",
name: "some item"
}
Each user can have multiple items embedded in the 'items' array.
Now, I want to be able to fetch an item by an item_id for a given user.
For example, I want to get the item with id "1234" that belong to the user with name "John".
Can I do this with mongoDB? I'd like to utilize its powerful array indexing, but I'm not sure if you can run queries on embedded arrays and return objects from the array instead of the document that contains it.
I know I can fetch users that have a certain item using {users.items.item_id: "1234"}. But I want to fetch the actual item from the array, not the user.
Alternatively, is there maybe a better way to organize this data so that I can easily get what I want? I'm still fairly new to mongodb.
Thanks for any help or advice you can provide.
The question is old, but the response has changed since the time. With MongoDB >= 2.2, you can do :
db.users.find( { name: "John"}, { items: { $elemMatch: { item_id: "1234" } } })
You will have :
{
name: "John",
items:
[
{
item_id: "1234",
name: "some item"
}
]
}
See Documentation of $elemMatch
There are a couple of things to note about this:
1) I find that the hardest thing for folks learning MongoDB is UN-learning the relational thinking that they're used to. Your data model looks to be the right one.
2) Normally, what you do with MongoDB is return the entire document into the client program, and then search for the portion of the document that you want on the client side using your client programming language.
In your example, you'd fetch the entire 'user' document and then iterate through the 'items[]' array on the client side.
3) If you want to return just the 'items[]' array, you can do so by using the 'Field Selection' syntax. See http://www.mongodb.org/display/DOCS/Querying#Querying-FieldSelection for details. Unfortunately, it will return the entire 'items[]' array, and not just one element of the array.
4) There is an existing Jira ticket to add this functionality: it is https://jira.mongodb.org/browse/SERVER-828 SERVER-828. It looks like it's been added to the latest 2.1 (development) branch: that means it will be available for production use when release 2.2 ships.
If this is an embedded array, then you can't retrieve its elements directly. The retrieved document will have form of a user (root document), although not all fields may be filled (depending on your query).
If you want to retrieve just that element, then you have to store it as a separate document in a separate collection. It will have one additional field, user_id (can be part of _id). Then it's trivial to do what you want.
A sample document might look like this:
{
_id: {user_id: ObjectId, item_id: "1234"},
name: "some item"
}
Note that this structure ensures uniqueness of item_id per user (I'm not sure you want this or not).
I'm receiving a "Ext.data.DataReader: #realize was called with invalid remote-data" error when I create a new record via a POST request. Although similar to the discussion at this SO conversation, my situation is slightly different:
My server returns the pk of the new record and additional information that is to be associated with the new record in the grid. My server returns the following:
{'success':true,'message':'Created Quote','data': [{'id':'610'}, {'quoteNumber':'1'}]}
Where id is the PK for the record in the mysql database. quoteNumber is a db generated value that needs to be added to the created record.
Other relevant bits:
var quoteRecord = Ext.data.Record.create([{name:'id', type:'int'},{name:'quoteNumber', type:'int'},{name:'slideID'}, {name:'speaker'},{name:'quote'}, {name:'metadataID'}, {name:'priorityID'}]);
var quoteWriter = new Ext.data.JsonWriter({ writeAllFields:false, encode:true });
var quoteReader = new Ext.data.JsonReader({id:'id', root:'data',totalProperty: 'totalitems', successProperty: 'success',messageProperty: 'message',idProperty:'id'}, quoteRecord);
I'm stumped. Anyone??
thanks
tom
[Responding with an answer instead of a comment for code formatting...]
Some indented formatting will make the difference clear. This (correct) form returns a single object with two properties:
{
'success':true,
'message':'Created Quote',
'data': [{
'id':'610',
'quoteNumber':'1'
}]
}
Your original format returned two separate objects with mismatched properties that cannot be resolved into columns:
{
'success':true,
'message':'Created Quote',
'data': [{
'id':'610'
},{
'quoteNumber':'1'
}]
}
Turns out that the response from the server should look like this:
{'success':true,'message':'Created Quote','data': [{'id':'610','quoteNumber':'1'}]}
A subtle difference, not one that I'm certain I understand.