How find from different multiple criteria in mongodb - javascript

I have a collection with different entries like this
foods{
{
name: 'rice'
type: 'Brazilian'
},
{
name: 'meat'
type: 'Spanish'
}
,
{
name: 'Cake'
type: 'Brazilian'
}
How do I select an get a specific amount foods of each type of food
for example it should return a collection(array) of 4 foods for each type in mongodb
[
[
{
name: 'rice'
type: 'Brazilian'
},
{
name: 'meat'
type: 'Brazilian'
}
{
name: 'pizza'
type: 'Brazilian'
},
{
name: 'bread'
type: 'Brazilian'
}
],
[
{
name: 'beans'
type: 'spanish'
},
{
name: 'fish'
type: 'spanish'
}
{
name: 'chocolare'
type: 'spanish'
},
{
name: 'ham'
type: 'spanish'
}
]
]

Use aggregation to get the desired result. In your aggregation pipeline, the $group operator takes center stage in creating the desired result since you can group the documents from the collection getting into the pipeline by the type key. Once grouped then you can use one of the accumulator operators $push to create the array. Something like the following:
var pipeline = [
{
"$group": {
"_id": "$type",
"docs": {
"$push": { "name": "$name", "type": "$type" }
}
}
}
]
var result = db.foods.aggregate(pipeline).map(function (doc){ return doc.docs });
printjson(result);

Related

Add field to each object of an array based on other field

I have the following Array of data:
{
_id: 5f5726ef7d475a61a95c5e0c,
attributes: [
{
values: [
{ name: '1' }
],
},
{
values: [
{ name: '2' }
]
}
],
attr1: [
{ name: "Study Code" },
{ name: "Patient Study" }
]
}
What I need is to add the correspondent value to each on of attr1 objects based on index. So the result would be:
{
_id: 5f5726ef7d475a61a95c5e0c,
attributes: [
{
values: [
{ name: '1' }
],
},
{
values: [
{ name: '2' }
]
},
],
attr1: [
{
name: "Study Code",
values: [{ name: "1" }]
},
{
name: "Patient Study",
values: [{ name: "2" }]
}
],
}
I wonder if that possible using aggregation $addFields in MongoDB
Query
query works if arrays same size
ziparray to make [[member1_1 member2_1], ....]
map to merge member1_1,member2_1 to a document
Playmongo
aggregate(
[{"$set": {"attr1": {"$zip": {"inputs": ["$attributes", "$attr1"]}}}},
{"$set":
{"attr1":
{"$map":
{"input": "$attr1",
"in":
{"$mergeObjects":
[{"$arrayElemAt": ["$$this", 1]},
{"$arrayElemAt": ["$$this", 0]}]}}}}}])
You can use $zip
db.collection.aggregate([
{
"$project": {
attributes: {
"$zip": {
"inputs": [
"$attributes",
"$attr1"
]
}
}
}
}
])
Here is the Mongo playground for your reference.

merge and remove elements in nested arrays

i have this array, i want to merge all elements inside the objects in the nested arrays and remove the duplicates..
the array is the output of mongo db populate so answers from there or just js will be amazing :)
"visitors": [
[
{
"name": "matan",
"id": "61793e6a0e08cdcaf213c0b1"
},
{
"name": "shani",
"id": "61793e910e08cdcaf213c0b5"
}
],
[
{
"name": "david",
"id": "6179869cb4944c6b19b05a23"
},
{
"name": "orit",
"id": "617986e535fdf4942ef659bd"
}
],
[
{
"name": "david",
"id": "6179869cb4944c6b19b05a23"
},
{
"name": "orit",
"id": "617986e535fdf4942ef659bd"
}
]
]
would like this output -
"visitors": [
{
"name": "matan",
"id": "61793e6a0e08cdcaf213c0b1"
},
{
"name": "shani",
"id": "61793e910e08cdcaf213c0b5"
},
{
"name": "david",
"id": "6179869cb4944c6b19b05a23"
},
{
"name": "orit",
"id": "617986e535fdf4942ef659bd"
},
]
these are my collections
i need to get all visitors on one solar system,
so > solars > planets > visitors
const solarsModel = new Schema({
planets: [ { type: Schema.Types.ObjectId ,ref:'planet'} ],
starName: { type: String, required: true, default: "" }
})
const planetModel = new Schema({
planetName: { type: String, required: true, default: "" },
system:{type: Schema.Types.ObjectId, ref: 'solar'},
visitors: [{ type: Schema.Types.ObjectId , ref: 'visitor'}]
})
const visitorModel = new Schema({
visitorName:{ type: String, required: true, default: "" },
homePlanet: {type: Schema.Types.ObjectId, ref:"planet" },
visitedPlanets: [{ type: Schema.Types.ObjectId, ref:"planet" }]
})
this is what i did to achieve a result would love to use Aggregate..
const response = await solarModel
.findById({ _id: data.id })
.select({ starName: 1, _id: 0 })
.populate({
path: "planets",
select: { visitors: 1, _id: 0 },
populate: {
path: "visitors",
select: "visitorName",
},
})
.exec();
solved with this
exports.findVisitorSystemHandler = async (data) => {
const systemName = await solarModel.findById({ _id: data.id });
const response = await planetModel.aggregate([
{ $match: { system: makeObjectId(data.id) } },
{
$lookup: {
from: "visitors",
localField: "visitors",
foreignField: "_id",
as: "solarVisitors",
},
},
{
$project: {
solarVisitors: {
visitedPlanets: 0,
homePlanet: 0,
__v: 0,
},
},
},
{ $unwind: "$solarVisitors" },
{
$group: {
_id: null,
system: { $addToSet: systemName.starName },
solarVisitors: {
$addToSet: {
id: "$solarVisitors._id",
name: "$solarVisitors.visitorName",
},
},
},
},
{ $unwind: "$system" },
{
$project: {
_id: 0,
},
},
]);
return response;
};
You can use aggregate() like this:
$unwind twice due to nested array
$group using $addToSet to not get duplicates.
db.collection.aggregate([
{
"$unwind": "$visitors"
},
{
"$unwind": "$visitors"
},
{
"$group": {
"_id": null,
"visitors": {
"$addToSet": {
"id": "$visitors.id",
"name": "$visitors.name"
}
}
}
}
])
Example here
(1) Flatten the array of arrays
visitors = visitors.flat();
Which gives us this:
[
{ name: 'matan', id: '61793e6a0e08cdcaf213c0b1' },
{ name: 'shani', id: '61793e910e08cdcaf213c0b5' },
{ name: 'david', id: '6179869cb4944c6b19b05a23' },
{ name: 'orit', id: '617986e535fdf4942ef659bd' },
{ name: 'david', id: '6179869cb4944c6b19b05a23' },
{ name: 'orit', id: '617986e535fdf4942ef659bd' }
]
(2) Get unique ids
let uniqueIds= [...new Set(visitors.map(v => v.id)]
Which gives us this:
[
'61793e6a0e08cdcaf213c0b1',
'61793e910e08cdcaf213c0b5',
'6179869cb4944c6b19b05a23',
'617986e535fdf4942ef659bd'
]
(3) Get new list of visitors based only on uniqueIds
visitors = uniqueIds.map(id => {
let name = visitors.find(v => v.id === id).name;
return {
id,
name
}
});
Which gives us this:
[
{ name: 'matan', id: '61793e6a0e08cdcaf213c0b1' },
{ name: 'shani', id: '61793e910e08cdcaf213c0b5' },
{ name: 'david', id: '6179869cb4944c6b19b05a23' },
{ name: 'orit', id: '617986e535fdf4942ef659bd' },
]
Query
reduce with concat to flatten
union with an empty array,just to remove duplicates
if you have other fields except visitors they are not affected
PlayMongo
aggregate(
[{"$set":
{"visitors":
{"$setUnion":
[{"$reduce":
{"input": "$visitors",
"initialValue": [],
"in": {"$concatArrays": ["$$value", "$$this"]}}},
[]]}}}])
Results
[{
"visitors": [
{
"name": "david",
"id": "6179869cb4944c6b19b05a23"
},
{
"name": "matan",
"id": "61793e6a0e08cdcaf213c0b1"
},
{
"name": "orit",
"id": "617986e535fdf4942ef659bd"
},
{
"name": "shani",
"id": "61793e910e08cdcaf213c0b5"
}
]
}]

Build nested array based on list of ancestors and depth value

I have category model referencing itself. Below is data of category in mongodb.
{ "_id":{"$oid":"5f55acc029d19e1ac402908f"},
"parents":null,
"name":"pizza",
"slug":"pizza",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[],
}
{ "_id":{"$oid":"5f55b3c0a7b68b3bc0fe16c5"},
"parents":{"$oid":"5f55acc029d19e1ac402908f"},
"name":"premium",
"slug":"premium",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[{
"_id":{"$oid":"5f55acc029d19e1ac402908f"},
"name":"pizza",
"parents":null,
"slug":"pizza",
"depth":{"$numberInt":"0"}
}],
}
{ "_id":{"$oid":"5f55b726b6b12042d09057c2"},
"parents":{"$oid":"5f55b3c0a7b68b3bc0fe16c5"},
"name":"peri peri chicken",
"slug":"peri-peri-chicken",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[{
"_id":{"$oid":"5f55b3c0a7b68b3bc0fe16c5"},
"name":"premium",
"parents":"5f55acc029d19e1ac402908f",
"slug":"premium",
"depth":{"$numberInt":"1"}
},
{
"_id":{"$oid":"5f55acc029d19e1ac402908f"},
"parents":null,
"name":"pizza",
"depth":{"$numberInt":"0"},
"slug":"pizza"
}]
}
{ "_id":{"$oid":"5f55bb8be3088f473c4e15ac"},
"parents":null,
"name":"burger",
"slug":"burger",
"userID":"5f38c867b10f740e38b12198",
"ancestors":[]
}
I have following model in mongoose
const ItemCategorySchema = new Schema({
name: {
type: String,
required: true
},
slug: {
type: String,
index: true
},
parents: {
type: Schema.Types.ObjectId,
default: null,
ref: 'ItemCategory'
},
ancestors: [{
_id: {
type: Schema.Types.ObjectId,
ref: "ItemCategory",
index: true
},
name: { type: String },
parents: { type: String },
slug: { type: String },
depth: { type: Number }
}],
userID: {
type: String,
required: true
}
});
How can I build array like below using the information in ancestors and depth. I am using mongoose. Is there any function in mongoose to populate all category of self referencing into any number of level or depth?
const options = [
{ value: 'pizza', label: 'pizza',
options: [
{ value: 'premium', label: 'premium',
options: [
{ value: 'peri-peri-chicken', label: 'peri peri chicken' },
{ value: 'chicken-and-bacon', label: 'chicken and bacon'},
],
},
{ value: 'traditional', label: 'traditional',
options: [
{ value: 'beef-and-onion', label: 'beef and onion' },
],
},
],
},
{ value: 'burger', label: 'burger',
options: [
{ value: 'chicken', label: 'chicken' },
{ value: 'beef', label: 'beef' },
],
},
]

Filtering data from json response object

I have an API call that returns a JSON response object. The shape of the data is bit a confusing and I can't figure out the exact object to run .map() and .filter() on to get my desired result.
.then(response => {
console.log(response); //see below for shape of data
var dataSourceInfo = response.data.included.filter(
element => element.type === "DataSource"
);
var dataSourceName = dataSourceInfo.map(function(included) {
return included["name"];
});
console.log(dataSourceName);
In the two arrays, I'm attempting to filter over response.data.included to find an element by type. Then map over the returned filter to create a new ordered array. In one of the included arrays there is an identifying type of DataSource, here's an example:
included: [
{
id: "2147483604",
type: "DataSource",
name: "Some DataSource"
},
I'm logging the dataSourceName but the array only has the one of the expected names, and it's only from the first array, so it's like the map isn't reaching the second data.data. Any idea how I can get both names to appear in the filtered array?
Edit: correct response object is in the codesandbox
If I understand you correctly, you want a flat list of names where type is "DataSource" given an input that looks like this:
const response = {
data: [
{
data: {
data: {
included: [
{ type: 'DataSource', name: 'First' },
{ type: 'Blah', name: 'Second' },
{ type: 'DataSource', name: 'Third' }
]
}
}
},
{
data: {
data: {
included: [
{ type: 'DataSource', name: 'Fourth' },
{ type: 'Blah', name: 'Fifth' },
{ type: 'DataSource', name: 'Sixth' }
]
}
}
},
]
}
const result = response.data.flatMap(({data: {data: {included}}}) =>
included.reduce((memo, {type, name}) => {
if (type === 'DataSource') {
memo.push(name)
}
return memo;
}, [])
)
console.log(result)
I've omitted parts of the response that aren't relative to the problem
Update:
Here's the code adjusted to work with the responseObject listed in https://codesandbox.io/s/ympo7pr0xx
const responseObject = [ { data: { data: { id: "2147483605", selfUri: "/schedules/2147483605", type: "Schedule", startTime: 1545409610826, status: "InProgress", query: { id: "2147483603", selfUri: "/queries/2147483603", type: "Query" }, dataSource: { id: "2147483604", selfUri: "/datasources/2147483604", type: "DataSource" } }, included: [ { id: "2147483603", selfUri: "/queries/2147483603", type: "Query", name: "Query1", status: "Scheduled", querySchema: { id: "2147483601", selfUri: "/queryschemas/2147483601", type: "QuerySchema" } }, { id: "2147483601", selfUri: "/dataschemas/2147483601", type: "DataSchema", name: "Phone Data" }, { id: "2147483601", selfUri: "/queryschemas/2147483601", type: "QuerySchema", name: "QS1", dataSchema: { id: "2147483601", selfUri: "/dataschemas/2147483601", type: "DataSchema" }, queriesUri: "/queryschemas/2147483601/queries" }, { id: "2147483604", selfUri: "/datasources/2147483604", type: "DataSource", name: "Standalone- 5K", description: "Standalone 5K record" } ] } }, { data: { data: { id: "2147483606", selfUri: "/schedules/2147483606", type: "Schedule", startTime: 1545410049652, status: "Pending", query: { id: "2147483603", selfUri: "/queries/2147483603", type: "Query" }, dataSource: { id: "2147483608", selfUri: "/datasources/2147483608", type: "DataSource" } }, included: [ { id: "2147483608", selfUri: "/datasources/2147483608", type: "DataSource", name: "Standalone 5", description: "Standalone 5 record" }, { id: "2147483603", selfUri: "/queries/2147483603", type: "Query", name: "Query1", status: "Scheduled", querySchema: { id: "2147483601", selfUri: "/queryschemas/2147483601", type: "QuerySchema" }, schedulesUri: "/queries/2147483603/schedules" }, { id: "2147483601", selfUri: "/dataschemas/2147483601", type: "DataSchema", name: "Phone Data" }, { id: "2147483601", selfUri: "/queryschemas/2147483601", type: "QuerySchema", name: "QS1", dataSchema: { id: "2147483601", selfUri: "/dataschemas/2147483601", type: "DataSchema" } } ] } } ];
const result = responseObject.flatMap(({data: {included}}) =>
included.reduce((memo, {type, name}) => {
if (type === 'DataSource') {
memo.push(name)
}
return memo;
}, [])
)
console.log(result)

How to use _.where(list, properties) to get inner array as property?

I have JSON structure as below
var listOfPlays = classRoom: [
{
title: "Dollhouse",
femaleLead: true,
student: [
{ name: "Echo", role: "doll" },
{ name: "Topher", role: "mad scientist" }
]
},
{
title: "Dr. Horrible's Sing-Along Blog",
student: [
{ name: "Billy", role: "mad scientist" },
{ name: "Penny", role: "love interest" }
]
}
]
I know basic about _.where in Underscore.js that it will Looks through each value in the list, returning an array of all the values that contain all of the key-value pairs listed in properties.
For example _.where(listOfPlays, {title: "Dollhouse"}); this will return me an object whose title is "Dollhouse", but how would I get an object base on student array's value? from listOfPlays?
I am looking for something like:
_.where(listOfPlays , {student: [name : "Echo"]});**
The way _.where(listOfPlays , {student: [name : "Echo"]}); you are looking for does not work anymore in the new version.
You can use:
_.filter that looks through each value in the list, returning an array of all the values that pass a truth test (predicate)
_.some that returns true if any of the values in the list pass the predicate truth test.
var listOfPlays = [{
title: "Dollhouse",
femaleLead: true,
student: [{
name: "Echo",
role: "doll"
},
{
name: "Topher",
role: "mad scientist"
}
]
},
{
title: "Dr. Horrible's Sing-Along Blog",
student: [{
name: "Billy",
role: "mad scientist"
},
{
name: "Penny",
role: "love interest"
}
]
}
]
var output = _.filter(listOfPlays, function(item) {
return _.some(item.student, {
name: "Echo"
});
});
console.log(output);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Categories

Resources