Mongoose dynamic schema field - javascript

I need to develop a model using mogoose with a field that will hold my object attributes. My problem is that these attributes are totaly changable, something like:
StockItem1 : {
sku: 23492349,
class: 'computer',
subclass: 'printer',
name: 'Hp Laserjet XXX',
qty: 120,
attr: {
laser: true,
speed: 1200,
color: white
}
}
StockItem2 : {
sku: 22342349,
class: 'homeappliance',
subclass: 'refrigerator',
name: 'GE Refrigerator',
qty: 23,
attr: {
stainlessstell: true,
doors: 2,
frostfree: true
}
}
The attr attributes fields are totally different depending of what type of class/subclass it belongs to.
What type should be given to attr field in mongoose ? I need to filter those in the future, like get all itens where attr.doors == 2.
Thanks for helping.

Use a Mixed Schema Type. Here are the docs. Mixed SchemaTypes are sort of an 'anything goes' type of deal. You have flexibility when it comes to defining data but it makes your collection harder to maintain.

Related

How to safely type hierarchical metadata objects in TypeScript?

I'd like to create some constants that define a hierarchy of categories with subcategories. These will then be re-used to render different pages in an application, but by defining them as constants in one place I can quickly scaffold pages.
For example, given categories like:
const CATEGORIES = [
{
id: 'diet',
name: 'Diet',
color: 'green',
},
{
id: 'energy',
name: 'Energy',
color: 'yellow',
},
...
]
But now I'd like to have these objects be typed with TypeScript. I could use the as const syntax to infer a category type of:
{
id: 'diet' | 'energy' | ...
name: 'Diet' | 'Energy' | ...
color: 'green' | 'yellow' | ...
}
Which is great. (Although I don't really need the name/color specificity.)
But then when defining subcategories I run into a problem...
const SUBCATEGORIES = [
{
id: 'red_meat',
category: 'diet',
name: 'Red Meat',
emoji: '🥩',
},
...
]
If I use as const on the SUBCATEGORIES object then there's no type safety for the category key. I could easily misspell 'deit' and never know.
But if I use a declaration like:
const SUBCATEGORIES: Array<{
id: string
category: typeof CATEGORIES[number]['id']
...
}> = [
...
]
Then I can no longer infer the id key strings from subcategories later, because they will have a type of string instead.
Is there a way to define these sorts of metadata structures in TypeScript to have some a middle-ground between as const and defined types? How can I get the id keys to be inferred as constants while the rest is typed?
I'd be open to defining them as objects instead of arrays, but I can't seem to figure out a way that that helps.
In cases like this I usually make a helper function which verifies that a value is assignable to a type without widening it to that type. Assuming CATEGORIES looks like
const CATEGORIES = [
{
id: 'diet',
name: 'Diet',
color: 'green',
},
{
id: 'energy',
name: 'Energy',
color: 'yellow',
},
// ...
] as const;
then the helper function asSubcategories could look like this:
const asSubcategories = <S extends ReadonlyArray<{
id: string;
category: typeof CATEGORIES[number]['id'];
name: string;
emoji: string;
}>>(s: S) => s;
Here asSubcategories literally just returns its input without changing it, but it only accepts arguments of generic type S that is assignable to the type you care about. Then you call it like this:
const SUBCATEGORIES = asSubcategories([
{
id: 'red_meat',
category: 'diet',
name: 'Red Meat',
emoji: '🥩',
},
//...
] as const);
Since there's no error, you didn't misspell the category, and the type of SUBCATEGORIES is still
/* const SUBCATEGORIES: readonly [{
readonly id: "red_meat";
readonly category: "diet";
readonly name: "Red Meat";
readonly emoji: "🥩";
}] */
If you do misspell things, you should get a big error:
const BADSUBCATEGORIES = asSubcategories([
{
id: 'read_met',
category: 'deit',
name: 'Read Met',
emoji: '🍔',
},
//...
] as const); // error!!
/* Types of property 'category' are incompatible.
Type '"deit"' is not assignable to type '"diet" | "energy"'
*/
Yay! There are ways to use helper functions to narrow some properties while widening others, but the above should hopefully be enough to help you proceed. Good luck!
Playground link to code

Apollo weirdly alters the query result

I'm using react-apollos Query-Component in React Native to get data from my backend.
The result looks something like this:
[
{
id: 1,
name: 'US Election',
parties: [
{
name: 'democrats',
id: 4,
pivot: {
id: 3,
answers: [
{
id: 13,
question_id: 3,
reason: 'Lorem ipsum',
__typename: "Answer"
},
{
id: 14,
question_id: 5,
reason: 'Lorem ipsum',
__typename: "Answer"
},
],
__typename: "ElectionPartyPivot"
},
__typename: "Party"
},
],
__typename: "Election"
},
{
id: 2,
name: 'Another one',
parties: [
{
name: 'democrats',
id: 4,
pivot: {
id: 7,
answers: [
{
id: 15,
question_id: 7,
reason: 'Lorem ipsum',
__typename: "Answer"
},
{
id: 18,
question_id: 9,
reason: 'Lorem ipsum',
__typename: "Answer"
},
],
__typename: "ElectionPartyPivot"
},
__typename: "Party"
},
],
__typename: "Election"
}
]
Now, when I console.log the result, the second election "Another one" has the pivot from the first entry US Election.
I think this is because of the normalization that goes on within Apollo (Cause the ID of the parties are the same in both) but I'm unsure how to fix it, so that it does not normalize this or normalizes it correctly.
EDIT
I came up with this solution, but it looks hacky. I now get the election_id together with the party and create a different Identifier within the cache. I wonder if this is good practice?
const cache = new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
case 'Party': return `${object.election_id}:${object.id}`;
default: return defaultDataIdFromObject(object);
}
}
});
const client = new ApolloClient({
uri: config.apiUrl,
cache
});
Yes, providing a custom dataIdFromObject would be necessary in this case. You should consider using Party:${object.election_id}:${object.id} as the key in case there are other Election fields in the future that will require the same treatment.
This is, at the root, an issue with the way the schema is designed. There's an underlying assumption in GraphQL that while the nodes in your GraphQL may have relationships with one another, they are fully independent of each other as well. That is to say, within the same context, the same node should not represent different data based on the presence or absence of other nodes in the response.
Unfortunately, that's exactly how this response is structured -- we have a node that represents a Party, but its fields are different depending on its relationship to another node -- the Election.
There's two ways to remedy this sort of issue. One way would be to maintain different instances of each Party with different ids for each Election. Rather than representing a political party over the course of its life, the underlying data model behind the Party type would present a political party only in the context of one election.
The other way would be to restructure your schema to more accurately represent the relationships between the nodes. For example, a schema that supported this kind of query:
{
elections {
id
name
parties {
id
name
# omit pivot field on Party type
}
# instead because pivots are related directly to elections, add this field
pivots {
id
answers
# because we may still care what party the pivot is associated with as well
# we can add a party field to pivot to show that relationship
party {
id
name
}
}
}
}

sequelize - get AVG of included model

I have a a schema that is like product: { ... ,ratings: [ {rating: 3} ] }, and using sequalize, I would like to add on a property product.avg_rating using sequelize.
Here is my code:
sequelize.models.product.findAll({
include: [{
model: sequelize.models.rating,
as: 'ratings',
attributes: {
include: ['rating',[sequelize.fn('AVG', 'ratings.rating'), 'avg_rating']]
},
group: ['rating.rating'],//also tried ratings.rating, and just rating
}],
}).then(products=>{...})
But I keep getting this error:
In aggregated query without GROUP BY, expression #1 of SELECT list
contains nonaggregated column 'text_rewriter.languageCombination.id';
this is incompatible with sql_mode=only_full_group_by
Goal Output:
products: [
{product: 'product name',
...
ratings: [...],
avg_rating: 3.7 //THIS AVG IS WHAT I WANT
},{...}
]
any ideas what I am missing? I have seen many examples, but none of them use include like I did that I found.
sequelize.models.product.findAll({
include: [{
model: sequelize.models.rating,
as: 'ratings',
attributes: ['avg_rating'] //this is column name here
}],
}).then(products=>{...})
This will return :-
products: [
{product: 'product name',
ratings: [...],
rating : { //here all the attributes you wanted, in this case only 'avg_rating' }
},
{...}
]
But you have to define relastionship between products table and rating table before using this.
Table : Product have id, name
Table : Rating have id, rating, product_id
In above case relationship will be 'rating.belongsTo(product) OR product.hasMay(rating)'
MySQL implements detection of functional dependence. If the ONLY_FULL_GROUP_BY SQL mode is enabled (which it is by default), MySQL rejects queries for which the select list, HAVING condition, or ORDER BY list refer to non-aggregated columns that are neither named in the GROUP BY clause nor are functionally dependent on them.
The error is related to sql_mode, you need to execute the following command on your database console.
SET GLOBAL sql_mode = '';

How to filter deeply stacked data inside an object(and edit\delete it afterwards)?

I have a problem here, and I can't find out how to solve it. I have an object with many levels. There is arrays with objects inside my object, and they have their arrays with objects too. Let me show you somekind of example here:
{
sections: [
{
currency_sections: [
{
positions: [
{
id: 131,
quantity: 24
},
{
id: 133,
quantity: 1
}
],
key: value,
key: value
},
{
positions: [
{
id: 136,
quantity: 2
},
{
id: 137,
quantity: 3
}
],
key: value,
key: value
}
],
key: value,
key: value
}
],
key: value,
key: value
}
I build my data via handlebars template. Which is not really important. But on my page I can change, let's say, quantity of the position. When I do that I have only id of the position I changed and new quantity.
So basically I need to filter my object to find one object in positions arrays that matches via id key and change quantity there.
Also I can delete whole position, and in that case I need to find position object with id needed and delete whole object.
The thing I can't understand is how I can filter all my data at once. And how can I manipulate unnamed object if I will find it.
And let's say I filtered it somehow. Can I return full path to that object for later use? In example - sections[0].currency_sections[1].positions[0] ? Because if I can do that than deleting and editing should be simple enough.
At this point I don't really have the time to redo everything on something more suitable like angular or ember. Although I have underscore and jquery.
Just wanted to update my question. At the end I just created a method that builds index of my elements.
function _createItemsIndex(data) {
if (!$.isEmptyObject(data)) {
$.each(data.sections, function (sectionIndex, section) {
$.each(section.currency_sections, function (curSecIndex, curSec) {
$.each(curSec.positions, function (positionIndex, position) {
_items.push({
id: position.id,
quantity: position.quantity,
price: parseFloat(position.price) || 0,
index: [sectionIndex, curSecIndex, positionIndex]
});
});
});
});
}
}
Thank you levi for your comments

Ext.Js model store objects

How can I adopt my ext js models to hold data containing objects.
Data example:
fruit {
apples {
quantity: '10',
color: 'red
}
pears {
color:'yellow',
taste: 'very bad'
}
How would then my model look like? I only know how to put data on one level:
Ext.define('app.mode.Fruits', {
extend: 'Ext.data.Model',
fields: [
{
name: 'apples'
},
{
name: 'pears'
},
],
Where can I put the other properties? I am working in Sencha Architect.
Take a read through the Ext.data.Field API docs. You'll see a variety of ways in which to configure data on your model.
I think what you're specifically asking for is the mapping config.

Categories

Resources