Cannot save properties of may-to-many relationship model(mongoose) - javascript

I`m creating a bookstore and I have 3 models: Book, Author and Genre.
Books stores an array of authors ids and the array of genres ids. Author stores the array of books ids. Genre too has the array of books ids.
BookSchema = new mongoose.Schema({
title: String,
authors: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Author"
}
],
image: String,
description: String,
price: Number,
genres: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Genre"
}
],
});
AuthorSchema = new mongoose.Schema({
name: String,
books: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Book"
}
],
});
GenreSchema = new mongoose.Schema({
name: String,
books: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Book"
}
],
});
Then I have a data array that stores information about the book we need to create like this
{
title: "The Monster at the End of This Book",
authors: ["Jon Stone"],
img: "https://images-na.jpg",
price: "0.35",
description: "Carve out family time for ...",
genres: ["Children's Humour (Books)"]
},
I`m trying to add authors, genres and books. After that I want to associate authors with books
Book.deleteMany({}, () => {
insertBooks().then(insertGenres).then(insertAuthors).then(connectBooksToAuthors);
}).then(function () {
connectBooksToGenres();
})
async function insertAuthors() {
let authorsArr = [];
data.forEach(dataPiece => {
dataPiece.authors.forEach(author => {
if (authorsArr.indexOf(author) === -1) {
authorsArr.push(author)
}
})
})
authorsArr.forEach(author => {
Author.findOne({name: author}, (err, a) => {
if (!a) {
Author.create({name: author});
}
})
})
}
async function insertGenres() {
let genresArr = [];
data.forEach(dataPiece => {
dataPiece.genres.forEach(genre => {
if (genresArr.indexOf(genre) === -1) {
genresArr.push(genre);
}
})
})
genresArr.forEach(genre => {
Genre.findOne({name: genre}, (err, g) => {
if (!g) {
Genre.create({name: genre});
}
})
})
}
async function insertBooks() {
data.forEach((dataPiece) => {
let obj = {
"title": `${dataPiece.title}`,
'description': `${dataPiece.description}`,
"price": `${dataPiece.price}`,
};
Book.create(obj);
})
}
async function connectBooksToAuthors() {
data.forEach(dataPiece => {
Book.findOne({"title": `${dataPiece.title}`}, (err, book) => {
let authorsArr = [];
dataPiece.authors.forEach(authorsName => {
Author.findOne({name: authorsName}, (err, author) => {
author.books.push(book);
author.save();
authorsArr.push(author);
if (authorsArr.length === dataPiece.authors.length) {
book.authors = [...authorsArr];
book.save();
}
});
});
})
})
}
async function connectBooksToGenres() {
data.forEach(dataPiece => {
Book.findOne({"title": `${dataPiece.title}`}, (err, book) => {
let genresArr = [];
dataPiece.genres.forEach(genreName => {
Genre.findOne({name: genreName}, (err, genre) => {
genre.books.push(book);
genre.save();
genresArr.push(genre);
if (genresArr.length === dataPiece.genres.length) {
book.genres = [...genresArr];
book.save();
}
});
});
})
})
}
When I run the code I get this exception:
(node:29028) UnhandledPromiseRejectionWarning: VersionError: No matching document found for id "5fa1dbbb969d727164f1f59e" version 0 modifiedPaths "genres"
at generateVersionError (C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:421:10)
at model.Model.save (C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:478:28)
at C:\Users\sasha\Desktop\Bookstore\seed.js:234:34
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:4844:16
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:4844:16
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:4867:21
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\query.js:4420:11
at C:\Users\sasha\Desktop\Bookstore\node_modules\kareem\index.js:135:16
at processTicksAndRejections (internal/process/task_queues.js:79:11)
at runNextTicks (internal/process/task_queues.js:66:3)
at processImmediate (internal/timers.js:434:9)
In the database books arrays with authors are filed. Ganres arrays are not The problem might be in book.save() calls from connectBooksToAuthors and connectBooksToGenres but I dont really know how to fix it

Related

How to create Gatsby Image data for nested nodes?

I am trying to use createRemoteFileNode to create optimised images for an array of nodes that exist on a Product.
I have a Product that has items and on each item, it has a featuredImg. I can create a featuredImg for a Product but as soon as I try to create it for the child nodes (items) then it is not queryable.
I am creating my nodes as such:
const products = [
{
id: "product_1",
imageUrl: "https://images.unsplash.com/photo-1665081661649-8656335a6cbb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1738&q=80",
items: [
{
id: 'item_1',
imageUrl: "https://images.unsplash.com/photo-1666120565124-7e763880444a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80"
}
]
}
]
const sourceNodes = async ({ actions, createNodeId, createContentDigest }, options) => {
products.forEach((testNode) => {
const node = {
...testNode,
id: createNodeId(`Product-${testNode.id}`),
}
actions.createNode({
...node,
internal: {
type: 'Product',
contentDigest: createContentDigest(node),
},
});
testNode.items.forEach(item => {
const itemNode = {
...item,
id: createNodeId(`Item-${item.id}`),
}
actions.createNode({
...itemNode,
parent: node.id,
internal: {
type: 'Item',
contentDigest: createContentDigest(itemNode),
},
});
})
})
};
module.exports = sourceNodes;
Then on the node creation, I am running the onCreateNode function which should create the remote file node for each item featuredImg.
const { createRemoteFileNode } = require(`gatsby-source-filesystem`);
const onCreateNode = async ({ node, cache, store, getCache, actions: { createNode, createNodeField }, createNodeId }) => {
if( node.internal.type === 'Item') {
const fileNode = await createRemoteFileNode({
url: node.imageUrl,
parentNodeId: node.parent,
createNode,
createNodeId,
getCache,
})
if (fileNode) {
createNodeField({ node, name: "localFile", value: fileNode.id })
}
}
if( node.internal.type === 'Product') {
const fileNode = await createRemoteFileNode({
url: node.imageUrl,
parentNodeId: node.id,
createNode,
createNodeId,
getCache,
})
if (fileNode) {
createNodeField({ node, name: "localFile", value: fileNode.id })
}
}
};
module.exports = onCreateNode
I have defined my types here:
module.exports = ({ actions }) => {
const { createTypes } = actions;
const typeDefs = `
type Product implements Node {
id: String!
imageUrl: String!
featuredImg: File #link(from: "fields.localFile")
items: [Item]
}
type Item implements Node {
id: String!
imageUrl: String!
featuredImg: File #link(from: "fields.localFile")
}
`;
createTypes(typeDefs);
};
For some reason, when I query Products.items[i].featuredImg it always returns null. However, I can see the node is generated because I can query item.featuredImg and it returns the gatsbyImageData.
I have created a simple example here and included a read me on how to replicate it: https://github.com/stretch0/gatsby-sandbox
I have also noticed that this post is a similar issue of not being able to create remote file nodes within a loop but because they have a different file structure, I can't figure out how their solution to use createSchemaCustomization or createResolvers would apply to my setup.

i can't populate mongoose deep subdocument

Below is the code that simplified the model and schema I'm having a hard time with
const guildSchema = new Schema<Guild>({
sheets: [sheetSchema],
crews: [crewSchema],
});
const GuildModel= getModel('Guild', guildSchema)
const sheetSchema = new Schema<Sheet>({
deales: [dealSchema]
})
const SheetModel = getModel('Guild.sheets', sheetSchema)
const dealSchema = new Schema<Deal>({
crew: [{ type: Schema.Types.ObjectId, refPath: 'Guild.crews' }],
damage: { type: Number, required: true },
})
const DealModel = getModel('Guild.sheets.deales', dealSchema)
const crewSchema = new Schema<Crew>({
name: { type: String, required: true },
})
const CrewModel= getModel('Guild.crews', crewSchema)
and this is Mocha-chai testcode what always throw exception
it("populated guild.sheets.deales.boss must have name",async () => {
const guild = await GuildModel.findOne({})
await guild.populate({
path: 'sheets.deales.crew'
}).execPopulate()
expect(guild.sheets[0].deales[0].crew).to.has.property("name") // expected [] to have property 'name'
})
None of the answers on stackoverflow solved my problem. I wasted 5 hours on just a few lines of this code. please help me
You checked this? https://github.com/Automattic/mongoose/issues/1377#issuecomment-15911192
This person changed nested code
var opts = {
path: 'author.phone',
select: 'name'
};
BlogPost.populate(docs, opts, function (err, docs) {
assert.ifError(err);
docs.forEach(function (doc) {
console.log(doc);
});
callback(null);
from this
var authors = docs.map(function(doc) {
return doc.author;
});
User.populate(authors, {
path: 'phone',
select: 'name'
}, callback);
to this.
author(User)is in BlogPost. BlogPost Schema has just User ObjectId, so can't understand author.phone
I might have already checked it, but I'm uploading it just in case.

Check if userid matches one in the array

I'm attempting to check if a user's ID is in this array and if they are, also get the "text" from it.
Array:
const staff = [
{
user: '245569534218469376',
text: 'dev'
},
{
user: '294597887919128576',
text: 'loner'
}
];
I've tried if (staff.user.includes(msg.member.id)) (Which I didn't think was going to work, and didn't.)
const findUser = (users, id) => users.find(user => user.id === id)
const usersExample = [
{
id: '123456765',
text: 'sdfsdfsdsd'
},
{
id: '654345676',
text: 'fdgdgdg'
}
]
//////////////////
const user = findUser(usersExample, '123456765')
console.log(user && user.text)
The some method on an array is used to tell if an item meets a condition, it is similar to the find method but the find method returns the item where the some method return true or false.
const staff = [
{
user: '245569534218469376',
text: 'dev'
},
{
user: '294597887919128576',
text: 'loner'
}
];
const isStaff = (staff, id) => staff.some(s => s.user === id);
console.log(isStaff(staff, '123'));
console.log(isStaff(staff, '245569534218469376'));
You may try something like this:
const staff = [
{
user: '245569534218469376',
text: 'dev'
},
{
user: '294597887919128576',
text: 'loner'
}
];
let item = staff.find(item => item.user == '294597887919128576'); // msg.member.id
if (item) {
console.log(item.text);
}
One another way to do that is:
const inArray = (array, id) => array.filter(item => item.user === id).length >= 1;
const users = [
{
user: '245569534218469356',
text: 'foo'
}, {
user: '245564734218469376',
text: 'bar'
}, {
user: '246869534218469376',
text: 'baz'
}
];
console.log(inArray(users, '246869534218469376')); // true
console.log(inArray(users, '222479534218469376')); // false

Not able to understand error in promise chain in ES6

I have a series of asynchronous operations in my Backend code . I have doing backend coding in node js .
My code is something like this
db.users
.findAll({
where: condition,
duplicating: false,
attributes: userAttributes,
include: inclusion,
group: ['users.id', 'organizationEntries.id'],
order: [['organizationEntries', 'createdAt', 'DESC']]
})
.then(users => {
let result = users.map(c => {
let output = {
user_id: c.id,
name: c.name,
mobile: c.mobile,
mobile_alternate: c.mobileAlternate,
email: c.email,
gender: c.gender,
dob: c.dob,
image: Utils.getImageUrl(c.image),
entry_id: c.organizationEntries[0].id,
doj: c.organizationEntries[0].fromDate,
status: c.organizationEntries[0].status
};
if (isPlayer && c.dataValues.amount) {
output.due_amount = c.dataValues.amount.toFixed(2);
}
let arenaPromise;
if (!isPlayer) {
output.address_text = addressController.prepareAddressText(c.address);
output.address = addressController.prepareAddressJson(c.address);
if (c.organizationEntries[0].arenaIds)
db.arenas
.findAll({
attributes: ['name'],
where: {
id: {
[Op.in]: c.organizationEntries[0].arenaIds
},
organizationId: req.ORG_ID
}
})
.then(arenas => {
console.log(arenas);
output.arenas = arenas.map(a => {
let arena = {};
arenas.forEach(element => {
arena.name = element.name;
// console.log(arena.name);
});
console.log(arena);
return arena;
});
console.log(output);
// res.send(output);
});
}
return output;
});
res.send(result);
All the things are working fine but in the last then block where I am trying to add arenas in the output object .
.then(arenas => {
console.log(arenas);
output.arenas = arenas[0].name;
output.arenas = arenas.map(a => {
let arena = {};
arenas.forEach(element => {
arena.name = element.name;
// console.log(arena.name);
});
console.log(arena);
return arena;
});
Actually what is happening I am able to fetch arenas data from database
but after fetching it is not assigning the arenas in output object .
I am learning promise chain so anyone can please help me or give me some hint

Data Binding with Smart table from json file

i want to bind the json file to a smart table. How to use the loop function for the iteration.. please help
It only shows the design of smart table.
didn't binding the data from json
this is the json file
[
{
"year": 2013,
"id": "",
"doctor": "Dr. Smith",
"illness": "Flu",
"apptdate": "3/12/2013",
"details":"Patient had flu for 5 days. No medicines prescribed"
}
]
i used to retrieve data using
#Injectable()
export class SmartTablesService {
constructor(private http: Http) {
}
smartTableData = [];
loadData() {
console.log('loadData');
this.http.get('http://192.168.0.100:8000/medical')
.subscribe((data) => {
setTimeout(() => {
var contactData = [];
$.each(data.json(), function (key, value) {
var tempData = value.source;
contactData.push(tempData);
});
this.smartTableData = contactData;
}, 1000);
});
}
getData(): Promise<any> {
console.log("Promise");
this.loadData();
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(this.smartTableData);
resolve(this.smartTableData);
}, 3000);
});
}
}
constructor(private http: Http) { }
getComments() {
return this.http.get('http://192.168.0.100:8000/article' )
.map((res: Response) => res.json())
.catch((error:any) => Observable.throw(error));
}
}*/
this is the component part
#Component({
selector: 'new',
template: '<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>'
})
export class NewComponent {
query: string = '';
settings = {
noDataMessage: 'Loading...',
columns: {
year: {
title: 'YEAR',
type: 'string'
},
id: {
title: 'ID',
type: 'string'
},
doctor: {
title: 'DOCTOR',
type: 'string'
},
illness: {
title: 'ILLNESS',
type: 'string'
},
apptdate: {
title: 'APPTDATE',
type: 'string'
},
details: {
title: 'DETAILS',
type: 'string'
}
}
};
// data
source: LocalDataSource = new LocalDataSource();
constructor(protected service: SmartTablesService){
this.service.getData().then((data) => {
this.source.load(data);
});
}
}
please anyone anyone know how to bind it ..help
simply change the subscribe part in the service page to
var tempData = value;
so .subscriber looks like
.subscribe((data) => {
setTimeout(() => {
var contactData = [];
$.each(data.json(), function (key, value) {
var tempData = value; contactData.push(tempData);
});
this.smartTableData = contactData;
}, 1000);
});
}
it works..!

Categories