Mongoose fetch array field which contain specific elements - node.js

I'm currently building objects in mongodb mongoose inside 1 collection.
Here's the mock data:
>[
> {_id: 1, name : "parent 1", parent: null},
> {_id:2, name = "child", parent : [1]},
> {_id:3, name = "grandchild", parent : [1,2]},
> {_id:4, name = "greatgrandchild 1", parent : [1,2,3]}
> {_id:5, name = "greatgrandchild 2", parent : [1,2,3]}
>]
so I was trying to query out all the children of "child" base on the array of parent id,
here's the mongoose method:
Model.find({"parent" : { $elemMatch: { $in : [1,2]}}});
my expected result is :
[
{_id:3, name = "grandchild", parent : [1,2]},
{_id:4, name = "greatgrandchild 1", parent : [1,2,3]}
{_id:5, name = "greatgrandchild 2", parent : [1,2,3]}
]
instead it gave me
[
{_id:2, name = "child", parent : [1]},
{_id:3, name = "grandchild", parent : [1,2]},
{_id:4, name = "greatgrandchild 1", parent : [1,2,3]}
{_id:5, name = "greatgrandchild 2", parent : [1,2,3]}
]
notice there's a document with id 2 included
I realized the mistake,
the method is equivalent to
select * from table where parent in(1,2)
is there a way to fetch properly?
cheers

Try this approach using $all which is similar to $and :
Model.find({"parent" : { $all:[1,2]}}, callbackfunction)
https://docs.mongodb.com/manual/reference/operator/query/all/

i found the answer, mongodb will trace into the sub document so normal and will work.
Model.find({$and : [{"parent" : 1}, {"parent" : 2}]});

Related

Mongoose getting specific item in Embedded Document

I am new in NoSQL and I'm kinda stuck. Can you help me?
Is there any way to get the specific field in an array if it matches a value?
For example, I would like to get the specific item in the array accountGroup where accountGroupName=="Account 1".
I tried many codes but it just returns the whole array which has the item that matches with the value.
By the way, I am using Mongoose and Nodejs. Thanks.
//here is the database
{
"_id" : ObjectId("60d2db4b90c66c3b0832a616"),
"accountType" : "Account Type 1",
"accountGroup" : [
{
"_id" : ObjectId("60d2db5a90c66c3b0832a619"),
"accountGroupName" : "Account 1",
"rangeFrom" : 25,
"rangeTo" : 35
},
{
"_id" : ObjectId("60d3fbfbc1502c3ed8cadf86"),
"accountGroupName" : "Account2",
"rangeFrom" : 850,
"rangeTo" : 2000
},
{
"_id" : ObjectId("60d2ddb1396dbf384898fbad"),
"accountGroupName" : "account 1 sample 3",
"rangeFrom" : 10,
"rangeTo" : 15
}
],
}
{
"_id" : ObjectId("60d2db4e90c66c3b0832a617"),
"accountType" : "Account Type 2",
"accountGroup" : [
{
"_id" : ObjectId("60d2e9586c4fa82310349c7c"),
"accountGroupName" : "account 2 sample 5",
"rangeFrom" : 50,
"rangeTo" : 60
}
]
}
You can use position operator $ into projection like this:
YourModel.find({
"accountGroup.accountGroupName": "Account 1"
},
{
"accountGroup.$": 1
})
Example here
Note that you are getting the whole document because using
YourModel.find({"accountGroup.accountGroupName": "Account 1"})
You are telling mongo "Give me a document where value accountGroupName into accountGroup array is equal to Account 1. And the document who match that condition contains the whole array.
So using positional operator, is waht you need according to its description:
The positional $ operator limits the contents of an to return the first element that matches the query condition on the array.
This is why with one query you get the whole document and with the other one you get the value you want.
Also note $ return only the first subdocument that match the query.

marshmallow EmbeddedDocument doesn't work

I made a simple board api with flask-restplus and mongoengie.
Also use marshmallow for serialize data.
Below code is now I worked.
[model]
class Article(Document):
no = SequenceField()
subject = StringField(required=True)
content = StringField(required=True)
userid = StringField(required=True)
comments = ListField(EmbeddedDocumentField(Comment))
created_at = DateTimeField(default=datetime.datetime.now())
updated_at = DateTimeField(default=datetime.datetime.now())
class Comment(EmbeddedDocument):
content = StringField(required=True)
userid = StringField(required=True)
created_at = DateTimeField(default=datetime.datetime.now())
[serializer]
class CommentSchema(Schema):
content = fields.String()
userid = fields.String()
created_at = fields.DateTime()
class ArticleSchema(Schema):
comments = CommentSchema(many=True)
class Meta:
fields = ('no', 'subject', 'content', 'userid', 'comments', 'created_at', 'updated_at')
I defined schema follow to model.
In ArticleSchema, to show comments, I definded comments = CommentSchema(many=True) and insert it to fields.
And get article function is here.
def get_all_articles():
articles = Article.objects.all()
data, errors = ArticleListSchema(many=True).dump(articles)
return data
But when I access to it, it occur Internal error and throw error message like this.
TypeError: Object of type Comment is not JSON serializable
After searched in google, I found some interest function, Nested. (https://marshmallow.readthedocs.io/en/3.0/nesting.html)
So I modified schema.
class ArticleSchema(Schema):
no = fields.Integer()
subject = fields.String()
content = fields.String()
userid = fields.String()
comments = fields.Nested(CommentSchema())
created_at = fields.DateTime()
updated_at = fields.DateTime()
(comments = fields.Nested(CommentSchema())
But it doesn't work properly too.
[result]
{
"subject": "string",
"content": "string",
"userid": "string",
"updated_at": "2018-11-06T17:04:55.197000+00:00",
"no": 20,
"created_at": "2018-11-06T17:04:55.197000+00:00",
"comments": {}
}
I already insert 2 comments and mongodb result is,
> db.article.find()
{ "_id" : ObjectId("5be14bb61b48d9113e3d1413"), "no" : 20, "subject" : "string", "content" : "string", "userid" : "string", "comments" : [ { "content" : "cosdadas", "userid" : "123123", "created_at" : ISODate("2018-11-06T17:34:44.199Z") }, { "content" : "Second comment", "userid" : "john", "created_at" : ISODate("2018-11-06T17:34:44.199Z") } ], "created_at" : ISODate("2018-11-06T17:04:55.197Z"), "updated_at" : ISODate("2018-11-06T17:04:55.197Z") }
But in API, comments doesn't show. Just empty {}.
Is there any solution here?
Thanks.
[SOLVED]
Change
comments = fields.Nested(CommentSchema()) to
comments = fields.Nested(CommentSchema, many=True) and it works perfectly.

update array in mongoose which matches the condition

my schema looks like
{
qty:{
property1:{
//something
}
property2:[{
size:40,
color:"black",
enabled:"true"
}]
}
}
property 2 is array what i want to do is update those array object whose enabled is true in single query
I tried writing the following query
db.col.update({
"qty.property2.enabled" = "true"
}, {
"qty.property2.color" = "green"
}, callback)
but it is not working
error:
[main] Error: can't have . in field names [qty.pro.size]
db.col.update({"qty.property2.enabled":"true"},{$set: {'qty.property2.$.color': 'green'}}, {multi: true})
this is the way to update element inside array.
equal sign '=' cannot be used inside object
updating array is done using $
Alternative solution for multiple conditions:
db.foo.update({
_id:"i1",
replies: { $elemMatch:{
_id: "s2",
update_password: "abc"
}}
},
{
"$set" : {"replies.$.text" : "blah"}
}
);
Why
So I was looking for similar solution as this question, but in my case I needed array element to match multiple conditions and using currently provided answers resulted in changes to wrong fields.
If you need to match multiple fields, for example let say we have element like this:
{
"_id" : ObjectId("i1"),
"replies": [
{
"_id" : ObjectId("s1"),
"update_password": "abc",
"text": "some stuff"
},
{
"_id" : ObjectId("s2"),
"update_password": "abc",
"text": "some stuff"
}
]
}
Trying to do update by
db.foo.update({
_id:"i1",
"replies._id":"s2",
"replies.update_password": "abc"
},
{
"$set" : {"replies.$.text" : "blah"}
}
);
Would result in updating to field that only matches one condition, for example it would update s1 because it matches update_password condition, which is clearly wrong. I might have did something wrong, but $elemMatch solution solved any problems like that.
Suppose your documet looks like this.
{
"_id" : ObjectId("4f9808648859c65d"),
"array" : [
{"text" : "foo", "value" : 11},
{"text" : "foo", "value" : 22},
{"text" : "foobar", "value" : 33}
]
}
then your query will be
db.foo.update({"array.value" : 22}, {"$set" : {"array.$.text" : "blah"}})
where first curly brackets represents query criteria and second one sets the new value.

Mongoose id function returning null

I'm trying to get a sub-document from MongoDB. I can get the parent document, which looks like:
{
"_id" : ObjectId("5550a7948be994430f7df1b4"),
"address" : "Grafton St, Dublin 2",
"coords" : [
-6.2597468,
53.3422998
],
"facilities" : [
"food",
"irish history"
],
"name" : "Laura Dunphy",
"openingTimes" : [
{
"days" : "Monday - Friday",
"times" : [
"10am",
"12pm",
"2pm"
]
},
{
"days" : "Saturday",
"times" : [
"8am",
"10am",
"12pm"
]
}
],
"rating" : 3,
"reviews" : [
{
"author" : "Simon Holmes",
"id" : ObjectId("5550c26e8c334adebc7c5dc3"),
"rating" : 5,
"timestamp" : ISODate("2013-07-15T23:00:00Z"),
"reviewText" : "Great guide, it was fun."
}
]
}
When I do:
console.log('guide.reviews is ');
console.log(guide.reviews);
I see:
guide.reviews is
[{ id: 5550c26e8c334adebc7c5dc3,
rating: 5,
timestamp: Tue Jul 16 2013 00:00:00 GMT+0100 (IST),
reviewText: 'Great guide, it was fun.',
createdOn: Fri May 15 2015 18:20:57 GMT+0100 (IST),
author: 'Simon Holmes' }]
Which is fine. But then when I try to get the review using the Mongoose id function, I always get back null:
review = guide.reviews.id('5550c26e8c334adebc7c5dc3');
console.log('review is ');
console.log(review);
With result:
review is
null
Any idea what I'm doing wrong?
The MongooseDocumentArray.id(id) method searches the document array for an _id property, but your subdocuments don't have an _id property, they instead have id. You'll either have to change it to _id, or use a plain old .filter() or similar workaround.
I may be wrong, but I've never seen that sort of API in Mongoose before.
document.id exists, but it's a function that returns the stringified version of the _id field.
In your case, you can just go through the list with a loop and select the one you want.
var review = false
for (var i = 0; i < guide.reviews.length; i++) {
var el = guide.reviews[i]
if(review._id == '5550c26e8c334adebc7c5dc3') {
review = el
}
}
If you use lodash, you can also use the indexBy method to convert the array to an hash in which all items are identified by the specified field:
var _ = require('lodash')
var reviews = _.indexBy(guide.reviews, '_id')
// Then you can do reviews['5550c26e8c334adebc7c5dc3']

How can i retrieve a subrecord from mongo DB

I have a db that looks like this :
{
"_id" : ObjectId("50525e55467f67da3f8baf06"),
"client" : "client1"
}
{
"_id" : ObjectId("505262f0deed32a758410b1c"),
"client" : "client2"
}
{
"_id" : ObjectId("5052fe0a37083fd981616589"),
"client" : "client3",
"products" : [
{"name" : "product1"},
{"name" : "product2"}
]
}
How can i retrieve the product list of client3 without retrieving the client3 record ?
The output should look like this :
[
{"name" : "product1"},
{"name" : "product2"}
]
I don't think you can completely exclude the client3 record as the products are part of this record, but you can select just the products field like this:
db.<dbname>.find({ 'client' : 'client3' }, { 'products' : 1, '_id' : 0 })
Also - if you want to get just the matching subrecord - see here
http://docs.mongodb.org/manual/reference/operator/projection/positional/
you use the $ operator in the project portion of find to specify the n'th subrecord where that is the first to match your query.

Resources