-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mongo fails to find user by id (hasMany relation not working) #2085
Comments
Ok, after much digging ⛰️⛏️ I figured out where the issue is, although I'm not exactly sure which library is responsible for fixing it. Basically, if you are using the mongo juggler adapter in loopback 4, and decide to implement a one-to-many relationship, the A solution to this problem is to add the @model({
settings: {
strictObjectIDCoercion: true
}
}) While this solution does indeed work, it breaks the The only way I have been able to reconcile these two "bugs" is to only pass the const devices = await this.userRepository.devices(userId).find(
{},
{
strictObjectIDCoercion: true
}
); However, please note that even when I do this, I still cannot look up a device by its id (other properties do work). So, for example, the following will not work. const devices = await this.userRepository.devices(userId).find(
{ id: deviceId },
{
strictObjectIDCoercion: true
}
);
console.log('devices', devices); // returns [] So, for now, to look up a has-many relationship by its id, I have to find all of them and then filter for the id. This works, but it is certainly not ideal because I have to load everything into memory. @get('/users/{userId}/devices/{deviceId}')
async findById(
@param.path.string('userId') userId: string,
@param.path.string('deviceId') deviceId: string
): Promise<Device> {
const devices = await this.userRepository.devices(userId).find(
{},
{
strictObjectIDCoercion: true
}
);
const device = _.find(devices, device => device.id === deviceId);
if (!device) throw new Error(`failed to find device '${deviceId}' on user '${userId}'`);
return device;
} So in summary, I do not want to have to add the This "bug" could be fixed in one of the following projects, but I'm not sure which one is most ideal to fix it from. @loopback/repository |
@codejamninja thank you for reporting this issue, it looks like a major bug to me! It makes me wonder what's the difference between LB3 (where this works) and LB4 (where it does not). Is there perhaps a difference in the way how the model properties are defined (described for the connector)? |
I'm not exactly sure. Could you give me some things to look for? |
@codejamninja Thank you for the detailed steps on the issue and in order to find the discrepancies between LB3 and LB4 as @bajtos, I thinking using something like https://github.com/strongloop/loopback-example-relations to check would be useful. I'm going to try it myself if I find some time later today. |
Also related #1987. @loredanacirstea @jormar, my proposed solution at #1987 (comment) is not comprehensive (see #2085 (comment) above):
|
Consider the following solution:
const propMeta: PropertyDefinition = { .... isForeignKey: true, };
MongoDB.prototype.isObjectIDProperty = function (model, prop, value, options) { if ( prop && (prop.type === ObjectID || (Array.isArray(prop.type) && prop.type[0] === ObjectID)) ) { return true; } else if (prop && prop.isForeignKey === true) { return false; } ...... } |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@hacksparrow , i'm assigning this to you because you're working on this already. |
Note for anyone trying to reproduce the issue While creating a If you specify the |
I have found a solution to this, but need your inputs. While finding a String which is actually an objectId, loopback-connector-mongodb converts it to ObjectId and then finds, which is perfect. But while inserting, if a string is a foreign key and not an Id, it is not converted to ObjectId What do you recommend? should we convert all the properties of regex /^[0-9a-fA-F]{24}$/ to objectId and then save @bajtos @hacksparrow what do you suggest, should we convert all the strings into ObjectId while saving in mongodb connector? |
Thanks for the input, @imonildoshi. I am looking at the possibilities. |
Here is a technical breakdown of the bug. It all boils down to the When
When
Note: |
I think the problem may be (partially) caused by the fact that the IIRC, LoopBack 3.x models usually did not define the id property explicitly, instead they relied on the connector to add that property - see https://github.com/strongloop/loopback-connector-mongodb/blob/3a79606ad67fc175020e5b0c20007a9771545bbb/lib/mongodb.js#L333-L335: MongoDB.prototype.getDefaultIdType = function() {
return ObjectID;
}; IMO, to support MongoDB, the id property should be declared as follows: @model({
settings: {
strictObjectIDCoercion: true
}
})
export class User extends Entity {
@property({
type: 'string',
id: true,
mongodb: {
dataType: 'ObjectID' // or perhaps 'objectid'?
}
})
id?: string;
// ...
} Maybe even using ObjectID class directly, but I am not sure how well will this play with the rest of LoopBack 4: import {ObjectID} from 'loopback-connector-mongodb';
@model({
settings: {
strictObjectIDCoercion: true
}
})
export class User extends Entity {
@property({
type: ObjectID,
id: true,
})
id?: ObjectID;
// ...
} Recently, @b-admike was implementing coercion for @b-admike @raymondfeng @hacksparrow @codejamninja thoughts? |
In #1875 (comment), I am proposing to fix |
I am arguing that the current behavior is actually correct. Because Of course, then there is the issue of one-to-many relationships that don't work in mongo. We need to fix that, but not necessarily by changing the behavior of |
I agree, the behavior is correct as per how This doesn't look very intuitive, though: export class User extends Entity {
@property({
type: 'string',
id: true,
mongodb: {
dataType: 'ObjectID' // or perhaps 'objectid'?
}
})
id?: string;
// ...
} I'd prefer: export class User extends Entity {
@property({
type: ObjectID,
id: true,
})
id?: ObjectID;
// ...
} Our APIs should be driven by intuitive DX. |
@devoNOTbevo thanks for the input. This will help us come up with a generic solution instead of just focussing on the MongoDB case. |
@hacksparrow I realized there was a bug in my code for the accessor function and so deleting comment. Disregard. I was passing the wrong type in the first place. |
I agree that would be a much better developer experience. However, it's also much more difficult to implement, because Consider the case where I am proposing to implement the simpler solution based on |
Obviously this problem extends to all functions of the crud repository that require searching by id. as:
In the project I created a workarround creating a second ID in the model to perform the post-creation actions and in the repository I created the methods for it. import * as uuid from 'uuid';
@model({
settings: {
strictObjectIDCoercion: true,
},
})
export class SchedulerOrder extends Entity {
@property({
type: 'string',
id: true
})
id: string;
@property({
type: 'string',
default: () => uuid()
})
key: string;
} Repository. export class SchedulerOrderRepository extends DefaultCrudRepository<
SchedulerOrder,
typeof SchedulerOrder.prototype.id
> {
constructor(
@inject('datasources.mongodb') dataSource: MongodbDataSource,
) {
super(SchedulerOrder, dataSource);
}
findByKey(key:string):Promise<SchedulerOrder|null>{
return this.findOne({where:{key:key}})
}
} I also thought about writing findById doing the ROW query to mongoDB but it seemed too great an effort if this issue is resolved soon. Thank you |
@hacksparrow, per our discussion, I believe this is done, and what we need is to release a major version of loopback-connector-mongodb? |
Yes, Also, we will now be able to configure a property as Eg: {
xid: {type: String, mongodb: {dataType: 'ObjectID'}}
} |
Closing this as done because the original scope is already completed. |
Description / Steps to reproduce / Feature proposal
When I set
stringObjectIDCoercion
totrue
, I get an error when looking up the user by id when using mongo as the datasource.I need to set
stringObjectIDCoercion
totrue
because if I don't, one-to-many relationships don't work in mongo.Current Behavior
Expected Behavior
I have tried setting the id type to
ObjectID
when a document is created, but it doesn't seem to fix it.I think this issue is related to #1875
The text was updated successfully, but these errors were encountered: