For example I have n doc in MongoDB collection
Films = new Mongo.Collection('films');
Film.insert({name: 'name n', actor: 'John'}); // *n
And I want to show array with only name values
var names = ['name 1', 'name 2',..,'name n'];
Any idea how to do it ?
And guys , ols write in comments correct title value of my question, to help other guys to find it, thx :)
You didn't provided any criteria for grouping name as an array.
You can use following query to get all names:
db.collection.distinct("name")
OR you can use MongoDB's aggregation to get all name by grouping them with some condition, if required. The query will be like following:
db.collection.aggregate({
$group: {
_id: null, //add condition if require
name: {
$push: "$name"
}
}
}, {
$project: {
name: 1,
_id: 0
}
})
If you want only distinct name then replace $push with $addToSet.
Related
I'm trying to execute a query that returns all the documents that match based on query parameters.
I have the following schema:
_id: ObjectId('631b875491b16c38eecfa4e9')
brandName: "Nick"
categories: Array
products: Array
0: Object
productName: "Vans Shoes dsds Old Skool"
description: "amazing Shoes."
categoryId: ObjectId('62f3eaff3ded19dcce71081e')
price: 240
numberOfBuyers: 0
_id: ObjectId(631b875491b16c38eecfa4ec)
1: Object
2: Object
3: Object
__v: 0
The following query should give me all the documents that match, but it returns only the first document:
const products = await Brand.find(
{
_id: brandId
},
{
products: {
$elemMatch: {
categoryId: categoryId,
price: {
$gte: minPrice,
$lte: maxPrice
}
}
}
})
What is wrong?
You are querying on "Brand" documents. This means your query tells Mongoose: if one of the products is matching (categoryId and price), return the (whole) Brand document.
In order to retrieve only specific elements of this array, you should include your $elemMatch object in the projection step of your find call:
const products = await Brand.find({
_id: brandId
}, {
//include other properties you want to include in your output document
products: {
$elemMatch: {
categoryId: "62f3eaff3ded19dcce71081e",
price: 240
}
}
}
})
Update after comment
Your products array will only contain the first element that was matched. This is intended behaviour (as described here: cs/manual/reference/operator/projection/elemMatch/):
Definition
$elemMatch
The
$elemMatch
operator limits the contents of an field from the query results to contain only the first element matching the
$elemMatch
condition.
In order to get several results you should probably use an aggregation pipeline using $unwind and $group.
I would like to know if its possible to keep the current order of the result the same as it is passed in on the filtering.
So lets say we have an array of IDS:
var arrayValues = [1,3,2]
I would like to aggregate the values but keep the result same order as im passing in the above array.
var result = Item.aggregate([{ $match: { _id: { $in: arrayValues } }}])
I would want the result in same order as the array values passed in as _id value.
Example Result :
result = [{ _id: 1 },{ _id: 3 },{ _id: 2 }]
You can use indexOfArray for this:
db.collection.aggregate([
{$match: {_id: {$in: arrayValues}}},
{$set: {index: {$indexOfArray: [arrayValues, "$_id"]}}},
{$sort: {index: 1}},
{$unset: "index"}
])
See how it works on the playground example
I have a collection named group on the mongoDb database . The collection includes 2 or more object and each object contains an array named members . what is the best possible and efficient way to concate and get all the members data from the database. what would be the mongoDB query ?
my collection looks like
[
{
id: ObjcetId("15215252"),
groupName: "travellers of Bangladesh",
members: ["1","2","3"]
},
{
id: ObjcetId("32643724362"),
groupName: "People from Bangladesh",
members: ["4","5","6"]
}
]
and i Want just this exact data
members: ["1","2","3","4","5","6"]
Use can use aggregations
$unwind to deconstruct the array
$group to reconstruct the array
Here is the code
db.collection.aggregate([
{ $unwind: "$members" },
{
$group: {
_id: null,
members: { $push: "$members" }
}
}
])
Working Mongo playground
I'm querying a Collection that has 350k+ Documents. I'm querying a field using the IN clause where the IN is an array of 27k+ fields.
This actually seems to return rather fast in Mongoose. However, some of the matches of each item in the IN can have multiple Documents associated with them. I'd like to only have 1 Document returned per each match (sorted by another field). Is this possible?
Example
Let's say I have a Collection of Fruit.
[
{type:'apple', price:10},{type:'apple', price:5},{type:'apple', price:3},
{type:'orange', price:2},
{type:'pear', price:12}, {type:'pear', price:2}
]
So, currently I have
const types = ['apple', 'orange', 'pear'];
//Will return full example above
//Returns 12k Docs in real app but bc multiple Docs are returned per item in IN
Fruit.find({type: { $in: types }}, (err, results) => {
if (err) return console.error(err);
console.log(results);
});
I'd like to just have
[
{type:'apple', price:10}
{type:'orange', price:2},
{type:'pear', price:12}
]
How can I adjust my query to do something like this? Thanks!
returned. So instead of all documents matching the type - I just get only 1 with the highest price.
You need to $group by type and use $max to get highest prices:
db.collection.aggregate([
{
$match: { type: { $in: ['apple', 'orange', 'pear'] } }
},
{
$group: {
_id: "$type",
price: { $max: "$price" }
}
},
{
$project: {
type: "$_id",
_id: 0,
price: 1
}
}
])
Mongo Playground
I'm trying to aggregate a collection in which there are documents that look like this:
[
{
"title" : 1984,
"tags" : ['dystopia', apocalypse', 'future',....]
},
....
]
And I have a criteria array of keywords, for instance:
var keywords = ['future', 'google', 'cat',....]
What I would like to achieve is to aggregate the collection in order to $group it according to a "convenience" criteria in order to sort the document by the one that contains the more of the keywords in its tags field.
This means, if one document contains in its tags: 'future', 'google', 'cat' it will be sorted before another one that has 'future', 'cat', 'apple'.
So far, I have tried something like this:
db.books.aggregate(
{ $group : { _id : {title:"$title"} , convenience: { $sum: { $cond: [ {tags: {$in: keywords}}, 1, 0 ] } } } },
{ $sort : {'convenience': -1}})
But the $in operator is not a boolean so it does not work. I've looked around and didn't find any operator that could help me with this.
As you said you need a logical operator in order to evaluate $cond. It's a bit terse, but here is an implementation using $or :
db.books.aggregate([
{$unwind: "$tags" },
{$group: {
_id: "$title",
weight: {
$sum: {$cond: [
// Test *equality* of the `tags` value against any of the list
{$or: [
{$eq: ["$tags", "future"]},
{$eq: ["$tags", "google"]},
{$eq: ["$tags", "cat"]},
]},
1, 0 ]}
}
}}
])
I'll leave the rest of the implementation up to you, but this should show the basic construction to the point of the matching you want to do.
Addition
From your comments there also seem to be a programming issue you are struggling with, related to how you perform an aggregation like this where you have an Array of items to query in the form you gave above:
var keywords = ['future', 'google', 'cat',....]
Since this structure cannot be directly employed in the pipeline condition, what you need to do is transform it into what you need. Each language has it's own approach, but in a JavaScript version:
var keywords = ['future', 'google', 'cat'];
var orCondition = [];
keywords.forEach(function(value) {
var doc = {$eq: [ "$tags", value ]};
orCondition.push(doc);
});
And then just define the aggregation query with the orCondition variable in place:
db.books.aggregate([
{$unwind: "$tags" },
{$group: {
_id: "$title",
weight: {
$sum: {$cond: [
{$or: orCondition }
1, 0 ]}
}
}}
])
Or for that matter, any of the parts you need to construct. This is generally how it is done in the real world, where we would almost never hard-code a data structure like this.