How I can make an query with mongoose from a function using a parameter? - mongodb

I try make a mongoose query using a function like this:
/*
* #param {function} Model - Mongoose Model
* #param {String} searchText- Text that will be used to search for Regexp
* #param {String} Key- key to search into a Model
* #param {object} res - Response of node.js / express
*/
function _partialSearch (Model, searchText, key, res) {
var search = new RegExp(searchText, "i");
Model.find({ key : { $regex : search } })
.exec(function (err, docs) {
if(err) log(err);
else {
res.json(docs);
}
})
}
My problem is the query take a parameter key literal and search like this:
I need this:
_partialSearch(Products, 'banana', 'fruts', res)
I spect this:
Products.find({ 'fruts' : 'banana})
But I get this:
Products.find({ key : 'banana})

Use the bracket notation to create the query object dynamically, so you could restructure your function as follows:
function _partialSearch (Model, searchText, key, res) {
var search = new RegExp(searchText, "i"),
query = {};
query[key] = { $regex : search };
Model.find(query)
.exec(function (err, docs) {
if(err) log(err);
else {
res.json(docs);
}
});
}

You can't directly use a variable as an object key like that, but assuming you're using Node.js 4.0 or above, you can use its ES6 support for computed property names to do this by surrounding key in brackets:
Model.find({ [key] : { $regex : search } })

Related

react js mongodb query issue

i'm having an issue querying my mongodb database. i'm using react js. the problem doesn't seem to be the connection because i can save to the database just fine, i can only assume its a syntax issue. i've looked around but couldn't find a fix. below is a snippet of code from the schema.js file and the index.js file:
Schema.js
//import dependency
var mongoose = require('mongoose')
var Schema = mongoose.Schema
//create new instance of the mongoose.schema. the schema takes an
//object that shows the shape of your database entries.
var UserSchema = new Schema({
socketId: String
})
//export our module to use in server.js
module.exports = mongoose.model('User', UserSchema)
index.js
var mongoose = require('mongoose')
var db = mongoose.connect('mongodb://mongodatabase', {
useMongoClient: true,
/* other options */
})
var UserSchema = require('../schema/Schemas');
function userExist(userList, username){
var usert = new UserSchema()
var query = usert.where({socketId: username})
query.findOne(function (err, usert) {
if (err) return handleError(err);
if (usert) {
// doc may be null if no document matched
}
});
return username in userList
// return query
}
I get the error : usert.where is not a function.
i tried using just find but i get the same error
any help would be welcome, Thank you
You should use .findOne function directly on your schema class instead of .where
Like this :
UserSchema.findOne({socketId: username}, function (err, usert) {
if (err) return handleError(err);
if (usert) {
// doc may be null if no document matched
}
});
.where must be used on query, not schema.
To use .where, you can do it like this :
User.findOne().where({ socketId: username }).exec( function (err, usert) {
if (err) return handleError(err);
if (usert) {
// doc may be null if no document matched
}
})

apollostack/graphql-server - how to get the fields requested in a query from resolver

I am trying to figure out a clean way to work with queries and mongdb projections so I don't have to retrieve excessive information from the database.
So assuming I have:
// the query
type Query {
getUserByEmail(email: String!): User
}
And I have a User with an email and a username, to keep things simple. If I send a query and I only want to retrieve the email, I can do the following:
query { getUserByEmail(email: "test#test.com") { email } }
But in the resolver, my DB query still retrieves both username and email, but only one of those is passed back by apollo server as the query result.
I only want the DB to retrieve what the query asks for:
// the resolver
getUserByEmail(root, args, context, info) {
// check what fields the query requested
// create a projection to only request those fields
return db.collection('users').findOne({ email: args.email }, { /* projection */ });
}
Of course the problem is, getting information on what the client is requesting isn't so straightforward.
Assuming I pass in request as context - I considered using context.payload (hapi.js), which has the query string, and searching it through various .split()s, but that feels kind of dirty. As far as I can tell, info.fieldASTs[0].selectionSet.selections has the list of fields, and I could check for it's existence in there. I'm not sure how reliable this is. Especially when I start using more complex queries.
Is there a simpler way?
In case you don't use mongDB, a projection is an additional argument you pass in telling it explicitly what to retrieve:
// telling mongoDB to not retrieve _id
db.collection('users').findOne({ email: 'test#test.com' }, { _id: 0 })
As always, thanks to the amazing community.
2020-Jan answer
The current answer to getting the fields requested in a GraphQL query, is to use the graphql-parse-resolve-info library for parsing the info parameter.
The library is "a pretty complete solution and is actually used under the hood by postgraphile", and is recommended going forward by the author of the other top library for parsing the info field, graphql-fields.
Use graphql-fields
Apollo server example
const rootSchema = [`
type Person {
id: String!
name: String!
email: String!
picture: String!
type: Int!
status: Int!
createdAt: Float
updatedAt: Float
}
schema {
query: Query
mutation: Mutation
}
`];
const rootResolvers = {
Query: {
users(root, args, context, info) {
const topLevelFields = Object.keys(graphqlFields(info));
return fetch(`/api/user?fields=${topLevelFields.join(',')}`);
}
}
};
const schema = [...rootSchema];
const resolvers = Object.assign({}, rootResolvers);
// Create schema
const executableSchema = makeExecutableSchema({
typeDefs: schema,
resolvers,
});
Sure you can. This is actually the same functionality that is implemented on join-monster package for SQL based db's. There's a talk by their creator: https://www.youtube.com/watch?v=Y7AdMIuXOgs
Take a look on their info analysing code to get you started - https://github.com/stems/join-monster/blob/master/src/queryASTToSqlAST.js#L6-L30
Would love to see a projection-monster package for us mongo users :)
UPDATE:
There is a package that creates a projection object from info on npm: https://www.npmjs.com/package/graphql-mongodb-projection
You can generate MongoDB projection from info argument. Here is the sample code that you can follow
/**
* #description - Gets MongoDB projection from graphql query
*
* #return { object }
* #param { object } info
* #param { model } model - MongoDB model for referencing
*/
function getDBProjection(info, model) {
const {
schema: { obj }
} = model;
const keys = Object.keys(obj);
const projection = {};
const { selections } = info.fieldNodes[0].selectionSet;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const isSelected = selections.some(
selection => selection.name.value === key
);
projection[key] = isSelected;
}
console.log(projection);
}
module.exports = getDBProjection;
With a few helper functions you can use it like this (typescript version):
import { parceGqlInfo, query } from "#backend";
import { GraphQLResolveInfo } from "graphql";
export const user = async (parent: unknown, args: unknown, ctx: unknown, info: GraphQLResolveInfo): Promise<User | null> => {
const { dbQueryStr } = parceGqlInfo(info, userFields, "id");
const [user] = await query(`SELECT ${dbQueryStr} FROM users WHERE id=$1;`, [1]);
return user;
};
Helper functions.
Few points:
gql_uid used as ID! string type from primary key to not change db types
required option is used for dataloaders (if field was not requested by user)
allowedFields used to filter additional fields from info like '__typename'
queryPrefix is used if you need to prefix selected fields like select u.id from users u
const userFields = [
"gql_uid",
"id",
"email"
]
// merge arrays and delete duplicates
export const mergeDedupe = <T>(arr: any[][]): T => {
// #ts-ignore
return ([...new Set([].concat(...arr))] as unknown) as T;
};
import { parse, simplify, ResolveTree } from "graphql-parse-resolve-info";
import { GraphQLResolveInfo } from "graphql";
export const getQueryFieldsFromInfo = <Required = string>(info: GraphQLResolveInfo, options: { required?: Required[] } = {}): string[] => {
const { fields } = simplify(parse(info) as ResolveTree, info.returnType) as { fields: { [key: string]: { name: string } } };
let astFields = Object.entries(fields).map(([, v]) => v.name);
if (options.required) {
astFields = mergeDedupe([astFields, options.required]);
}
return astFields;
};
export const onlyAllowedFields = <T extends string | number>(raw: T[] | readonly T[], allowed: T[] | readonly T[]): T[] => {
return allowed.filter((f) => raw.includes(f));
};
export const parceGqlInfo = (
info: GraphQLResolveInfo,
allowedFields: string[] | readonly string[],
gqlUidDbAlliasField: string,
options: { required?: string[]; queryPrefix?: string } = {}
): { pureDbFields: string[]; gqlUidRequested: boolean; dbQueryStr: string } => {
const fieldsWithGqlUid = onlyAllowedFields(getQueryFieldsFromInfo(info, options), allowedFields);
return {
pureDbFields: fieldsWithGqlUid.filter((i) => i !== "gql_uid"),
gqlUidRequested: fieldsWithGqlUid.includes("gql_uid"),
dbQueryStr: fieldsWithGqlUid
.map((f) => {
const dbQueryStrField = f === "gql_uid" ? `${gqlUidDbAlliasField}::Text AS gql_uid` : f;
return options.queryPrefix ? `${options.queryPrefix}.${dbQueryStrField}` : dbQueryStrField;
})
.join(),
};
};

Query MongoDB to implement typheahead in ui

I am trying to query my MongoDB to find all the matching name fields in the documents of my collection from the typeahead of my angular ui, I have to display the contents of the matched documents in table format, I referred few docs and wrote this API, when I try to test in Advanced REST client , it displays connection timed out, can anyone suggest me where I am going wrong?
My API code
var mongoose = require('mongoose');
var enterprise = mongoose.model('enterprise');
var search = function(req, res){
function searchEnterprise(){
var name = req.params.name;
enterprise.find({"name": '/^'+ name + '$/i'},function(err, data){
if (err){
console.log('err',err);
} else {
res.json(data);
console.log(data);
}
});
}
}
module.exports = {
searchEnterprise : search
};
no need nested function searchEnterprise(). just use it
var search = function(req, res){
var name = req.params.name;
// can also use $regex like bellow line
//enterprise.find({'name': {$options:'i', $regex: name }}, or
// enterprise.find({"name": '/'+ name + '/i'}
enterprise.find({'name': {$options:'i', $regex: name }},function(err, data){
if (err){
console.log('err',err);
return res.status(400).send({msg: "error"});
} else {
console.log(data);
return res.json(data);
// or
//return res.status(200).send(data);
}
});
};
module.exports = {
searchEnterprise : search
};

Sailsjs: How do I transform a POST request's body/fields for the REST API

It is awesome how sails will automatically generate a REST API for me. But, how can I add extra processing to a REST route? For example, I might have a POST /users route to create new users and it accepts a password as one of the attributes for the users model. But, I wouldn't want to store passwords as-is; rather I would want to hash passwords and salt them for better security. Is there a way to write code before the REST API handles the request so that way I can transform the input or do some kind of processing in general if need be?
You can implement beforeValidate or beforeCreate method under your user model
Check out the doc here http://www.sailsjs.org/#!/documentation/concepts/ORM/Lifecyclecallbacks.html
I use this for hash password :
/**
* User.js
*
* #description :: TODO: You might write a short summary of how this model works and what it represents here.
* #docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
attributes : {
name : 'string',
password : {
type : 'string',
required : true,
minLength : 6
},
email : {
type : 'email',
unique : true,
required : true
},
toJSON : function ()
{
var obj = this.toObject();
delete obj.password;
return obj;
}
},
beforeCreate : function (attrs, next)
{
var bcrypt = require('bcrypt');
bcrypt.genSalt(10, function (err, salt)
{
if (err)
{
return next(err);
}
bcrypt.hash(attrs.password, salt, function (err, hash)
{
if (err)
{
return next(err);
}
attrs.password = hash;
next();
});
});
}
};

Convert console command to ODM

I'm trying to run this exact query in php with mongodb-ODM
db.runCommand ( { distinct: "messages",
key: "conversation",
query: { conversation: { $in: ["533f28c9211b6f7e448b4567","52cb29b0211b6fd9248b456b"] } }
} )
How can I transate it with distinct() ?
thanks.
You can see My Implementation :
Define this method that execute javascript Query in mongoDB
/**
* Execute javascript in mongodb
*
* #param string $js JavaScript function
*
* #return array
*/
protected function executeJs($js)
{
// get database name
$mongoDatabaseName = $this->dm->getConfiguration()->getDefaultDB();
// get connection
$m = $this->dm->getConnection();
// return results, get mongodb client
return $m->getMongo()
// select database
->selectDB($mongoDatabaseName)
// execute javasctipt function
->command(array(
// js
'eval' => $js,
// no lock database, while js will be executed
'nolock' => true,
));
}
Define your String Query
$_myQuery = 'function() {var messages = [];
db.runCommand ( { distinct: "messages",
key: "conversation",
query: { conversation: { $in: ["533f28c9211b6f7e448b4567","52cb29b0211b6fd9248b456b"] } }
}
).forEach(function(msg){ messages[]= msg });
return messages; }';
Finally , execute your Query
$messageCollection = $this->executeJs($_myQuery);
if(isset(messageCollection))
var_dump(messageCollection['retval']); // it will show result