Is MongoDB conditional aggregation available in Meteor 1.4.x ? - javascript

I'm trying to publish certain fields to a page based on a parameter of a document in a mongo collection. This is from the MongoDB manual: https://docs.mongodb.com/manual/reference/operator/aggregation/cond/
Does Meteor support conditional aggregation?
return Cases.find({
subscribers: { $in: [this.userId] }
}, {
fields: { $cond: [ { $eq: ['classified', true] } , Cases.privateFields, Cases.publicFields ] }
});

So, I realized I was approaching aggregates incorrectly. After doing some research on options to implement aggregation (meteorhacks) I decided to implement my own, which keeps the reactivity of the collection:
Extend Mongo.Collection
class CasesCollection extends Mongo.Collection {
conditionalFields(selector, modifier) {
let cond = modifier.fields[0],
pos = modifier.fields[1],
neg = modifier.fields[2];
if ( cond ) {
modifier = { fields: pos };
} else {
modifier = { fields: neg };
}
const cursor = this.find(selector, modifier);
return cursor;
}
}
Usage:
Meteor.publish('cases.dashboard', function casesDashboard() {
if (!this.userId) {
return this.ready();
}
return Cases.conditionalFields({
subscribers: { $in: [this.userId] }
}, {
fields: [ { $eq: ['classified', true] } , Cases.privateDashboardFields, Cases.dashboardFields ]
});
});

Related

Is there any way to compare an array in mongo aggregate?

This code does not match the bu.subUser with subUser and due to mismatch nothing is returned. How to achieve the output?
{
$match: {
subUser: {
$ne: blockedUsers.map((bu) => {
return bu.subUser;
}),
},
},
}
{ $match: { subUser: { $nin: blockedUsers.map((bu) => bu.subUser) } } }

how to print all students name which have percentage more than 70% in javascript?

I am using json-rule-engine .
https://www.npmjs.com/package/json-rules-engine
I am having a student list which have name and their percentage, Also I have business rule the percentage should be greater thank or equal to than 70 . so I want to print all students name those have percentage more than 70
here is my code
https://repl.it/repls/AlienatedLostEntropy#index.js
student list
const students = [
{
name:"naveen",
percentage:70
},
{
name:"rajat",
percentage:50
},
{
name:"ravi",
percentage:75
},
{
name:"kaushal",
percentage:64
},
{
name:"piush",
percentage:89
}
]
rule
engine.addRule({
conditions: {
all: [
{
fact: "percentage",
operator: "greaterThanInclusive",
value: 70
}
]
},
onSuccess(){
console.log('on success called')
},
onFailure(){
console.log('on failure called')
},
event: {
type: "message",
params: {
data: "hello-world!"
}
}
});
code
https://repl.it/repls/AlienatedLostEntropy#index.js
any update
The json-rules-engine module takes data in a different format. In your Repl.it you have not defined any facts.
Facts should be:
let facts = [
{
name:"naveen",
percentage:70
},
[...]
Also, the module itself doesn't seem to process an array of facts. You have to adapt it to achieve this. This can be done with:
facts.forEach((fact) => {
engine
.run(fact)
[...]
Finally, the student data is found inside the almanac. You can get these values with: results.almanac.factMap.get('[name|percentage|age|school|etc]').value
Here is the updated Repl.it: https://repl.it/#adelriosantiago/json-rules-example
I might have submitted a completely unrelated answer, but here goes. Since the students object is an array, you could just loop through it and then use an if else statement.
for (let i = 0; i < students.length; i++) {
if (students[i].percentage >= 70) {
console.log(students[i].name);
}
}
Sorry if this is incorrect!
Here is a working example.
Counting success and failed cases
const { Engine } = require("json-rules-engine");
let engine = new Engine();
const students = [
{
name:"naveen",
percentage:70
},
{
name:"rajat",
percentage:50
},
{
name:"ravi",
percentage:75
},
{
name:"kaushal",
percentage:64
},
{
name:"piush",
percentage:89
}
]
engine.addRule({
conditions: {
all: [{
fact: 'percentage',
operator: 'greaterThanInclusive',
value: 70
}]
},
event: { type: 'procedure_result'}
})
let result = {success_count : 0 , failed_count : 0}
engine.on('success', () => result.success_count++)
.on('failure', () => result.failed_count++)
const getResults = function(){
return new Promise((resolve, reject) => {
students.forEach(fact => {
return engine.run(fact)
.then(() => resolve())
})
})
}
getResults().then(() => console.log(result));

Mongodb - Update property per object in array of object, Insert if doesn't exists

I wish to update a property per object in array of objects, but if some of the objects doesn't exists, insert the object instead.
Currently I used "upsert", which creates a new document when no document matches the query, unfortunately it is replacing a single item with my entire list.
Worth to mention that I am using mongoist to perform an async requests.
My code:
this.tokenArray = [
{ token: "654364543" },
{ token: "765478656" },
{ token: "876584432" },
{ token: "125452346" },
{ token: "874698557" },
{ token: "654364543" }
]
database.upsertDatebaseItem(this.tokenArray.map(x => { return x.token }), { valid : true }, 'Tokens');
async upsertDatebaseItem(itemKey, itemValue, collectionName) {
try {
await this.database[collectionName].update({ token : { $in: itemKey}}, { $set: itemValue }, {upsert : true} , {multi : true});
} catch (error) {
console.log(`An error occurred while attempting to update ${itemType} to the database: ${error}`);
return false;
}
}
Found the way to do it:
const bulkUpdate = this.tokenArray.map((x) => {
return {
"updateOne": {
"filter": { "token": x.token },
"update": { "$set": { "valid": true } },
"upsert": true
}
};
});
and:
this.database[collectionName].bulkWrite(bulkUpdate);
To upsert with mongoist, use the following:
var bulk = db.collection.initializeOrderedBulkOp()
for(var doc of docs) bulk.find( { _id: doc._id } ).upsert().updateOne(doc); // or use replaceOne()
await bulk.execute();
Converted to your case that would be
var bulk = db.collectionName.initializeOrderedBulkOp()
for(var tokenItem of tokenArray) bulk.find( { token : tokenItem.token } ).upsert().updateOne(tokenItem); // or use replaceOne()
await bulk.execute();

Merge two filters in kibana3/elastic.js

I have two filters in my custom panel in Kibana3:
request = request.query(
$scope.ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids()) // has to be merged with BoolFilter2
))
.size($scope.panel.size);
This works for either of the two filters but I want to filter with the two objects but I dont know how to merge them. Can anyone help?
Kibana 3 filters can be set to the "either" mode instead of "must".
This translates into filters that look like this:
{
"filter": {
"bool": {
"should": [
{
"fquery": {
"query": {
"query_string": {
"query": "test1"
}
},
"_cache": true
}
},
{
"fquery": {
"query": {
"query_string": {
"query": "test2"
}
},
"_cache": true
}
}
]
}
}
}
Here is the filterSrv code that handles that in Kibana 3: https://github.com/elasticsearch/kibana/blob/3.0/src/app/services/filterSrv.js#L144
this.getBoolFilter = function(ids) {
var bool = ejs.BoolFilter();
// there is no way to introspect the BoolFilter and find out if it has a filter. We must keep note.
var added_a_filter = false;
_.each(ids,function(id) {
if(dashboard.current.services.filter.list[id].active) {
added_a_filter = true;
switch(dashboard.current.services.filter.list[id].mandate)
{
case 'mustNot':
bool.mustNot(self.getEjsObj(id));
break;
case 'either':
bool.should(self.getEjsObj(id));
break;
default:
bool.must(self.getEjsObj(id));
}
}
});
// add a match filter so we'd get some data
if (!added_a_filter) {
bool.must(ejs.MatchAllFilter());
}
return bool;
};
this.getEjsObj = function(id) {
return self.toEjsObj(dashboard.current.services.filter.list[id]);
};
You could modify that function to take an optional argument that sets the mandate to either.

Update field in Object array in Meteor (mongoDB)

I have the following document:
{
"gameName":"Shooter",
"details":[
{
"submitted":1415215991387,
"author":"XYZ",
"subPlayer":{
"members":{
"squad1":[
{
"username":"John",
"deaths":0
}
]
},
"gameSlug":"0-shooter"
}
}
],
"userId":"foL9NpoZFq9AYmXyj",
"author":"Peter",
"submitted":1415215991608,
"lastModified":1415215991608,
"participants":[
"CXRR4sGf5AdvSjdgc",
"foL9NpoZFq9AYmXyj"
],
"slug":"1-shooterConv",
"_id":"p2QQ4TBwidjeZX6YS"
}
... and the following Meteor method:
Meteor.methods({
updateDeaths: function(gameSlug, user, squad) {
Stats.update({details.subPlayer.gameSlug: gameSlug}, ...}); // ???
}
});
My goal is to update the field deaths. The method has the argument user which is the user object (username = user.username). Furthermore, the argument squad is the squad name as string, e.g. squad1.
How can I do this?
Any help would be greatly appreciated.
You should use something like this:
db.collection.update(
{'userId':'foL9NpoZFq9AYmXyj', 'details.subPlayer.gameSlug':'0-shooter'},
{'$set':
{'details': [{
'subPlayer': {
'members': {
'squad1':[{'username':'John','death':1}]
}
}
}]
}
}
)
UPD:
Also, maybe a better way is to use find().snapshot().forEach(). But I don't know how Meteor supports it. Example:
Stats.find({'userId':'foL9NpoZFq9AYmXyj', 'details.subPlayer.gameSlug':'0-shooter' }).map(function (e) {
e.details.forEach(function (d) {
var squad = 'squad1';
d.subPlayer.members[squad].forEach(function (s) {
s.deaths = 10000;
});
});
Stats.update({'_id': e._id}, e);
});

Categories