neo4j javascript API: SORT and COLLECT a UNIONS MATCH - neo4j

I have to run a query based on union of queries, and I have to apply COLLECT and SORT of the results
(I am on neo4j 3.5)
So my starting pseudo-code is the following:
var session = driver.session();
return session
.run("QUERY_1 return value as COL1, value2 as COL2 UNION QUERY_2 return value as COL1, value2 as COL",{paramslit})
.then(result => {
session.close();
return result.records.map(record => { return new RESULT(...)}
});
})
.catch(error => {
session.close();
throw error;
});
My problem is that I need to get the result of col2 as a set, so I need to apply collect to it in the cypher query; but the collect should be applied after the union because both QUERY_1 and QUERY_2 can return values for a given col1
QUERY_1 and QUERY_2 are already pretty complicated and I don't think there is a way to get the output of those queries with a single query without UNION
For cypher only code I know that there is the apoc.cypher.run
but I have not been able to make it work as expected within the session.run code

In neo4j 4.0, a new subquery feature was added to Cypher, and it supports post-UNION processing.
You can use that feature to do what you want. Here is the new Cypher pseudo-code:
CALL {
QUERY_1 return value as COL1, value2 as COL2
UNION
QUERY_2 return value as COL1, value2 as COL2
}
RETURN COL1, COLLECT(COL2) as SET_OF_COL2

Related

Google sheet array formula with Filter & Join

I need to build a table based on the following data:
Ref
Product
R1
ProdA
R2
ProdC
R1
ProdB
R3
ProdA
R4
ProdC
And here the result I need:
My Product
All Ref
ProdA
R1#R3
ProdC
R2#R4
The particularity is that the 'My Product' column is computed elsewhere. So I need an arrayformula based on 'My Product' column to look in the first table to build the 'All Ref' column. You follow me?
I know that Arrayformula is not compatible with filter and join ... I expect a solution like this one Google sheet array formula + Join + Filter but not sure to understand all steps and if really adapted to my case study.
Hope you can help.
You could try something like this:
CREDIT: player0 for the method shared to similar questions
=ARRAYFORMULA(substitute(REGEXREPLACE(TRIM(SPLIT(TRANSPOSE(
QUERY(QUERY({B2:B&"😊", A2:A&"#"},
"select max(Col2)
where Col1 !=''
group by Col2
pivot Col1"),,999^99)), "😊")), "#$", )," ",""))
Step by step:
Instead of the workaround hacks I implemented a simple joinMatching(matches, values, texts, [sep]) function in Google Apps Script.
In your case it would be just =joinMatching(MyProductColumn, ProductColumn, RefColumn, "#").
Source:
// Google Apps Script to join texts in a range where values in second range equal to the provided match value
// Solves the need for `arrayformula(join(',', filter()))`, which does not work in Google Sheets
// Instead you can pass a range of match values and get a range of joined texts back
const identity = data => data
const onRange = (data, fn, args, combine = identity) =>
Array.isArray(data)
? combine(data.map(value => onRange(value, fn, args)))
: fn(data, ...(args || []))
const _joinMatching = (match, values, texts, sep = '\n') => {
const columns = texts[0]?.length
if (!columns) return ''
const row = i => Math.floor(i / columns)
const col = i => i % columns
const value = i => values[row(i)][col(i)]
return (
// JSON.stringify(match) +
texts
.flat()
// .map((t, i) => `[${row(i)}:${col(i)}] ${t} (${JSON.stringify(value(i))})`)
.filter((_, i) => value(i) === match)
.join(sep)
)
}
const joinMatching = (matches, values, texts, sep) =>
onRange(matches, _joinMatching, [values, texts, sep])```

How to collapse sheets query result into a single cell

I have a sheets query that almost does what I want but I need a bit of help to get to the last step.
=QUERY(Sales!$A$2:$C,"SELECT B, SUM(C)
WHERE A='"&B3&"'
GROUP BY B
ORDER BY SUM(C) DESC
LIMIT 3
LABEL SUM(C) ''
FORMAT SUM(C) '$##,##0' "
,0)
This gives me the result in C3:D4. What I want is in E3.
I have two goals. First output the data stacked and joined like in cell E3. Second, the ideal solution is an array formula in C3 that does this for all the 'Partners'.
Sample Data
As always thanks in advance for the assistance and education!
Here is how I solved it. Maybe not the optimal solution because I need to duplicate the calculation, but it does exactly what I need. I've added my solution to the example data.
=REGEXREPLACE(REGEXREPLACE(textjoin(" ♦ ",0,
QUERY({Sales!$A$2:$A,Sales!$B$2:$B,Sales!$C$2:$C},"SELECT Col2, SUM(Col3)
WHERE Col1='"&B3&"'
GROUP BY Col2
ORDER BY SUM(Col3) DESC
LIMIT 3
LABEL SUM(Col3) ''
FORMAT SUM(Col3) '$##,##0' "
,0)
)," ♦ \$"," = \$")," ♦ ",CHAR(10))
One way to accomplish this is creating a custom function in Google Apps Script. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function GET_TOP_CUSTOMERS(partners) {
const sheet = SpreadsheetApp.getActive().getSheetByName("Sales");
const data = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3).getValues();
return partners.map(row => row[0]).map(partner => {
const partnerRows = data.filter(row => row[0] === partner);
const uniqueCustomers = [...new Set(partnerRows.map(row => row[1]))];
const topCustomers = uniqueCustomers.map(customer => {
return [customer, partnerRows.filter(row => row[1] === customer)
.reduce((acc, current) => {
return acc + current[2] }, 0)];
}).sort((a,b) => b[1] - a[1]).slice(0, 3);
return topCustomers.map(customer => customer[0] + "; $" + customer[1].toFixed())
.join("\n");
});
}
Now, if you go back to your spreadsheet, you can use this function like any in-built one. You just have to provide the appropriate range (in this case it would be B3:B12), as you can see here:
Reference:
Custom Functions in Google Sheets

Getting the unique values and counts of them in a column of comma separated values in Google Sheets without using JOIN?

I have a column with a bunch of ingredients lists in it. I'm trying to figure out how many times different individual ingredients appear. There are 73,000 rows. The answers on this question works for a small amount of data in Google Sheets.
Formula is =UNIQUE(TRANSPOSE(SPLIT(JOIN(", ";A2:A);", ";FALSE)))
But I've overwhelmed JOIN with more than 50000 characters here. Is there another way to tackle this?
Sheet: https://docs.google.com/spreadsheets/d/1t0P9hMmVpwhI2IbATmIMjobuALTg8VWhl8-AQaq3zIo/edit?usp=sharing
=ARRAYFORMULA(UNIQUE(TRIM(TRANSPOSE(SPLIT(TRANSPOSE(
QUERY(","&A1:A,,5000000)),",")))))
=QUERY(QUERY(ARRAYFORMULA(TRIM(TRANSPOSE(SPLIT(TRANSPOSE(
QUERY(","&A1:A,,5000000)),",")))),
"select Col1,count(Col1)
where Col1 is not null
group by Col1
label count(Col1)''"),
"order by Col2 desc")
demo spreadsheet
=UNIQUE(TRANSPOSE(SPLIT(REGEXREPLACE(TRANSPOSE(
QUERY(ARRAYFORMULA(","&A1:A),,5000000))," ,",","),",")))
but maybe you need this (?):
=QUERY(TRANSPOSE(SPLIT(REGEXREPLACE(TRANSPOSE(
QUERY(ARRAYFORMULA(","&A1:A),,5000000))," ,",","),",")),
"select Col1,count(Col1)
where Col1 is not null
group by Col1
label count(Col1)''")
I did a google scripting solution because I wanted to play with key map pairs.
function myFunction() {
var myMap = {"candy":0};
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheetByName("FIRSTSHEETNAME");
var os = sh.getSheetByName("Ingredients");
var data = ss.getDataRange().getValues();
for (var i=0; i<data.length;i++)//full
//for (var i=1; i<4000;i++)//test
{
var array = data[i][0].split( ",");
for (var j=0; j<array.length;j++)
{
var item = array[j];
//Logger.log(array[j]);
if (myMap[item]>-1){
//Logger.log("REPEAT INGREDIENT");
var num = parseInt(myMap[item]);
num++;
myMap[item]=num;
//Logger.log(item +" "+num);
} else {
myMap[item]=1;
//Logger.log("New Ingredient: "+item);
//Logger.log(myMap);
}
}
}
//Logger.log(myMap);
var output=[];
for (var key in myMap){
//Logger.log("Ack");
output.push([key,myMap[key]]);
}
//Logger.log(output);
os.getRange(2,1,output.length,output[0].length).setValues(output);
}
You'll need to add an "Ingredients" tab for the output and change your first tab to be called FIRSTSHEETNAME (or change the code). In my testing it took 4 seconds for 4 items, 5 seconds for 400 items, and 6 seconds for 4000 items. there might be an issue with leading spaces but this gives you a place to start.
A fast running formula that works with columns of at least 40,000 rows:
=query(arrayformula(TRIM(flatten(split(A2:A20000,",")))),"select Col1,Count(Col1) Where NOT (Col1='' OR Col1 contains '#VALUE!') Group By Col1 order by Count(Col1) desc label Col1 'Ingredient',Count(Col1) 'Freq.'")
FLATTEN function, combined with SQL (QUERY function) can be a solution for fast filtering of values (such as empty or error messages).
TRIM function avoids artifacts in the result due to meaningless spaces before/after each string.
Sheet: https://docs.google.com/spreadsheets/d/1m9EvhQB1Leg2H7L52WhPe66_jRrTc8VsnZcliQsxJ7s/edit?usp=sharing
*In case of false case differences, you could normalize all characters of the strings to uppercase before within the same formula with UPPER(A2:A20000).

How to use WHERE clause in a query when number of columns is dynamic?

I have searched this, but can't seem to find an answer:
I use the query function in a spreadsheet to collect comments from work assosiates. There is 17 commenters and I query from this week and ten weeks ahead. BUT, I only want the comments and not empty fields in my end result. I am almost there, but with the formula I use now, I have to manually update number of columns in the last part of the query, because the number of columns vary acording to number of comments. Here is my formula:
=transpose(query(transpose(query(QUERY(
IMPORTRANGE("1oQVZDEKLqx6ruz2yzIzUgppkWvBEOB_Eo-a4NW1WTSQ";"Comments!A1:U");
"select Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14,
Col15, Col16, Col17, Col18, Col19 where todate(Col1) > date '"&text(today()-7;
"yyyy-mm-dd")&"' limit 10");"select * where Col2<>'' or Col3<>'' or Col4<>'' or
Col5<>'' or Col6<>'' or Col7<>'' or Col8<>'' or Col9<>'' or Col10<>'' or Col11<>'' or
Col12<>'' or Col13<>'' or Col14<>'' or Col15<>'' or Col16<>'' or Col17<>'' or Col18<>''"));
"select * where Col2<>'' or Col3<>'' or Col4<>''"))
Is there any way to get this formula to work no matter how many columns the two "innermost" querys returns?
Here is the spreadsheet with the queried comments. It collects comments from this sheet (in real life this is collected from 19 sheets with the importrange-formula).
I hope my question is understandable, and most of all, my problem solvable!
This function removes empty columns and rows from a range that has row and column headers.
You can call it like this:
=FILTERROWSANDCOLS(IMPORTRANGE("1oQVZDEKLqx6ruz2yzIzUgppkWvBEOB_Eo-a4NW1WTSQ";"Comments!B1:U"))
Please note that I excluded column A for this.
function FILTERROWSANDCOLS(input) {
function rowIsBlank(row) {
return row.slice(1, row.length).join("") !== "";
}
return input.filter(rowIsBlank).transpose().filter(rowIsBlank).transpose();
}
Object.defineProperty(Object.prototype, "transpose", {value: function(){
var output = [];
for (var row = 0; row < this.length; row++) {
for (var col = 0; col < this[row].length; col++) {
if (row === 0){
output.push([this[row][col]]);
} else {
output[col].push(this[row][col]);
}
}
}
return output;
}});

How do I use a in clause 'in' a grails.gorm.DetachedCriteria?

Is it possible to use an 'in' criteria in a Grails DetachedCriteria?
This is what I have,
def query = new DetachedCriteria(DomainObject)
// list is actually built up from another query,
// but for this example I will use a predefined list
query.where { 'in' 'id', [4L, 5L, 9L] }
def count = query.count()
What I am seeing is that the count, which you would expect to be 3, is actually just the entire DomainObject table.
How do I get this query to work?
Try assigning the result of where to a query:
query = query.where { 'in' 'id', [4L, 5L, 9L] }

Resources