Skip to content

Support deeply nested pointers in 'containedIn' queries #7414

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

Closed
2 tasks
oallouch opened this issue Jun 2, 2021 · 15 comments · Fixed by #9738
Closed
2 tasks

Support deeply nested pointers in 'containedIn' queries #7414

oallouch opened this issue Jun 2, 2021 · 15 comments · Fixed by #9738
Labels
state:released-alpha Released as alpha version

Comments

@oallouch
Copy link
Contributor

oallouch commented Jun 2, 2021

New Issue Checklist

Issue Description

Querying using "containedIn" in deep nested objects using pointers doesn't seems to work.

Steps to reproduce

See example below.

Actual Outcome

The queries return empty arrays.

Expected Outcome

We expected an array of Parse.Object .

Failing Test Case / Pull Request

  • 🤩 I submitted a PR with a fix and a test case.
  • 🧐 I submitted a PR with a failing test case.

To reproduce the issue, I added this at the end of ParseQuery.spec.js

  it('deeply nested Pointers (issue #7413)', async function (done) {
    const parent = new Parse.Object('Parent');
    const child1 = new Parse.Object('Child');
    const child2 = new Parse.Object('Child');

    parent.set('children', [
      { child: child1, count: 2 },
      { child: child2, count: 3 },
    ]);
    await parent.save();

    const results = await new Parse.Query('Parent')
      .containedIn('children.child', [child1])
      .find();
    expect(results.length).toBe(1);
    done();
  });

This test passes if I replace the line :
.containedIn('children.child', [child1])
by
.containedIn('children.child.objectId', [child1.id])

Environment

parse-server 4.5.0

Server

  • Parse Server version: 4.5.0
  • Operating system: Windows and debian
  • Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): local Windows and Google App Engine

Database

  • System (MongoDB or Postgres): MongoDB
  • Database version: 4.4.6
  • Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): local and MongoDB Atlas

Client

  • SDK (iOS, Android, JavaScript, PHP, Unity, etc): Javascript
  • SDK version: 3.2

Logs

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

Thanks for reporting.

I think that makes sense. Are you suggesting that when the objectId is omitted, it should be inferred, as a convenience? That would mean children.child becomes a shorthand for children.child.objectId.

Or should stating the children.child.objectId throw an error?

What would currently happen if a custom field is used, e.g. children.child.myField. Is that an invalid query or would Parse Server generate a proper MongoDB query?

@oallouch
Copy link
Contributor Author

oallouch commented Jun 3, 2021

What I suggest is using children.child as a shorthand for children.child.objectId, like it's actually the case with equalTo.
children.child.objectId should also work, like it does today.
I just would like it to work like it does with simple Pointer arrays.

To sum up, the actual state is :
✅ .containedIn('children.child.objectId', [child1.id])
❌ .containedIn('children.child', [child1])
✅.equalTo('children.child.objectId', child1.id)
✅.equalTo('children.child', child1)

The 2 options without objectId are a more natural way for Parse users to query.
"Give me the Parent with this Child"

Many of my developers have faced this issue.
It's part of the little caveats we tell new devs when they enter the company, like the infamous "dirty() always returns true for new objects, even if the property doesn't exist" 😋

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

The 2 options without objectId are a more natural way for Parse users to query.

I think you are right. I agree that it should be possible to do .containedIn('children.child', [child1]).

I am less sure about whether we should allow .containedIn('children.child.objectId', [child1.id]).
Because to me that infers that it would be possible to do children.child.myCustomField, which I think it isn't.
That's why I wonder if we have anything comparable to "specifying an object ID optionally" in other places in the SDK.
If this is the only exception, we could think about removing this bit - or is there any use?

@oallouch
Copy link
Contributor Author

oallouch commented Jun 3, 2021

That would be a breaking change for sure, and I don't think being able to use any Pointer subfield is bad.
I like the fact that Parse provides added value without going to much in the way.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

I like the fact that Parse provides added value without going to much in the way.

We are now following a phased deprecation policy to avoid sudden breaking changes. So developers would have time to adapt to a change. We need to maintaining the code base and that means stripping features that are duplicates or provide little value, even if they are breaking changes.

That's why I'm wondering what benefit .containedIn('children.child.objectId', [child1.id]) provides when this PR adds .containedIn('children.child', [child1]).

@oallouch
Copy link
Contributor Author

oallouch commented Jun 3, 2021

Maybe I just have the id.
I can do .containedIn('children.child.objectId', [childId]) without having to retrieve the object from the base, just to use an id I already have.

And it's not only about objectId. Maybe one time, I'd like to get all the Parse.Object documents with an array containing at least a pointer of class Child.
For this, I would use .containedIn('children.child.className', 'Child')

As I said, I love the way parse-server doesn't get in the way. For example, you can create a Parse.Function or use a direct express route (to download pdf file, for instance). That's what make my clients adopt parse-server. You're not blocked.

I'm curious to know the proportion of parse-server projects that have the equivalent of those few line :

function getDatabaseController() {
	const config = Config.get(Parse.applicationId);
	return config.database;
}

async function getMongoCollection(className) {
	const mongoAdapter = getDatabaseController().adapter;
	await mongoAdapter.connect();
	return mongoAdapter.database.collection(className);
}

Btw, we should get a public API for this 😋. Parse can't (and shouldn't) do everything, it already does so much.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

Would you want to open a PR to add the .containedIn('children.child', [child1]) functionality?

@oallouch
Copy link
Contributor Author

oallouch commented Jun 3, 2021

I can try...

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

Great, I assume the .equalTo('children.child.objectId', child1.id) should give some hints on how to approach it.

@oallouch
Copy link
Contributor Author

oallouch commented Jun 8, 2021

I did some tests, and I think I understand the problem now.
I thought equalTo only compared objectId, but it also uses __type and className.
I understand now why you called changing children.child to children.child.objectId a "shorthand".

I found the cause. It's actually a bug in MongoTransform's transformConstraint.

This is what it outputs now.

with equalTo (good result) :

mongoWhere: {
'children.child': { __type: 'Pointer', className: 'Child', objectId: 'BoXaGRlIqO' },
_rperm: { '$in': [ null, '', '' ] }
}

with containedIn (bad result) :

mongoWhere: {
'children.child': { '$in': [ 'Child$BoXaGRlIqO' ] },
_rperm: { '$in': [ null, '', '' ] }
}

I'm preparing the PR.

@oallouch
Copy link
Contributor Author

The PR is #7426

@oallouch
Copy link
Contributor Author

@mtrezza , tell me if something is missing from the PR

@RahulLanjewar93
Copy link
Contributor

Even i debugged, found the same issue

@RahulLanjewar93
Copy link
Contributor

RahulLanjewar93 commented Apr 29, 2025

Ok for anyone wondering this is not a bug anymore since parse v6 check this pr #8209. So you may need to run migration which updates the nested parse related objects (object,dates,etc)

Maybe we can close this issue.

EDITED: SEEMS NESTED OBJECTS ARE STORED AS JSON check this comment #8209 (comment)

@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 8.2.1-alpha.1

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label May 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released-alpha Released as alpha version
Projects
None yet
4 participants