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

ActionJS : handle many to many relationships #181

Open
proxygear opened this issue Jun 18, 2020 · 3 comments
Open

ActionJS : handle many to many relationships #181

proxygear opened this issue Jun 18, 2020 · 3 comments

Comments

@proxygear
Copy link

proxygear commented Jun 18, 2020

Let's say we have to collections authors and books.
A book has many authors and an author has many books. Great.

In an action I tried something like this :

allEntries(
  'books', 
  { 'authors': 'an-author-slug' }
)

But it does not works. And I started to wonder how to do the following requests :
'authors.in' : ['a-first-author-slug', 'a-second-author-slug']
or
'authors.ne' : 'a-first-author-slug'

Is it even possible ?
From what I checked, I can access to author_ids :

findEntry('books', 'my-book-slug').author_ids

However it returns an array of mongoDB ids.
In the other hand I can't access to the mongoDB id of an object :

findEntry('books', 'my-book-slug').id

Some help with this confusion ?

@proxygear
Copy link
Author

proxygear commented Jun 18, 2020

From what I see, I should start to dug from ActionService.
However the actual implementation is in the engine ending in the ContentEntry.
So my issue is Engine related and not Steam.

Refreshing my memory, the id method in mongoDB is _id.

So I'm trying something like :

allEntries(
  'books', 
  { 'author_ids': findEntry('authors',  'an-author-slug') }
)

Still without success.

End of this, I'll update the doc :D

@proxygear
Copy link
Author

proxygear commented Jul 14, 2020

You should actually do something like this in the JS Action :

allEntries(
  'books', 
  {
    'author_ids': {
      '$in': [findEntry('authors',  'an-author-slug')._id]
   }
)

However, _id return a string. To be working with Mongoid you should make it a BsonId Object.
Here is what the ruby counter part should be doing :

ContentEntry.where(
  book_ids: {
    '$in' => BSON::ObjectId.from_string(string_id)
  }
).first

If the conversion to BSON::Object is omitted, it will not work.

My first guess would be update steam/lib/locomotive/adapters/mongodb.rb to do some automatic conversion but ... meh ?

@proxygear
Copy link
Author

proxygear commented Jul 14, 2020

So my idea to fix it is to, within the JS Action to write something like this

var pseudo_id = "BSON(" + findEntry('authors',  'an-author-slug')._id + ")";
allEntries(
  'books', 
  {
    'author_ids': {
      '$in': [pseudo_id]
   }
)

And with a sanitizer, to handle the pseudo ids :

module Locomotive::Steam
  module Adapters
    module Mongodb
      module IdsSanitizer
        PSEUDO_ID_REGEXP = /^BSON\((?<id>[a-z0-9]+)\)$/.freeze

        def self.call(data)
          replace_leaf_strings_within(data, &method(:fix_pseudo_id))
        end

        def self.fix_pseudo_id(string)
          result = PSEUDO_ID_REGEXP.match(string)
          result ? BSON::ObjectId.from_string(result[:id]) : string
        end

        def self.replace_leaf_strings_within(data, &block)
          case data.class
          when Hash
            {}.tap do |h|
              data.each do |key, value|
                h[key] = replace_leaf_strings_within(value, &block)
              end
            end
          when Array
            data.map { |value| replace_leaf_strings_within(value, &block) }
          when String
            block.call(data)
          else
            data
          end
        end
      end
    end
  end
end

It could be used in steam/services/action_service.rb line 99 :

def all_entries_lambda(liquid_context)
    -> (type, conditions) { content_entry_service.all(type, sanitize_conditions(conditions), true) }
end

def sanitize_conditions(conditions)
  Adapters::Mongodb::IdsSanitizer.call(conditions)
end

What do you think @did ?

PS: Using this monkey patch, I was able to solve my problem.
I would like to do a pull request, however my solution is quite specific, I would like to discuss with some maintainer to see if something more generic cannot be done.
Also, this solution is quite MongoDB specific, it will not work with wagon.
But I guess that's another topic.

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

No branches or pull requests

1 participant