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

Allow refType to be defined in @prop and @arrayProp #369

Closed
wants to merge 1 commit into from

Conversation

mfulop
Copy link

@mfulop mfulop commented Aug 2, 2019

Merged changes from pr #149, solved conflicts, added itemsRefType (works the same as refType) to @arrayProps and tests

@coveralls
Copy link

coveralls commented Aug 2, 2019

Coverage Status

Coverage increased (+0.9%) to 88.488% when pulling 189a6ce on mfulop:master into 0d68100 on szokodiakos:master.

Copy link
Contributor

@hasezoey hasezoey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when these are resolved, could you squash some of your commits?

i would recommend reverting 00ddb93 and then rebase, not merge :)

src/prop.ts Outdated Show resolved Hide resolved
test/models/refTestBuffer.ts Outdated Show resolved Hide resolved
test/models/refTestNumber.ts Outdated Show resolved Hide resolved
test/models/refTestString.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
@mfulop
Copy link
Author

mfulop commented Aug 2, 2019

Sure will do. vscode merge somehow messed up things, will try another way, sorry

@hasezoey
Copy link
Contributor

hasezoey commented Aug 2, 2019

i will review the changed in 60aa3ca tomorrow

@mfulop
Copy link
Author

mfulop commented Aug 3, 2019

ok, you might want to look at ab91ee0 instead

@mfulop
Copy link
Author

mfulop commented Aug 3, 2019

Btw though it’s working, I think Ref is now a bit too liberal. To be 100% correct, in Ref<R,T>, T should be R or the type of the designated id field of R.
Sadly it is beyond my current typescript knowledge :)

Copy link
Contributor

@hasezoey hasezoey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would request setting the types to all lowercase to keep everything in style and not "string" and "Buffer" just to write it easy

PS: sorry that it took so long, my Desktop Environment just kept crashing

PPS: could you please squash all your commits? (git rebase -i master)
and try to reword your commits to comply with CONTRIBUTING.md (if not, i will at the merge squash & reword it)

src/prop.ts Outdated Show resolved Hide resolved
src/prop.ts Outdated Show resolved Hide resolved
src/prop.ts Outdated Show resolved Hide resolved
src/prop.ts Outdated Show resolved Hide resolved
src/prop.ts Outdated Show resolved Hide resolved
@mfulop
Copy link
Author

mfulop commented Aug 3, 2019

How about now?
Another thing came to mind before we finalize this. I merged Matt's changes from #149 regarding the refType, but does that have to be string? Would not specifying actual schema types make more sense? (it would to me...):
@prop({ ref: Car, refType: mongoose.Schema.Types.String })

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

I merged Matt's changes from #149 regarding the refType, but does that have to be string?

yes, would be a better idea, only that "I merged Matt's changes" doesnt not seem to be actually merged (its still string)

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

OK, so here c0ab190 is how I would do it. I think it's much cleaner this way. What do you think?

src/prop.ts Outdated Show resolved Hide resolved
@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

I noticed after some testing, that if i do

const test = new RefTestModel({ refFieldString: new RefTestStringModel() }); // just as an example
if (isDocument(test.refFieldString)) {
  test.refFieldString // i mean here
}

the type of test.refFieldString in the if is InstanceType<T> | (string & Document) where it should only be InstanceType<T>

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

got it fixed, please modify typeguards.ts:

export function isDocument<T, S extends RefType>(doc: Ref<T, S>): doc is InstanceType<T> {

and

export function isDocumentArray<T, S extends RefType>(docs: Ref<T, S>[]): docs is InstanceType<T>[] {

and if possible, add a test for it (just to check if the types are not brocken)
otherwise it looks really great 👍

(example for the tests:

const test = new RefTestModel({ refArrayString: [new RefTestStringModel()] });
if (isDocumentArray(test.refArrayString)) {
  test.refArrayString;
  console.log('in if isDocument');
} else {
  console.log('in else isDocument');
}

const test2 = new RefTestModel({ refFieldString: new RefTestStringModel() });
if (isDocument(test2.refFieldString)) {
  test2.refFieldString;
  console.log('in if isDocument');
} else {
  console.log('in else isDocument');
}

but please note that this only the thing i used to get it working, please modify it so that it uses expect and throw)

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

I implemented all the changes and added the test. That actually brought up this:
Shouldn't
export type Ref<R, T extends RefType = mongoose.Schema.Types.ObjectId> = R | T;
be
export type Ref<R, T extends RefType = mongoose.Types.ObjectId> = R | T;
instead? (the actual values are not SchemaTypes but ObjectIds).

But would this change break compatibility?

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

a mongoose issue explaining the difference (for further readers) Automattic/mongoose#1671

so yes, it would be more right to use Types.T insteace of Schema.Types.T
and i cant see any obvious "breaking" changes, "Ref<T, S>" is just a type and so-or-so it works as expected

so i would say, yes it would be good to keep it so

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

This: new RefTestModel({ refField: mongoose.Types.ObjectId() }) is actually not possible without casting when using SchemaType

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

This: new RefTestModel({ refField: mongoose.Types.ObjectId() }) is actually not possible without casting when using SchemaType

ok, then lets keep it at Schema.Types.T

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

Then its done ;-)

Copy link
Contributor

@hasezoey hasezoey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything is and looks good, just some small style changes

test/models/refTests.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
test/tests/ref.test.ts Outdated Show resolved Hide resolved
@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

Could you check something for me?

I am putting together the test you requested above. When running the below snippet, the created RefTestModel has both refFieldString and refFieldNumber fields created with the corresponding ID.
However, while the RefTestNumber document is created in the database, for some reason, the RefTestString document is not. Therefore, the next populate will not find it.
What makes the difference? Or is it a race condition somewhere?

await RefTestModel.create({
      refFieldString: new RefTestStringModel({ _id: 'test1' }), // document not created
      refFieldNumber: new RefTestNumberModel({ _id: 1234 }), // document created
    });

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

By using create refFieldString: await RefTestStringModel.create({ _id: 'test1' }) it works (of course) but I guess that's not the point.

Similarly strange:

refArrayNumber: [new RefTestNumberModel({ _id: 5678 }), new RefTestNumberModel({ _id: 9876 })]

puts both IDs to the field correctly but only creates the first document in the database so populate returns an array with 1 item.

Should it even work?

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

Or is it a race condition somewhere?

typegoose nowhere sets a race condition, at least not knowingly


could you push it to another branch so i can check it out?

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

However, while the RefTestNumber document is created in the database, for some reason, the RefTestString document is not.

sorry, cant confirm that, for me everything gets saved
modified it to

const { _id: populatedId } = await RefTestModel.create({
  refField: new RefTestModel(), // gets saved
  refArray: [new RefTestModel(), new RefTestModel()], // gets saved
  refFieldString: new RefTestStringModel({ _id: 'test1' }), // gets saved
  refArrayString: [
    new RefTestStringModel({ _id: 'test2' }), // gets saved
    new RefTestStringModel({ _id: 'test3' }) // gets saved
  ],
  refFieldNumber: new RefTestNumberModel({ _id: 1234 }), // gets saved
  refArrayNumber: [new RefTestNumberModel({ _id: 5678 }), new RefTestNumberModel({ _id: 9876 })], // gets saved
  refFieldBuffer: new RefTestBufferModel({ _id: Buffer.from([1, 2, 3, 4]) }), // gets saved
  refArrayBuffer: [
    new RefTestBufferModel({ _id: Buffer.from([5, 6, 7, 8]) }), // gets saved
    new RefTestBufferModel({ _id: Buffer.from([9, 8, 7, 6]) }) // gets saved
  ]
});

db document:

{
  "_id": "5d4733adb736993b51db6a45",
  "refArray": [
    "5d4733adb736993b51db6a43",
    "5d4733adb736993b51db6a44"
  ],
  "refArray2": [],
  "refArrayString": [
    "test2",
    "test3"
  ],
  "refArrayString2": [],
  "refArrayNumber": [
    5678,
    9876
  ],
  "refArrayNumber2": [],
  "refArrayBuffer": [
    "BQYHCA==",
    "CQgHBg=="
  ],
  "refArrayBuffer2": [],
  "refField": "5d4733adb736993b51db6a42",
  "refFieldString": "test1",
  "refFieldNumber": 1234,
  "refFieldBuffer": "AQIDBA==",
  "__v": 0
}

(Note: mongodb compass exported it as json, so example buffer got converted to string, but i assure you, everything is saved how it should)

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

Do tests run without an error on the reftests branch for you?

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

Oh yes, the root document is correctly created. But do the referenced documents (eg. the one with id 5678) all exist?

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

Do tests run without an error on the reftests branch for you?

no thests do not run, because not populated properly

Oh yes, the root document is correctly created. But do the referenced documents (eg. the one with id 5678) all exist?

i dont have any with the id with 5678, only an array of numbers

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

currently it fails at expect(foundPopulated.refArray.length).to.equal(2);, with AssertionError: expected 0 to equal 2

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

currently it fails at expect(foundPopulated.refArray.length).to.equal(2);, with AssertionError: expected 0 to equal 2

yes and that means populate can't find the foreign document with that id

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

yes and that means populate can't find the foreign document with that id

i can confirm that Field refArrayNumber documents are saved on the DB

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

Ok, I will dig deeper tomorrow to see what’s going on. If the documents are there and the ids are there then populate should work.

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

got it further with

const pop = Object.keys(RefTestModel.schema.obj);
const foundPopulated = await RefTestModel.findById(populatedId).populate(pop).exec();

its still the same error, only for refArrayString this time, but many others are populated


will investigate further

Update:
all properties from refField to refArrayString do not get populated, but all the others

Update2:
i searched through the schema, but didnt find any differences (except types) between refFieldString and refFieldNumber

Update3:
after some much more testing i found that refField has an id (in the DB), but the actual document dosnt exist... (same with refFieldString)

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

First thing I will try in the morning is the same with native mongoose (and a real mongodb instance)

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

Update(4):
i noticed after disabling all other tests, that the documents were never created (including the ones i said are existing), because they have duplicate ids (like the number 5678 is used 3 times)

TL;DR: what i said was wrong, no new refs are actually saved

Edit:
i read more about it, it is a mongoose "by design" because it is a reference, not a subdocument
Automattic/mongoose#2228
-> we could implement such things, but this would mean that we would need to redo the .create / .save functions OR do an "invisible" pre-save-hook
--> but it is out of scope for this PR

@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

OK, so it is not supposed to work. Then await create() will do for tests, give me a minute

test/tests/ref.test.ts Outdated Show resolved Hide resolved
@mfulop
Copy link
Author

mfulop commented Aug 4, 2019

-> we could implement such things, but this would mean that we would need to redo the .create / .save functions OR do an "invisible" pre-save-hook
My 2 cents - I would not... it's a quite big difference compared to how mongoose behaves on its own, could be misleading for the ones with Mongoose experience

--> but it is out of scope for this PR
I agree

@hasezoey
Copy link
Contributor

hasezoey commented Aug 4, 2019

i see no problem anymore, only resolve the last review, squash your commits, and i will approve 👍

…kodiakos#181)

- added @prop({ refType })
- added @arrayProp({ itemsRefType })
- added Ref<R, T> to define Ref types with string, number of buffer id
- Use mongoose.Schema.Types as refType
- changed Ref<> value to mongoose.Types.ObjectId
- added refType support to IsDocument and isDocumentArray typeguards
- added tests
@mfulop
Copy link
Author

mfulop commented Aug 5, 2019

There you go 189a6ce

And thanks for all :)

Copy link
Contributor

@hasezoey hasezoey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything got resolved, everything conforms with the Coding Style, Commits are structured right 👍

@hasezoey hasezoey mentioned this pull request Aug 5, 2019
14 tasks
@hasezoey
Copy link
Contributor

hasezoey commented Aug 6, 2019

@Ben305 (if you are back) i would want to know if this should be in release 6.0.0 or release 5.10.0?

@mfulop
Copy link
Author

mfulop commented Aug 6, 2019

Hi!

Sorry, I was out for a bit. Actually I would appreciate this before v6. I am planning to port some of our proprietary sw to Typegoose and this was one missing feature. (There might be more but then you’ll see a PR about them anyways :) ). Unfortunately this project is now iced for a few days but I’ll be back.

@hasezoey
Copy link
Contributor

hasezoey commented Aug 6, 2019

Unfortunately this project is now iced for a few days but I’ll be back.

which project? this pr is finished an ready to go :) (ben is out for a week)

@mfulop
Copy link
Author

mfulop commented Aug 6, 2019

The one where I am porting stuff to run on Typegoose :)

hasezoey referenced this pull request in typegoose/typegoose Aug 12, 2019
@hasezoey
Copy link
Contributor

hasezoey commented Aug 12, 2019

Merged it for now into 6.0.0(-19) 27f5d59

Edit / Update:
I (still) dont think that this change will be in a 5.x version anymore

@hasezoey
Copy link
Contributor

this can be closed because it is included in v6.0.0, and will not be included a v5.x release

@Ben305

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

Successfully merging this pull request may close these issues.

None yet

4 participants