Skip to content
This repository has been archived by the owner on Jun 29, 2021. It is now read-only.

Clarification on casting string to ObjectId #308

Closed
RyannGalea opened this issue Jun 23, 2019 · 8 comments
Closed

Clarification on casting string to ObjectId #308

RyannGalea opened this issue Jun 23, 2019 · 8 comments

Comments

@RyannGalea
Copy link

RyannGalea commented Jun 23, 2019

Hey Everyone,

Confused if this is the proper implementation...

I created a field called 'createdBusiness' which has a default value of ObjectId (imported from 'mongod') and it is stored in the DB as an ObjectId

When I try and do:

const { business } = ctx.req.session.state;
return ContactModel.find({createdBusiness: business})

It returns no values, but if I create an ObjectId and pass it in it works fine as below

import { ObjectId } from 'mongodb';

const { business } = ctx.req.session.state
const createdBusiness = new ObjectId(business.toString())
return ContactModel.find({createdBusiness})

Now from my previous experience I always found that mongoose would cast the string value into an ObjectId but it doesn't happen here. I just want to clarify this is expected behavior.

Thanks for looking.

@hasezoey
Copy link
Contributor

could you provide your model?

@RyannGalea
Copy link
Author

RyannGalea commented Jun 26, 2019

Contact modal:

      @ObjectType()
      export class Contact extends CreatedTypegoose {
        
        @Field()
        readonly _id: ObjectId

        @Field(type => ContactType)
        @Property({required: true, enum: Object.values(ContactType), index: true})
        contactType: ContactType

        @Field()
        @Property({lowercase: true}) 
        name: string 

        @Field()
        @Property({lowercase: true})
        surname: string

        @Field()
        @Property({lowercase: true})
        email: string

        @Field()
        @Property({default: false, index: true})
        emailConfirmed: boolean

        @Field()
        @Property()
        mobile: string 

        @Field()
        @Property()
        phone: string

        @Field()
        @Property({default: false, index: true})
        mobileConfirmed: boolean

        @Field(type => Address)
        @Property({ _id: false })
        postalAddress: Address

        @Field(type => Address)
        @Property({ _id: false })
        billingAddress: Address

        @Field()
        fullName(@Root() parent: Contact): string { 
          const fullName = `${parent.name} ${parent.surname || ''}` 
          return fullName.trim();
        }

        @Field(type => [Note])
        allNotes(@Root() parent: Contact) {
          return NoteModel.find({ contactRef: parent._id }).select('body')
        }


      }

Which extends a shared class:

      @ObjectType()
      export class CreatedTypegoose extends Typegoose {
        
        @Field()
        @Property({ default: () => unixTimestampSeconds() })
        createdDate: number

        @Field(type => User)
        @Property({ index: true })
        createdUser: Ref<User>

        @Field(type => Business)
        @Property({ index: true })
        createdBusiness: Ref<Business>

      }

The add contact resolver:

       @Resolver()
       export class AddContactMutation {
      
        @Authorized()
        @UseMiddleware(hasRole([Roles.ADMIN, Roles.OFFICE_WORKER, Roles.CONTACT_ADD], 'Insufficient Roles to add a Contact'))
        @InjectCreated()
        @Mutation(() => Contact)
        addContact(
          @Ctx() ctx: Context,
          @Arg('input') input: ContactInput
        ): Promise<Contact | Error> {
          try { return new ContactModel({...input}).save() } 
          catch (error) { throw new Error('Add Contact Save Error') }
        }

      }

The InjectCreated() decorator:

      export const InjectCreated = () => {

        return (target: Object, key: string, descriptor: PropertyDescriptor) => {

          const original  = descriptor.value

          descriptor.value = function(...args: any[]) {

            const [ {state}, input ] = args
            
            if (!state || !input) return original.apply(this, args)

            input['createdUser']     = new ObjectId(state.user as ObjectId)
            input['createdBusiness'] = new ObjectId(state.business as ObjectId)
            args[1] = input

            return original.apply(this, args)

          }

        }
      }

@hasezoey
Copy link
Contributor

@Property({ index: true })
createdBusiness: Ref

basic mistake, fix it with this below:

@Property({ index: true, ref: "Business" }) 
createdBusiness: Ref<Business>

Note: use ref as a string do avoid Errors like "cant use 'Business' before creation"


PS: if this does not answer your question, please provide what @Field, @ObjectType & @Property(i assume its prop as Property) is, thanks

@RyannGalea
Copy link
Author

RyannGalea commented Jun 29, 2019

Thanks @hasezoey, ill give this a try and report back. Knew it would likely be something simple.

Edit: This fixed it right away, thank you. Not sure this is immediately clear in the docs. Of course, it's likely I missed it thanks @hasezoey

@Laubeee
Copy link

Laubeee commented Apr 24, 2020

I basically have the same question. How am I supposed to convert a string to a ref-object?

If I do something like this
const x: Ref<User> = new ObjectId("myhexstring");
I get an error that ObjectId cannot be assigned to Ref type.
Currently I am doing some workaround like this:
const x: Ref<User> = (new Types.ObjectId("myhexstring") as unknown) as Schema.Types.ObjectId;
which is VERY ugly. Is there a better way?

@hasezoey
Copy link
Contributor

@Laubeee please use the new repository for questions, this repo is not maintained anymore

@Laubeee
Copy link

Laubeee commented Apr 24, 2020

phew. It sure seems to be difficult to actually get an answer to questions in this repo. Nevertheless I am sure I won't be the last that's gonna be led here by google, so posting an answer here (if there is a simple one) would still be beneficial.
If there is none and this needs to be addressed as means of further development I will be happy to open a new issue in the new repo and link it here for those who end up at this place (or you could do so)

@hasezoey
Copy link
Contributor

@Laubeee i would like this repo to be archived (like #398) but only the repository owner can do this

It sure seems to be difficult to actually get an answer to questions in this repo

i know, but i say this because much changed since the move to the new repo, because this one couldnt be maintained (the original author abandoned this project, and the other 2 maintainers that i know of either left or didnt have much time to review pr's and maintain this project)

(if there is a simple one) would still be beneficial.

the answer as low because this repo here is old, and if written here it is either not noticed, or targeted at 5.x or earlier which are versions that are not maintained by the newer project anymore (version 6 and 7 are already out on the new one)

PS: and this is because and documentation website exists now for the project instead of an long readme (most of the new questions here are answered by this)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants