Skip to content
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

Support 'and' in many-to-many relationship (post tags) #6158

Closed
ErisDS opened this issue Dec 2, 2015 · 3 comments
Closed

Support 'and' in many-to-many relationship (post tags) #6158

ErisDS opened this issue Dec 2, 2015 · 3 comments
Labels
affects:api Affects the Ghost API bug [triage] something behaving unexpectedly

Comments

@ErisDS
Copy link
Member

ErisDS commented Dec 2, 2015

There is currently a limitation on using 'and' when trying to posts by specific tag slugs.

Using OR will work: {{#get "posts" filter="tag:quick-guide,tag:spa"}}
So will IN: {{#get "posts" filter="tag:[quick-guide,spa]"}}

However, and will not work {{#get "posts" filter="tag:quick-guide+tag:spa"}}

The reason it will not work, is that tags are only ever joined into posts once, meaning there is only one tag per row. As no row has multiple tags, no row can match.

There are two potential solutions:

  1. join tags once per item we need to check
  2. use an 'IN' and 'having count(*) > items query.

The first is more complex to do, the latter has potential performance issues in MySQL

Explanation in more detail here: http://stackoverflow.com/questions/1054299/sql-many-to-many-table-and-query

@ErisDS ErisDS added the affects:api Affects the Ghost API label Dec 2, 2015
@vislamov
Copy link

vislamov commented Dec 2, 2015

Also this type of query doesn't work, should be related:

{{#get "posts" filter="tag:quick-guide+tag:-platform" debug="true"}}

The result includes all documents tagged with quick-guide along with those tagged with platform tag, which should be omitted.

QUERY:
select * from "posts" left outer join "posts_tags" on "posts_tags"."post_id" = "posts"."id" left outer join "tags" on "posts_tags"."tag_id" = "tags"."id" where ("posts"."status" = 'published' and "posts"."page" = false) and ("tags"."slug" = 'quick-guide' and "tags"."slug" != 'platform') group by "posts"."id" order by "posts"."status" ASC, "posts"."published_at" DESC, "posts"."updated_at" DESC, "posts"."id" DESC limit 15

@ErisDS
Copy link
Member Author

ErisDS commented Dec 2, 2015

Yeah it's exactly the same query, just with a not, no AND query across multiple tags will work because tags only exist once in the row.

@ErisDS ErisDS mentioned this issue Dec 14, 2015
4 tasks
@ErisDS ErisDS added the bug [triage] something behaving unexpectedly label Jan 5, 2016
@ErisDS ErisDS changed the title Support 'and' in many-to-many tags relationship Support 'and' in many-to-many tags relationship (post tags) Jan 5, 2016
@ErisDS ErisDS changed the title Support 'and' in many-to-many tags relationship (post tags) Support 'and' in many-to-many relationship (post tags) Jan 5, 2016
@johnny94
Copy link
Contributor

After digging the code base I think there are two possible ways to fix the bug.

The first is to modify the implementation of GQL. The reason is that I found much of the query is constructed in

// core\server\models\plugins\filter.js

this.query(function (qb) {
   gql.knexify(qb, self._filters);
});

Before executing this line of the code, the raw query is something like select * from "posts", then
it will become:
select * from "posts" where ("posts"."status" = 'published' and "posts"."page" = false) and (other query...)
So If we want to implement the solution like the stackoverflow mentioned, modifying the GQL is necessary.

The second way is to extract 'tag related query' from filter object before executing gql.knexify.
Suppose that the filter is filter="tag:new-tag,hello-ghost" The filter object is something like:

 group undefined
  { op: '=', value: 'published', prop: 'posts.status' }
  { op: '=', value: false, prop: 'posts.page', func: 'and' }
 group and
  { op: '=', value: 'new-tag', prop: 'tags.slug' }
  { op: '=', value: 'hello-ghost', prop: 'tags.slug', func: 'or' }

before executing gql.knexify

Maybe we can extract the tag part before executing gql.knexify.
After then we run the special query rule for remaining tag related query. With this way, we don't need to modify the GQL. But I am not sure if this solution is possible (maybe it will be very 'hack').

@ErisDS
Copy link
Member Author

ErisDS commented Jan 16, 2016

This is definitely something that should be solved in GQL, not in Ghost. IMO GQL needs to become aware of relationship types, so that it is able to handle many-to-many relationships.

@vislamov
Copy link

Hey guys, any updates on this one? It's really limiting query language and needs to solved eventually. Workarounds are clunky and always require extra network bandwidth, extra data etc. etc.

@ErisDS ErisDS added the later [triage] Things we intend to work but are not immediate priority label Sep 20, 2016
@ErisDS
Copy link
Member Author

ErisDS commented Sep 20, 2016

I'm closing all OAuth and most API issues temporarily with the later label.

RE: OAuth, for the next 2-3 months we'll be implementing an official Ghost OAuth login system, providing global access to all Ghost blogs with a single login. We'll be opening issues around this system soon, and I don't want to cause confusion with OAuth for the API.

JSON API Overhaul & OAuth access are currently scheduled next on the roadmap

@ErisDS ErisDS closed this as completed Sep 20, 2016
kirrg001 added a commit that referenced this issue Dec 11, 2018
refs #10105, closes #10108, closes #9950, refs #9923, refs #9916, refs #9574, refs #6345, refs #6309, refs #6158, refs TryGhost/GQL#16

- removed GQL dependency
- replaced GQL with our brand new NQL implementation
- fixed all known filter limitations
- GQL suffered from some underlying filter bugs, which NQL tried to fix
- the bugs were mostly in how we query the database for relation filtering
- the underlying problem was caused by a too simple implementation of querying the relations
- mongo-knex has implemented a more robust and complex filtering mechanism for relations
- replaced logic in our bookshelf filter plugin
- we pass the custom, default and override filters from Ghost to NQL, which then are getting parsed and merged into a mongo JSON object. The mongo JSON is getting attached by mongo-knex.

NQL: https://github.com/NexesJS/NQL
mongo-knex: https://github.com/NexesJS/mongo-knex
@ErisDS ErisDS removed the later [triage] Things we intend to work but are not immediate priority label Jan 23, 2019
daniellockyer pushed a commit to TryGhost/framework that referenced this issue Jun 15, 2021
refs #10105, closes #10108, closes TryGhost/Ghost#9950, refs TryGhost/Ghost#9923, refs TryGhost/Ghost#9916, refs TryGhost/Ghost#9574, refs TryGhost/Ghost#6345, refs TryGhost/Ghost#6309, refs TryGhost/Ghost#6158, refs TryGhost/GQL#16

- removed GQL dependency
- replaced GQL with our brand new NQL implementation
- fixed all known filter limitations
- GQL suffered from some underlying filter bugs, which NQL tried to fix
- the bugs were mostly in how we query the database for relation filtering
- the underlying problem was caused by a too simple implementation of querying the relations
- mongo-knex has implemented a more robust and complex filtering mechanism for relations
- replaced logic in our bookshelf filter plugin
- we pass the custom, default and override filters from Ghost to NQL, which then are getting parsed and merged into a mongo JSON object. The mongo JSON is getting attached by mongo-knex.

NQL: https://github.com/NexesJS/NQL
mongo-knex: https://github.com/NexesJS/mongo-knex
daniellockyer pushed a commit to TryGhost/framework that referenced this issue Jun 15, 2021
refs #10105, closes #10108, closes TryGhost/Ghost#9950, refs TryGhost/Ghost#9923, refs TryGhost/Ghost#9916, refs TryGhost/Ghost#9574, refs TryGhost/Ghost#6345, refs TryGhost/Ghost#6309, refs TryGhost/Ghost#6158, refs TryGhost/GQL#16

- removed GQL dependency
- replaced GQL with our brand new NQL implementation
- fixed all known filter limitations
- GQL suffered from some underlying filter bugs, which NQL tried to fix
- the bugs were mostly in how we query the database for relation filtering
- the underlying problem was caused by a too simple implementation of querying the relations
- mongo-knex has implemented a more robust and complex filtering mechanism for relations
- replaced logic in our bookshelf filter plugin
- we pass the custom, default and override filters from Ghost to NQL, which then are getting parsed and merged into a mongo JSON object. The mongo JSON is getting attached by mongo-knex.

NQL: https://github.com/NexesJS/NQL
mongo-knex: https://github.com/NexesJS/mongo-knex
daniellockyer pushed a commit to TryGhost/framework that referenced this issue Jun 15, 2021
refs #10105, closes #10108, closes TryGhost/Ghost#9950, refs TryGhost/Ghost#9923, refs TryGhost/Ghost#9916, refs TryGhost/Ghost#9574, refs TryGhost/Ghost#6345, refs TryGhost/Ghost#6309, refs TryGhost/Ghost#6158, refs TryGhost/GQL#16

- removed GQL dependency
- replaced GQL with our brand new NQL implementation
- fixed all known filter limitations
- GQL suffered from some underlying filter bugs, which NQL tried to fix
- the bugs were mostly in how we query the database for relation filtering
- the underlying problem was caused by a too simple implementation of querying the relations
- mongo-knex has implemented a more robust and complex filtering mechanism for relations
- replaced logic in our bookshelf filter plugin
- we pass the custom, default and override filters from Ghost to NQL, which then are getting parsed and merged into a mongo JSON object. The mongo JSON is getting attached by mongo-knex.

NQL: https://github.com/NexesJS/NQL
mongo-knex: https://github.com/NexesJS/mongo-knex
daniellockyer pushed a commit to TryGhost/framework that referenced this issue Jun 15, 2021
refs #10105, closes #10108, closes TryGhost/Ghost#9950, refs TryGhost/Ghost#9923, refs TryGhost/Ghost#9916, refs TryGhost/Ghost#9574, refs TryGhost/Ghost#6345, refs TryGhost/Ghost#6309, refs TryGhost/Ghost#6158, refs TryGhost/GQL#16

- removed GQL dependency
- replaced GQL with our brand new NQL implementation
- fixed all known filter limitations
- GQL suffered from some underlying filter bugs, which NQL tried to fix
- the bugs were mostly in how we query the database for relation filtering
- the underlying problem was caused by a too simple implementation of querying the relations
- mongo-knex has implemented a more robust and complex filtering mechanism for relations
- replaced logic in our bookshelf filter plugin
- we pass the custom, default and override filters from Ghost to NQL, which then are getting parsed and merged into a mongo JSON object. The mongo JSON is getting attached by mongo-knex.

NQL: https://github.com/NexesJS/NQL
mongo-knex: https://github.com/NexesJS/mongo-knex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects:api Affects the Ghost API bug [triage] something behaving unexpectedly
Projects
None yet
Development

No branches or pull requests

3 participants