How to publish posts depend on privacy? - mongodb

I need to publish a queue from collection Posts depending of privacy. But I don't have any idea how to get this. I've draw mindmap of main concept what I try to realize:
Input:
var currentUser = "Andrey";
Events.find();
Output:
[
{
//it's going to output, becouse I've created this post
"_id" : "1",
"createdBy" : "Andrey",
"private" : true,
"title" : "",
"text": "",
"members" : [
"Sheldon", "Mike"
]
},
{
//it's going to output, becouse this post not private
"_id" : "2",
"createdBy" : "Sheldon",
"private" : false,
"title" : "",
"members" : []
},
{
//it's going to output, becouse I'm one of members'
"_id" : "3",
"createdBy" : "Mike",
"private" : true,
"title" : "",
"text": "",
"members" : [
"Andrey"
]
},
{
//it's NOT going to output, becouse it's private post, I'm not the member or author
"_id" : "4",
"createdBy" : "Ana",
"private" : true,
"title" : "",
"text": "",
"members" : [
"Sheldon"
]
},
]
Expected result:
[
{
"_id" : "1",
"createdBy" : "Andrey",
"private" : true,
"title" : "",
"text": "",
"members" : [
"Sheldon", "Mike"
]
},
{
"_id" : "2",
"createdBy" : "Sheldon",
"private" : false,
"title" : "",
"text": "",
"members" : []
},
{
"_id" : "3",
"createdBy" : "Mike",
"private" : true,
"title" : "",
"text": "",
"members" : [
"Andrey"
]
}
]
But this idea can be wrong at all, maybe you have another way?

I am not sure if this is the right approach but probably I'd try something like that if I face this issue. (By the way I'd use userId instead of names. If you used names in order to explain your aim, please ignore this comment)
As long as I remember, you can return multiple query results like that
return [
Result1,
Result2,
]
For the public posts, no need to worry I guess. Just returning the ones with private: false is sufficient.
For the private ones, I'd use userId as parameter and try this composite publication:
Meteor.publish('posts', function postPublication(userId) {
return [
Posts.find({
$and: [{
$or: [
{_id: userId},
{memberId: {$in: userId}}
]},
{private: {$eq: true}}
]}), // => should return private posts created or commented by member
Posts.find({private: {$eq: false}}) // => should return public ones
];
}
Then do the subscription with userId
Meteor.subscribe('posts', userId);

This code how I solved this task, with $or $and. But not sure too, if it's a best solution. But I get expected result.
Meteor.publish('posts', function(currentUser) {
selector = {
$or : [
{ $and : [ {'private': true, 'members': currentUser} ] },
{ $and : [ {'private': true, 'author': currentUser} ] },
{ $and : [ {'private': false, 'author': currentUser} ] },
{ $and : [ {'private': undefined} ] },
{ $and : [ {'private': false} ] },
]
}
return Posts.find(selector);
});

Related

How to join two table in mongodb with aggregate and lookup I have as array in second table?

I have two collections:
clients
{
"_id" : ObjectId("60d203145b7b6c19b00ba576"),
"isDeleted" : false,
"createdAt" : ISODate("2021-06-22T15:06:21.564Z"),
"status" : "live",
"customerGUID" : "C8B910A3F74E",
"apps" : [
{
"url" : "https://test.com",
"loginGUID" : "12324654",
"loginAPIAccessKey" : "B072-266C369743CA",
}
],
"avatar" : "",
"description" : "",
"firstName" : "firstname",
"lastName" : "lastname",
"email" : "company#test.com",
"companyName" : "Company name",
}
visitors
{
"_id" : ObjectId("60aed4e0d6e30865f060be3c"),
"lastName" : "CAIN",
"firstName" : "DAVID",
"loginAPIAccessKey" : "B072-266C369743CA",
"email" : "cainins#att.net",
"createdAt" : ISODate("2021-05-26T23:08:16.505Z"),
"activity" : []
}
I want all visitors which have active clients with isDeleted: false status. and the relationship between visitors and clients is visitors.loginAPIAccessKey and clients.apps.loginAPIAccessKey which is in an array of objects.
Can someone help me?
You have to make use of the $lookup stage with the pipeline option.
db.visitors.aggregate([
{
"$lookup": {
"from": "clients",
"let": {
"loginAPIAccessKey": "$loginAPIAccessKey"
},
"pipeline": [
{
"$unwind": "$apps"
},
{
"$match": {
"$expr": {
"$eq": [
"$apps.loginAPIAccessKey",
"$$loginAPIAccessKey"
],
},
"isDeleted": false,
},
},
],
"as": "matchedClients"
}
},
{
"$match": {
"matchedClients": {
"$ne": []
}
},
},
])

MongoDB - group documents by field value

I need to group following documents by featured and trending.
[
{
"_id" : ObjectId("5f22546cd49ffe0d6f087a2e"),
"title" : "....",
"image" : "...",
"featured" : true,
"trending" : false,
"creationDate" : ISODate("2020-07-30T05:02:36.592Z")
},
{
"_id" : ObjectId("5f22546cd49ffe0d6f087a2f"),
"title" : "Cras non dolor",
"image" : "...",
"featured" : false,
"trending" : true,
"creationDate" : ISODate("2020-07-30T05:02:36.592Z")
}
]
So, after grouping they should be in following format -
[
{
_id: null,
featured: [
{
"_id" : ObjectId("5f22546cd49ffe0d6f087a2e"),
"title" : "....",
"image" : "...",
"featured" : true,
"trending" : false,
"creationDate" : ISODate("2020-07-30T05:02:36.592Z")
}
],
trending: [
{
"_id" : ObjectId("5f22546cd49ffe0d6f087a2f"),
"title" : "Cras non dolor",
"image" : "...",
"featured" : false,
"trending" : true,
"creationDate" : ISODate("2020-07-30T05:02:36.592Z")
}
]
}
]
How can I get this result through aggregation or any other way?
I have been trying with aggregate $group. But I can't figure out how can I $group via featured/trending value with equal true.
So, I don't need corresponding false value when grouping them. Also, there might have other fields like highlighted etc along with featured trending.
This is quite easy to achieve, the easiest way to do it is using $facet
db.collection.aggregate([
{
$facet: {
featured: [
{
$match: {
"featured": true
}
}
],
trending: [
{
$match: {
"trending": true
}
}
]
}
}
])
MongoPlayground

MongoDB query using values priority

I have following documents in my collection:
/* 1 */
{
"_id" : ObjectId("5ea32d11f213c27c35395fd3"),
"name" : "Test",
"state" : "OH",
"code" : "CDM"
}
/* 2 */
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
/* 3 */
{
"_id" : ObjectId("5ea32d38f213c27c35395fe7"),
"name" : "Test2",
"state" : "OH",
"code" : "ALL"
}
/* 4 */
{
"_id" : ObjectId("5ea32d46f213c27c35395feb"),
"name" : "Test3",
"state" : "ALL",
"code" : "ALL"
}
Trying to filter documents based on state and code.
But there are few criteria if particular state or code is not found then search with value ALL
Example 1:
state = CA
code = CDM
then return only
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
Example 2:
state = CA
code = DCM
then return only
{
"_id" : ObjectId("5ea32d46f213c27c35395feb"),
"name" : "Test3",
"state" : "ALL",
"code" : "ALL"
}
etc..
The query that i tried was state = CA ,code = CDM:
db.getCollection('user_details').aggregate([
{'$match': { 'state': {'$in': ['CA','ALL']},
'code': {'$in': ['CDM','ALL']}}}
])
return was:
/* 1 */
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
/* 2 */
{
"_id" : ObjectId("5ea32d46f213c27c35395feb"),
"name" : "Test3",
"state" : "ALL",
"code" : "ALL"
}
Expected was :
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
Could you help to solve this issue.
Try below query :
db.collection.find({
$or: [
{
state: "CA",
code: "CDM"
},
{
state: "ALL",
code: "CDM"
},
{
state: "ALL",
code: "ALL"
},
{
state: "CA",
code: "ALL"
}
]
})
So basically you can not achieve what you're looking for in one query, you can try aggregation operator $facet & $switch to do this but I would not prefer to do that cause each stage in facet will do the given operation on entire collection's data. Instead get fewer docs using above query & in your code you can actually filter for best suited doc, Which would be easy & efficient. Don't forget to maintain indexes on DB.
Test : mongoplayground
Try this one:
db.user_details.aggregate([
{
$facet: {
match: [
{
"$match": {
$or: [
{
"state": "CA",
"code": {
"$in": [ "CDM", "ALL" ]
}
},
{
"state": {
"$in": [ "CA", "ALL"]
},
"code": "CDM"
}
]
}
}
],
all: [
{
"$match": {
"state": "ALL",
code: "ALL"
}
}
]
}
},
{
$project: {
match: {
$cond: [
{
$eq: [ { $size: "$match" }, 0]
},
"$all",
"$match"
]
}
}
},
{
$unwind: "$match"
},
{
$replaceWith: "$match"
}
])
MongoPlayground

Create an unique list of multiple arrays inside a subdocument.

I would like to create an unique list of array values inside a subdocument.
Document:
{
"_id" : ObjectId("5aee0e3c059638093b69c8b3"),
"firstname" : "John",
"lastname" : "Doe",
"websites" : [
{
"_id" : ObjectId("123"),
"key" : "website2",
"url" : "www.xxx.com",
"tags" : [
"php",
"python",
"java"
]
},
{
"_id" : ObjectId("456"),
"key" : "website2",
"url" : "www.yyy.com",
"tags" : [
"java",
"php"
]
},
{
"_id" : ObjectId("789"),
"key" : "website3",
"url" : "www.zzz.com",
"tags" : [
"java",
"html",
"css"
]
}
]
}
Expected output:
{
"_id" : ObjectId("5aee0e3c059638093b69c8b3"),
"firstname" : "John",
"lastname" : "Doe",
"unique_tags": [
"java",
"php",
"python",
"html",
"css",
],
"websites" : [
{
"_id" : ObjectId("123"),
"key" : "website2",
"url" : "www.xxx.com",
"tags" : [
"php",
"python",
"java"
]
},
{
"_id" : ObjectId("456"),
"key" : "website2",
"url" : "www.yyy.com",
"tags" : [
"java",
"php"
]
},
{
"_id" : ObjectId("789"),
"key" : "website3",
"url" : "www.zzz.com",
"tags" : [
"java",
"html",
"css"
]
}
]
}
It looks like Mongo has a distinct functionality, but this does not work inside an aggregate query (right?!).
Also tried to unwind on websites.tags and use the addToSet functionality but it also hasn't the right output.
Any ideas?
You can try below aggregation:
db.col.aggregate([
{
$addFields: {
unique_tags: {
$reduce: {
input: {
$concatArrays: {
$map: {
input: "$websites",
as: "website",
in: "$$website.tags"
}
}
},
initialValue: [],
in: { $setUnion : ["$$value", "$$this"]}
}
}
}
}
])
To flatten an array of arrays (websites -> tags) you can use $map with $concatArrays. Then you'll get an array of all tags from all websites. To get only unique values you can use $reduce with $setUnion (which drops duplicates).

mongodb Can't get the query to work

I'm trying to do a query in mongodb but I can't get it to work.
My document looks something like this.
{
"_id" : ObjectId("5305e54133e65b7341d63af3"),
"clients" : [
{
"aggregations" : {
"department" : [
"department1",
"department3"
],
"customer" : "customer2"
},
"lastLogin" : ISODate("2014-02-26T09:41:56.445Z"),
"locale" : "en"
"name" : "Test",
"validFrom" : null,
"validTo" : null,
"visiting" : {
"phone" : "031-303030",
"company" : "MyCompany",
"office" : [
"jag är ett test",
"lite mer data"
],
"country" : "Norge"
}
},
{
"approvedEmailSent" : true,
"lastLogin" : ISODate("2014-03-01T15:27:12.252Z"),
"locale" : "en",
"name" : "Test2",
"visiting" : {
"phone" : "031-307450",
"company" : "Other Company",
"branch" : "Advertising agency"
}
}
],
"firstname" : "Greger",
"lastname" : "Aronsson",
"username" : "TheUsername"
}
As you can see a user can have many clients. They are matched by name. The clients have visiting.company but sometimes this will not be the case.
I want to query where the clients.name is Test and regexp for visting.company and also firstname, lastname. If I'm logged in at Test2 I don't want hits on visiting.company "MyCompany". Hope this makes sense!
You can write query like :
db.visitCompany2.find({ $or : [
{'clients.name': 'Test2'}, //Company name
{'clients.visiting.company': {
$regex: /Other/g //Your visiting company regex
}},
{firstname: "Greger"},
{"lastname": "Aronsson}"
]}, {
'clients.$': 1, //projection for clients
firstname: 1,
lastname: 1
});
Output:
{
"_id": ObjectId("5305e54133e65b7341d63af3"),
"clients": [{
"approvedEmailSent": true,
"lastLogin": ISODate("2014-03-01T15:27:12.252Z"),
"locale": "en",
"name": "Test2",
"visiting": {
"phone": "031-307450",
"company": "Other Company",
"branch": "Advertising agency"
}
}],
"firstname": "Greger",
"lastname": "Aronsson"
}