Format response from mongoose into a model - mongodb

How do you go about formatting the data response from mongoose? For a simple Post Schema
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
}
},{
timestamps: true
});
Whenever I do a GET request to find all the post, It returns me all its fields including _id and __v in which I wouldn't want to return those fields in an API.
Is there a way I would select only certain fields that I would want to return?
As far as I've found was that I could set a second parameter of title onto my query and it would return only the _id and title.
const post = await Post.find({},'title');
I find the method above isn't the proper way to filter fields in cases in the future where the values are deeply nested object and we would like to pick out certain values.
Is there perhaps a way to create a Model/Class and pick the fields based on the Model/Class and return the respond?

You can use select from mongoose.
You can either select only the fields you want.
var find = await model.find({}).select("my_field")
Or not show the fields you don't want
var find = await model.find({}).select("-my_field")
Check the documentation

Related

Mongodb Ref dynamic populate with grapqhl?

I have to decide whether to populate or not according to the query request, but I don't know how to do it.
So Example
If my model User is looks like this
below syntax is from typegoose and typegraphql
class User {
#Field()
#prop()
name: string;
#Field()
#prop(ref:"House")
house: Ref<House>
}
And here is two diffent query
Query1
user {
name
}
Query2
user {
name
house {
location
}
}
And in the resolver
User: () => {
const user = UserModel.find(blahblah)**.populate("house")**
return user
}
Query1 dose not need populate
but Query2 need
in same resolver!
I want to decide whether to populate or not depending on the requirements of the query.
I can't decide whether to populate or not without knowing what is the actual query was in resolver.
I found very similar question in stackoverflow
But there is not proper answer...
Solving relationships in Mongoose and GraphQL
i dont know much about graphql, but i think there is some method to get if that path is requested, so the following should work:
let query = Model.find(basicQuery);
if (req.path) { // replace this with graphql's method to get if the path is requested
query = query.populate(yourPath);
}
const doc = await query.exec();
PS: but as an comment already noted, i think there is some better method to do this in graphql (another resolver?)

How can I dynamically build a mongoose query that has an objectId?

I'm trying to dynamically build a mongoose query. The problem I'm having is that I can't figure out how to do this when the query contains an objectId.
I'm using mongoose 5.4.6.
My mongoose schema looks something like this:
stuff: String,
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
moreStuff: String,
});
I need to dynamically build my query, and one of the possible fields to query for is user. The fields to query for are sent to the server via a params object. When the field I'm querying for is stuff, then it's easy. I just do something like the following:
let query = {};
query.stuff = params.stuff;
this.model('MyModel').find(query)...
However, I can't figure out how to search for user, because params.user is just the string version of the mongo id, but it needs to be formatted as objectId("params.user") for the query to work. That is, I need something like:
query.user = objectId("params.stuff");
How can I get this to work?
I usually do like this.
const ObjectId = require('mongodb').ObjectID
const MyModel = require('../models/mymodel')
let query = MyModel.findOne()
query.where('_id').equals(new ObjectId(params.stuff))

Mongoose - populate return _id only instead of a Object [duplicate]

In Mongoose, I can use a query populate to populate additional fields after a query. I can also populate multiple paths, such as
Person.find({})
.populate('books movie', 'title pages director')
.exec()
However, this would generate a lookup on book gathering the fields for title, pages and director - and also a lookup on movie gathering the fields for title, pages and director as well. What I want is to get title and pages from books only, and director from movie. I could do something like this:
Person.find({})
.populate('books', 'title pages')
.populate('movie', 'director')
.exec()
which gives me the expected result and queries.
But is there any way to have the behavior of the second snippet using a similar "single line" syntax like the first snippet? The reason for that, is that I want to programmatically determine the arguments for the populate function and feed it in. I cannot do that for multiple populate calls.
After looking into the sourcecode of mongoose, I solved this with:
var populateQuery = [{path:'books', select:'title pages'}, {path:'movie', select:'director'}];
Person.find({})
.populate(populateQuery)
.execPopulate()
you can also do something like below:
{path:'user',select:['key1','key2']}
You achieve that by simply passing object or array of objects to populate() method.
const query = [
{
path:'books',
select:'title pages'
},
{
path:'movie',
select:'director'
}
];
const result = await Person.find().populate(query).lean();
Consider that lean() method is optional, it just returns raw json rather than mongoose object and makes code execution a little bit faster! Don't forget to make your function (callback) async!
This is how it's done based on the Mongoose JS documentation http://mongoosejs.com/docs/populate.html
Let's say you have a BookCollection schema which contains users and books
In order to perform a query and get all the BookCollections with its related users and books you would do this
models.BookCollection
.find({})
.populate('user')
.populate('books')
.lean()
.exec(function (err, bookcollection) {
if (err) return console.error(err);
try {
mongoose.connection.close();
res.render('viewbookcollection', { content: bookcollection});
} catch (e) {
console.log("errror getting bookcollection"+e);
}
//Your Schema must include path
let createdData =Person.create(dataYouWant)
await createdData.populate([{path:'books', select:'title pages'},{path:'movie', select:'director'}])

How to design a REST search with backbone

I'm designing an API and also consuming it with Backbone.js. Part of the API will include search operations. For example when searching for cars, I might have something like:
http://api.mysite.com/search/cars?q=volvo
With backbone, I can see two options for consuming the results.
Option 1: A search is a Collection
var CarSearch = Backbone.Collection.extend({
model: Car,
initialize : function(models, options){
this.query = options.query;
},
url: function(){
return "http://api.mysite.com/search/cars?q="+this.query;
}
});
var volvos = new CarSearch([], {query:'volvo'});
volvos.fetch();
Option 2: A search is a Model, and the results are a Collection
var CarSearchResults = Backbone.Collection.extend({
model: Car
});
var CarSearch = Backbone.Model.extend({
defaults: {
"query":"",
"carSearchResults":null
},
url: function(){
return "http://api.mysite.com/search/cars?q="+this.get('query');
},
parse: function(resp,xhr){
resp.carSearchResults = new CarSearchResults(resp.carSearchResults);
return resp;
}
});
var volvoSearch = new CarSearch();
volvoSearch.set({query:'volvo'});
volvoSearch.save();
What are the advantages / disadvantages of these options? Is there a backbone-y way of designing this?
I'm leaning towards option 2 because it seems easier to add things to the response like pagination details, or a next url. But option 2 seems messier in a couple of ways. For example, would I generate an ID on the server for the search model when it is saved? Don't think I need to get that model by ID, deleting or updating it doesn't really make sense either cause I'm not persisting it.
i dont know if its a good practice,
but i use for my search the "data" option in the "fetch" method.
https://stackoverflow.com/a/6659501/1067061
Maybe it helps.
Good Luck!
EDIT
This is the right way to pass query parameters in your collections url,
The reference to the Docs shows how to pass the data attribute in fetch options, the data attribute is actually an object with key value pairs referring to query params and their values
I would go with option one. At least imo a model should correspond to a single search result and the collection to the entire set of search results. so if you search for volvo and there are 6 items returned, each item should be a model contained within your collection.
Now this will largely depend on how you are representing a result on your server. If say for instance you have car instances then you just do the search server side using the query and return the resulting objects as json. Then you can have the returned list be the collection of car models that match the criteria. but if you are planning on returning the query results some other way then you will have to think about how the model should represent the data
I would recommend using a collection, like in option 1, but without the need to define a new collection just for the search.
Take a look at my blog post about this here: http://willdemaine.ghost.io/restful-search-with-backbone/
var SearchableCollection = Backbone.Collection.extend({},{
search: function(query, options){
var search = $.Deferred();
options = options || {};
var collection = new this([], options);
collection.url = _.result(collection, 'url') + 'search?q=' + query;
var fetch = collection.fetch();
fetch.done(_.bind(function(){
Backbone.Events.trigger('search:done');
search.resolveWith(this, [collection]);
}, this));
fetch.fail(function(){
Backbone.Events.trigger('search:fail');
search.reject();
});
return search.promise();
}
});
Then you can do:
var Cars = SearchableCollection.extend({});
var findCars = Cars.search('volvo');
findCars.done(function(cars){
var carsView = new CarsView({
collection: cars
});
carsView.render();
});

How to put embedded document from one document into another document using Mongoose?

What I'm trying to do should be straight forward but for some reason I'm having real difficulties figuring this out. I have the following Mongoose schemas (simplified).
var Status = new Schema({
name : { type: String, required: true },
description : { type: String }
});
var Category = new Schema({
statuses : [Status], // contains a list of all available statuses
// some other attributes
});
var Book = new Schema({
statuses : [Status], // preferably this would not be an array but a single document, but Mongoose doesn't seem to support that
// some other attributes
});
Now, I want to do the following:
Retrieve the Category document
Find a particular embedded Status document (based on request param)
Assign that particular embedded Status document to a particular Book document. I want to replace the existing Book status as at any given time there should only be one status set for a book.
Here is what I'm currently doing:
mongoose.model('Category').findOne({_id: id}, function(err, category){
if(err) next(err);
var status = category.statuses.id(statusId); // statusId available via closure
book.statuses[0] = status; // book available via closure; trying to replace the existing status here.
book.save(function(err){
if(err) next(err);
next();
});
});
The above seems to run fine and I don't get any errors. However, the new status is not saved to the document. Next time I output the updated Book document, it will still have the old status. I debugged this and the find() methods as well as setting the status seems to be fine.
The only thing I can think of right now is that somehow the status value I'm assigning is not in the right format to be saved with Mongoose. Although, I would expect some kind of error message then.
Or maybe there is a better way to do all of this anyway?
It could be because you are trying to copy an embedded document, which itself could have an ObjectId associated with it. Trying to save the duplicate Status within the Book would create two embedded documents with the same ObjectId. Try creating a new Status object and copying the fields.
It is hard to find docs on ObjectsIds for embedded documents, but they are mentioned here: http://mongoosejs.com/docs/embedded-documents.html.