I have a map/reduce method using node-mongodb-native. I'm trying to only return distinct records on 'product_id'. This is what I have so far:
var map = function() {
emit("_id", {"_id" : this._id,
"product_id" : this.product_id,
"name" : this.name
});
}
var reduce = function(key, values) {
var items = [];
var exists;
values.forEach(function(value) {
if(items.length > 0) {
items.forEach(function(item_val, key) {
if(item_val.product_id == value.product_id) {
exists = true;
}
});
if(!exists) {
items.push(value);
}
exists = false;
} else {
items.push(value);
}
});
return {"items" : items};
}
this.collection.mapReduce(map, reduce, {out : {inline: 1}, limit: 4}, function(err, results) {
if (err) {
console.log(err);
} else {
console.log(results[0].value.items);
}
});
Something with my logic doesn't seem to work. It still adds duplicate records where product_id's are the same.
Any help would be awesome - thank you!
In fact, an example of what your trying to do:
var map = function() {
emit(this.product_id, {"_id" : this._id, "name" : this.name });
}
var finailise = function(key, value){
return value[0]; // This might need tweaking, ain't done MRs in a while :/
}
Do note however there are two types of distinct:
First find
Last find
There is no standard way to distinct and each DB has it's own methods, it isn't even standard across SQL databases so it is upto you to know which way you want to distinct. The one above does a first find distinct. You can do a last find distinct like:
var finailise = function(key, value){
return value[value.length-1];
}
Or something similar, should get you started anyway.
Hope it helps,
Related
Parse-server doesn't support groupBy for queries. So instead of adapting code to work with the duplicate entries i've decided to create a Job to clean the data.
I've created a cloud function using underscore but the results are not good. It's deleting non-duplicate entries too.
I want to remove a entry if another entry exists with the same post_id and user_id
Parse.Cloud.job("removeDuplicateItems", function(request, status) {
var _ = require("underscore");
var hashTable = {};
function hashKeyForTestItem(testItem) {
var fields = ["user_id", "post_id"];
var hashKey = "";
_.each(fields, function (field) {
hashKey += testItem.get(field) + "/" ;
});
return hashKey;
}
var testItemsQuery = new Parse.Query("Post_shares");
testItemsQuery.each(function (testItem) {
var key = hashKeyForTestItem(testItem);
if (key in hashTable) { // this item was seen before, so destroy this
return testItem.destroy();
} else { // it is not in the hashTable, so keep it
hashTable[key] = 1;
}
}).then(function() {
status.success("removal completed successfully.");
}, function(error) {
status.error("Uh oh, something went wrong.");
});
});
Is there a better way of doing this?
I'm working with mongoose in NodeJS and I have an id from a child array. I the models are defined like this:
var thingSchema = new schema({
thingId: String,
smallerThings : [smallerThingsSchema]
});
var smallerThingsSchema = new schema({
smallerThingId: String,
name : String,
anotherName : String
});
I have the smallerThingId, but I want to get the thingId.
Right now I have a for loop that looks like this (pretty ineffective I suppose). Potentially, there could be 100,000 things.
//Find all things
thingModel.find({}, null, function (error, things) {
if (error) {
callback(error);
}
else {
//Go through all things
for(var i = 0; i < things.length; i++){
//check if a thing with the array of smaller things matches the id
for(var j = 0; j<things[i].smallerThings.length; j++){
if(things[i].smallerThings[j].id === smallerThingId){
//return it
return things[i];
}
}
}
}
});
Grateful for any help or where I can look (docs/blog/other) to learn how to handle such scenario.
To get document by sub-document id you can use following code:
thingModel.find({"smallerThings.smallerThingId":smallerThingId}, null, function (error, things) {
});
This will return document having "smallerThingId".
You can use mongoose find like:
thingModel.find({"things.smallerThings.id": smallerThingId }, null, function (error, things) {
if (error) {
callback(error);
}
else {
//do what you need
}
});
How would I go about filtering the return of a FindOne function in Iron Router? I assume that aggregation is out of the question, but I may be wrong. I've tried many different ways that don't work. I'd like to return the id, name, and the season object that it finds a matching season_number in.
My database is setup like so:
_id
name
seasons (array)
season (object)
season_number
episodes (array)
episode (object)
episode_title
episode_number
Here's my iron router code that is currently just running a findOne function.
Router.route('/show/:_id/season/:season_number', {
name: 'viewSeasonPage', // This links to the template
data: function() { return Tv.findOne({_id:"KQBXq4nri7zssDna2", "seasons.season_number": 2}))}
});
Router.route('/show/:_id/season/:season_number', {
name: 'viewSeasonPage', // This links to the template
data: function() {
let tv = Tv.findOne({_id:"KQBXq4nri7zssDna2", "seasons.season_number": 2});
if (tv && tv.seasons) { return tv.seasons.find(function(season) { return season.season_number == 2; })
}
});
You need to filter the result to create the data object you want to return with the information you need. If your search doesn't find anything, your data is an empty object.
Router.route('/show/:_id/season/:season_number', {
name: 'viewSeasonPage',
data: function() {
// Find your object
var tv = Tv.findOne({
_id: this.params._id,
seasons: {
$elemMatch: {
season_number: this.params.season_number
}
}
});
// Fill data by filtering the correct season
var data = {};
if (tv) {
data._id = tv._id; // 1. ID
data.name = tv.name; // 2. NAME
// Find correct season in array
var season;
for (var i = tv.seasons.length - 1; i >= 0; i--) {
season = tv.seasons[i];
if (season.season_number == this.params.season_number) {
data.season = season; // 3. SEASON
}
};
}
return data;
}
});
I know it's a lot of coding, but this is for understanding the idea and the process.
I've got a remote JSON file that contains the list of the last 100 users who logged into a service. This JSON is updated constantly and lists the users from the most recently logged in to the "least recently" logged in.
If the user who appears as number X logs back in, they get removed from their position X and put back at the very top of the JSON at position [0].
I retrieve the JSON every 5 minutes. What I'd like to do is detect the differences between the old object oldUsers and the new newUsers and store them in another object that would only contain the users who are present in newUsers but not in oldUsers. I have no real idea as to how to achieve this.
Here's the JSON structure:
[{
"id":"foo09",
"name":"John",
"age":28
}, {
"id":"bar171",
"name":"Bryan",
"age":36
},
...
]
Is there a rather straightforward way to do it? Thanks!
You need to write your own diff algorithm. Here is one I whipped up in JSBin:
I will need a utility function to merge two arrays (Underscore would help here).
function mergeArrays(val1, val2) {
var results = val1.splice(0);
val2.forEach(function(val) {
if (val1.indexOf(val) < 0) {
results.push(val);
}
});
return results;
}
Diff algorithm
function diff(val1, val2) {
var results = [];
var origKeys = Object.keys(val1);
var newKeys = Object.keys(val2);
mergeArrays(origKeys, newKeys)
.forEach(function(key) {
if (val1[key] === val2[key]) { return; }
var result = {
key: key,
orig: val1[key],
'new': val2[key]
};
if (val1[key] == null) {
result.type = 'add';
} else if (val2[key] == null) {
result.type = 'delete';
} else {
result.type = 'change';
}
results.push(result);
});
return results;
}
Currently I get back a JSON response like this...
{items:[
{itemId:1,isRight:0},
{itemId:2,isRight:1},
{itemId:3,isRight:0}
]}
I want to perform something like this (pseudo code)
var arrayFound = obj.items.Find({isRight:1})
This would then return
[{itemId:2,isRight:1}]
I know I can do this with a for each loop, however, I am trying to avoid this. This is currently server side on a Node.JS app.
var arrayFound = obj.items.filter(function(item) {
return item.isRight == 1;
});
Of course you could also write a function to find items by an object literal as a condition:
Array.prototype.myFind = function(obj) {
return this.filter(function(item) {
for (var prop in obj)
if (!(prop in item) || obj[prop] !== item[prop])
return false;
return true;
});
};
// then use:
var arrayFound = obj.items.myFind({isRight:1});
Both functions make use of the native .filter() method on Arrays.
Since Node implements the EcmaScript 5 specification, you can use Array#filter on obj.items.
Have a look at http://underscorejs.org
This is an awesome library.
http://underscorejs.org/#filter
edited to use native method
var arrayFound = obj.items.filter(function() {
return this.isRight == 1;
});
You could try find the expected result is using the find function, you can see the result in the following script:
var jsonItems = {items:[
{itemId:1,isRight:0},
{itemId:2,isRight:1},
{itemId:3,isRight:0}
]}
var rta = jsonItems.items.find(
(it) => {
return it.isRight === 1;
}
);
console.log("RTA: " + JSON.stringify(rta));
// RTA: {"itemId":2,"isRight":1}
Actually I found an even easier way if you are using mongoDB to persist you documents...
findDocumentsByJSON = function(json, db,docType,callback) {
this.getCollection(db,docType,function(error, collection) {
if( error ) callback(error)
else {
collection.find(json).toArray(function(error, results) {
if( error ) callback(error)
else
callback(null, results)
});
}
});
}
You can then pass {isRight:1} to the method and return an array ONLY of the objects, allowing me to push the heavy lifting off to the capable mongo.