Mongoose $elemMatch with multiple of the same property - arrays

I'm trying to find() with Mongoose with the following query:
let users = await models.users.find({
inv: { $elemMatch: { name: "Some Item", name: "Another Item" } }
});
These documents should be found:
{
inv: [{ name: "Some Item", amount: 5 }]
}
//and
{
inv: [{ name: "Another Item", amount: 15 }]
}
//and
{
inv: [{ name: "Some Item", amount: 5 }, { name: "Another Item", amount: 15 }]
}
//and
{
inv: [{ name: "Some Item", amount: 5 }, { name: "Another Item", amount: 15 }, { name: "Different Item", amount: 1 }]
}
But these shouldn't:
{
inv: [{ name: "Different Item", amount: 1 }]
}
//and
{
inv: []
}
This works fine with regular MongoDB queries, but with Mongoose, this is a problem since you can't have multiple of the same properties in a JavaScript object (name and name in this case). How should I go about handling this?

You are probably looking for $or query operator
db.collection.find({ inv: { $elemMatch: { "$or": [ { name: "Some Item" }, { name: "Another Item" } ] } } })

Related

ReactJS and Material UI TreeView: Can I use a multi-level JSON/array of objects with different names to populate the TreeView?

I have an array of objects that is multi-leveled with different names that I need to populate a TreeView component :
export const application_group_one = [
{
id: uniqueId(),
name: "Application 1",
organizations: [
{
id: uniqueId(),
name: "Organization 1",
artifacts: [
{
id: uniqueId(),
name: "Artifact 1",
},
],
},
],
},
{
id: uniqueId(),
name: "Application 2",
organizations: [
{
id: uniqueId(),
name: "Organization 1",
artifacts: [],
},
],
},
];
export const application_group_two = [
{
id: uniqueId(),
name: "Application 3",
organizations: [
{
id: uniqueId(),
name: "Organization 1",
artifacts: [
{
id: uniqueId(),
name: "Artifact 1",
},
{
id: uniqueId(),
name: "Artifact 2",
},
{
id: uniqueId(),
name: "Artifact 3",
},
],
},
{
id: uniqueId(),
name: "Organization 2",
artifacts: [
{
id: uniqueId(),
name: "Artifact 1",
},
{
id: uniqueId(),
name: "Artifact 2",
},
{
id: uniqueId(),
name: "Artifact 3",
},
],
},
{
id: uniqueId(),
name: "Organization 3",
artifacts: [
{
id: uniqueId(),
name: "Artifact 1",
},
{
id: uniqueId(),
name: "Artifacy 2",
},
{
id: uniqueId(),
name: "Artifact 3",
},
],
},
{
id: uniqueId(),
name: "Organization 4",
artifacts: [
{
id: uniqueId(),
name: "Artifact 1",
},
{
id: uniqueId(),
name: "Artifact 2",
},
{
id: uniqueId(),
name: "Artifact 3",
},
],
},
],
},
];
I'm able to output the Organizations but not the artifacts. Having varying names for each children object is giving me a hard time. I would like a TreeView that can expand as many levels as I want with differing names for the nest objects. :
const getTreeItemsFromData = (treeItems) => {
return treeItems.map((treeItemData) => {
let organizations = undefined;
if (treeItemData.organizations && treeItemData.organizations.length > 0) {
organizations = getTreeItemsFromData(treeItemData.organizations);
}
return <TreeItem key={treeItemData.id} nodeId={treeItemData.id} label={treeItemData.name} children={organizations} />;
});
};
const DataTreeView = ({ treeItems }) => {
return (
<TreeView defaultCollapseIcon={<ExpandMoreIcon />} defaultExpandIcon={<ChevronRightIcon />}>
{getTreeItemsFromData(treeItems)}
</TreeView>
);
};
export const Tree = () => {
return (
<div className="App">
<DataTreeView treeItems={application_group_one} />
<br />
<DataTreeView treeItems={application_group_two} />
</div>
);
};

Error in array on aggregation query with lookup & nest array of objects

I have a problem with a query that I can't solve 100%
The fact is that when a user does not have any comment within the post. As inside the comments there is a "createdBy" and I need to make a lookup of that user inside the array. If there are no comments, it returns an array with an empty object, but it must return an empty array, not with empty objects.
Who can help me? Thank you very much in advance!
HERE MY USER collection (data)
[
{
_id: ObjectId("619d0f5df3f74665aff1a551"),
name: "Test Name",
surname: "Test Surname2",
createdAt: ISODate("2021-11-11T17:21:58.624+01:00"),
updatedAt: ISODate("2021-11-25T10:35:25.842+01:00"),
posts: [
{
_id: ObjectId("619d0f5df3f74575aff1a551"),
updatedAt: ISODate("2021-11-23T16:57:17.816+01:00"),
createdAt: ISODate("2021-11-23T16:57:17.816+01:00"),
content: "Test content....",
comments: [
{
createdBy: ObjectId("618d4326f1668007b3b98404"),
comment: "test comment...",
_id: ObjectId("619dfaaaa88266dc91b9489c"),
},
{
createdBy: ObjectId("618d4326f1668007b3b98404"),
comment: "test comment...",
_id: ObjectId("619dfc60a88266dc91b95741"),
},
],
date: ISODate("2021-11-23T16:57:17.820+01:00"),
},
{
_id: ObjectId("619d0f5df3f74575aff1a551"),
updatedAt: ISODate("2021-11-23T16:57:17.816+01:00"),
createdAt: ISODate("2021-11-23T16:57:17.816+01:00"),
content: "Test content....",
comments: [],
date: ISODate("2021-11-23T16:57:17.820+01:00"),
},
],
},
{
_id: ObjectId("619d0f5df3f74665aff1a551"),
name: "Test Name",
surname: "test surname",
createdAt: ISODate("2021-11-11T17:21:58.624+01:00"),
updatedAt: ISODate("2021-11-25T10:35:25.842+01:00"),
posts: [
{
_id: ObjectId("619d0f5df3f74575aff1a551"),
updatedAt: ISODate("2021-11-23T16:57:17.816+01:00"),
createdAt: ISODate("2021-11-23T16:57:17.816+01:00"),
content: "Test content....",
comments: [
{
createdBy: ObjectId("618d4326f1668007b3b98404"),
comment: "test comment...",
_id: ObjectId("619dfaaaa88266dc91b9489c"),
},
{
createdBy: ObjectId("618d4326f1668007b3b98404"),
comment: "test comment...",
_id: ObjectId("619dfe7ba88266dc91b961b6"),
},
],
date: ISODate("2021-11-23T16:57:17.820+01:00"),
},
{
_id: ObjectId("619d0f5df3f74575aff1a551"),
updatedAt: ISODate("2021-11-23T16:57:17.816+01:00"),
createdAt: ISODate("2021-11-23T16:57:17.816+01:00"),
content: "Test content....",
comments: [
{
createdBy: ObjectId("618d4326f1668007b3b98404"),
comment: "test comment...",
_id: ObjectId("619dfaaaa88266dc91b9489c"),
},
{
createdBy: ObjectId("618d4326f1668007b3b98404"),
comment: "test comment...",
_id: ObjectId("619dfc60a88266dc91b95741"),
},
],
date: ISODate("2021-11-23T16:57:17.820+01:00"),
},
],
},
];
HERE MY AGGREGATE QUERY
db.users.aggregate([
{ $unwind: { path: '$posts', preserveNullAndEmptyArrays: true } },
{ $unwind: { path: '$posts.comments', preserveNullAndEmptyArrays: true } },
{
$lookup: {
from: 'users',
localField: 'posts.comments.createdBy',
foreignField: '_id',
as: 'posts.comments.createdBy'
}
},
{ $unwind: { path: '$posts.comments.createdBy', preserveNullAndEmptyArrays: true } },
{
$group: {
_id: { _id: '$_id', post_id: '$posts._id' },
name: { $first: '$name' },
posts: { $push: '$posts' },
comments: { $push: '$posts.comments' },
}
},
{
$group: {
_id: '$_id._id',
name: { $first: '$name' },
posts: {
$push: {
_id: '$_id.post_id',
date: { $first: '$posts.date' },
content: { $first: '$posts.content' },
comments: '$comments'
}
}
}
},
])
Here an image with the fail array:
you have to remove preserveNullAndEmptyArrays field from unwind to don't have empity objects

Angular 2+ compare differences between 2 arrays

The arrays I have
const users = [
{ id: 1, name: "field 1" },
{ id: 2, name: "field 2" },
{ id: 3, name: "field 3" },
{ id: 4, name: "field 4" },
];
const onlineUsers = [
{ id: 1, name: "field 1" },
{ id: 3, name: "field 3" }
];
I would like to find the online and offline ones by comparing the two series
I want to do:
const userLists = [
{ id: 1, name: "field 1", online: true },
{ id: 2, name: "field 2", online: false },
{ id: 3, name: "field 3", online: true },
{ id: 4, name: "field 4", online: false },
];
Using Array.map and Array.some
const users = [
{ id: 1, name: "field 1" },
{ id: 2, name: "field 2" },
{ id: 3, name: "field 3" },
{ id: 4, name: "field 4" },
];
const onlineUsers = [
{ id: 1, name: "field 1" },
{ id: 3, name: "field 3" }
];
var retVal=users.map(u=>{
var isOnline=onlineUsers.some(ou=> ou.id==u.id);//this will check if onlineUsers have some record with given userid
return {...u,online:isOnline}
})
console.log(retVal)
You can just traverse through the user list and you can find out the onlineuser using find and just push it in the onlineuserList.
const users = [
{ id: 1, name: "field 1" },
{ id: 2, name: "field 2" },
{ id: 3, name: "field 3" },
{ id: 4, name: "field 4" },
];
const onlineUsers = [
{ id: 1, name: "field 1" },
{ id: 3, name: "field 3" }
];
const userLists = [];
users.forEach(user => {
if(onlineUsers.find(q => q.id == user.id)){
userLists.push({
id: user.id,
name: user.name,
online: "true"
})
}
else{
userLists.push({
id: user.id,
name: user.name,
online: "false"
})
}
})
console.log(userLists);
a bit faster approach using Array.indexOf() and JSON.strigify()
const onlineUsers = JSON.stringify([
{ id: 1, name: "field 1" },
{ id: 3, name: "field 3" }
]);
const userList = users.map(user =>
({
...user,
online: onlineUsers.indexOf(JSON.stringify(user)) > -1
})
);
OR if you neither want to change original onlineUsers array nor declare another variable:
const userList = users.map(user =>
({ ...user, online: JSON.stringify(onlineUsers).indexOf(JSON.stringify(user)) > -1 })
);

Filter Array of Object from another Array of Object

Currently I'm filtering data based from questions that have checked property value equals to true..
const data = [
{Name: "foo", X1: "1", X2: "1", Other: "Test1"},
{Name: "bar", X1: "2",X2: "2",Other: "Test2"},
{Name: "test",X1: "2",X2: "3",Other: "Test3"}
];
const questions = [{rows: {text: "Text 1", checked: true,fields: "1",column: "X1"}
}, {rows: {text: "Text 2", checked: true,fields: "2",column: "X1"}
}, {rows: {text: "Text 3", checked: false,fields: "1",column: "X2"}
}, {rows: {text: "Text 4", checked: false,fields: "2",column: "X2"}
}, {rows: {text: "Text 5", checked: false,fields: "3",column: "X2"}
}];
console.log(questionArr);
// console.log(dataArr);
const res = data.filter(d => questions.find(f => d[f.rows.column] === f.rows.fields && f.rows.checked));
which works but does not work when filtering the actual data below. I think there's a slight difference between the question object and the actual question object below.. What should be my filter code when accessing these kind of structure ?
I think this is what you're looking for. I matched the data structure to the image in your question. Let me know if I missed something.
const data = [
{ Name: "foo", X1: "1", X2: "1", Other: "Test1" },
{ Name: "bar", X1: "2", X2: "2", Other: "Test2" },
{ Name: "test", X1: "2", X2: "3", Other: "Test3" }
];
const questions = [
{ rows: [{ text: "Text 1", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 2", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 3", checked: false, fields: "1", column: "X2" }] },
{ rows: [{ text: "Text 4", checked: false, fields: "2", column: "X2" }] },
{ rows: [{ text: "Text 5", checked: false, fields: "3", column: "X2" }] }
];
const result = data.filter(function(item){
return questions.some(function(question){
return question.rows.some(function(row){
return (row.checked && item[row.column] === row.fields);
});
});
});
console.log(result);
The compact version
const result = data.filter((item) => questions.some((question) => question.rows.some((row) => (row.checked && item[row.column] === row.fields))));
With perf in mind
const data = [
{ Name: "foo", X1: "1", X2: "1", Other: "Test1" },
{ Name: "bar", X1: "2", X2: "2", Other: "Test2" },
{ Name: "test", X1: "2", X2: "3", Other: "Test3" }
];
const questions = [
{ rows: [{ text: "Text 1", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 2", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 3", checked: false, fields: "1", column: "X2" }] },
{ rows: [{ text: "Text 4", checked: false, fields: "2", column: "X2" }] },
{ rows: [{ text: "Text 5", checked: false, fields: "3", column: "X2" }] }
];
const result = {};
for(let a = 0, b = data.length; a < b; a++){
const item = data[a];
for(let c = 0, d = questions.length; c < d; c++){
const rows = questions[c].rows;
for(let e = 0, f = rows.length; e < f; e++){
const row = rows[e];
if(row.checked && item[row.column] === row.fields){
result[item.Name] = item;
break;
}
}
}
}
// this could be replaced with Object.values(result);
const matches = [];
for(let match in result){
matches.push(result[match]);
}
// not supported by IE yet
// Object.values(result);
console.log(matches);

ng-selected true but not selecting

so, i have a select list and I am trying to select the item dynamically. The thing is, even doe the expression is evaluated to "true" the option is still not being selected
<label for="articlePage">Page:</label>
<select class="form-control pageList" name="articlePage" required="" ng-model="article.page_ID">
<option ng-repeat="page in pages" ng-selected ="{{page.id == article.page_ID}}" value={{page.id}}>{{page.name}}</option>
</select><br>
the scope(pages):
$scope.pages = [
{ name: "Home", id: "1" },
{ name: "Plugs", id: "2" },
{ name: "Pack Bedding", id: "3" },
{ name: "Pot Bedding", id: "4" },
{ name: "Poinsettias", id: "5" },
{ name: "Sales", id: "6" },
{ name: "Process Quality", id: "7" },
{ name: "Environment", id: "8" },
{ name: "Process Technology", id: "9" },
{ name: "Process Control", id: "10" },
{ name: "Infrastructure", id: "11" },
{ name: "News", id: "12" },
{ name: "About", id: "13" },
{ name: "Contact", id: "14" },
{ name: "Find Us", id: "15" },
{ name: "Key Personnel", id: "16" },
{ name: "Recruitment", id: "17" },
{ name: "Legal Information", id: "18" },
{ name: "Hidden", id: "19" }
];
there is also an service that is retrieving my articles, i know i get that right and like i said, the expression is being evaluated. here is a print screen to prove that:
does anyone have any idea why the option isn't being selected?
Thank you! :)
You don't need expression inside the ng-selected! Try this:
<label for="articlePage">Page:</label>
<select class="form-control pageList" name="articlePage" required="" ng-model="article.page_ID">
<option ng-repeat="page in pages" ng-selected ="page.id == article.page_ID" value="{{page.id}}">{{page.name}}</option>
</select><br>

Resources