How to get the length of a string field in Mongoose? - javascript

I am new to MongoDB and Mongoose.
I am practicing some of the queries with the following Schema and Model:
const articleSchema = mongoose.Schema({
title: String,
content: String,
});
const Article = mongoose.model('article', articleSchema);
Let's say I added the following two documents:
{title: "One", content: "Content One"}
{title: "LongerTitleThanOne", content: "Content Two"}
Which query can I use to find all documents where the "title" length is greater than 3?
I tried the following query but it only works with numbers:
Article.find({title:{$gt:3}}, (err,foundItems)=>{});
Many thanks in advance for your help here.

you can use $strLenCP and $expr:
mongo playground
Article.find({
"$expr": {
"$gt": [
{
"$strLenCP": "$title"
},
3
]
}
})

You can also use aggregation query to achieve the result.
Article.aggregate(
[
{
'$project': {
'titleLength': { '$strLenCP': '$name' }
}
}, {
'$match': {
'titleLength': {
'$gt': 2
}
}
}
])

This is not the best way to do this but it works
I advise you to not use this in production.
First get all your data from you database and store it in a variable then use JavaScript filter function to accomplish your task
For Example
let data = model.find({});
let filter_data = data.filter(data.title.length>8);

Related

How to filter only some rows of an array inside a document?

I have a MongoDB collection of documents, using a schema like this:
var schema = new Schema({
name: String,
images: [{
uri: string,
active: Boolean
}]
});
I'd like to get all documents (or filter using some criteria), but retrieve - in the images array - only the items with a specific property (in my case, it's {active: true}).
This is what I do now:
db.people.find( { 'images.active': true } )
But this retrieves only documents with at least one image which is active, which is not what I need.
I know of course I can filter in code after the find is returned, but I do not like wasting memory.
Is there a way I can filter array items in a document using mongoose?
Here is the aggregation you're looking for:
db.collection.aggregate([
{
$match: {}
},
{
$project: {
name: true,
images: {
$filter: {
input: "$images",
as: "images",
cond: {
$eq: [
"$$images.active",
true
]
}
}
}
}
}
])
https://mongoplayground.net/p/t_VxjfiBBMK

'InvalidPipelineOperator' with $lookup when adding .toArray()

I'm new to MongoDB, and working with aggregation. Here goes:
I have two collections 'video_card' and 'vendors'. I am fetching a video card document from the collection with the following structure:
_id:ObjectId(x),
vendorList:
0: ID1;
1: ID2;
I am trying to do a join between this document and this vendor collection:
_id: id:ObjectId(y)
name: "Amazon"
My aggregate is as follows so far:
const products = await db
.collection("video_card")
.aggregate([
{
$project: {
mappedVendors: {
$map: {
input: "$vendorList",
as: "vendorName",
in: {
$lookup: {
from: "vendors",
localField: "vendorList",
foreignField: "name",
as: "VendorNames"
},
},
},
},
},
},
]);
This returns a cursor object. However, when I attach .toArray() to the end of this, I get a code:168 'InvalidPipelineOperator'. Why is this?
To clarify, my intent is to return the data with vendorList ids swapped with names.
To follow up, I realized that my foreignField in the $lookup was incorrect, and changed it to _id. I hope this helps other people learning aggregate functions.

Mongoose search for items on array containing _id with aggregate [duplicate]

If I have this schema...
person = {
name : String,
favoriteFoods : Array
}
... where the favoriteFoods array is populated with strings. How can I find all persons that have "sushi" as their favorite food using mongoose?
I was hoping for something along the lines of:
PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});
(I know that there is no $contains in mongodb, just explaining what I was expecting to find before knowing the solution)
As favouriteFoods is a simple array of strings, you can just query that field directly:
PersonModel.find({ favouriteFoods: "sushi" }, ...); // favouriteFoods contains "sushi"
But I'd also recommend making the string array explicit in your schema:
person = {
name : String,
favouriteFoods : [String]
}
The relevant documentation can be found here: https://docs.mongodb.com/manual/tutorial/query-arrays/
There is no $contains operator in mongodb.
You can use the answer from JohnnyHK as that works. The closest analogy to contains that mongo has is $in, using this your query would look like:
PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
I feel like $all would be more appropriate in this situation. If you are looking for person that is into sushi you do :
PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})
As you might want to filter more your search, like so :
PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})
$in is like OR and $all like AND. Check this : https://docs.mongodb.com/manual/reference/operator/query/all/
In case that the array contains objects for example if favouriteFoods is an array of objects of the following:
{
name: 'Sushi',
type: 'Japanese'
}
you can use the following query:
PersonModel.find({"favouriteFoods.name": "Sushi"});
In case you need to find documents which contain NULL elements inside an array of sub-documents, I've found this query which works pretty well:
db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})
This query is taken from this post: MongoDb query array with null values
It was a great find and it works much better than my own initial and wrong version (which turned out to work fine only for arrays with one element):
.find({
'MyArrayOfSubDocuments': { $not: { $size: 0 } },
'MyArrayOfSubDocuments._id': { $exists: false }
})
Incase of lookup_food_array is array.
match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}
Incase of lookup_food_array is string.
match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
Though agree with find() is most effective in your usecase. Still there is $match of aggregation framework, to ease the query of a big number of entries and generate a low number of results that hold value to you especially for grouping and creating new files.
PersonModel.aggregate([
{
"$match": {
$and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ] }
},
{ $project : {"_id": 0, "name" : 1} }
]);
There are some ways to achieve this. First one is by $elemMatch operator:
const docs = await Documents.find({category: { $elemMatch: {$eq: 'yourCategory'} }});
// you may need to convert 'yourCategory' to ObjectId
Second one is by $in or $all operators:
const docs = await Documents.find({category: { $in: [yourCategory] }});
or
const docs = await Documents.find({category: { $all: [yourCategory] }});
// you can give more categories with these two approaches
//and again you may need to convert yourCategory to ObjectId
$in is like OR and $all like AND. For further details check this link : https://docs.mongodb.com/manual/reference/operator/query/all/
Third one is by aggregate() function:
const docs = await Documents.aggregate([
{ $unwind: '$category' },
{ $match: { 'category': mongoose.Types.ObjectId(yourCategory) } }
]};
with aggregate() you get only one category id in your category array.
I get this code snippets from my projects where I had to find docs with specific category/categories, so you can easily customize it according to your needs.
For Loopback3 all the examples given did not work for me, or as fast as using REST API anyway. But it helped me to figure out the exact answer I needed.
{"where":{"arrayAttribute":{ "all" :[String]}}}
In case You are searching in an Array of objects, you can use $elemMatch. For example:
PersonModel.find({ favoriteFoods : { $elemMatch: { name: "sushiOrAnytthing" }}});
With populate & $in this code will be useful.
ServiceCategory.find().populate({
path: "services",
match: { zipCodes: {$in: "10400"}},
populate: [
{
path: "offers",
},
],
});
If you'd want to use something like a "contains" operator through javascript, you can always use a Regular expression for that...
eg.
Say you want to retrieve a customer having "Bartolomew" as name
async function getBartolomew() {
const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol
console.log(custStartWith_Bart);
console.log(custEndWith_lomew);
console.log(custContains_rtol);
}
I know this topic is old, but for future people who could wonder the same question, another incredibly inefficient solution could be to do:
PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});
This avoids all optimisations by MongoDB so do not use in production code.

Nested array query using mongoose

I'm using mongoose (with nodejs) to make my queries.
I got the following Schemas for my database model (minified ofc):
var HistorySchema = new Schema({
status : String,
time : Date
});
var TaskSchema = new Schema({
game_id : Schema.Types.ObjectId,
history : [HistorySchema]
}, {collection: 'task'});
Now I want to give an example (insertion) for this which I want to use to show my problem and wish:
(Insertion)
{
"_id" : ObjectId("5772ca87439632101510fa6b"),
"history" :
[
{
"status" : "open",
"time" : ISODate("2016-06-25T12:17:46.982Z")
},
{
"status" : "complete",
"time" : ISODate("2016-06-30T12:17:46.982Z")
}
]
}
so far so good... Now I have a given date, in this case:
ISODate("2016-06-28T12:17:46.982Z")
Now I want to get from my collection all TaskSchema objects including matching HistorySchmea objects from the array. So I want to exclude the not matching part in history array.
I tried many things like $pull operation like
db.task.find(
{
"game_id": ObjectId("57711397893a97aa170aa983"),
"history.time":{
$lte: ISODate("2016-06-28T12:17:46.982Z")
}
},{$pull: {
"history": {
time: {
$gte: ISODate("2016-06-28T12:17:46.982Z")
}
}
}
}
but then I get errors like
Unsupported projection option: $pull: { history: { time: { $gte: new Date(1467116266982) } } }
Does anyone know how I could realize this query? I'm working now for days on this and couldn't find any help.
Thanks in advance!
One solution is to use the aggregation framework :
db.task.aggregate([
{$match: {_id : ObjectId("5772ca87439632101510fa6b")}},
{$unwind : "$history"},
{$match :{"history.time" :
{
$lte: ISODate("2016-06-30T12:17:46.982Z"),
$gte: ISODate("2016-06-01T12:17:46.982Z")
}
}
},
{$group:
{
_id:"$_id",
history: { $push: { status: "$history.status", time: "$history.time" } }
}
}
]);

Use mongoose to find by a nested documents properties

I currently have a collection of documents that each contain arrays pointing at other documents within that collection. I need to query this collection for documents where the ones nested in arrays contain a certain property. I hope this will explain my request more clearly:
if doc.list1[0].prop = 'foo' or doc.list2[0].prop = 'foo' then select doc
I have tried using .find() like this but to no avail.
{
'doc.list1': 'foo',
$or: [
{ 'doc.list2': 'foo' }
]
}
Am I on the right track? Because I don't think I am. This is the best I can gleam from the documentation.
Edit
Here is my actual query initialisation using the same layout as Thomas's suggestion.
var query = this.Word.find({
$or: [
{ 'before.0.cleanWord': topic },
{ 'after.0.cleanWord': topic },
{ 'cleanWord': topic }
]
});
{
$or: [
{ 'doc.list1.0.prop': 'foo' }
{ 'doc.list2.0.prop': 'foo' }
]
}
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24or

Categories