Conditional array items with Joi - javascript

I have an issue to create Joi schema for this input
{
projects: ["*","ABC123","ABC456"]
}
With the input above, it should throw an error.
I did try to use Joi.alternatives() like this
const schema = Joi.object({
projects:
Joi.array()
.items(
Joi.alternatives(
Joi.string().equal('*'),
Joi.string().regex(new RegExp(/^ABC_([0-9]{3,3})$/))
)
)
})
but it appears to allow both ["*"] and ["ABC123","DEF456"] together. I wanted it to be either ["*"] or ["ABC123","DEF456"], else it will be an error.
How can I actually achieve it by using Joi?

You can try something like this:
const schema = Joi.object({
projects: Joi.alternatives(
Joi.array().length(1).items(
Joi.string().equal('*')
),
Joi.array().items(
Joi.string().regex(/^ABC[0-9]{3}$/)
)
)
});
That is, havingh two alternative array schemas instead of having a single array schema with alternative schemas of elements.
The .length(1) is there to reject values like ["*", "*"] if you want to reject arrays like that, but could be omitted otherwise.
Also, the regex could be written in a simpler way so I simplified it, but I guess that it was just an example so that is not so important.
What is important though is that i removed the underscore ("_") from the regex, because a regex in your example didn't match values like "ABC123" in your example but e.g. "ABC_123".

Related

how to use .populate() in javascript

I have a collection called
CR
what I want to do is query CR like so:
let cre = await CR.find({myid: "xxxxxx"})
and i also have a collection called cla, but that one i need to query based off of the results of cre. The results of cre will return a class_id, in which I need to use to query the cla collection to find the _id. At the end of all of this, I want to ideally merge the two, which I believe you can do through .populate(), and then send it to teh front-end as one.;
I have tried this:
let cre = await cr.find({myid: "xxx"}).populate('loc').populate('desc').populate('dt');
but this isn't working. how can I fix this?
It may be due to schemas, but this is how it's clean and simple to use;
let cre = await cr.find({myid: "xxx"}).populate(['loc','desc','dt']);
Firstly, you can take cla collection "_id" in CR collection schema. In schema of CR collection refer to cla model id like this,
const creSchema = mongoose.Schema({
name: String,
classnId: { type: mongoose.Types.ObjectId, ref: "Cla" }
});
Then you can populate like,
const cres = await CR.find({}).populate({path:'classnId', select:'columnName'});
Hopefully, this will solve your issue.
Note: There in populating you can give multiple column names by space and if you give a minus before a column name like this (-columnName) then that column will not show when you will call the API.

GraphQL field with space

I'm trying to use a new GraphQL server on a very old legacy code, where the column names have spaces, e.g: "Poke ball"
I've been trying to run this query:
query{{userItems{Poke ball}}}
and got this:
extensions: {code: "GRAPHQL_VALIDATION_FAILED",…}
locations: [{line: 1, column: 12}]
message: "Cannot query field "Poke" on type "UserItems"."
I've tried to use quotes with no luck, any idea if this is supported / workaround?
Thanks.
The GraphQL specification requires that names of things (fields, types, arguments, etc.) only contain letters, numbers and underscores. A field name cannot contain a space because spaces and other whitespace are used to separate individual tokens. In other words, one or more spaces or line returns are used to indicate that, for example, one field's name has terminated and another has begun.
If your underlying data layer is returning data with keys that contain spaces, you need to define a field with an allowed name (like pokeball) and then write a resolver for that field. For example:
const UserItems = new GraphQLObjectType({
name: "UserItems",
fields: () => ({
pokeball: {
type: Pokeball,
resolve: (parent) => {
// The parent value will be whatever the parent field resolved to.
// We look for a property named "Poke ball" on that value and return it.
return parent["Poke ball"];
},
},
...
}),
});
or in the schema, do this
directive #fetch(from : String!) on FIELD_DEFINITION
type Product {
Pokeball : String #fetch(from:"Poke ball")
}

Problem in handling many field through function in mongoose

I want to generate field in mongoose with function.
Because there are many fields, but they are much the same, I wanna use function to create them to keep code short.
I wrote a function, but there exists lints.
import { Schema } from 'mongoose'
function fieldGen(name, type="string", isRequired=true) {
var field = {}
field[name] = {
type: type,
required: isRequired
}
return {...field}
}
const testSchema = new Schema({
fieldGen("firstname")
fieldGen("lastname")
fieldGen("location")
})
In VS Code, Problem shows as below
Identifier expected. ts(1003) [20, 12]
I expect first argument "firstname" matches name in function, and return object.
You're adding values to your testSchema object without giving them names.
Also, you're spreading the properties of the field object into a new object literal. That doesn't accomplish anything. Just returning the field object would yield the same result.
I see what you are trying to do. If you debug this in smaller steps and take a closer look at the data you are handling, I think you'll figure it out on your own.

Cannot add a key to an object in Javascript [duplicate]

I have a document from a mongoose find that I want to extend before JSON encoding and sending out as a response. If I try adding properties to the doc it is ignored. The properties don't appear in Object.getOwnPropertyNames(doc) making a normal extend not possible. The strange thing is that JSON.parse(JSON.encode(doc)) works and returns an object with all of the correct properties. Is there a better way to do this?
Mongoose Models inherit from Documents, which have a toObject() method. I believe what you're looking for should be the result of doc.toObject().
http://mongoosejs.com/docs/api.html#document_Document-toObject
Another way to do this is to tell Mongoose that all you need is a plain JavaScript version of the returned doc by using lean() in the query chain. That way Mongoose skips the step of creating the full model instance and you directly get a doc you can modify:
MyModel.findOne().lean().exec(function(err, doc) {
doc.addedProperty = 'foobar';
res.json(doc);
});
JohnnyHK suggestion:
In some cases as #JohnnyHK suggested, you would want to get the Object as a Plain Javascript.
as described in this Mongoose Documentation there is another alternative to query the data directly as object:
const docs = await Model.find().lean();
Conditionally return Plain Object:
In addition if someone might want to conditionally turn to an object,it is also possible as an option argument, see find() docs at the third parameter:
const toObject = true;
const docs = await Model.find({},null,{lean:toObject});
its available on the functions: find(), findOne(), findById(), findOneAndUpdate(), and findByIdAndUpdate().
NOTE:
it is also worth mentioning that the _id attribute isn't a string object as if you would do JSON.parse(JSON.stringify(object)) but a ObjectId from mongoose types, so when comparing it to strings cast it to string before: String(object._id) === otherStringId
the fast way if the property is not in the model :
document.set( key,value, { strict: false });
A better way of tackling an issue like this is using doc.toObject() like this
doc.toObject({ getters: true })
other options include:
getters: apply all getters (path and virtual getters)
virtuals: apply virtual getters (can override getters option)
minimize: remove empty objects (defaults to true)
transform: a transform function to apply to the resulting document before returning
depopulate: depopulate any populated paths, replacing them with their original refs (defaults to false)
versionKey: whether to include the version key (defaults to true)
so for example you can say
Model.findOne().exec((err, doc) => {
if (!err) {
doc.toObject({ getters: true })
console.log('doc _id:', doc._id)
}
})
and now it will work.
For reference, see: http://mongoosejs.com/docs/api.html#document_Document-toObject
To get plain object from Mongoose document, I used _doc property as follows
mongooseDoc._doc //returns plain json object
I tried with toObject but it gave me functions,arguments and all other things which i don't need.
The lean option tells Mongoose to skip hydrating the result documents. This makes queries faster and less memory intensive, but the result documents are plain old JavaScript objects (POJOs), not Mongoose documents.
const leanDoc = await MyModel.findOne().lean();
not necessary to use JSON.parse() method
You can also stringify the object and then again parse to make the normal object.
For example like:-
const obj = JSON.parse(JSON.stringify(mongoObj))
I have been using the toObject method on my document without success.
I needed to add the flattenMap property to true to finally have a POJO.
const data = document.data.toObject({ flattenMaps: true });

Is there a native feature to convert string based JSON into Mongoose Schema object instance?

I am using Express and I am looking for a convenient way to convert this kind of object (which comes on the request req.body.myObject):
{
"name": "Foo",
"someNumber": "23",
"someBoolean": "on"
}
Into an instance of this Schema:
var myObjectSchema = new Schema({
name: String,
someNumber: Number,
someBoolean: Boolean
});
Notice that the first object comes from the request, so its made entirely by Strings.
Is there some nice way to achieve this? If not, would you have any suggestions on how to implement this feature as a middleware???
By referring to this thread Mongoose : Inserting JS object directly into db I figured out that yes, theres a built in feature for this.
You simply build a new model passing request values (from the form) as parameters:
function add(req, res){
new Contact(req.body.contact).save(function(err){
console.log("Item added");
res.send();
});
};
It automatically converts stuff for you!
I know this answer has already been accepted, but I wanted to point out that mongoose takes care of most of the casting for you... most of the time. While it's convenient that mongoose does this, it abstracts away the true behavior of mongo. For example, mongoose lets you do something like this:
PersonModel.findById("4cdf00000000000000007822", ...);
However, if you tried to query the database directly (without mongoose), this would not work:
PersonCollection.find({_id: "4cdf00000000000000007822"}, ...);
This is because ObjectIds are not strings... they are objects. Internally, mongoose converts that string to an ObjectId and then performs a query against the database so that the final query looks kinda like this:
PersonCollection.find({_id: ObjectId("4cdf00000000000000007822")}, ...);
Also, each path in a schema has a "caster" method. This is a private method, but it's darn handy when you need it. PLEASE NOTE THAT THE caster METHODS DESCRIBED BELOW ARE UNDOCUMENTED AND CAN CHANGE WITHOUT WARNING. USE AT YOUR OWN RISK (sorry for yelling):
// Use PersonModel.schema.paths() to get all paths and loop over them if you want
var key = "name";
var pathObj = PersonModel.schema.path( key );
if( !pathObj ) pathObj = PersonModel.schema.virtualpath( key );
if( !pathObj ) { /* not found: return, continue, exit, whatever */ }
// UNDOCUMENTED: USE AT YOUR OWN RISK
var caster = pathObj.caster || pathObj;
var castedValue = caster.cast( req.body.name );
Why do I know this? Because if you want to use some of the more advanced features of mongo such as aggregation, you will need to cast your own values as you build the pipeline. I have also needed to manually cast values for certain queries which used the $in operator... maybe this is not needed any more. Point is, if you are having trouble getting the results you expect, try casting the values yourself.
Provided the schema is static, one could theoretically go the lazy, non-sophisticated way and just hardcode the values instead of passing the object itself:
var someObject = {
name: "Foo",
someNumber: "23",
someBoolean: "on"
}
var myObjectSchema = new Schema({
name: someObject.name,
someNumber: parseInt(someObject.someNumber, 10),
someBoolean: (someObject.someBoolean == "on")
});
Possibly not the answer you were looking for, but might be something to consider if nothing better is available.

Categories

Resources