Как я могу перекомпонировать вызов функции Q в виде строки из списка аргументов?

#c# #kdb

#c# #kdb

Вопрос:

Я использую C # для выполнения q-кода в базе данных KDB с использованием c.cs библиотеки, предоставляемой KX. Я предоставляю аргументы в виде объектов (целых чисел, строк, экземпляров Dict и т. Д.) c.k методу в этой библиотеке, И все это работает нормально. Однако, когда у меня есть запрос, который не работает с использованием этого метода, я неизменно хочу сделать, это взять запрос, который я только что выполнил через адаптер C #, и повторно выполнить его в консоли Q, чтобы я мог поработать с ним и выяснить, что я сделал неправильно. Проблема в том, что в среде C # то, что у меня есть в качестве аргументов для моей функции Q (в некоторых случаях несколько сложные деревья аргументов на основе словаря), очень сложно вручную преобразовать мои аргументы в func[arg1;arg2;arg3...]; формат, ожидаемый консолью Q. Что я действительно хотел бы иметь возможность сделать, так это передать мои аргументы функции, которая перекомпонировала бы исходный запрос в синтаксисе, который позволил бы мне легко повторно выполнить исходный запрос в консоли. Что-то вроде этого:

В C#

 var client = new c();
[connect]
var funcName = "myFunc";
var arg1 = "test";
var arg2 = 12345;
var arg3 = new c.Dict(new [] { "a", "b" }, new [] { true, false });
try {
  var result = c.k(funcName, arg1, arg2, arg3);
} catch {
  var composedStatement = c.k("getComposedStatement", funcName, new[] { arg1, arg2, arg3 });
  // composedStatement: "myFunc[`test;12345i;`a`b!10b];"
  Logger.LogError("KDB Query failed: "   composedStatement);
}
  

В Q

 getComposedStatement:{[funcName;arguments]
  :(string funcName),"[",(qWizardryGoesHere arguments),"];";
  }
  

Надеюсь, это понятно, чего я пытаюсь достичь. Важно то, что getComposedStatement функция принимает имя функции и произвольное количество аргументов, которые могут быть любого типа, включая вложенные типы (списки списков, список словарей, словарь разных типов и т.д.) И возвращает инструкцию, которую можно напрямую скопировать и вставить в консоль Q длявыполнение.

Комментарии:

1. Попробуйте следующее: var result = c.k(funcName, новый объект[] {arg1, arg2, arg3});

2. Прошу прощения, я думаю, вы, возможно, пропустили суть моего вопроса. var result = c.k(funcName, arg1, arg2, arg3); работает нормально (я также иногда делаю это так, как вы предлагали). То, что я ищу, — это универсальный способ перекомпонирования исполняемого оператора Q из аргументов объекта, чтобы, если в myFunc есть ошибка, я мог протестировать ее с помощью аргументов, чтобы выяснить, почему. Я ищу помощи в части Q, в частности, там, где я сказал «qWizardryGoesHere» 🙂

Ответ №1:

Я думаю, это то, что вы ищете в функции kdb.

Используется .Q.s1 для получения строкового представления для каждого параметра, а затем sv для получения ; отдельной строки для параметров. Также есть несколько дополнительных битов для создания . / @ apply:

 getComposedStatement:{
    // conditional to create a . or @ apply depending on number of parameters
    // @ apply will only work with 1 parameter 
    $[1 < count y;".";"@"],
       "[`",x,";(",(";" sv .Q.s1 each y),")]"
}
  

Полезные ссылки:

https://code.kx.com/q/ref/dotq/#qs1-string-representation

https://code.kx.com/q/ref/apply/

https://code.kx.com/q/ref/sv/

Редактировать: лучшим подходом, который поможет вам отлаживать в q, было бы полностью отказаться от решения на основе строк и создать отладочную переменную. Тогда у вас могли бы быть функции и параметры, уже доступные в консоли q, которые можно запускать с eval .debug

 createDebug:{
    // simply create a list with function name and parameters
    // assign it to the .debug namespace so it is available 
    // outside of the function call.
    .debug:(,/)enlist each x,y;

    }

testFunc:{1 2 3!(x;y;z)}

createDebug[`testFunc;("test";12345i;`a`b!10b)]
show .debug
(`testFunc;"test";12345;(`a`b)!10b)

eval .debug
key value
1   "test"
2   12345
3   (`a`b)!10b

  

Редактировать 2:

Поскольку у OP возникают проблемы с объектами размером более 2000 символов, другим способом сделать это было бы использовать JSON. Я не уверен, как это будет обрабатываться в C #, но попробовать стоит.

 getComposedStatement:{
    
    (x;.j.j each y)    

    }
    

// 3rd arg is a very large dictionary
getComposedStatement[`testFunc;("test";12345i;(til 2000)!2000?`3)]

(`testFunc;(""test"";"12345";{"0":"gia","1":"dmj","2":"pnh","3":"ofc","4":"hon","5":"jjb", ...

  

Затем вам придется использовать функцию run на другой стороне, чтобы преобразовать json обратно:

 runComposedStatement:{
    
    eval (first x),.j.k each last x    

}

runComposedStatement[getComposedStatement[`testFunc;("test";12345i;(til 2000)!2000?`3)]]

(1j, 2j, 3j)!("test";12345f;(`0`1`2`3`4`5`6`7`8`9`10`11`12`13`14`15 ...
  

Я не хочу публиковать весь словарь, но я проверил, что он был полным. Надеюсь, это может достичь того, чего вы хотите, или хотя бы на один шаг ближе. Может потребоваться больше для обработки JSON.

Комментарии:

1. Отлично — это именно то, что мне было нужно, большое спасибо! На самом деле есть только одна проблема — .Q.s1, похоже, жестко запрограммирован, чтобы ограничить его вывод до 1999 символов — вы можете подумать, что этого будет достаточно для меня, но вы ошибаетесь. Есть идеи, как увеличить этот предел?

2. Интересно. Я не уверен, что это ограничение .Q.s1, но мне придется изучить его. Тем не менее, я думаю, что другой подход позволит получить желаемую отладку, в которой вы нуждаетесь. См. Редактирование выше

3. Спасибо, Мэтт. Я действительно рассматривал этот подход и сделаю это таким образом, если необходимо — я предпочитаю подход .Q.s1, хотя контекст, в котором я его использую, — это несколько производственных приложений, множество разных запросов, разные серверы и попытки фиксировать неудачные запросы в журналах для последующего просмотра. Сохранение параметров в памяти на сервере отлично подходит для отладки, но я продолжаю сталкиваться с производственными проблемами, вызванными неожиданными вводами пользовательских данных, и хранение аргументов в памяти будет потеряно при перезапуске Q процессов.

4. Что касается проблемы .Q.s1, вы можете воспроизвести ее с помощью инструкции «count .Q.s1 `long_string_with_2000_or_more_chars» — результатом всегда будет 1999, а представленная строка будет заканчиваться на «..» — это довольно досадное ограничение на то, что в противном случае было бы идеальным решением моей проблемы.

5. Я думаю, значение ограничения в 2000 символов заключается в том, что это максимальный размер, до которого вы можете развернуть консоль. После каждого перезапуска я обычно запускаю c 10000 10000, чтобы увеличить размер консоли, но когда я затем запускаю c, он сообщает размер консоли как 2000 2000i , что, очевидно, является пределом. .Q.s1, очевидно, должен использовать настроенный размер консоли для ограничения вывода