mongodb aggregate - select with special conditions - javascript

I have 2 collections.
1. user:
{
"_id": "111",
...,
"type": 1
}
2. query:
{
"_id": {
"$oid": "58bbf5bf17cc9100046b0dff"
},
"query": "some query",
"user_id": "111",
"links": []
}
What i want to select: all queries for some special user type.
Now I'm selecting all users with some user type and include all it's queries as a field - that's not actually what I need.
db.collection('user').aggregate([
{$match : {'type': userType}},
{$lookup: {from: 'query', localField: '_id', foreignField: 'user_id', as: 'queries'}},
{$unwind: '$queries'}
], function(err, results) {...});
Thanks.
Edit:
Example: I want to select all the queries for the users with the type 1. So expect output is like:
[{
"_id": {
"$oid": "58bbf5bf17cc9100046b0dff"
},
"query": "some query",
"user_id": "111",
"links": []
},
{
"_id": {
"$oid": "58bbf5bf17cc9100046b0dff"
},
"query": "some query",
"user_id": "111",
"links": []
}]

it would be easier to achieve this with 2 distinct queries :
get a list of user _id which have desired type
get all queries where userId is in the list
here is a sample script:
// filter users, and push all matching _id in listId field
var cursor = db.user.aggregate([{$match: {type: 1}}, {$group: {_id: null, listId: {$push: "$_id"}}}]);
if (cursor != null && cursor.hasNext()) {
var listId = cursor.next().listId;
// find queries where user_id is in listId
var result = db.query.find({user_id: {$in: listId}});
if (result != null && result.hasNext()) {
while (result.hasNext()) {
print(JSON.stringify(result.next()));
}
}
}
to run it, paste the code in a file named script.js, and then run from your shell
mongo databaseName < script.js

You can try below aggregation.
db.collection('user').aggregate([
{$match : {'type': userType}},
{$lookup: {from: 'query', localField: '_id', foreignField: 'user_id', as: 'queries'}},
{$unwind: '$queries'},
{$replaceRoot:{newRoot:"$queries"}}
]

Related

Filter on mongoDb an array of strings

im building a chatbot and i want to filter a MongoDb database with the input of the users
words=["hello","price","bye"]
and my db is like that
{
"Lang": "en",
"kw1": [
"price"
],
"Keyword Group 2": [
"test"
],
"Keyword Group 3": [
"res"
],
"Type": "Text",
"Text": "aaaaaAAAAAaaa",
"createdAt": 1662724328993,
"etag": "d-Zwyn11c6q6DfK+AV6RVxl9i7OJQ",
"_version": 2,
"updatedAt": 1662724336488
}
i've tried to do it like this
this.fetchDataFromDataSource({ channel: this.channel, collectionName: "62a985781cd96396e4e1cba3_test", filter:{ kw1: words
} }).then((result) => {
console.log(result)
})
when the input is only a word it works well but when i send more than one word it doesn't behave as expected, how im supposed to do it?
Try using the $in operator:
this.fetchDataFromDataSource({
channel: this.channel,
collectionName: '62a985781cd96396e4e1cba3_test',
filter: { kw1: { $in: words } },
}).then((result) => {
console.log(result);
});

How can I merge two mongo collections?

I am banging my head against the wall on this...
SEE UPDATE 1 (below) !
I am merging two collections together... I looked at this example ( and ~several~ other examples here on SO ... )
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#lookup-single-equality
I think I am really close, but my expected results are not the same as what I would expect out of the example.
Here is the schema for 'Event'
const EventSchema = new Schema({
name: {type: String, required: true},
})
Here is some 'Event' data
[
{
"_id": "5e8e4fcf781d96df5c1f5358",
"name": "358 Event"
},
{
"_id": "5e8e55c5a0f5fc1431453b5f",
"name": "b5f Event"
}
]
Here is 'MyEvent' schema:
const MyEventSchema = new Schema({
userId: {type: Schema.Types.ObjectId, required: true},
eventId: {type: Schema.Types.ObjectId, required: true},
})
Here is some 'MyEvent' data
[
{
"_id": "5e8f4ed2ddab5e3d04ff30b3",
"userId": "5e6c2dddad72870c84f8476b",
"eventId": "5e8e4fcf781d96df5c1f5358",
}
]
Here is my code ( the code is wrapped in a promise so it returns resolve and reject with data )
var agg = [
{
$lookup:
{
from: "MyEvent",
localField: "_id",
foreignField: "eventId",
as: "userIds"
}
}
];
Event.aggregate(agg)
.then( events => {
return resolve(events);
})
.catch(err => {
return reject(null);
})
Here are my results,
[
{
"_id": "5e8e4fcf781d96df5c1f5358",
"name": "358 Event",
"__v": 0,
"UserIds": []
},
{
"_id": "5e8e55c5a0f5fc1431453b5f",
"name": "b5f Event",
"__v": 0,
"UserIds": []
}
]
I expect to see UserIds filled in for event '358 Event', like this
What am I missing ???
[
{
"_id": "5e8e4fcf781d96df5c1f5358",
"name": "358 Event",
"__v": 0,
"UserIds": [
{"userId": "5e6c2dddad72870c84f8476b"}
]
},
{
"_id": "5e8e55c5a0f5fc1431453b5f",
"name": "b5f Event",
"__v": 0,
"UserIds": []
}
]
UPDATE 1
I found a mongo playground and what I have works there, but it doesn't work in my code ??
https://mongoplayground.net/p/fy-GP_yx5j7
In case the link breaks, here is configuration: * select 'bson multiple collections'
db={
"collection": [
{
"_id": "5e8e4fcf781d96df5c1f5358",
"name": "358 Event"
},
{
"_id": "5e8e55c5a0f5fc1431453b5f",
"name": "b5f Event"
}
],
"other": [
{
"_id": "5e8f4ed2ddab5e3d04ff30b3",
"userId": "5e6c2dddad72870c84f8476b",
"eventId": "5e8e4fcf781d96df5c1f5358",
}
]
}
Here is Query:
db.collection.aggregate([
{
$lookup: {
from: "other",
localField: "_id",
foreignField: "eventId",
as: "userIds"
}
}
])
Here is the result:
[
{
"_id": "5e8e4fcf781d96df5c1f5358",
"name": "358 Event",
"userIds": [
{
"_id": "5e8f4ed2ddab5e3d04ff30b3",
"eventId": "5e8e4fcf781d96df5c1f5358",
"userId": "5e6c2dddad72870c84f8476b"
}
]
},
{
"_id": "5e8e55c5a0f5fc1431453b5f",
"name": "b5f Event",
"userIds": []
}
]
any suggestions as to why this doesn't work in my code... but works in the playground?
UPDATE 2
I found this:
Need a workaround for lookup of a string to objectID foreignField
UPDATE 3
I have changed the schema to use ObjectId for ids now
still doesn't work
And they are ObjectIds :
RESOLUTION:
So the real answer was a combination of Update 2 and Update 3 and using the right collection name in the lookup.
Update 2 is pretty much my very same question... just using different table names
Update 3 is the correct way to solve this issue.
Mohammed Yousry pointed out the collection name might be wrong... so I looked at my schema and I did have it wrong - changed the name to the right name (along with ObjectId types) and it worked !
It seems there's a typo in from property in $lookup, MyEvent maybe not the collection name
db.collection.aggregate([
{
$lookup: {
from: "MyEvent", // here is the issue I think, check the collection name and make sure that it matches the one you write here
localField: "_id",
foreignField: "eventId",
as: "userIds"
}
}
])
in mongo playground you attached in the question, if you change the 'other' in the $lookup to anything else, or make a typo in it .. like others instead of other, you will face the same issue
so check that there is no typo in the word MyEvent that you populate from

mongodb: Perform operation on data according to date

This is a sample JSON object, among 1000 like them, stored in my MongoDB collection.
{
"_id": ObjectId("5b1bb74ffc7ee601c6915939"),
"groupId": "-abcde",
"applicationId": "avcvcvc",
"integration": "web",
"Category": "message",
"Action": "message",
"Type": "newMessage",
"Id": "activity",
"data": {
"test": "good morning"
},
"timestamp": 1528543055858.0,
"createdAt": ISODate("2018-06-09T11:17:35.868+0000"),
"updatedAt": ISODate("2018-06-09T11:17:35.868+0000"),
"__v": NumberInt(0)
}
This is a query where i fetch data according to date
db.collection.find({"createdAt" : { $gte : new ISODate("2018-06-09T11:17:35.868+0000") }});
This is an operation which i need to perform to the JSON objects recieved from fetching data acc. to date
db.collection.aggregate( [
{ $match: { $or: [ { Type:"on mouse hover click" },{Type:"on mouse out"},
{Type : "on chat start"},{Type :"Load Event"}
] } },
{ $group: { _id:null , count: { $sum: 1 } } }
] );
Is there any way where i can make both these operations perform in a single query rather than fetching data acc. to date first and performing aggregation after? I am new to MongoDB so i cant quite figure out how to do this.
You could just $match for the timestamp during aggregation:
db.collection.aggregate( [
{ $match: { "createdAt" : { $gte : new ISODate("2018-06-09T11:17:35.868+0000") }}},
{ $match: { $or: [ { Type:"on mouse hover click" },{Type:"on mouse out"}, {Type : "on chat start"},{Type :"Load Event"} ] } },
{ $group: { _id:null , count: { $sum: 1 } } }
]);

How to use Group by with find in mongoose?

I have collection named "listing" with fields such as metadata and status. metadata is a object containing user object inside and status is a string.
The structure looks like this,
{ "status": "Active", "metadata": {
"user": {
"urlProfile": "",
"averageRating": 5,
"reviewCount": 2,
"userId": "1244324"
} } }
Now the status field have values such as "Active" and "Inactive". I need to group by those status and filter by the userId. so i have a function inside the helper as follows,
query: function (model, conditon, options) {
console.log(conditon, options);
return new Promise(function (resolve, reject) {
options = options || {};
model.find(conditon, {}, options).exec(function (error, data) {
if (error) {
reject(error);
}
resolve(data);
})
})
}
Question is how can i pass the group by along with the user id and query the data needed. Right now my querying part looks like this,
return dbService.query(sellerModel, {
'metadata.user.userId': userIdRetrieved
}, {});
how can i pass the group by condition? i looked for sample, did not find any solution till now.
Sample Collection
Expected Output:
[{
"Status": "Active",
"Results": {
"user": {
"urlProfile": "",
"averageRating": 5,
"reviewCount": 2,
"userId": "1244324"
}
}
}
,
{
"Status": "InActive",
"Results": {
"user": {
"urlProfile": "",
"averageRating": 5,
"reviewCount": 2,
"userId": "1244324"
}
}
}]
To get the desired output, you would need to use the aggregate method since it offers the operators which allow you to aggregate the documents and return the said result.
Consider constructing a pipeline that consists of a $group stage, whereby you aggregate the average rating via the $avg accumulator, the reviewCount with $sum and the other fields in the group using $first or $last. Your group by key is a subdocument with two fields Status and userId.
A final $project step would allow you to reshape the output from the above group aggregates to the desired form and the aggregate() method returns a query which you can then call exec() to get a Promise.
To explain the above framework, follow this example:
query: function (model, conditon, options) {
console.log(conditon, options);
options = options || {};
return model.aggregate([
{ "$match": conditon },
{
"$group": {
"_id": {
"Status": "$status",
"userId": "$metadata.user.userId"
},
"urlProfile": { "$first": "$metadata.user.urlProfile" },
"averageRating": { "$avg": "$metadata.user.averageRating" },
"reviewCount": { "$sum": "$metadata.user.reviewCount" }
}
},
{
"$project": {
"_id": 0,
"Status": "$_id.Status",
"Results": {
"user": {
"averageRating": "$averageRating",
"reviewCount": "$reviewCount",
"userId": "$_id.userId"
}
}
}
}
]).exec();
}

$lookup giving no results in mongodb

Note: Edits below where I tried this directly using mongo shell and correct collection names, but still the same issue.
I am currently trying to learn Node and Mongodb. I am looking to understand how to add one document with another in a query. All the documentation points back to $lookup.
I have the two following models set up, which both work perfectly on their own
var BearSchema = new Schema({
name: String
});
module.exports = mongoose.model('Bear', BearSchema);
var CommentSchema = new Schema({
creator_id : { type: String, ref: 'Bear' },
comment: String
});
module.exports = mongoose.model('Comment', CommentSchema);
I will omit other set up details and get straight to the queries.
When I run Bear.find() I get the expected result...
[
{
"_id": "585887a29b7915f437742b88",
"name": "new bear",
"__v": 0
}
]
When I run Comment.find() I get the expected result...
[
{
"_id": "585887ae9b7915f437742b89",
"creator_id": "584de876238179030d7d7916",
"comment": "yoyoyo",
"__v": 0
},
{
"_id": "585887e09b7915f437742b8a",
"creator_id": "585887a29b7915f437742b88",
"comment": "ok lets give this a go",
"__v": 0
}
]
Note the creator_id in the second comment is the same as the _id in the bear result.
I then run
Bear.aggregate([
{
$lookup: {
from: "Comment",
localField: "_id",
foreignField: "creator_id",
as: "comments"
}
}
], function (err, bears) {
if (err)
res.send(err);
res.json(bears);
});
and get the following:
[
{
"_id": "585887a29b7915f437742b88",
"name": "new bear",
"__v": 0,
"comments": []
}
]
I was hoping the following would appear:
[
{
"_id": "585887a29b7915f437742b88",
"name": "new bear",
"__v": 0,
"comments": [
{
"_id": "585887e09b7915f437742b8a",
"creator_id": "585887a29b7915f437742b88",
"comment": "ok lets give this a go",
"__v": 0
}
]
}
]
I cant understand in this situation how it would know what "Comment" is referring to.
EDIT: From the documentation I can see the from field says: Specifies the collection in the same database to perform the join with. The from collection cannot be sharded.
EDIT 2: In mongoshell I have ran the following queries and their results, as you can see the same issue is still appearing even with the correct collection name, however I can now see ObjectId() may be the issue...
> show collections
bears
comments
> db.bears.find();
{ "_id" : ObjectId("585887a29b7915f437742b88"), "name" : "new bear", "__v" : 0 }
> db.comments.find();
{ "_id" : ObjectId("585887ae9b7915f437742b89"), "creator_id" : "584de87623817903
0d7d7916", "comment" : "yoyoyo", "__v" : 0 }
{ "_id" : ObjectId("585887e09b7915f437742b8a"), "creator_id" : "585887a29b7915f4
37742b88", "comment" : "ok lets give this a go", "__v" : 0 }
> db.bears.aggregate([ { $lookup: { from: "comments", localField: "_id", foreign
Field: "creator_id", as: "comments" } } ]);
{ "_id" : ObjectId("585887a29b7915f437742b88"), "name" : "new bear", "__v" : 0,
"comments" : [ ] }
whenever you'r using $lookup, you must add an extra "s" in "from" field.
for example:
if your table name is
"register"
then you have to write
"registers"
Note: at the time of $lookup only
I resolved this. There were two issues.
The Bear Schema _id is actually an ObjectID() so it wasnt comparing the two correctly.
I misunderstood what collection names were and so Comment would not have been recognised.
Solution:
When creating the Comment Model I used Schema.ObjectId
var CommentSchema = new Schema({
creator_id : { type: Schema.ObjectId, ref: 'Bear' },
comment: String
});
When doing the query I used comments instead of Comment as this was the collection named Mongoose created.
Bear.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "creator_id",
as: "comments"
}
}

Categories