I have following JSON data I would like to decode to Lua to access each the publish_topic and sample_rate value.
{"00-06-77-2f-37-94":{"publish_topic":"/stations/test","sample_rate":5000}}
If I understand correctly the Lua table will look like this:
{00-06-77-2f-37-94 = "publish_topic":"/stations/test","sample_rate":5000}
Next I would go through the table to save each value into a local variable.
However, if I try printing out the values of the table (using following code), I get 'nil' as return. Is the code for reading table values wrong?
Does the table have two values or is it just the one: ["publish_topic":"/stations/test","sample_rate":5000] ?
lua_value = JSON:decode(data)
for _,d in pairs(lua_value) do
print(lua_value[d])
end
local topic = lua_value[0]
local timer = lua_value[1]
end
Edit: I am using following JSON library for Lua: http://regex.info/blog/lua/json
Edit2: #Piglet: I implemented your script and modified it by adding a table (conversionTable) in which both elements "publish_topic":"/stations/test" and "sample_rate:5000" would be respectively saved in the variables pubtop and rate. When I however print each of both variables, nil ist returned in both cases.
How can I extract the information out of this table to save in variables?
Ultimately I actually only would like to save the values "/stations/test" and "5000" into these variables. Would I need to parse each of the elements above to get these or is there another way?
local pubtop
local rate
local function printTable(t)
local conversionTable = {}
for k,v in pairs(t) do
if type(v) == "table" then
conversionTable [k] = string.format("%q: {", k)
printTable(v)
print("}")
else
print(string.format("%q:", k) .. v .. ",")
end
end
pubtop = conversionTable[0]
rate = conversionTable[1]
end
local lua_value
local function handleOnReceive(topic, data, _, _)
print("handleOnReceive: topic '" .. topic .. "' message '" .. data .. "'")
-- This sample publishes the received messages to test/topic2
print(data)
lua_value = JSON:decode(data)
printTable(lua_value)
print(pubtop)
print(rate)
end
client:register('OnReceive', handleOnReceive)
I don't know which json library you're using so I can't tell you wether JSON:decode(data) is the correct way.
Assuming lua_value would like like so:
local lua_value = {
["00-06-77-2f-37-94"] = {
publish_topic = "/stations/test",
sample_rate = 5000
}
}
Then your loop
for _,d in pairs(lua_value) do
print(lua_value[d])
end
will indeed print nil.
lua_value has a single element at key "00-06-77-2f-37-94" which is a table.
Each loop iteration will give you a key value pair. So d is actually the value and hence the inner table of lua_value
So you're actually doing this:
local innerTable = lua_value["00-06-77-2f-37-94"]
print(lua_value[innerTable])
Of course lua_value[innerTable] is nil.
Edit:
Try something like
function printTable(t)
for k,v in pairs(t) do
if type(v) == "table" then
print(string.format("%q: {", k))
printTable(v)
print("}")
else
print(string.format("%q:", k) .. v .. ",")
end
end
end
printTable(lua_value)
Im trying to replicate the searching list style of crunchbase using ruby on rails.
I have an array of filters that looks something like this:
[
{
"id":"0",
"className":"Company",
"field":"name",
"operator":"starts with",
"val":"a"
},
{
"id":"1",
"className":"Company",
"field":"hq_city",
"operator":"equals",
"val":"Karachi"
},
{
"id":"2",
"className":"Category",
"field":"name",
"operator":"does not include",
"val":"ECommerce"
}
]
I send this json string to my ruby controller where I have implemented this logic:
filters = params[:q]
table_names = {}
filters.each do |filter|
filter = filters[filter]
className = filter["className"]
fieldName = filter["field"]
operator = filter["operator"]
val = filter["val"]
if table_names[className].blank?
table_names[className] = []
end
table_names[className].push({
fieldName: fieldName,
operator: operator,
val: val
})
end
table_names.each do |k, v|
i = 0
where_string = ''
val_hash = {}
v.each do |field|
if i > 0
where_string += ' AND '
end
where_string += "#{field[:fieldName]} = :#{field[:fieldName]}"
val_hash[field[:fieldName].to_sym] = field[:val]
i += 1
end
className = k.constantize
puts className.where(where_string, val_hash)
end
What I do is, I loop over the json array and create a hash with keys as table names and values are the array with the name of the column, the operator and the value to apply that operator on. So I would have something like this after the table_names hash is created:
{
'Company':[
{
fieldName:'name',
operator:'starts with',
val:'a'
},
{
fieldName:'hq_city',
operator:'equals',
val:'karachi'
}
],
'Category':[
{
fieldName:'name',
operator:'does not include',
val:'ECommerce'
}
]
}
Now I loop over the table_names hash and create a where query using the Model.where("column_name = :column_name", {column_name: 'abcd'}) syntax.
So I would be generating two queries:
SELECT "companies".* FROM "companies" WHERE (name = 'a' AND hq_city = 'b')
SELECT "categories".* FROM "categories" WHERE (name = 'c')
I have two problems now:
1. Operators:
I have many operators that can be applied on a column like 'starts with', 'ends with', 'equals', 'does not equals', 'includes', 'does not includes', 'greater than', 'less than'. I am guessing the best way would be to do a switch case on the operator and use the appropriate symbol while building the where string. So for example, if the operator is 'starts with', i'd do something like where_string += "#{field[:fieldName]} like %:#{field[:fieldName]}" and likewise for others.
So is this approach correct and is this type of wildcard syntax allowed in this kind of .where?
2. More than 1 table
As you saw, my approach builds 2 queries for more than 2 tables. I do not need 2 queries, I need the category name to be in the same query where the category belongs to the company.
Now what I want to do is I need to create a query like this:
Company.joins(:categories).where("name = :name and hq_city = :hq_city and categories.name = :categories[name]", {name: 'a', hq_city: 'Karachi', categories: {name: 'ECommerce'}})
But this is not it. The search can become very very complex. For example:
A Company has many FundingRound. FundingRound can have many Investment and Investment can have many IndividualInvestor. So I can select create a filter like:
{
"id":"0",
"className":"IndividualInvestor",
"field":"first_name",
"operator":"starts with",
"val":"za"
}
My approach would create a query like this:
SELECT "individual_investors".* FROM "individual_investors" WHERE (first_name like %za%)
This query is wrong. I want to query the individual investors of the investments of the funding round of the company. Which is a lot of joining tables.
The approach that I have used is applicable to a single model and cannot solve the problem that I stated above.
How would I solve this problem?
You can create a SQL query based on your hash. The most generic approach is raw SQL, which can be executed by ActiveRecord.
Here is some concept code that should give you the right idea:
query_select = "select * from "
query_where = ""
tables = [] # for selecting from all tables
hash.each do |table, values|
table_name = table.constantize.table_name
tables << table_name
values.each do |q|
query_where += " AND " unless query_string.empty?
query_where += "'#{ActiveRecord::Base.connection.quote(table_name)}'."
query_where += "'#{ActiveRecord::Base.connection.quote(q[fieldName)}'"
if q[:operator] == "starts with" # this should be done with an appropriate method
query_where += " LIKE '#{ActiveRecord::Base.connection.quote(q[val)}%'"
end
end
end
query_tables = tables.join(", ")
raw_query = query_select + query_tables + " where " + query_where
result = ActiveRecord::Base.connection.execute(raw_query)
result.to_h # not required, but raw results are probably easier to handle as a hash
What this does:
query_select specifies what information you want in the result
query_where builds all the search conditions and escapes input to prevent SQL injections
query_tables is a list of all the tables you need to search
table_name = table.constantize.table_name will give you the SQL table_name as used by the model
raw_query is the actual combined sql query from the parts above
ActiveRecord::Base.connection.execute(raw_query) executes the sql on the database
Make sure to put any user submitted input in quotes and escape it properly to prevent SQL injections.
For your example the created query will look like this:
select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'
This approach might need additional logic for joining tables that are related.
In your case, if company and category have an association, you have to add something like this to the query_where
"AND 'company'.'category_id' = 'categories'.'id'"
Easy approach: You can create a Hash for all pairs of models/tables that can be queried and store the appropriate join condition there. This Hash shouldn't be too complex even for a medium-sized project.
Hard approach: This can be done automatically, if you have has_many, has_one and belongs_to properly defined in your models. You can get the associations of a model using reflect_on_all_associations. Implement a Breath-First-Search or Depth-First Search algorithm and start with any model and search for matching associations to other models from your json input. Start new BFS/DFS runs until there are no unvisited models from the json input left. From the found information, you can derive all join conditions and then add them as expressions in the where clause of the raw sql approach as explained above. Even more complex, but also doable would be reading the database schema and using a similar approach as defined here by looking for foreign keys.
Using associations: If all of them are associated with has_many / has_one, you can handle the joins with ActiveRecord by using the joins method with inject on the "most significant" model like this:
base_model = "Company".constantize
assocations = [:categories] # and so on
result = assocations.inject(base_model) { |model, assoc| model.joins(assoc) }.where(query_where)
What this does:
it passes the base_model as starting input to Enumerable.inject, which will repeatedly call input.send(:joins, :assoc) (for my example this would do Company.send(:joins, :categories) which is equivalent to `Company.categories
on the combined join, it executes the where conditions (constructed as described above)
Disclaimer The exact syntax you need might vary based on the SQL implementation you use.
Full blown SQL string is a security issue, because it exposes your application to a SQL injection attack. If you can get your way around this, it is completely ok to make those query concatenations, as long as you make them compatible with your DB(yes, this solution is DB specific).
Other than that you can make some field that marks some querys as joined, as I have mentioned in the comment, you would have some variable to mark the desired table to be the output of the query, something like:
[
{
"id":"1",
"className":"Category",
"field":"name",
"operator":"does not include",
"val":"ECommerce",
"queryModel":"Company"
}
]
Which, when processing the query, you would use to output the result of this query as the queryModel instead of the className, in those cases the className would be used only to join the table conditions.
I would suggest altering your JSON data. Right now you only send name of the model, without the context, it would be easier if your model would have context.
In your example data would have to look like
data = [
{
id: '0',
className: 'Company',
relation: 'Company',
field: 'name',
operator: 'starts with',
val: 'a'
},
{
id: '1',
className: 'Category',
relation: 'Company.categories',
field: 'name',
operator: 'equals',
val: '12'
},
{
id: '3',
className: 'IndividualInvestor',
relation: 'Company.founding_rounds.investments.individual_investors',
field: 'name',
operator: 'equals',
val: '12'
}
]
And you send this data to QueryBuilder
query = QueryBuilder.new(data)
results = query.find_records
Note: find_records returns array of hashes per model on which you execute query.
For example it would return [{Company: [....]]
class QueryBuilder
def initialize(data)
#data = prepare_data(data)
end
def find_records
queries = #data.group_by {|e| e[:model]}
queries.map do |k, v|
q = v.map do |f|
{
field: "#{f[:table_name]}.#{f[:field]} #{read_operator(f[:operator])} ?",
value: value_based_on_operator(f[:val], f[:operator])
}
end
db_query = q.map {|e| e[:field]}.join(" AND ")
values = q.map {|e| e[:value]}
{"#{k}": k.constantize.joins(join_hash(v)).where(db_query, *values)}
end
end
private
def join_hash(array_of_relations)
hash = {}
array_of_relations.each do |f|
hash.merge!(array_to_hash(f[:joins]))
end
hash.map do |k, v|
if v.nil?
k
else
{"#{k}": v}
end
end
end
def read_operator(operator)
case operator
when 'equals'
'='
when 'starts with'
'LIKE'
end
end
def value_based_on_operator(value, operator)
case operator
when 'equals'
value
when 'starts with'
"%#{value}"
end
end
def prepare_data(data)
data.each do |record|
record.tap do |f|
f[:model] = f[:relation].split('.')[0]
f[:joins] = f[:relation].split('.').drop(1)
f[:table_name] = f[:className].constantize.table_name
end
end
end
def array_to_hash(array)
if array.length < 1
{}
elsif array.length == 1
{"#{array[0]}": nil}
elsif array.length == 2
{"#{array[0]}": array[1]}
else
{"#{array[0]}": array_to_hash(array.drop(1))}
end
end
end
I feel you are over complicating things by having one single controller for everything. I would create a controller for every model or entity that you would want to show and then implement the filters like you said.
Implementing a dynamic where and order by is not very hard but if, as you said, you need to have also the logic to implement some joins you are not only over complicating the solution (because you will have to keep this controller updated every time you add a new model, entity or change the basic logic) but you are also enabling people start playing with your data.
I am not very familiar with Rails so sadly I cannot give you any specific cde other than saying that your approach seems OK to me. I would explode it into multiple controllers.
I am using NodeJS and node-mysql, I've created a DB layer to allow easy integration with other DBs (example MongoDB) for later use. At this point in time, the syntax is essentially the same as MySQL.
I am placing a call to the query object of MySQL, and it's returning the results to my callback function, which I can send to the console with console.log, and it shows the following:
[ { username: 'test', total: 1 } ]
Yet, when I try to access the total, it says undefined. Here's my code:
db.query("SELECT username, COUNT(user_id) AS total FROM users WHERE username = '" + message.username + "'", function(err, info) {
console.log(info);
self.sendRequest(client, '{ "command" : "CALLBACK", "result" : ' + info.total + ' }');
});
when using console.log(info), it returns valid JSON syntax, however I cannot access the total via info.total, as it returns undefined....
Any ideas on why I'm having this issue? If you need to see more code I will be happy to provide it..
Figured it out;
info[0].total returns what I need (d'oh)...
I am trying to delete multiple rows in the sqlite table in my Adobe AIR app (runtime 2.5).
Here is the statement, using the "IN" operator:
"DELETE FROM mylist WHERE tdId IN (tdId1, tdId2, tdId3, ...)";
Where tdId1, tdId2, etc. will be determined at runtime based on which row(s) the user chooses to delete. The user can delete arbitrary number of rows.
I've tried something like:
//delete statement text
"DELETE FROM mylist WHERE tdId IN :tdId";
//delete statement parameters: take 1.
//Got "argument error: near ':tdId': syntax error"
deleteStmt.parameters[":tdId"] = "(26, 32)";
//delete statement parameters: take 2.
//Also got "argument error: near ':tdId': syntax error"
var arr:Array = [26, 32];
deleteStmt.parameters[":tdId"] = arr;
How I go about deleting multiple rows?
[Edit] So it looks like the aforementioned cached statement with parameter [":tdId"] doesn't work when deleting multiple rows. When attempting to execute the delete statement multiple times in asynchronous mode, after the very first row in the queue is deleted, Flash throws the following error:
"Error #3110: Operation cannot be
performed while SQLStatement.executing
is true."
It would seem too much of trouble to chain these deletes with a callback. So I guess I am using my last resort: building the sql at runtime. Conclusion: Cached statements can't be used in these kind of situations...
The problem occurs when you insert the parameter "(26,32)". As the parameter is not purely a substitution of value, it represents a variable to SQL, NOT A STRING. Hence you statement effectively became (or roughly) in your first take...
"DELETE FROM mylist WHERE tdId IN '(26,32)'"
Hence your error, due to the syntax... In your second take it gets worse...
"DELETE FROM mylist WHERE tdId IN *Array(26,32)*"
As the variable does not convert to a string value, this does not actually happen. But what happens is that when the interprater (SQL) tries to understand the code after the 'IN' text, it gets an ARRAY object, which it has completely no idea on what to do... Its not even a valid SQL type....
Solution? [I yet to fully test it, so please do]
var toDel:Array = [26,32]
//delete statement text
var baseStr:String = "DELETE FROM mylist WHERE tdId IN (";
var midStr:String = '';
//delete statement parameters: Processing parameter
for( var i = 0; i < toDel.length; i++ ) {
deleteStmt.parameters[i] = toDel[i];
if(midStr.length > 0) { midStr += ' , '; }
midStr += '?';
}
deleteStmt.text = baseStr + midStr + ' )';
//Then execute
So what happens in this case is that u effectively execute...
"DELETE FROM mylist WHERE tdId IN ( :val1 , :val2 )"
In this way you still maintain the safe (good practice) use of parameters, without converting everything to a string.
EDIT: if you dun understand the use of parameter / '?' refer to :
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/data/SQLStatement.html#parameters
If the IN clause does not allow parameters, you can try old-school SQL style: append multiple
" OR (tdId = :param" + paramCounter.toString() + ")"
to the SQL string
I'm a principiant of Linq so i need some help..
I don’t know if in Linq syntax by breaking a query in 2 or more parts,
just like the following example,
the records will be downloaded immediatly from sql server in each step,or they will be sent to server at the moment when I’ll start to see all the data?
for exemple when I bind some objects (a Datagrid for exemple)
System.Linq.IQueryable<Panorami> Result = db.Panorami;
byte FoundOneContion = 0;
//step 1
if (!string.IsNullOrEmpty(Title))
{
Result = Result.Where(p => SqlMethods.Like(p.Title, "%" + Title + "%"));
FoundOneContion = 1;
}
//step 2
if (!string.IsNullOrEmpty(Subject))
{
Result = Result.Where(p => SqlMethods.Like(p.Subject, "%" + Subject + "%"));
FoundOneContion = 1;
}
if (FoundOneContion == 0)
{
return null;
}
else
{
return Result.OrderBy(p => p.Title).Skip(PS * CP).Take(PS);
}
If unfortunatly Linq download immediately all the records
(Therefore such a doubt I have had was right!)
exist any syntax to round the problem?
For example: ternary operator ( condition ? true part : false part )
For any suggestions i would appreciate them a lot. thanks everyone!
The above method does not enumerate the query - therefore no database calls are made. The query is constructed and not executed.
You can enumerate the query by calling foreach, or calling some method that calls foreach (such as ToList, ToArray), or by calling GetEnumerator(). This will cause the query to execute.