`array_agg` throwing "could not find array type for data type bigint[]" - phoenix-framework

In my db, the timeline_items table has a many-to-many relationship with tags, joined by the timeline_items_tags table. If given a list of tag ids, I want to be able to find TimelineItems that are associated with all those Tags. For instance, let's say I have a TimelineItem associated with tags 20 and 10. Given the following queries, I would expect these results:
[20, 10] -> OK!
[20] -> OK!
[20, 10, 33] -> NOT OK
Given these requirements, I attempted to write a query:
join(TimelineItem, :inner, [ti], tag in "timeline_items_tags", tag.timeline_item_id == ti.id and tag.tag_id in ^tag_ids) |>
group_by([ti], ti.id) |>
having([ti, tag], fragment("array_agg(?)", tag.tag_id) in ^tag_ids)
The first line joins any timeline_items_tags that match even a single tag, which is too permissive. So to ensure that all the tags are present, I attempt to group the TimelineItems and create an array of their tag_id. Unfortunately, this last line throws:
could not find array type for data type bigint[]
What's happening? Is there a better way to approach this problem?

I was close. Needed to actually end with this line:
having([ti, tag], fragment("? <# array_agg(?)", ^tag_ids, tag.tag_id))

Related

How to check if a property value is a list in Memgraph?

I am using Memgraph Platform 2.6.5 and I want to check whether the property of a node is a list. I see there is a type function for relationships but would be good to have some kind of data type filtering for properties. I tried using the size function but it also works on strings and paths so it can't tell if a property is a list or not. Any idea on how to do that?
Here is an example of a small trick on how to do that:
If you have a node with list and string properties:
CREATE (n:Node {list: [1, 2, 3, 4, 5, 6, 7, 8], str: "myString"});
And then run:
MATCH (n:Node)
RETURN size(n.list + 11) = size(n.list)+1 AS isList;
I get true.
Otherwise, if I run this:
MATCH (n:Node)
RETURN size(n.str + 11) = size(n.str)+1 AS isList;
I get false.
This is because if you add a number consisting of two chars to a string, you get string which size has increased by 2. But, if you add the same number to a list, list size increased by 1.
Besides that, you can always create a custom procedure in Memgraph to extend Cypher query language. These procedures can be written in Python, C/C++ or Rust, and here is a how-to guide.

Power Query - Multiple OR statement with values

I've been doing research on this and I find a plethora of articles related to Text, but they don't seem to be working for me.
To be clear this formula works, I'm just looking to make it more efficient. My formula looks like:
if [organization_id] = 1 or [organization_id] = 2 or [organization_id] = 3 then "North" else if … where organization_id is of type "WholeNumber"
I'd like to simplify this by doing something like:
if [organization_id] in {1, 2, 3} then "North" else if …
I've tried wrapping in Parenthesis, Braces, & Brackets. Nothing seems to work. Most articles are using some form of text.replace function and mine is just a custom column.
Does MCode within Power Query have any efficiencies like this or do I have to write out each individual statement like the first line?
I've had success with the a List.Contains formulation:
List.Contains({1,2,3}, [organization_id])
The above checks if [organization_id] is in the list supplied in the first argument.
In some cases, you may not want to hardcode a list as shown above but reference a table column instead. For example,
List.Contains(TableWithDesiredIds[id_column], [organization_id])

Filtering erlang ets tables without using guard clauses

In elixir, I would like to be able to filter an ets table using a function.
I currently have a simple ets table example in the iex shell...
iex> :ets.new(:nums, [:named_table])
:nums
iex> :ets.insert :nums, [{1}, {2}, {3}, {4}, {5}]
true
fun = :ets.fun2ms(fn {n} when n < 4 -> n end)
[{{:"$1"}, [{:<, :"$1", 4}], [:"$1"]}]
:ets.select(:nums, fun)
[1, 3, 2]
This all works as you would expect. My question relates to the function being used to query the ets table. Currently it uses a guard clause to filter for results less than 4.
I would like to know if there is a way put the guard clause syntax into the function body. For example...
iex> fun2 = :ets.fun2ms(fn {n} -> if n < 4, do: n end)
but if I do this then I get the following error...
Error: the language element case (in body) cannot be translated into match_spec
{:error, :transform_error}
Is something like this possible?
It turns out, this is the only way to go
From erlang documentation
The fun is very restricted, it can take only a single parameter (the object to match): a sole variable or a tuple. It must use the is_ guard tests. Language constructs that have no representation in a match specification (if, case, receive, and so on) are not allowed.
More info about Match Specifications in Erlang

sort values of an orddict

In order to extract the values (records) of an orddict as a sorted list, tried this:
-module(test).
-compile(export_all).
-record(node, {name="", cost=0}).
test() ->
List = orddict:append("A",#node{name="A",cost=1},
orddict:append("B",#node{name="B",cost=2},
orddict:new())),
lists:sort(fun({_,A},{_,B}) -> A#node.cost =< B#node.cost end,
orddict:to_list(List)).
The sort fails with exception error: {badrecord,node}.
What would be the correct syntax?
Solved:
The correct insertion method is orddict:store/2 instead of orddict:append/2. Then the pattern {_,A} matches for the comparison function.
The correct syntax is:
lists:sort(fun({_,[A]},{_,[B]}) -> A#node.cost =< B#node.cost end,
orddict:to_list(List)).
I not found note about this in documentation,but you can look in source code of module.
As #Pascal write in comments the reason is that orddict:append/3 is a function provided to append a value to an existing Key/Value pair where Value must be a list. In the use case, the key doesn't exist, so the pair is created and the Value append to an empty list.
Btw, you always can print and compare real and expected result.
io:format("~p~n",[orddict:to_list(List)])
For your example that is:
[{"A",[{node,"A",1}]},{"B",[{node,"B",2}]}]

Best approach for deleting when using Array.combination()?

I want to compare every object in lectures with each other and if some_condition is true, the second object has to be deleted:
toDelete=[]
lectures.combination(2).each do |first, second|
if (some_condition)
toDelete << second
end
end
toDelete.uniq!
lectures=lectures-toDelete
I got some weird errors while trying to delete inside the .each loop, so I came up with this approach.
Is there a more efficient way to do this?
EDIT after first comments:
I wanted to keep the source code free of unnecessary things, but now that you ask:
The elements of the lectures array are hashes containing data of different university lectures, like the name, room,the calendar weeks in which they are taught and begin and end time.
I parse the timetables of all student groups to get this data, but because some lectures are held in more than one student group and these sometimes differ in the weeks they are taught, I compare them with each other. If the compared ones only differ in certain values, I add the values from the second object to the first object and delete the second object. That's why.
The errors when deleting while in .each-loop: When using the Rails Hash.diff method, I got something like "Cannot convert Symbol to Integer". Turns out there was suddenly an Integer value of 16 in the array, although I tested before the loop that there are only hashes in the array...
Debugging is really hard if you have 9000 hashes.
EDIT:
Sample Data:
lectures = [ {:day=>0, :weeks=>[11, 12, 13, 14], :begin=>"07:30", :end=>"09:30", :rooms=>["Li201", "G221"], :name=>"TestSubject1", :kind=>"Vw", :lecturers=>["WALDM"], :tut_groups=>["11INM"]},
{:day=>0, :weeks=>[11, 12, 13, 14], :begin=>"07:30", :end=>"09:30", :rooms=>["Li201", "G221"], :name=>"TestSubject1", :kind=>"Vw", :lecturers=>["WALDM"], :tut_groups=>["11INM"]} ]
You mean something like this?
cleaned_lectures = lectures.combination(2).reject{|first, second| some_condition}

Resources