Custom sort of nested document - mongodb

I have a collection fruits {name:'orange', id:1}.... and another one users, users have document favorites: [1,2,3]. I want to display the favourite fruits at first and then the rest.
I can query twice like first with fruits.find({id: {$id:[user.favorites]}}) and then with not in and then merge them.
Is there any other way to do it? Is there a possibility to pass custom function to sort, if so. How can I do that?

No, Meteor doesn't allow a custom sort function. What you can do, is use the sortBy() and contains() functions from the underscore library to sort your items:
var fruitsSorted = _.sortBy(fruits.find(selector).fetch(), (fruit) => {
if(_.contains(user.favorites, fruit.id)){
return 0;
}
return 1;
});
You now have a sorted array in fruitsSorted.

Lets say you have queried fruits collection for all fruits.
var fruits = [
{ name: 'orange', id: 1},
{ name: 'apple', id: 2},
{ name: 'banana', id: 3}
];
Now you can use sortBy and includes function from lodash library to sort the fruits as per user favourites.
var sortedFruits = _.sortBy(fruits, function(fruit) {
return _.includes(user.favourites, fruit.id) ? 0 : 1;
});

Related

mongoengine filter on DictField's dynamic keys

class UserThings(DynamicDocument):
username = StringField()
things = DictField()
dcrosta_things = UserThings(username='dcrosta')
dcrosta_things.things['foo'] = 'bar'
dcrosta_things.things['bad'] = 'quack'
dcrosta_things.save()
Results in a MongoDB document like:
{ _id: ObjectId(...),
_types: ["UserThings"],
_cls: "UserThings",
username: "dcrosta",
things: {
foo: "bar",
baz: "quack"
}
}
Im using mongoengine and I'm not able to query on dict field's keys.
for e.g I have a list of things thing_list = ['foo', 'faa', 'baz', 'xyz']
and I want to filter all UserThings that contains anyof these things...
something like .. UserThings.objeect.filter(things__in=thing_list)
definitely this wont work. IS there any way to perform filtering on variable/dynamic keys on dictfield. if not, can we do it using pymongo/raw query?

Mongoose - find documents with maximum no of counts

I am using Mongoose to fetch data from MongoDB. Here is my model.
var EmployeeSchema = new Schema({
name: String,
viewCount: { type: Number, default: 0 },
description: {
type: String,
default: 'No description'
},
departments: []
});
I need to find top 5 employees where count(viewCount) is highest order by name.
I am thinking of finding all the employee by using find() & then read viewCount property & produce the result. is there any better way to get the desired result.
All you need here is .sort() and .limit():
Employee.find().sort({ "viewCount": -1, "name": 1 }).limit(5)
.exec(function(err,results) {
});
And that is the top 5 employees in views ordered by name after the viewCount.
If you want them ordered by "name" in your final five, then just sort that result:
Employee.find().sort({ "viewCount": -1, "name": 1 }).limit(5)
.exec(function(err,results) {
// sort it by name
results.sort(function(a,b) {
return a.name.localeCompare(b.name);
});
// do something with results
});
You can sort by the view count and limit the search results to 5.
In code it might look like this:
Employee
.find()
.sort([['viewCount',-1], ['name',-1]])
.limit(5)
.exec(function(err, results){
//do something with the results here
});

Update nested array object (put request)

I have an array inside a document of a collection called pown.
{
_id: 123..,
name: pupies,
pups:[ {name: pup1, location: somewhere}, {name: pup2, ...}]
}
Now a user using my rest-service sends the entire first entry as put request:
{name: pup1, location: inTown}
After that I want to update this element in my database.
Therefore I tried this:
var updatedPup = req.body;
var searchQuery = {
_id : 123...,
pups : { name : req.body.name }
}
var updateQuery = {
$set: {'pups': updatedPup }
}
db.pown.update(searchQuery, updateQuery, function(err, data){ ... }
Unfortunately it is not updating anythig.
Does anyone know how to update an entire array-element?
As Neil pointed, you need to be acquainted with the dot notation(used to select the fields) and the positional operator $ (used to select a particular element in an array i.e the element matched in the original search query). If you want to replace the whole element in the array
var updateQuery= {
"$set":{"pups.$": updatedPup}
}
If you only need to change the location,
var updateQuery= {
"$set":{"pups.$.location": updatedPup.location}
}
The problem here is that the selection in your query actually wants to update an embedded array element in your document. The first thing is that you want to use "dot notation" instead, and then you also want the positional $ modifier to select the correct element:
db.pown.update(
{ "pups.name": req.body.name },
{ "$set": { "pups.$.locatation": req.body.location }
)
That would be the nice way to do things. Mostly because you really only want to modify the "location" property of the sub-document. So that is how you express that.

Building a dynamic mongo query for meteor

I'm building an app that has clickable 'filters'; I'm creating a list of objects(?) that I want to pass to a mongo 'find', so that I can pull out listings if selected attributes match a certain score.
My data is structured like this (a snippet):
name: 'Entry One',
location: {
type: 'Point',
coordinates: [-5.654182,50.045414]
},
dogs: {
score: '1',
when: 'seasonal',
desc: 'Dogs allowed from October to April'
},
lifeguard: {
score: '1',
when: 'seasonal',
desc: 'A lifeguard hut is manned between April and October',
times: ''
},
cafe: {
score: '1',
name:'Lovely cafe',
open:'seasonal'
}, ...
My search variable is a list of objects (I think?) that I assign to a session variable. If I output this session var ('searchString') via JSON.stringify, it looks like this:
{"cafe":{"score":"1"},"dogs":{"score":"1"}}
I'd like to pass this to my mongo find so that it only lists entries that match these scores on these attributes, but it's returning zero results. Do I need to somehow make this an $and query?
Currently it looks like this:
Beaches.find(searchString);
Unfortunately as soon as I drop searchString into the find, I get zero results even if it's empty {}. (When it's just a find() the entries list fine, so the data itself is ok)
What am I doing wrong? I'm relatively new to mongo/meteor, so I apologise in advance if it's something stupidly obvious!
Don't stringify the query. Flatten the object instead. Example:
Beaches.find({
"cafe.score": 1,
"dogs.score": 1,
});

Rx (RxJS) group Observable stream according to an array of keys

I am using RxJS to group the results of an AJAX API call (Google Knowledge Graph) according to a type key. Each result item may contain multiple types. The results look like this:
const data = [
{ name: 'item1', types:['A', 'B']},
{ name: 'item2', types:['C', 'D']},
{ name: 'item3', types:['A', 'B', 'C']},
{ name: 'item4', types:['B']},
{ name: 'item5', types:['B', 'C']},
{ name: 'item6', types:['A', 'D']},
{ name: 'item7', types:['B', 'C']},
];
My approach to achieving this is to flatten the results so that each element contains one type key only to be able to use the built-in operator groupBy.
Rx.Observable.from(data)
.flatMap(item => {
return item.types
.map(type => Object.assign({}, item, {type}) );
})
.groupBy(item => item.type)
.subscribe(type => {
type.count().subscribe(count => console.log(`${count} items has type ${type.key}`));
});
The results will transform into something like:
{ name: 'item1', type:'A'},
{ name: 'item1', type:'B'},
{ name: 'item2', type:'c'},
...
And the output will be:
3 items has type A
5 items has type B
4 items has type C
2 items has type D
This achieves the desired behavior, but I am not sure if duplicating the items is the most efficient approach to use with a live stream of large data before completion. I also assume that this is a common use case so I wonder if there is another built-in operator to achieve this that I missed in the API doc. I'd love to get some input or suggestions.
The above example is available at https://jsfiddle.net/t9Ls1p2f/