I need to send the following example MongoDB query from a React.JS frontend app to a backend Node.js API via URL request params:
{
_id: ObjectId('507f1f77bcf86cd799439011'),
name: {
$in: ['foo', 'bar']
}
}
However, ObjectId is a function and cannot be serialized to JSON. So, I tried this BSON form as proposed in https://stackoverflow.com/a/34486720/6039697:
{
"_id": {
"$oid": "507f1f77bcf86cd799439011"
},
"name": {
"$in": ["foo", "bar"]
}
}
But MongoDB show this error
{
"message" : "unknown operator: $oid",
"ok" : 0,
"code" : NumberInt(2),
"codeName" : "BadValue",
"name" : "MongoError"
}
I Know I could check for _ids and parse in the API, but I would like to make this transparent and automatic. Does anyone have an idea on how can I get this working?
I found a solution to my question. This package solved my problem https://www.npmjs.com/package/bson
const { EJSON } = require('bson');
const mongoDBQuery = EJSON.parse(JSON.stringify({
"_id": {
"$oid": "507f1f77bcf86cd799439011"
},
"name": {
"$in": ["foo", "bar"]
}
}), { relaxed: true });
console.log(mongoDBQuery);
Related
I am working on an app that uses MongoDB (I use Mongoose) as its database.
I have a question, suppose I have this kind of schema:
[{
"user_id":"2328292073"
"username":"Bob",
"subscriptions":[
{
"id":"38271281,
"payments":[
{
"id":"00001",
"amount":"1900"
},
{
"id":"00002",
"amount":"2000"
},
{
"id":"00003",
"amount":"3000"
}
]
}
]
}]
In my case I want to get the payments array for subscription with id = '38271281' of user with id '2328292073', but I just want to retrieve the payment array, nothing else
My query is the following:
Mongoose.findOne({
"user_id": "2328292073",
"subscriptions.id": "38271281"
},
{
"subscriptions.payments": 1
})
But I get the entire document of subscriptions. How can i get the payment array only?
you can try using unwind if you want filteration from db only.
Mongoose.aggregate([
{
'$match': {
'user_id': '2328292093'
}
}, {
'$unwind': {
'path': '$subscriptions'
}
}, {
'$match': {
'subscriptions.id': '38271281'
}
}
])
if you will have multiple documents having same subscription id then you have to group it .
using code level filter function can also be one another approach to do this .
You can try aggregation operators in projection in find method or also use aggregation method,
$reduce to iterate loop of subscriptions and check the condition if id matched then return payment array
db.collection.find({
"user_id": "2328292073",
"subscriptions.id": "38271281"
},
{
payments: {
$reduce: {
input: "$subscriptions",
initialValue: [],
in: {
$cond: [
{ $eq: ["$$this.id", "38271281"] },
"$$this.payments",
"$$value"
]
}
}
}
})
Playground
My scenario is a dynamo database in aws, I've a table containing a structure like the following one:
{
"data": {
"23fc4fa8-0037-4d09-90be-506eed497a15": {
"text": "first text"
},
"4708f80a-e301-48ab-8351-f598c4300600": {
"text": "second text"
}
},
"types": "type",
"order": 3
}
My goal is to delete the second text, I receive from clientside the id 4708f80a-e301-48ab-8351-f598c4300600 so I'm building my call to delete that field only, in order to have this data after the removal operation:
{
"data": {
"23fc4fa8-0037-4d09-90be-506eed497a15": {
"text": "first text"
}
},
"types": "type",
"order": 3
}
This is the code I'm using:
const type = 'type'
const id = '4708f80a-e301-48ab-8351-f598c4300600'
const dynamoBd = new AWS.DynamoDB.DocumentClient()
const params = {
TableName: DB_TABLE,
Key: {
types: type
},
UpdateExpression : `REMOVE data.${id}`
}
await dynamoDbClient
.update(params, (error, _) => {
if (error) {
console.error(`Error: `, error)
return
}
})
.promise()
But I receive this error:
Error: ValidationException: Invalid UpdateExpression: Syntax error; token: "4708", near: ".4708f80a"
I believe the issue is the way this string is build 'REMOVE data.${id}' that parsed look like this 'REMOVE data.4708f80a-e301-48ab-8351-f598c4300600'
Any ideas how to solve this, thanks in advance to everyone who could help me out. Ps. if you find some documentation on aws website about it, could you also post it, because currently I couldn't find it. Thanks again!
This is the way to build the const params, with the support of ExpressionAttributeNames:
The reason is that some keys or values might be keywords or contain special characters (like dash in the example). So ExpressionAttributeNames (for the example) and ExpressionAttributeValues are used to create substitute placeholders of the keys and values.
const params = {
TableName: DB_TABLE,
Key: {
types: type
},
UpdateExpression : `REMOVE #data.#id`,
ExpressionAttributeNames: {
'#data': 'data',
'#id': id
}
}
I have a user with various post ID's inside of my mongodb database, I am using mongoose to talk to it.
this is the user object
[
{
"premium": true,
"max_posts": 55,
"posts_made": 52,
"posts": [
"5e10046c0be4f92228f6f532",
"5e1005a9dceb1344241c74c5",
"5e100753a6cfcb44d8f1fa09",
"5e1007bea6cfcb44d8f1fa0a",
"5e1008149324aa1d002a43be",
"5e1009562826a308a0812e92",
"5e100a625e6fcb2c90a07bec",
"5e157143536d6e04a80651bd",
"5e1e320dc749f23b189ccef7",
"5e1e3273546d55384c3a975c",
"5e1e340183d0b0080816cedd",
"5e1e368bd921f3194c22b3d2",
"5e1e3732d921f3194c22b3d3",
"5e1e3a6f9b3017189cff0fe2",
"5e1e3c1a8c38f11c60354052",
"5e1e9ab208d0a5416828d0a3"
],
"_id": "5e0fe3f33c2edb2f5824ddf2",
"email": "user#gmail.com",
"createdAt": "2020-01-04T01:01:39.840Z",
"updatedAt": "2020-01-15T04:53:08.987Z",
"__v": 16
}
]
So, I make a request to the database using express, and I try to filter the array, using an id of one post, then I ask express to save that modified user model...
router.delete('/testing', (req,res,next) =>{
userModel.findOne({ email: req.body.author }, function(error, user) {
user.posts.filter(item => item != req.body.postid)
user.save((err) => {
console.log(err);
});
res.json(user)
});
});
my request in postman :
As you can see, the item is still there in that array... checking the console.log.
Please Advise as I am having doubts, thanks.
You no need to find & update the document of user, which makes two DB calls, plus .filter(), try this :
router.delete('/testing', (req, res, next) => {
userModel.findOneAndUpdate({ email: req.body.author},
{ $pull: { "posts": req.body.postid } }, { new: true }, function (error, user) {
res.json(user);
});
});
Here we're using .findOneAndUpdate() as it returns the updated user document which you need to send back.
This is how I would do it using $pull operator.
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
userModel.update(
{email: req.body.author},
{ $pull: { posts: { $in: [ req.body.postid ] } } },
{ multi: true }
)
For now it seems like you are passing single postid in request body. In future if you needed to delete multiple posts at the same time, you can use the same query by just replacing { $in: [ req.body.postid ] } with { $in: [ ...req.body.postid ] }
I am trying to convert from an introspection query to a GraphQL schema using the npm GraphQL library.
It keeps stating:
devAssert.mjs:7 Uncaught Error: Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: { ... }
Issue is I am getting it directly from GraphiQL Shopify and can't figure out how to validate my introspection return is correct.
Code:
var introspection = `
{
"data": {
"__schema": {
"types": [
{
"name": "Boolean"
},
{
"name": "String"
},
{
"name": "QueryRoot"
},
{
"name": "Job"
},
{
"name": "ID"
},
{
"name": "Node"
},
{
"name": "Order"
}
]
}
},
"extensions": {
"cost": {
"requestedQueryCost": 2,
"actualQueryCost": 2,
"throttleStatus": {
"maximumAvailable": 1000,
"currentlyAvailable": 980,
"restoreRate": 50
}
}
}
}`;
let schema = buildClientSchema(introspection);
//console.log(schema.printSchema());
I thought the introspection result could be a string? Or is there not enough info to build a schema? Do I need to expand the number of fields? What's the bare minimum needed to exchange an introspection result into a schema?
You should use getIntrospectionQuery to get the complete introspection query needed. If the response you're working with is a string, it should then be parsed to an object before being passed to buildClientSchema -- the function accepts an object, not a string.
Here's an example directly querying a schema using the graphql function -- you'll need to modify it based on how you're actually executing the query.
const { getIntrospectionQuery, buildClientSchema, graphql } = require('graphql')
const schema = new GraphQLSchema(...)
const source = getIntrospectionQuery()
const { data } = await graphql({ source, schema })
const clientSchema = buildClientSchema(data)
Make sure that you are only passing in the data portion of the response. The object you pass in should look like this:
{
__schema: {
// ...more properties
}
}
This question already has an answer here:
Combine two OR-queries with AND in Mongoose
(1 answer)
Closed 8 years ago.
I want to query mongo for "unexpired or evergreen(posts with null expiration) posts created or bookmarked by me"
The problem with my query is that mongoose is combining the or statements together so that I'm incorrectly getting (unexpired or evergreen or mine or bookmarked) rather than ( (unexpired or evergreen) and (mine or bookmarked) )
How do I change the mongoose query to be the latter correct case I outlined above. Should I use an "and" clause... or perhaps I should just do a not(expiration > now) ?
var query =
Invite.find({ isActive:true })
.or([
{ 'expiration': {$gt: new Date()} },
{ 'expiration' : null }
])
.or([
{ createdBy:userId },
{ 'bookmarked.id' : userId }
])
You can put both of the $or clauses into a single $and by using the Query#and helper as:
var query =
Invite.find({ isActive:true })
.and([
{$or: [
{ 'expiration': {$gt: new Date()} },
{ 'expiration': null }
]},
{$or: [
{ 'createdBy': userId },
{ 'bookmarked.id' : userId }
]}
])
This is where I think "helper" methods are not really helping much because they are confusing the issue. JavaScript is a dynamically typed language so you don't need these helper methods to define the data structure which makes up the query. All the native operators to MongoDB are just accepted in the single query path:
Invite.find(
{
"isActive": true,
"$and": [
{ "$or": [
{ "expiration": null },
{ "expiration": { "$gt": new Date() } }
]},
{ "$or": [
{ "createdBy": userId }
{ "bookmarked.id": userId }
]}
]
},
function(err,result) {
}
);