MongoDB embedded document BSON ID - ruby

I have a webapp where I'm created an embedded document for educations. The job document looks something like this:
"educations" : [
{
"school" : "Brandywine High School",
"major" : "Testingasdf",
"grad_year" : ISODate("1979-01-01T00:00:00Z"),
"school_type" : "Graduate",
"_id" : ObjectId("4fb26c9ce5be08208b000ce4")
}
],
"email" : "user#domain.com",
The education hash has the details of the job. I noticed that if i create the hash without an ID:
User.collection.update(
{ _id: #user.id },
{ :$push => { educations: education } },
{ safe: true }
)
and I query the education from the Rails console, the ID will change each time:
irb(main):004:0> User.brandon.educations.map(&:id)
=> [BSON::ObjectId('4fb26e13e5be082384000007')]
irb(main):005:0> User.brandon.educations.map(&:id)
=> [BSON::ObjectId('4fb26e13e5be082384000009')]
However, if I do this:
User.collection.update(
{ _id: #user.id },
{ :$push => { educations: BSON::ObjectId.create_pk(education) } },
{ safe: true }
)
The ID is the same each time queried from the console. Because of this, I'm having trouble referencing the education to edit the embedded doc in the browser.
Do I always have to supply a BSON ID when I create an embedded document?

If you don't supply some kind of identifier (it could be a String, an int, etc) in the _id field, then MongoDB will automatically create one for you, to make the document unique (so that it can be looked up by _id). There is normally a unique index on _id, so if the document isn't unique, insertion will fail (or the existing document will be updated instead, etc).
MongoDB's strategy for making the document unique is to use an ObjectID, as these are globally unique. They are also different every time you create one — that's how they are globally unique.
Long story short: if you have a key that makes your document 100% unique already (possibly :school in your example), then store it as _id, and MongoDB will do the hard work for you.

Related

Get complete GraphQL response using POST without specify field name in request [duplicate]

Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

Do a full query simply in GraphQL [duplicate]

Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

How could I structure my graphql schema to allow for the retrieval of possible dropdown values?

I'm trying to get the possible values for multiple dropdown menus from my graphQL api.
for example, say I have a schema like so:
type Employee {
id: ID!
name: String!
jobRole: Lookup!
address: Address!
}
type Address {
street: String!
line2: String
city: String!
state: Lookup!
country: Lookup!
zip: String!
}
type Lookup {
id: ID!
value: String!
}
jobRole, city and state are all fields that have a predetermined list of values that are needed in various dropdowns in forms around the app.
What would be the best practice in the schema design for this case? I'm considering the following option:
query {
lookups {
jobRoles {
id
value
}
}
}
This has the advantage of being data driven so I can update my job roles without having to update my schema, but I can see this becoming cumbersome. I've only added a few of our business objects, and already have about 25 different types of lookups in my schema and as I add more data into the API I'll need to somehow to maintain the right lookups being used for the right fields, dealing with general lookups that are used in multiple places vs ultra specific lookups that will only ever apply to one field, etc.
Has anyone else come across a similar issue and is there a good design pattern to handle this?
And for the record I don't want to use enums with introspection for 2 reasons.
With the number of lookups we have in our existing data there will be a need for very frequent schema updates
With an enum you only get one value, I need a code that will be used as the primary key in the DB and a descriptive value that will be displayed in the UI.
//bad
enum jobRole {
MANAGER
ENGINEER
SALES
}
//needed
[
{
id: 1,
value: "Manager"
},
{
id: 2,
value: "Engineer"
},
{
id: 3,
value: "Sales"
}
]
EDIT
I wanted to give another example of why enums probably aren't going to work. We have a lot of descriptions that should show up in a drop down that contain special characters.
// Client Type
[
{
id: 'ENDOW',
value: 'Foundation/Endowment'
},
{
id: 'PUBLIC',
value: 'Public (Government)'
},
{
id: 'MULTI',
value: 'Union/Multi-Employer'
}
]
There are others that are worse, they have <, >, %, etc. And some of them are complete sentences so the restrictive naming of enums really isn't going to work for this case. I'm leaning towards just making a bunch of lookup queries and treating each lookup as a distinct business object
I found a way to make enums work the way I needed. I can get the value by putting it in the description
Here's my gql schema definition
enum ClientType {
"""
Public (Government)
"""
PUBLIC
"""
Union/Multi-Employer
"""
MULTI
"""
Foundation/Endowment
"""
ENDOW
}
When I retrieve it with an introspection query like so
{
__type(name: "ClientType") {
enumValues {
name
description
}
}
}
I get my data in the exact structure I was looking for!
{
"data": {
"__type": {
"enumValues": [{
"name": "PUBLIC",
"description": "Public (Government)"
}, {
"name": "MULTI",
"description": "Union/Multi-Employer"
}, {
"name": "ENDOW",
"description": "Foundation/Endowment"
}]
}
}
}
Which has exactly what I need. I can use all the special characters, numbers, etc. found in our descriptions. If anyone is wondering how I keep my schema in sync with our database, I have a simple code generating script that queries the tables that store this info and generates an enums.ts file that exports all these enums. Whenever the data is updated (which doesn't happen that often) I just re-run the code generator and publish the schema changes to production.
You can still use enums for this if you want.
Introspection queries can be used client-side just like any other query. Depending on what implementation/framework you're using server-side, you may have to explicitly enable introspection in production. Your client can query the possible enum values when your app loads -- regardless of how many times the schema changes, the client will always have the correct enum values to display.
Enum values are not limited to all caps, although they cannot contain spaces. So you can have Engineer but not Human Resources. That said, if you substitute underscores for spaces, you can just transform the value client-side.
I can't speak to non-JavaScript implementations, but GraphQL.js supports assigning a value property for each enum value. This property is only used internally. For example, if you receive the enum as an argument, you'll get 2 instead of Engineer. Likewise, you would return 2 instead of Engineer inside a resolver. You can see how this is done with Apollo Server here.

Linking 3 types of document for a view

I'm struggling with linked documents when creating a view.
A salesperson has multiple clients, each client has multiple
purchases.
I need to get a view containing:
salesperson ids for each client purchase.
In a relational database I would join:
purchase.clientid -> client._id
client.salesperson -> salesperson._id
Given:
{ _id: "1", type: "purchase", clientid: "2", items: [] }
{ _id: "2", type: "client", salespersonid: "3", name: "Chris the client" }
{ _id: "3", type: "salesperson", name: "Simon the salesperson" }
I've tried reading a lot of stuff, but nothing has clicked. How would I do this in a view?
{
_id: 'purchase-client-2-<unique-purchase-id>',
salespersonId: 'sales-3'
}
{
_id: 'sales-3',
name: 'Simon the salesperson'
}
{
_id: 'client-2',
name: 'Chris the client'
}
With the above documents you could query for all documents starting with 'purchase-client2' to get an array of purchase document. Each purchase document then tells you who the sales person was. Depending on the number of sales staff you may already have everything you need right there, assuming your map of sales id to name is already in memory.
If not, you could do a further lookup (and potentially cache that result). If that in-memory lookup or extra lookup doesn't work for you you could also duplicating the sales person's name in the purchase document. After all, NoSQL DB's don't follow the same rules as relational DB's and it's ok to duplicate now and again. You just have to think about how you keep the dups sync'ed up later.
If you can use and abuse the ID field and getaway without views then you may be better off. Views bring their own set of problems. Good luck!

MongoDB driver ruby, remove field in document

I am triying remove field in a big document, therefore I would like to do something:
collection.update({'_id' => #id}, {"$unset" => {'herField'})
But it is not possible. I don't want to rewrite entire document, any idea?
EDIT: I am using https://github.com/mongodb/mongo-ruby-driver
Your syntax looks slightly incorrect. As per docs:
collection.update( { _id: #id }, { $unset: { herField: true } }, { multi: true });
Need the 'multi' option if you want to update multiple documents. E.g. from all records on this collection.
http://docs.mongodb.org/manual/reference/operator/unset/#op._S_unset

Resources