Mongoose query using URI with colons - mongodb

I am trying to query my MongoDB based on a userID and spotify song's URI, which looks something like this: spotify:local:A%24AP+Rocky::Goldie:194. However, it doesn't seem to work. My queries look something like this.
app.patch('/api/songs/:spotifyId/:songUri', (req, res) => {
var id = req.params.spotifyId;
var uri = req.params.songUri;
Song.findOne({
userId: id,
uri
}).then((song) => {
song.update({$inc: {count:1}}, (e)=>{console.log(e)})
res.send(song);
}).catch((e) => {
res.status(400).send();
})
})
I tried querying using only the userID, which worked, and only the URI, which didn't work. I also created some fake data using a URI that doesn't have colons or percentage signs, which worked. So, I think that my query is not working because of the colons or something.
Does anyone know exactly why the query is not working, and also if there is a way to get my query working using the URI? Or will I have to query using different information?

In that example, it seems that you are specifying that the object's userId should match "id" but don't specify what key you want "uri" to match with (Presumably something like "songUri").
You have:
{
userId: id,
uri
}
And maybe it should be something more like:
{
userId: id,
songUri: uri
}
Finally if that doesn't work, maybe try passing in a string for the Uri (surrounding it with quotes) and then parsing it on the backend with JSON.parse()

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?)

elemMatch for fields in different levels

I'm asking this question after trying solve this problem all day long.
I want to get my users address by userId and by addressId. Since I receive data from post requests, I need to ensure that the query contains both userId and addressId in order to avoid security problems. The result of the query below returns all addresses from my user, instead of just the address containing the correct addressId.
async getUserAddress(_id: string, addressId: string) {
const user = await this.userModel.findOne(
{
_id,
addresses: {
$elemMatch: {
addressId: Types.ObjectId(addressId)
}
}
}
)
if (!user) {
throw new NotFoundException();
}
return user.addresses[0];
}
Since is impossible to use $elemMatch in different document levels, or even at the top level of the query, I find no better way to make this query without using filters. Are there any insights?
Tks in advance

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'}])

Query sailsjs blueprint endpoints by id array using request

I'm using the request library to make calls from one sails app to another one which exposes the default blueprint endpoints. It works fine when I query by non-id fields, but I need to run some queries by passing id arrays. The problem is that the moment you provide an id, only the first id is considered, effectively not allowing this kind of query.
Is there a way to get around this? I could switch over to another attribute if all else fails but I need to know if there is a proper way around this.
Here's how I'm querying:
var idArr = [];//array of ids
var queryParams = { id: idArr };
var options: {
//headers, method and url here
json: queryParams
};
request(options, function(err, response, body){
if (err) return next(err);
return next(null, body);
});
Thanks in advance.
Sails blueprint APIs allow you to use the same waterline query langauge that you would otherwise use in code.
You can directly pass the array of id's in the get call to receive the objects as follows
GET /city?where={"id":[1, 2]}
Refer here for more.
Have fun!
Alright, I switched to a hacky solution to get moving.
For all models that needed querying by id arrays, I added a secondary attribute to the model. Let's call it code. Then, in afterCreate(), I updated code and set it equal to the id. This incurs an additional database call, but it's fine since it's called just once - when the object is created.
Here's the code.
module.exports = {
attributes: {
code: {
type: 'string'//the secondary attribute
},
// other attributes
},
afterCreate: function (newObj, next) {
Model.update({ id: newObj.id }, { code: newObj.id }, next);
}
}
Note that newObj isn't a Model object as even I was led to believe. So we cannot simply update its code and call newObj.save().
After this, in the queries having id arrays, substituting id with code makes them work as expected!