So, I was creating a graphql schema,
type Details {
1: User!
2: User!
3: User!
id: String!
item: String!
}
What I want is to use the Int (1, 2, 3) in my Schema but not sure how.
Someone must have been in the same situation before, help me figure it out.
Names in GraphQL can include numbers, but they cannot start with a number.
If you need to map over an existing API that returns numbers as fields, you can do a mapping such as this one: Graphql: How to map fields that start with number
Related
Reaching to you all as I am in the learning process and integration of Apollo and graphQL into one of my projects. So far it goes ok but now I am trying to have some mutations and I am struggling with the Input type and Query type. I feel like it's way more complicated than it should be and therefore I am looking for advice on how I should manage my situation. Examples I found online are always with very basic Schemas but the reality is always more complex as my Schema is quite big and look as follow (I'll copy just a part):
type Calculation {
_id: String!
userId: String!
data: CalculationData
lastUpdated: Int
name: String
}
type CalculationData {
Loads: [Load]
validated: Boolean
x: Float
y: Float
z: Float
Inputs: [Input]
metric: Boolean
}
Then Inputs and Loads are defined, and so on...
For this I want a mutation to save the "Calculation", so in the same file I have this:
type Mutation {
saveCalculation(data: CalculationData!, name: String!): Calculation
}
My resolver is as follow:
export default resolvers = {
Mutation: {
saveCalculation(obj, args, context) {
if(context.user && context.user._id){
const calculationId = Calculations.insert({
userId: context.user._id,
data: args.data,
name: args.name
})
return Calculations.findOne({ _id: calculationId})
}
throw new Error('Need an account to save a calculation')
}
}
}
Then my mutation is the following :
import gql from 'graphql-tag';
export const SAVE_CALCULATION = gql`
mutation saveCalculation($data: CalculationData!, $name: String!){
saveCalculation(data: $data, name: $name){
_id
}
}
`
Finally I am using the Mutation component to try to save the data:
<Mutation mutation={SAVE_CALCULATION}>
{(saveCalculation, { data }) => (
<div onClick={() => saveCalculation({ variables : { data: this.state, name:'name calcul' }})}>SAVE</div>
}}
</Mutation>
Now I get the following error :
[GraphQL error]: Message: The type of Mutation.saveCalculation(data:)
must be Input Type but got: CalculationData!., Location: undefined,
Path: undefined
From my research and some other SO posts, I get that I should define Input type in addition to the Query type but Input type can only avec Scalar types but my schema depends on other schemas (and that is not scalar). Can I create Input types depending on other Input types and so on when the last one has only scalar types? I am kinda lost cause it seems like a lot of redundancy. Would very much appreciate some guidance on the best practice. I am convinced Apollo/graphql could bring me quite good help over time on my project but I have to admit it is more complicated than I thought to implement it when the Schemas are a bit complex. Online examples generally stick to a String and a Boolean.
From the spec:
Fields may accept arguments to configure their behavior. These inputs are often scalars or enums, but they sometimes need to represent more complex values.
A GraphQL Input Object defines a set of input fields; the input fields are either scalars, enums, or other input objects. This allows arguments to accept arbitrarily complex structs.
In other words, you can't use regular GraphQLObjectTypes as the type for an GraphQLInputObjectType field -- you must use another GraphQLInputObjectType.
When you write out your schema using SDL, it may seem redundant to have to create a Load type and a LoadInput input, especially if they have the same fields. However, under the hood, the types and inputs you define are turned into very different classes of object, each with different properties and methods. There is functionality that is specific to a GraphQLObjectType (like accepting arguments) that doesn't exist on an GraphQLInputObjectType -- and vice versa.
Trying to use in place of another is kind of like trying to put a square peg in a round hole. "I don't know why I need a circle. I have a square. They both have a diameter. Why do I need both?"
Outside of that, there's a good practical reason to keep types and inputs separate. That's because in plenty of scenarios, you will expose plenty of fields on the type that you won't expose on the input.
For example, your type might include derived fields that are actually a combination of the underlying data. Or it might include fields to relationships with other data (like a friends field on a User). In both these case, it wouldn't make sense to make these fields part of the data that's submitted as as argument for some field. Likewise, you might have some input field that you wouldn't want to expose on its type counterpart (a password field comes to mind).
Yes, you can:
The fields on an input object type can themselves refer to input object types, but you can't mix input and output types in your schema. Input object types also can't have arguments on their fields.
Input types are meant to be defined in addition to normal types. Usually they'll have some differences, eg input won't have an id or createdAt field.
I have a Mongoose schema set up for a user profile on a forum. What I'd like to do is set up the user's forum title to be an ObjectID and reference the Title schema. I already have this part set up. However by default I'd like for this field to be a string called "Noob" until a title is set from the user's profile which then would change this value to an ObjectID referencing the Title from the database.
title: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Title',
default: 'Noob'
},
This is what I have and is basically what I'm wanting to achieve, however this throws an error because the default I set is a string and not an ObjectID. I'm not sure how to achieve this or what alternatives I may have.
Since you've pointed out that you want to maintain the strong type ObjectId for performance reasons, you'll have to use that same type as the default. You could use an id of all 0s for example:
title: {
type: mongoose.Schema.Types.ObjectId,
ref: "Title",
default: 00000000-0000-0000-0000-000000000000
}
You can then check for this and display "Noob" in its place later?
You can just make it a string:
title: {
type: String,
ref: 'Title',
default: 'Noob'
},
You can still set it as an ObjectID-looking string later.
You can't have it both ways: if you want a field to be an ObjectID type, then it needs to hold ObjectIDs. If you want to be able to hold strings in it, then it needs to be of type String.
If title is a reference to an ObjectID in another collection, Mongoose will still cast an ObjectID-ish string to an ObjectID when it performs the lookup:
Title.find({ _id: doc.title }) will still lookup an ObjectID if the doc.title is a string.
I am using Simple Schema to validate my database entries in a meteor application. I started developing a module to create forms automatically (I know autoform is quite good, but it was not exactly what I needed). To make the radio component I need to know the allowed values for that field, and since it was already specified in the schema I wanted to know if it is possible to retrieve it. Any ideas?
Consider a very simple schema:
s=new SimpleSchema({
list: {
type: String,
allowedValues: ["foo","bar"]
}
});
If you explore the created object you'll find that:
s._schema['list'].allowedValues
returns
["foo", "bar"]
One can deduce the general pattern is:
schemaObject._schema['keyName'].allowedValues
If you had a collection as such.
user {
name: String,
email: String,
information: String
}
You would do something like so to get a list of all John's that have information of doctor.
db.user.find({name: "John", information: "doctor" });
Now this makes the code redundant when having variable inputs. Such as having permutations of fields to filter. I'm trying to make my query generic, such as this bad broken example.
Therefore I might want to be able to explicitly state fields that can match any value. The following examples should return the same documents in theory.
Example:
Un-Explicit (normal) db.user.find({});
Explicit (weird) db.user.find({name: {$ANY}});
Or make it even more complex.
db.user.find({name: {$ANY}, information: "doctor"});
This would not work, but the intention is to get all the documents that are doctors but have ANY sort of value on the name field, not just for John's. Maybe even something more complex like so.
db.user.find({name: function(){
if(req.query.name)//check if empty
{ return req.query.name; }
else { return $ANY; }
}, information: "doctor"});
This would then be generic enough to use a single query instance for dynamic request behavior.As far as I know there isn't anything like this implemented.
Thank you in advance.
To get all the documents that are doctors but have ANY sort of value on the name field, you need the $exists and $ne operators in your query, since the $exists operator matches the documents that contain the field, including documents where the field value is null and the $ne operator selects the documents where the name field is not null:
db.user.find({"name": { "$exists": true, "$ne": null }, "information": "doctor"});
If you want to find all the documents with information = 'doctor', why not just query without the name?
db.user.find({information: "doctor" });
I'm searching on Google since days and I tried many things but I still can not perform a good full text search on my user collection.
I tried ElasticSearch but was pretty impossible to query and paginate...
I tried many plugins for Mongoose like ElMongo, mongoose-full-text, Mongoosastic, etc... everyone are really bad documented and I don't know how to perform a good full text search.
So, my collection is a normal collection:
user = {
name: String,
email: String,
profile: {
something: String,
somethingElse: String
}
}
I have a search input in a page with a simple POST, if I type hello world what I need is to search on the entire collection fields the matching words of my search query and get the results.
It will be really nice also to have options to handle a pagination like 10 items per page or something...
What is the best solution to achieve this? I'm using MongoDB 2.6.* with Mongoose, NodeJS and ExpressJS.
Thanks.
You can add a text index to your Mongoose schema definition that lets you use the $text operator in your find queries to search all fields included in the text index.
To create an index to support text search on, say, name and profile.something:
var schema = new Schema({
name: String,
email: String,
profile: {
something: String,
somethingElse: String
}
});
schema.index({name: 'text', 'profile.something': 'text'});
Or if you want to include all string fields in the index, use the '$**' wildcard:
schema.index({'$**': 'text'});
This would enable you to perform a paged text search query like:
MyModel.find({$text: {$search: searchString}})
.skip(20)
.limit(10)
.exec(function(err, docs) { ... });
For more details, read the full MongoDB Text Indexes documentation.
This is not an extra answer but an addUp, but if the above answer by JohnnyHK is giving you an empty array [] .
Try something basic first without limit and skip
const results = await MyModel.find({ $text: { $search: "text" } });
Please check if the index was created properly using mongosh using
db.stories.getIndexes() or compass GUI both attached. Else create one via COMPASS GUI or via mongosh using docs
Then try changing searchText multiple times and try.
Even after all this it took me 7- 8 runs of same code to get proper results,