Filter array of objects to a single matching object in mongodb/meteor - javascript

I have a query which looks as so:
var writer = Writers.findOne({
_id: writerId,
books: {
$elemMatch: {
id: Books.findOne({ slug: bookSlug })._id
}
}
});
However, this will return the full list of classes in the query.
{
name: "H.P. Lovecraft",
books: [{
id: "1234",
slug: "at-the-mountains-of-madness"
}, {
id: "5678",
slug: "herbert-west-reanimator"
}]
}
Would there be a way to eliminate all information except for the one item in the list I want and make it into an object? That is to say, I want my final result to be:
{
name: "H.P Lovecraft",
book: {
id: "1234",
slug: "herbert-west-reanimator"
}
}
How would this be done in Meteor with mongodb?

One approach you could take is to use the $elemMatch projection operator with the findOne() query. For the document with _id equal to writerId, the $elemMatch projection returns only the first matching element from the array:
var bookId = Books.findOne({ slug: bookSlug })._id,
writer = Writers.findOne({ _id: writerId },
{ books: { $elemMatch: { id: bookId } },
_id: 0,
name: 1
}
);
Another approach would be to use Underscore library's _.find() method to return the specific array element:
var bookId = Books.findOne({ slug: bookSlug })._id,
writer = Writers.findOne({
_id: writerId,
books: {
$elemMatch: {
id: bookId
}
}
}),
book = _.find(writer.books, function(book) {return book.id === bookId});

Related

Node Js how to fetch data from database in an hierarchical way

I'm writing a back code using NodeJs to fetch some data from backend, I want dataBase data to be like this
like this:
data = [{
name: "Admin",
id: '1',
children: [
{ name: "Admin", id: "1" },
{ name: "groupe1", id: "2" },
{
name: "groupe2", id: "1455", children: [
{ name: "groupe2", id: "1455" },
{ name: "gro", id: "5444" },
{ name: "hhrr", id: "45" }
]
}
]
}]
the idea is simple we have a list of group each group has a parent I want to display all the groups list in an hierarchical way the top one of the tree is done
Some groups are parents and groups in the same time and some others are only groups if the group is not parent we add an object with its name and ID in the array of children of his parent
if this groups is a parent that's mean it has children we add an object with its ID and name in the array of children of his parents, and we add property children for the object which is array named children with for the first time an object with the name and the id of the group etc...
i tryed to do this but it did not work
const getParentsByType = async ({ name, _id }) => {
let parentResult = [
{
id: _id,
name: name,
children: [
{
id: _id,
name: name,
},
],
},
];
parentResult= await findParent(_id, parentResult[0].children, 0);
return parentResult;
};
const findParent = async (parentId, parentResult, itemPos) => {
let children = await Models.GroupModel.find({ parent: parentId, status: true }).select('name _id');
for (let i = 0; i < children.length; i++) {
let childrenList = await Models.GroupModel.find({ parent: children[i]._id, status: true }).select('name _id');
if (childrenList.length != 0) {
parentResult.push(buildParentWithChild(children[i]._id, children[i].name));
findParent(children[i]._id,parentResult.children[i],itemPos++)
} else {
parentResult.push(buildParent(children[i]._id, children[i].name));
}
}
return parentResult
};
and this the model of the data base
const Group = mongoose.Schema({
name: {
type: String,
required: true,
},
status: {
type: Boolean,
required: true,
},
parent: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Group',
},
});
i had two days trying to resolve tis but with no result
i need some helps and Thank you
Try parsing your returned data. It validates your data as objects i dont see any problem with your function regardless i still have no idea what format your a trying to build.
let children = JSON.parse(JSON.stringify(await Models.GroupModel.find({ parent: parentId, status: true }).select('name _id')));
let childrenList = JSON.parse(JSON.stringify(await Models.GroupModel.find({ parent: children[i]._id, status: true }).select('name _id')));
If I understand you right, you want to convert the array returned by Models.GroupModel.find, and which looks like
var dbresult = [
{_id: "1", parent: null, name: "one"},
{_id: "2", parent: "1", name: "two"}
];
into a hierarchical structure. This can be done with a function that adds all children of a given parent p, including, recursively, their children. Like the following:
function children(p) {
var result = [];
for (r of dbresult) if (r.parent === p) {
var row = {_id: r._id, name: r.name};
var chld = children(r._id);
if (chld.length > 0) row.children = chld;
result.push(row);
}
return result;
}
console.log(JSON.stringify(children(null)));
Note that this approach requires only one database access (to fill the dbresult) and is therefore probably faster than your findParent function.

Mongodb find documents with given field value inside an array and its id in another array inside the same document

My data model:
{
_id: ObjectId,
persons:[{
_id: ObjectId,
name: String,
...
}],
relations: [{
type: String,
personId: ObjectId,
...
}],
...
}
Here's my issue:
I am trying to find documents where person's name is x and it's _id is inside the relations array (personId) with a given type.
Example:
My data:
[{
_id:"1",
persons:[{
_id:"1",
name: "Homer"
},
{
_id:"2",
name: "Bart"
}],
relations: [{
type:"House_Owner",
personId: 1,
}],
}]
Request_1:
Find all documents where "Homer" is the house owner
Result:
[{
_id:"1",
...
}]
Request_2:
Find all documents where "Bart" is the house owner
Result:
[]
Any help would be appreciated.
The only solution I see here is to do the find operation with the given name value and after that filter the mongodb result.
PS: I cannot change the existing data model
EDIT:
I found a solution to do this by using $where operator with a javascript function but I am not sure that's the most efficient way.
db.myCollection("x").find({
$where: function() {
for (const relation of this.relations) {
if(relation.type === "House_Owner") {
for (const person of this.persons) {
if(person.name === "Homer" && person._id.equals(relation.personId)) {
return true;
}
}
}
}
}
})
You can do something like this:
const requiredName="x"
const requiredId = "id"
await yourModel.find({$and:[{"relations.personId":requiredId },{"persons.name":requiredName}]})

Adding element inside nested array in mongoose

Server Started at Port 3000...
{
_id: new ObjectId("61c707e9f4ff040a47d27c3f"),
username: 'adityaaryam',
password: '1234',
nameOfUser: 'Aditya Aryam',
emailOfUser: 'adityaaryam#gmail.com',
userAllLists: [
{
name: 'Hello',
items: [],
_id: new ObjectId("61c70d915448262d1dca1a69")
},
{
name: 'Work',
items: [],
_id: new ObjectId("61c70d965448262d1dca1a70")
},
{
name: 'Home Work',
items: [],
_id: new ObjectId("61c70d9b5448262d1dca1a79")
},
{
name: 'Hello',
items: [],
_id: new ObjectId("61c70e7f5448262d1dca1a84")
},
{
name: 'Play',
items: [],
_id: new ObjectId("61c7126a5448262d1dca1a9b")
},
{
name: 'Eat',
items: [],
_id: new ObjectId("61c71325b0219e6ce4f57990")
},
{
name: 'Walla',
items: [],
_id: new ObjectId("61c7197de9564390d506cbe9")
}
],
__v: 7
}
This is how my database looks like. I want to push new elements to "items" array which is nested inside the "userAllLists" array using mongoose. How do I implement this?
I have been trying findOneAndUpdate using $push but I am not able to achieve my desriable results.
My Schemas are as follows:
const itemSchema = {
name: String
};
const customListSchema ={
name:String,
items:[itemSchema]
};
const userSchema={
username: String,
password: String,
nameOfUser: String,
emailOfUser: String,
userAllLists: [customListSchema],
};
Thanks in Advance!
I think $push is the right way to push new elements to nested arrays, you didn't show the code you tried to see if it works or not, at all here is an example based on your schema
User.update({_id: "61c707e9f4ff040a47d27c3f", }, {
'$push': {
"userAllLists.$[].items": {name: "test item name"}
}
});
Note: $[] expressions will push the specified object inside all items arrays that exist in userAllLists
To push the item for only specific userAllLists object you can use the following syntax
User.update({_id: "61c707e9f4ff040a47d27c3f", "usersAllLists._id": "61c70d915448262d1dca1a69"}, {
'$push': {
"userAllLists.$.items": {name: "test item name"}
}
});
this will ensure to push the item object to the specified usersAllLists object which has this id 61c70d915448262d1dca1a69

Map the nested data from other table using promise and async-await

I need the expert advice for this code. I need to know Is there any better way to solve this.
I am using the mongoose for db. I have a dataset like this:
Below is matchTable:
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
}
],
status: true
}
And I have a Car table in which car name is there on behalf of id
like this
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febce'), name: 'ford' },
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febcg'), name: 'mitsubishi' },
So I want to make join the data from car table, so that response get name on behalf of aid.
Desired result will be like
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
name: 'mitsubishi'
}
],
status: true
}
For that I have to merge the car table on matchTable. I have done this but I want to give some suggestion that is there any better way to do or is it fine. I need expert advice.
const getData = await matchTable.find(
{ status: true }
).lean().exec();
let dataHolder = [];
await Promise.all (
getData.map(async x => {
await Promise.all(
x.car.map(async y => {
let data = await Car.findOne(
{ _id: ObjectId(y.aid) },
{ name: 1 }
).lean().exec();
y.name = '';
if (data) {
y.name = data.name;
}
})
)
// If I return { ...x }, then on response it will return {}, {} on car column
dataHolder.push(x) //So I have chosen this approach
})
);
Please guide me if any better and efficient solution is there. Thanks in advance
You can make use of aggregation here.
const pipeline = [
{
$match : { status : true }
},
{
$unwind: '$matchtable',
},
{
$lookup: {
from: "cars",
localField: "car.aid",
foreignField: "_id",
as: "matchcars"
}
},
{
$addFields: {
"car.carName": { $arrayElemAt: ["$matchcars.name", 0] }
}
},
{
$group: {
_id: "$_id",
cars: { $push: "$matchcars" }
}
}
]
const result = await matchTable.aggregate(pipeline).exec();
Please make sure, aid field inside car array (in matchTable collection) is an ObjectId because its being matched to _id (which is an ObjectId) inside cars collection.

How to return an array of objects in GraphQL, possibly using the same endpoint as the one that returns a single object?

I am making a GraphQL API where I would be able to retrieve a car object by its id or retrieve all the cars when no parameter is provided.
Using the code below, I am successfully able to retrieve a single car object by supplying id as a parameter.
However, in the case where I would expect an array of objects i.e. when I supply no parameter at all, I get no result on GraphiQL.
schema.js
let cars = [
{ name: "Honda", id: "1" },
{ name: "Toyota", id: "2" },
{ name: "BMW", id: "3" }
];
const CarType = new GraphQLObjectType({
name: "Car",
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString }
})
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
cars: {
type: CarType,
args: {
id: { type: GraphQLString }
},
resolve(parent, args) {
if (args.id) {
console.log(cars.find(car => car.id == args.id));
return cars.find(car => car.id == args.id);
}
console.log(cars);
//***Problem Here***
return cars;
}
}
}
});
Test queries and their respective results:
Query 1
{
cars(id:"1"){
name
}
}
Query 1 Response (Success)
{
"data": {
"cars": {
"name": "Honda"
}
}
}
Query 2
{
cars{
name
}
}
Query 2 Response (Fail)
{
"data": {
"cars": {
"name": null
}
}
}
Any help would be much appreciated.
A Car and a List of Cars are effectively two separate types. A field cannot resolve to a single Car object one time, and an array of Car object another.
Your query is returning null for the name because you told it the cars field would resolve to a single object, but it resolved to an array instead. As a result, it's looking for a property called name on the array object and since one doesn't exist, it's returning null.
You can handle this in a couple of different ways. To keep things to one query, you can use filter instead of find and change the type of your query to a List.
cars: {
type: new GraphQLList(CarType), // note the change here
args: {
id: {
type: GraphQLString
},
},
resolve: (parent, args) => {
if (args.id) {
return cars.filter(car => car.id === args.id);
}
return cars;
}
}
Alternatively, you could split this into two separate queries:
cars: {
type: new GraphQLList(CarType),
resolve: (parent, args) => cars,
},
car: {
type: CarType,
args: {
id: {
// example of using GraphQLNonNull to make the id required
type: new GraphQLNonNull(GraphQLString)
},
},
resolve: (parent, args) => cars.find(car => car.id === args.id),
}
Check the docs for more examples and options.

Categories

Resources