Skip to content

Commit a947c20

Browse files
committed
WIP: add multi-level link expansion for edition links
1 parent 7cad8f3 commit a947c20

File tree

6 files changed

+278
-36
lines changed

6 files changed

+278
-36
lines changed

Diff for: app/models/link_graph/node_collection_factory.rb

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ def collection
1515
attr_reader :link_graph, :with_drafts, :parent_node
1616

1717
def links_by_link_type
18-
# We don't support nested edition links
19-
return [] if parent_is_edition?
20-
2118
if root?
2219
link_reference.root_links_by_link_type(
2320
content_id: link_graph.root_content_id,
@@ -27,6 +24,8 @@ def links_by_link_type
2724
else
2825
link_reference.child_links_by_link_type(
2926
content_id: parent_node.content_id,
27+
locale: link_graph.root_locale,
28+
with_drafts: link_graph.with_drafts,
3029
link_types_path: parent_node.link_types_path,
3130
parent_content_ids: parent_node.parent_content_ids,
3231
might_have_own_links: parent_node.might_have_own_links?,

Diff for: app/queries/edition_links.rb

+197-23
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,38 @@ class EditionLinks
33
def self.from(content_id,
44
locale:,
55
with_drafts:,
6-
allowed_link_types: nil)
6+
allowed_link_types: nil,
7+
parent_content_ids: [],
8+
next_allowed_link_types_from: nil,
9+
next_allowed_link_types_to: nil)
710
new(
811
content_id:,
912
mode: :from,
1013
locale:,
1114
with_drafts:,
1215
allowed_link_types:,
16+
parent_content_ids:,
17+
next_allowed_link_types_from:,
18+
next_allowed_link_types_to:,
1319
).call
1420
end
1521

1622
def self.to(content_id,
1723
locale:,
1824
with_drafts:,
19-
allowed_link_types: nil)
25+
allowed_link_types: nil,
26+
parent_content_ids: [],
27+
next_allowed_link_types_from: nil,
28+
next_allowed_link_types_to: nil)
2029
new(
2130
content_id:,
2231
mode: :to,
2332
locale:,
2433
with_drafts:,
2534
allowed_link_types:,
35+
parent_content_ids:,
36+
next_allowed_link_types_from:,
37+
next_allowed_link_types_to:,
2638
).call
2739
end
2840

@@ -34,42 +46,79 @@ def call
3446

3547
private
3648

37-
attr_reader :content_id, :mode, :locale, :with_drafts, :allowed_link_types
49+
attr_reader :content_id,
50+
:mode,
51+
:locale,
52+
:with_drafts,
53+
:allowed_link_types,
54+
:parent_content_ids,
55+
:next_allowed_link_types_from,
56+
:next_allowed_link_types_to
3857

3958
def initialize(
4059
content_id:,
4160
mode:,
4261
locale:,
4362
with_drafts:,
44-
allowed_link_types:
63+
allowed_link_types:,
64+
parent_content_ids: [],
65+
next_allowed_link_types_from: nil,
66+
next_allowed_link_types_to: nil
4567
)
4668
@content_id = content_id
4769
@mode = mode
4870
@locale = locale
4971
@with_drafts = with_drafts
5072
@allowed_link_types = allowed_link_types
73+
@parent_content_ids = parent_content_ids
74+
@next_allowed_link_types_from = next_allowed_link_types_from
75+
@next_allowed_link_types_to = next_allowed_link_types_to
5176
end
5277

5378
def links_results
54-
query = Link.left_joins(edition: :document)
55-
where(query)
56-
.order(link_type: :asc, position: :asc)
57-
.pluck(:link_type, link_field, "documents.locale", "editions.id")
58-
end
79+
condition = {}
80+
# condition[:"documents.locale"] = locale if locale
81+
condition[:link_type] = allowed_link_types if allowed_link_types
5982

60-
def link_field
61-
mode == :from ? :target_content_id : "documents.content_id"
83+
if mode == :from
84+
Rails.logger.debug "from"
85+
Link
86+
.left_joins(edition: :document)
87+
.left_joins(:link_set)
88+
.where("documents.content_id": content_id)
89+
.or(Link.where("link_sets.content_id": content_id))
90+
.where(condition)
91+
.where(draft_condition)
92+
.order(link_type: :asc, position: :asc)
93+
.pluck(*fields)
94+
else
95+
Rails.logger.debug "to"
96+
links = Link
97+
.left_joins(edition: :document)
98+
.left_joins(:link_set)
99+
.where("links.target_content_id": content_id)
100+
.where(condition)
101+
# .where(draft_condition)
102+
.order(link_type: :asc, position: :asc)
103+
.pluck(*fields)
104+
Rails.logger.debug content_id
105+
Rails.logger.debug links.inspect
106+
Rails.logger.debug Link.left_joins(edition: :document).left_joins(:link_set).where("links.target_content_id": content_id).where(condition).inspect
107+
links
108+
end
62109
end
63110

64-
def where(query)
65-
condition = if mode == :from
66-
{ "documents.content_id": content_id }
67-
else
68-
{ target_content_id: content_id }
69-
end
70-
condition[:"documents.locale"] = locale if locale
71-
condition[:link_type] = allowed_link_types if allowed_link_types
72-
query.where(condition).where(draft_condition)
111+
def fields
112+
base_fields = [
113+
:link_type,
114+
mode == :from ? :target_content_id : "documents.content_id",
115+
mode == :from ? :target_content_id : "link_sets.content_id",
116+
"documents.locale",
117+
"editions.id",
118+
]
119+
base_fields << has_own_links_field if check_for_from_children?
120+
base_fields << is_linked_to_field if check_for_to_children?
121+
base_fields
73122
end
74123

75124
def draft_condition
@@ -98,10 +147,135 @@ def group_results(results)
98147

99148
def result_hash(row)
100149
{
101-
content_id: row[1],
102-
locale: row[2],
103-
edition_id: row[3],
150+
content_id: row[1] || row[2],
151+
locale: row[3],
152+
edition_id: row[4],
153+
has_own_links: has_own_links_result(row),
154+
is_linked_to: is_linked_to_result(row),
104155
}
105156
end
157+
158+
def has_own_links_result(row)
159+
return false unless could_have_from_children?
160+
161+
check_for_from_children? ? row[5] : nil
162+
end
163+
164+
def is_linked_to_result(row)
165+
return false unless could_have_to_children?
166+
167+
check_for_from_children? ? row[6] : row[5]
168+
end
169+
170+
def check_for_from_children?
171+
next_allowed_link_types_from && next_allowed_link_types_from.present?
172+
end
173+
174+
def check_for_to_children?
175+
next_allowed_link_types_to && next_allowed_link_types_to.present?
176+
end
177+
178+
def could_have_from_children?
179+
next_allowed_link_types_from.nil? || next_allowed_link_types_from.present?
180+
end
181+
182+
def could_have_to_children?
183+
next_allowed_link_types_to.nil? || next_allowed_link_types_to.present?
184+
end
185+
186+
def has_own_links_field
187+
if mode == :from
188+
Arel.sql(%{
189+
EXISTS(
190+
SELECT nested_links.id
191+
FROM links AS nested_links
192+
INNER JOIN link_sets AS nested_link_sets
193+
ON nested_link_sets.id = nested_links.link_set_id
194+
WHERE nested_link_sets.content_id = links.target_content_id
195+
#{and_not_parent('nested_links.target_content_id')}
196+
AND (#{allowed_links_condition(next_allowed_link_types_from)})
197+
LIMIT 1
198+
) OR EXISTS(
199+
SELECT nested_links.id
200+
FROM links AS nested_links
201+
INNER JOIN documents AS nested_documents
202+
ON nested_documents.content_id = nested_links.target_content_id
203+
WHERE nested_documents.content_id = links.target_content_id
204+
#{and_not_parent('nested_links.target_content_id')}
205+
AND (#{allowed_links_condition(next_allowed_link_types_from)})
206+
LIMIT 1
207+
)
208+
})
209+
else
210+
Arel.sql(%{
211+
EXISTS(
212+
SELECT nested_links.id
213+
FROM links AS nested_links
214+
INNER JOIN link_sets AS nested_link_sets
215+
ON nested_link_sets.id = nested_links.link_set_id
216+
WHERE nested_links.target_content_id = link_sets.content_id
217+
#{and_not_parent('nested_links.target_content_id')}
218+
AND (#{allowed_links_condition(next_allowed_link_types_from)})
219+
LIMIT 1
220+
) OR EXISTS(
221+
SELECT nested_links.id
222+
FROM links AS nested_links
223+
INNER JOIN documents AS nested_documents
224+
ON nested_documents.content_id = nested_links.target_content_id
225+
WHERE nested_links.target_content_id = documents.content_id
226+
#{and_not_parent('nested_links.target_content_id')}
227+
AND (#{allowed_links_condition(next_allowed_link_types_from)})
228+
LIMIT 1
229+
)
230+
})
231+
end
232+
end
233+
234+
def is_linked_to_field
235+
query = if mode == :from
236+
"nested_links.target_content_id = links.target_content_id"
237+
else
238+
"(nested_links.target_content_id = link_sets.content_id OR nested_links.target_content_id = documents.content_id)"
239+
end
240+
241+
Arel.sql(%{
242+
EXISTS(
243+
SELECT nested_links.id
244+
FROM links AS nested_links
245+
LEFT JOIN link_sets AS nested_link_sets
246+
ON nested_link_sets.id = nested_links.link_set_id
247+
LEFT JOIN documents AS nested_documents
248+
ON nested_documents.content_id = nested_links.target_content_id
249+
WHERE #{query}
250+
#{and_not_parent('nested_link_sets.content_id')}
251+
AND (#{allowed_links_condition(next_allowed_link_types_to)})
252+
LIMIT 1
253+
)
254+
})
255+
end
256+
257+
def and_not_parent(field)
258+
return if parent_content_ids.empty?
259+
260+
quoted = parent_content_ids.map { |c_id| quote(c_id) }
261+
262+
"AND #{field} NOT IN (#{quoted.join(', ')})"
263+
end
264+
265+
def allowed_links_condition(allowed_links)
266+
each_link_type = allowed_links.map do |(link_type, next_links)|
267+
raise "Empty links for #{link_type} on #{content_id}" if next_links.empty?
268+
269+
quoted_next_links = next_links.map { |n| quote(n) }
270+
271+
"(links.link_type = #{quote(link_type)} AND nested_links.link_type IN (#{quoted_next_links.join(', ')}))"
272+
end
273+
274+
each_link_type.join(" OR ")
275+
end
276+
277+
def quote(field)
278+
ActiveRecord::Base.connection.quote(field)
279+
end
106280
end
107281
end

0 commit comments

Comments
 (0)