I have a complex hierarchical query which looks like this:
MATCH (comp:Company {id: 7})-[:HAS_SPACE]->(s:Space)-[:HAS_BOARD]->(b),
(col)<-[:HAS_COLUMN]-(b)-[:HAS_LANE]->(lane)
OPTIONAL MATCH (b)-[:HAS_CARD]->(card {archived: false}),
(cardCol:Column)-[:HAS_CARD]->(card {archived: false})<-[:HAS_CARD]-(cardLane:Lane)
WITH s, b, col, lane, { id: card.id, title: card.title, sort_order: card.sort_order, column_id: cardCol.id, lane_id: cardLane.id } as crd
WITH s, { id: b.id, title: b.title, left: b.left, top: b.top,
columns: collect(distinct {id: col.id, title: col.title, col_count: col.col_count, sort_order: col.sort_order}),
lanes: collect(distinct {id: lane.id, title: lane.title, row_count: lane.row_count, sort_order: lane.sort_order}),
cards: collect(distinct crd)} as brd
RETURN {id: s.id, title: s.title, boards: collect(distinct brd)}
This query slows down to 10 sec when number of cards becomes about 200. What is the problem with it and also how can I profile it? Looks like there is a PROFILE keyword, but the output doesn't look like really informative. Btw, we are using GrapheneDB on heroku.
I think one issue you have with this query is the combinatorial explosion along the paths, you can help cypher a bit (the next version will be cleverer about it).
Also where is your "optional relationship" ? between board and card?
create index on :Company(id);
MATCH (comp:Company {id: 7})-[:HAS_SPACE]->(s:Space)-[:HAS_BOARD]->(b)
WITH distinct s, b
MATCH (col)<-[:HAS_COLUMN]-(b)-[:HAS_LANE]->(lane)
OPTIONAL MATCH (b)-[:HAS_CARD]->(card {archived: false})
WITH distinct s, b, col, lane, b, card
MATCH (cardCol:Column)-[:HAS_CARD]->(card {archived: false})<-[:HAS_CARD]-(cardLane:Lane)
WITH s, b, col, lane,
{ id: card.id, title: card.title, sort_order: card.sort_order,
column_id: cardCol.id, lane_id: cardLane.id } as crd
WITH s,
{ id: b.id, title: b.title, left: b.left, top: b.top,
columns: collect(distinct {id: col.id, title: col.title,
col_count: col.col_count, sort_order: col.sort_order}),
lanes: collect(distinct {id: lane.id, title: lane.title, row_count: lane.row_count,
sort_order: lane.sort_order}),
cards: collect(distinct crd)} as brd
RETURN {id: s.id, title: s.title, boards: collect(distinct brd)}
It helps to analyse the different parts of the query separately and see where the combinatorial explosion kicks in. Then fix the cardinality of that piece back with distinct.
You can also try the new query planner by prefixing your query with cypher 2.1.experimental
After some research found out that this query runs 20 times faster if we "denormalize" nodes a little bit adding lane_id and column_id to card. Still it is not the fastest solution and I don't like this denormalization which eliminates relations. So I would appreciate any other solutions
Related
I have two lists in elixir. One list (list1) has values which get consumed in another list (list2). So I need to iterate over list2 and update values in list1 as well as list2.
list1 = [
%{
reg_no: 10,
to_assign: 100,
allocated: 50
},
%{
reg_no: 11,
to_assign: 100,
allocated: 30
},
%{
reg_no: 12,
to_assign: 100,
allocated: 20
}
]
list2 = [
%{
student: student1,
quantity: 60,
reg_nos: [reg_no_10, reg_no_11]
},
%{
student: student2,
quantity: 40,
reg_nos: [reg_no_11, reg_no_12]
},
%{
student: student3,
quantity: 30,
reg_nos: nil
}
]
I need to assign values from list1 to quantity field of list2 till quantity is fulfilled. e.g. student1 quantity is 60 which will need reg_no 10 and reg_no 11.
With Enum.map I cannot pass updated list1 for 2nd iteration of list2 and assign value reg_nos: reg_no_11, reg_no_12for student2.
So, my question is how can I send updated list1 to 2nd iteration in list2?
I am using recursion to get quantity correct for each element in list2. But again, should I use recursion only to send updated list1 in list2? With this approach, there will be 2 nested recursions. Is that a good approach?
If I understand your question correctly, you want to change values in a given list x, based on a list of values in another list y.
What you describe is not possible in a functional language due to immutability, but you can use a reduce operation where x is the state or so-called "accumulator".
Below is an example where I have a ledger with bank accounts, and a list with transactions. If I want to update the ledger based on the transactions I need to reduce over the transactions and update the ledger per transaction, and pass the updated ledger on to the next transaction. This is the problem you are seeing as well.
As you can see in the example, in contrast to map you have a second parameter in the user-defined function (ledger). This is the "state" you build up while traversing the list of transactions. Each time you process a transaction you have a change to return a modified version of the state. This state is then used to process the second transaction, which in turn can change it as well.
The final result of a reduce call is the accumulator. In this case, the updated ledger.
def example do
# A ledger, where we assume the id's are unique!
ledger = [%{:id => 1, :amount => 100}, %{:id => 2, :amount => 50}]
transactions = [{:transaction, 1, 2, 10}]
transactions
|> Enum.reduce(ledger, fn transaction, ledger ->
{:transaction, from, to, amount} = transaction
# Update the ledger.
Enum.map(ledger, fn entry ->
cond do
entry.id == from -> %{entry | amount: entry.amount - amount}
entry.id == to -> %{entry | amount: entry.amount + amount}
end
end)
end)
end
This is my query to fetch data from 2 different table.
$variant = Variant::with(['v_detail' => function($q){
$q->select('variant_dtl_name');
}])->where('product_id','=',$productId)->get();
There is output, but v_detail returning empty list
result:
created_at: "2015-11-07 12:37:26"
id: 1
product_id: 30
updated_at: "2015-11-07 12:37:26"
v_detail: []
variant_name: "Pricing"
But with these query:
$variant = Variant::with('v_detail')->where('product_id','=',$productId)->get();
The result is:
created_at: "2015-11-07 12:37:26"
id: 1
product_id: 30
updated_at: "2015-11-07 12:37:26"
v_detail: [{id: 1, variant_id: 1, variant_dtl_name: "Adult", variant_dtl_price: 25,…},…]
0: {id: 1, variant_id: 1, variant_dtl_name: "Adult", variant_dtl_price: 25,…}
1: {id: 2, variant_id: 1, variant_dtl_name: "Senior", variant_dtl_price: 15,…}
2: {id: 3, variant_id: 1, variant_dtl_name: "Children", variant_dtl_price: 8,…}
variant_name: "Pricing"
Now, on the query that work, how can I fetch a specific column names. Thanks!!
You have this:
$variant = Variant::with(['v_detail' => function($q)
{
// Either add the related foreign key or select all
$q->select('related_foreign_key', 'variant_dtl_name');
}])->where('product_id','=',$productId)->get();
Since you are selecting only a single field which is variant_dtl_name then it's not possible to find out the related models because the relation builder foreign key is required. So, you have to select that foreign key as well. Notice the related_foreign_key in sub-query so use the right one, probably that is variant_id but not sure because you didn't mention anything about that.
Coming from c#, I'm used to being able to do the following using LINQ
var people = new List<Person>
{
new Person(Race.Black, "Mitch",30),
new Person(Race.White, "Mike",30),
new Person(Race.Mexican, "Mel",30),
};
var groups = people.GroupBy (p => p.Race)
.Select(g => new {race = g.Key, person = g});
Moving into Ruby, I would like to do grouping and projection into a hash, but is there an out of the box method for this or do I need to roll my own? Here's my implementation but it'd be great if this was offered in the language, or a 3rd party library that offered an implementation
def group(arr,group_sym)
groups = {}
arr.each do |i|
race = i[group_sym]
groups[race] = [] unless groups.has_key?(race)
i.delete(group_sym)
groups[race].push(i)
end
groups
end
Edit: So what I'm expecting from this is the following:
input:
people = [{name: 'mike', race: 'white', age: 30},
{name: 'mel', race: 'white', age: 31},
{name: 'mitch', race: 'black', age: 30},
{name: 'megan', race: 'asian', age: 30},
{name: 'maebe', race: 'black', age: 30},]
function call:
groupedPeople = groupBy(people,'race')
returns:
[{'white' => [{name: 'mike', age: 30},
{name: 'mel', race: 'white'}],
{'black' => [{...black people}],
{'asian' => [{...asian people}]
}]
For this specific example, I'd want to get a hash where my people array is grouped by race
Because of the fact that C#'s query expressions are meant to look like SQL queries, the method names are a bit unusual compared to other languages: Select is usually called map, Aggregate is usually called fold or reduce, Where is usually called select or filter, etc.
If you simply translate the method names, you can almost literally translate your code to Ruby:
Person = Struct.new(:race, :name, :age)
people = [
Person.new(:black, 'Mitch', 30),
Person.new(:white, 'Mike', 30),
Person.new(:mexican, 'Mel', 30)
]
groups = people.group_by(&:race).map {|race, people| { race: race, person: people } }
I used a Hash as the closest replacement for IGrouping.
I have 2 columns in my awards table: ranking (integer) and name (string). I want to create a scope that shows all the records with the ranking column filled in in ascending order (1, 2, 3...), and then the rest of the records that don't have a ranking to show by name ascending (a, b, c) so it would look like this:
ranking: 1, name: "zz"
ranking: 2, name: "aaa"
ranking: nil, name: "bbbb"
ranking: nil, name: "ccc"
ranking: nil, name: "ddd"
etc...
This doesn't seem to work: scope :book_form_sort_order, -> { order("ranking ASC, name ASC").group(:ranking) }
scope :book_form_sort_order, -> { order("ranking IS NULL, ranking ASC, name ASC") }
How do I use LINQ to update objects in one list from objects in a second list? My question is very similar to LINQ In Line Property Update During Join except that, in my case, the second list is smaller than the parent list.
In other words, I want to update those members of the master collection that have corresponding updates in a second collection. Otherwise I want the master object left unaltered. The technique in the article referenced above seems to result in an inner join of the two collections.
Thanks
The answer in the other article is fine for your problem too, as what you really want is an inner join. It's important to note that the inner join is only used to perform the function, it's not modifying the list (i.e. items that don't meet the inner join are left untouched in the list).
For completeness here's the solution I would use:
List<Person> people = new List<Person>();
people.Add( new Person{ Name = "Timothy", Rating = 2 } );
people.Add( new Person{ Name = "Joe", Rating = 3 } );
people.Add( new Person{ Name = "Dave", Rating = 4 } );
List<Person> updatedPeople = new List<Person>();
updatedPeople.Add( new Person { Name = "Timothy", Rating = 1 } );
updatedPeople.Add( new Person { Name = "Dave", Rating = 2 } );
ShowPeople( "Full list (before changes)", people );
Func<Person, Person, Person> updateRating =
( personToUpdate, personWithChanges ) =>
{
personToUpdate.Rating = personWithChanges.Rating;
return personToUpdate;
};
var updates = from p in people
join up in updatedPeople
on p.Name equals up.Name
select updateRating( p, up );
var appliedChanges = updates.ToList();
ShowPeople( "Full list (after changes)", people );
ShowPeople( "People that were edited", updatedPeople );
ShowPeople( "Changes applied", appliedChanges );
Here's the output I get:
Full list (before changes)
-----
Name: Timothy, Rating: 2
Name: Joe, Rating: 3
Name: Dave, Rating: 4
Full list (after changes)
-----
Name: Timothy, Rating: 1
Name: Joe, Rating: 3
Name: Dave, Rating: 2
People that were edited
-----
Name: Timothy, Rating: 1
Name: Dave, Rating: 2
Changes applied
-----
Name: Timothy, Rating: 1
Name: Dave, Rating: 2