I have the following schema:
const MenuSchema = new mongoose.Schema({
name: String,
type: String,
children: [{ type: ObjectId, ref: 'Menu' }],
});
And the following query:
const res = await Menu.aggregate([
{ $graphLookup: { from: "menus", startWith: "$children", connectToField: "children", connectFromField: "_id", as: "menus" }}
]);
As you can see, the menu schema is a self-referential data structure, children stores references to other instances of the same entity, with a 'type' field to differentiate the levels. I'm attempting to find and populate documents for each array of children (which are just BSON IDs) and return the results.
The above example seems to get most of the way there, however when I access one of the populated menus, it has a list of all the populated children in a flattened array, it doesn't retain the relationship structure.
For example, if I print out res, I get:
[ {
_id: 5f1212d053e5494bb45f18f3,
children: [ 5f1212d053e5494bb45f18f1 ],
name: 'Vodka',
type: 'Item',
__v: 0,
menus: [ [Object], [Object], [Object], [Object] ]
},
{
_id: 5f1212d053e5494bb45f18f4,
children: [ 5f1212d053e5494bb45f18f3, 5f1212d053e5494bb45f18f2 ],
name: 'Drinks',
type: 'Category',
__v: 0,
menus: [ [Object], [Object] ]
},
{
_id: 5f1212d053e5494bb45f18f5,
children: [ 5f1212d053e5494bb45f18f4 ],
name: 'Main Menu',
type: 'Menu',
__v: 0,
menus: [ [Object] ]
}
]
But when I print out res[1].menus, I get:
[
{
_id: 5f1212d053e5494bb45f18f3,
children: [ 5f1212d053e5494bb45f18f1 ],
name: 'Vodka',
type: 'Item',
__v: 0
},
{
_id: 5f1212d053e5494bb45f18f1,
children: [ 5f1212d053e5494bb45f18f0 ],
name: 'Double',
type: 'Variant',
__v: 0
},
{
_id: 5f1212d053e5494bb45f18f4,
children: [ 5f1212d053e5494bb45f18f3, 5f1212d053e5494bb45f18f2 ],
name: 'Drinks',
type: 'Category',
__v: 0
}
]
Which is all of the children in a flat array.
Is $graphLookup the correct approach, or am I just using it wrong?
I don't know if you are still looking for the answer for this, but if you use mongoose you can take advantage of the populate feature and use it as a middleware
Here's an example:
Let's say I want a list of people and their friends, and their friends-friends, etc. The result should look like this:
[
{
_id: "abc123",
name: "John Doe",
friends: [
{
_id: "efg456",
name: "Foo bar",
friends: [
{
_id: "hij789",
name: "Jane Doe",
friends: [more friends...]
}
]
}
]
]
In the db they are stored like this
{_id: "abc123", name: "John Doe", friends: ["efg456"]}
{_id: "efg456", name: "Foo bar", friends: ["hij789"]}
{_id: "hij789", name: "Jane Doe", friends: [more friends...]}
Your schema and middleware would be:
const Person = new Schema<Folder>({
name: {type: String, required: true},
friends: [{type: Schema.Types.ObjectId, ref: "person"}],
}, {timestamps: true})
Person.pre("find", function(next) {
this.populate("friends")
next()
})
Adding the function as a middleware to find will make it run for every person found. That includes the children in the friends array.
Related
I'm trying to create an object according to my model in mongoose using a controller, but I'm running into a problem that the last array nesting with prize patterns is not being written. Theoretically it is, but it is empty. I do not understand what the problem is, since I cannot add the necessary value to it even after creating the object through the constructor and after that doing something like: newGame.prizes = prizes;
With this behavior, all other properties within the prize property appear in the object, but the pattern array is empty.
Here is what my model looks like:
const GameSchema = mongoose.Schema({
type: String,
roundId: String,
userId: String,
calloutNumbersCount: Number,
tickets: [
{
id: String,
columns: [[Number]],
},
],
prizes: [
{
id: String,
name: String,
nameLocaleKey: String,
winAmount: Number,
patterns: [
{
type: String,
count: Number,
},
],
},
],
ticketPrice: Number,
boughtTicketIds: [String],
ended: Boolean,
});
Controller:
function createGame(gameType, userId) {
const currentGameConfig = config[gameType];
console.log("Controller");
currentGameConfig.prizes.map((el) => console.log(el.patterns));
const roundId = nanoid(LENGTH_OF_ROUND_ID);
const game = new Game({
type: currentGameConfig.type,
roundId,
userId,
calloutNumbersCount: currentGameConfig.callout.numbersCount,
tickets: [
{
id: 123, // temp
columns: [[0]], // temp
},
],
prizes: currentGameConfig.prizes,
ticketPrice: currentGameConfig.tickets.price,
boughtTicketIds: ["1", "2"], // temp
ended: false,
});
return game;
}
Place of creation and modification of the object in the route:
if (!game) {
const newGame = gameController.createGame(type, userId);
// newGame.prizes = currentGameConfig.prizes;
Game.addGame(newGame, (err, game) => {
if (err) {
res.json({ success: false, message: err.message });
throw err;
}
return res.json({
roundId: newGame.roundId,
});
});
}
Expected result:
The result:
EDITED
CurrentGameConfig:
{
type: 'BINGO_75',
name: 'Bingo 75',
nameLocaleKey: 'GAMES.BINGO_75',
prizes: [
{
name: 'Prize 1',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_1',
winAmount: 10,
patterns: [Array]
},
{
name: 'Prize 2',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_2',
winAmount: 10,
patterns: [Array]
},
{
name: 'Prize 3',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_3',
winAmount: 10,
patterns: [Array]
},
{
name: 'Prize 4',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_4',
winAmount: 10,
patterns: [Array]
},
{
name: 'Full house',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_5',
winAmount: 10,
patterns: [Array]
}
],
tickets: { price: 10, quantity: 72, grid: { cols: [Object], rows: [Object] } },
callout: { numbersCount: 25 }
}
In node.js patterns look like [Array], but if display each pattern it look like:
[
{type: 'HORIZONTAL', count: 1},
{type: 'VERTICAL', count: 1},
{type: 'DIAGONAL', count: 1}
]
I'm building a chat application and have an array of chats which contains an array of users:
[
{
_id: 'chatId1',
createdAt: 'createdDate',
latestMessage: {_id: 'someId', sender: {...}, content: 'someContent', ...},
updatedAt: 'updatedDate',
users: [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId4', name: 'userName4'}
},
{
_id: 'chatId2',
createdAt: 'createdDate',
latestMessage: {_id: 'someId', sender: {...}, content: 'someContent', ...},
updatedAt: 'updatedDate',
users: [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId2', name: 'userName2'}
},
{
_id: 'chatId3',
createdAt: 'createdDate',
latestMessage: {_id: 'someId', sender: {...}, content: 'someContent', ...},
updatedAt: 'updatedDate',
users: [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId3', name: 'userName3'}
},
]
And a separate array of users:
[
{_id: 'userId1', name: 'userName1'},
{_id: 'userId2', name: 'userName2'},
{_id: 'userId3', name: 'userName3'},
{_id: 'userId4', name: 'userName4'}
]
The recurring user in chats, userName1 will be the logged in user.
My chats array is already sorted by updatedAt when they are fetched from the backend so the most recent chat is at the top.
What I'm trying to do is arrange my users array so that they match the order that the unique user appears in the chats array which can be updated as messages get sent.
This is what I have tried so far:
const usersToSort = [];
chats.map((c) => {
usersToSort.push(c.users.filter((u) => u._id !== userId1));
});
const sorted = [].concat.apply([], usersToSort);
By filtering out the logged in user and pushing the unique user's id into an empty array I can order the users the way I need to, however the problem with this is that it empties the chats array of the user who is receiving the message as they have a completely different array of users so I need to find another way of doing this.
Have been working at it for some time now and still coming up stuck.
// Input params.
const chats = [
{
_id: 'chatId1',
createdAt: 'createdDate',
latestMessage: {_id: 'someId', sender: {}, content: 'someContent'},
updatedAt: 'updatedDate',
users: [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId4', name: 'userName4'}
]
},
{
_id: 'chatId2',
createdAt: 'createdDate',
latestMessage: {_id: 'someId', sender: {}, content: 'someContent'},
updatedAt: 'updatedDate',
users: [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId2', name: 'userName2'}
]
},
{
_id: 'chatId3',
createdAt: 'createdDate',
latestMessage: {_id: 'someId', sender: {}, content: 'someContent'},
updatedAt: 'updatedDate',
users: [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId3', name: 'userName3'}
]
}
];
const users = [
{_id: 'userId1', name: 'userName1'},
{_id: 'userId2', name: 'userName2'},
{_id: 'userId3', name: 'userName3'},
{_id: 'userId4', name: 'userName4'}
];
// Result.
const sortedUsers = chats
.map(v => v.users)
.flat()
.reduce((t, v) => [...t, ...(t.some(i => i._id === v._id) ? [] : [v])], []);
console.log(sortedUsers);
I have this multiple array and i want to convert it to one array with javascript codes and get data from it with flatMap:
[
{
id: '46892319372',
user_name: 'testerOne',
identifier: '20202'
}
]
[
{
id: '15243879678',
user_name: 'testerTwo',
identifier: '20201'
}
]
[
{
id: '02857428679',
user_name: 'testerThree',
identifier: '20203'
}
]
[
{
id: '65284759703',
user_name: 'testerFour',
identifier: '20204'
}
]
i want to convert this multiple arrays to just one array like this with javascript:
I have only one variable that contains this ↓ or maybe more than 4 or less than 4 i don't know i just have a variable that contains the arrays like this in it and i can't put these to another variable or separate them with , or ... i just have this ↓
[
{
id: '46892319372',
user_name: 'testerOne',
identifier: '20202'
},
{
id: '15243879678',
user_name: 'testerTwo',
identifier: '20201'
},
{
id: '02857428679',
user_name: 'testerThree',
identifier: '20203'
},
{
id: '65284759703',
user_name: 'testerFour',
identifier: '20204'
}
]
I tried array.concat but i have only one variable with that arrays so i tried list.concat(list) but i got something like this as result :
[
{
id: '46892319372',
user_name: 'testerOne',
identifier: '20202'
},
{
id: '46892319372',
user_name: 'testerOne',
identifier: '20202'
}
]
[
{
id: '15243879678',
user_name: 'testerTwo',
identifier: '20201'
},
{
id: '15243879678',
user_name: 'testerTwo',
identifier: '20201'
}
]
[
{
id: '02857428679',
user_name: 'testerThree',
identifier: '20203'
},
{
id: '02857428679',
user_name: 'testerThree',
identifier: '20203'
}
]
[
{
id: '65284759703',
user_name: 'testerFour',
identifier: '20204'
},
{
id: '65284759703',
user_name: 'testerFour',
identifier: '20204'
}
]
What you have here is not a multiple array (an array of arrays), but actually multiple arrays. Not sure how you got here but I am assuming you didn't create these, as the solution would be to just be to wrap these in an array and separate each array with a comma, then anything you do to flatten it will work.
Sorry if this is just too rudimentary an answer for now.
Did you create this (can you easily edit the structure) or were these returned to you?
Im not sure if this is what youre after but.. this only works with arrays that contain object literals.
let arrayList = [
[{
id: '46892319372',
user_name: 'testerOne',
identifier: '20202'
}],
[{
id: '15243879678',
user_name: 'testerTwo',
identifier: '20201'
},
{
id: '15243879678',
user_name: 'testerTwo',
identifier: '20201'
}
],
[{
id: '02857428679',
user_name: 'testerThree',
identifier: '20203'
}],
[{
id: '65284759703',
user_name: 'testerFour',
identifier: '20204'
}]
]
function mergeArrays() {
let arrayObject = []
for (let arrayLists of arguments) {
for (let array of arrayLists) {
if (array.constructor !== Array) continue;
for (let object of array) {
if (object.constructor !== Object) continue;
arrayObject.push(object);
}
}
}
return arrayObject;
}
console.log(mergeArrays(arrayList))
So, I get a JSON response from the server that looks something like:
{
data: [
{ id: 1, type: 'person', emails: [ { id: 1 }, { id: 3 } ], phones: [] },
{ id: 2, type: 'person', emails: [ { id: 2 } ], phones: [ { id: 2 } ] },
{ id: 3, type: 'person', emails: [ { id: 4 } ], phones: [ { id: 3 }, { id: 3 }] }
],
included: [
{ id: 1, type: 'emails', ... },
{ id: 2, type: 'emails', ... },
{ id: 3, type: 'emails', ... },
{ id: 4, type: 'emails', ... },
{ id: 1, type: 'phones', ... },
{ id: 2, type: 'phones', ... },
{ id: 3, type: 'phones', ... }
]
}
The data property is an array of contact objeccts all with the same structure. Each contact object has an array of related emails and phones.
The included property is an array of ALL types of related objects which means they can share and id or even have a difference object structure.
I'm looking to try and flatten the response to be easier to work with and resemble:
{
entities: {
contacts: [ ... ],
emails: [ ... ],
phones: [ ... ]
},
result: [ 1, 2, 3 ]
}
I've managed to normalize just the contact data using:
const contactSchema = new schema.Entity('contacts');
const contactListSchema = [ contactSchema ];
const normalizedData= normalize(response, contactListSchema);
But that obviously won't include the emails or phones in the entities.
I don't actually know if this library is capable of what I'm trying to achieve, but any help would be appreciated.
While not based on the data above, the API is based off of the jsonapi.org schema, so the example on the homepage matches exactly with the structure.
I actually found a library specifically designed to do this based on the original normalizr:
https://github.com/stevenpetryk/jsonapi-normalizer
Hope this may help someone in the future!
Sequelize allows for data relationships, but I can't seem to see if there is a way to have it return a query result composed of the relation? I could do this as two queries, but I am curious as to whether Sequelize provides for this? For example Playlist and PlaylistEntry:
Playlist: sequelize.define('playlist', {
name: Sequelize.STRING,
description: Sequelize.STRING
}),
PlaylistEntry: sequelize.define('playlist_entry', {
playlist: Sequelize.INTEGER
//track: Sequelize.INTEGER
})
PlaylistEntry.belongsTo(
Playlist,
{ as: 'Playlist', foreignKey: { name: 'fk_playlist' }});
What I would be hoping for (pseudocode):
Query:
Playlist.find(where: {id: playlistId}, nestedResult: true);
Result:
[{
id: 123,
name: 'abc'
playlistEntries: [{
id: 321,
track: 431
}]
}]
Playlist.find({
where: {},
include: [
{model: PlaylistEntry} //this should be PlaylistEntry model
]
})
http://docs.sequelizejs.com/en/latest/docs/models-usage/#eager-loading