documentdb returns empty object from null values - azure

I am calling something similar to the following code against a documentdb collection:
List<DocumentCollection> collectionList = documentClient
.queryCollections(
getTodoDatabase().getSelfLink(),
"SELECT * FROM root r WHERE r.id='" + COLLECTION_ID
+ "'", null).getQueryIterable().toList();
The json that I am returning has null on certain fields, which is fine, but when I return the collectionList the nulls are converted to empty JSON objects; for example:
"`content`" : `null` is converted to "`content`" : `{}`
Does anyone know if there is a way around this?

Add a WHERE clause specifying the type you are expecting using the type checking functions
IS_ARRAY, IS_BOOL, IS_NULL, IS_NUMBER, IS_OBJECT, IS_STRING, IS_DEFINED, and IS_PRIMITIVE

Related

How to avoid to add duplicate string in append string connector logic app

I have following json array input -
"results": [
{ "tableName" : "ABC","id":"11"},
{ "tableName" : "ZX","id":"11"},
{ "tableName" : "ABC","id":"11"}
]}
In logic app i have used `` in For_each I'm able to append string successfuly but how to avoid adding already present string ? like above example my current output is -
ABC,ZX,ABC
i want - ABC,ZX
You could use the Array to implement, there is a union function to return a collection that has all the items from the specified collections. It will return a collection without duplicate string. Then use join action to return the string.
Cause the union function must contain two collection at least, so I used two same collections. The expression is like this: union(variables('tablename'),variables('tablename'))
The below is the result.
Hope this could help you.

Camel CQL Component - Query with multiple parameters

I'm creating an Camel route that consumes data from Cassandra DB using CQL component and I want to pass multiple parameters to the where statement.
How can I pass multiple parameters to construct the prepared stratement internally?
I've checked in DOC and I found the way to pass one parameter:
rest("cassandra")
.get()
.route()
.routeId("teste")
.removeHeaders("Camel*")
.setHeader(Exchange.HTTP_METHOD, simple(HttpMethod.GET.name()))
.process(e-> e.getIn().setBody("Gabriel")
.to("cql://localhost:9042/teste?cql=SELECT * FROM teste_table where name = ? ALLOW FILTERING")
The way above works fine, but I want to pass more than one parameter.
After some retries, I found a way to do it, simply passing an object array in the exchange in body, see below:
import com.datastax.driver.core.LocalDate;
import com.datastax.driver.core.Row;
...
rest("cassandra")
.get()
.route()
.routeId("test-route")
.removeHeaders("Camel*")
.setHeader(Exchange.HTTP_METHOD, simple(HttpMethod.GET.name()))
.process(e-> e.getIn().setBody(new Object[] {"Gabriel", LocalDate.fromYearMonthDay(1994, 10, 25)}))
.to("cql://localhost:9042/teste?cql=SELECT * FROM teste_table where name = ? and date_of_birth= ? ALLOW FILTERING")
.process(e -> {
Row[] rows = e.getIn().getBody(Row[].class);
for (Row row : rows)
System.out.println("name: " + row.getString("name") + ", idade: " + row.getInt("idade"));
});

WHERE IN with Azure DocumentDB (CosmosDB) .Net SDK

Having seen this question I'm not sure why the following code for my DocDb instance isn't working:
var userApps = _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
new SqlQuerySpec(#"SELECT r.appId FROM ROOT r WHERE r.userId = #userId", (#"#userId", userId).ToSqlParameters()))
.ToList()
.Select(s => (string)s.appId);
var query = _docs.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.APP_DEFINITIONS),
new SqlQuerySpec(#"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN (#userApps)", (#"#userApps", userApps.ToArray()).ToSqlParameters()),
new FeedOptions { EnableCrossPartitionQuery = true })
.AsDocumentQuery();
When I execute this, though I know the data should be returning me back a result set, it comes back empty every time.
Troubleshooting so far
Variants of .Select()
Return strings that I string.Join in to a comma-separated list.
Eg:
var userApps = string.Join(#",", _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
new SqlQuerySpec(#"SELECT r.appId FROM ROOT r WHERE r.userId = #userId", (#"#userId", userId).ToSqlParameters()))
.ToList()
.Select(s => $#"'{s.appId}'");
Don't encapsulate IN parameter
Removing the () around the parameter spec in the query thinking maybe the SqlParameter spec was doing the array specification?
Eg: #"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN #userApps")
Ends up throwing "Syntax error, incorrect syntax near '#userApps'."
Validate via Azure Portal queries
Ran the (expected) SQL that this code should be running.
I get back my expected results without issue (ie: I know there is a result set for these queries as-written).
Debugger output for Query 1
AppIds are coming back from query 1.
Unsatisfactory workaround
Change query 2 to not be parameterized. Rather, inject the comma-separated list of IDs from query 1 in to it:
var userApps = string.Join(#",", _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
new SqlQuerySpec(#"SELECT r.appId FROM ROOT r WHERE r.userId = #userId", (#"#userId", userId).ToSqlParameter()))
.ToList()
.Select(s => $#"'{s.appId}'"));
var query = _docs.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.APP_DEFINITIONS),
new SqlQuerySpec($#"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN ({userApps})"),
new FeedOptions { EnableCrossPartitionQuery = true })
.AsDocumentQuery();
Works perfectly but I'm not going to accept it as an answer to this problem as it goes against a couple decades-worth of SQL best practices and, frankly, shouldn't be a solution.
Here's my ToSqlParameters() extension method in case it's the culprit (this works everywhere else I've used it, though. Maybe something special is needed for arrays?):
public static SqlParameterCollection ToSqlParameters(this (string, object) parmDef) => new SqlParameterCollection(new[] { new SqlParameter(parmDef.Item1, parmDef.Item2) });
Thanks!
If you use a parameterized IN list, then it will be considered as a single value when the parameter is expanded.
For instance, for this query:
SELECT * FROM r WHERE r.item IN (#items) And #items is defined as "['val1', 'val2', 'val3']" will be interpreted as such:
SELECT * FROM r WHERE r.item IN (['val1', 'val2', 'val3'])
which basically means that you're comparing r.item to a single value that is an array of three elements (i.e. equivalent to r.item = ['val1', 'val2', 'val3']).
To compare to multiple items, you need to use a parameter for each value. Something like this: SELECT * FROM r WHERE r.item IN (#val1, #val2, #val3])
A more convenient way to write this query is to use ARRAY_CONTAINS instead and pass the array of items as a single parameter. So the above query will be written like this:
SELECT * FROM r WHERE ARRAY_CONTAINS(#items, r.item)

Azure DocumentDB: order by and filter by DateTime

I have the following query:
SELECT * FROM c
WHERE c.DateTime >= "2017-03-20T10:07:17.9894476+01:00" AND c.DateTime <= "2017-03-22T10:07:17.9904464+01:00"
ORDER BY c.DateTime DESC
So as you can see I have a WHERE condition for a property with the type DateTimeand I want to sort my result by the same one.
The query ends with the following error:
Order-by item requires a range index to be defined on the corresponding index path.
I have absolutely no idea what this error message is about :(
Has anybody any idea?
You can also do one thing that don't require indexing explicitly. Azure documentBD is providing indexing on numbers field by default so you can store the date in long format. Because you are already converting date to string, you can also convert date to long an store, then you can implement range query.
I think I found a possible solution, thanks for pointing out the issue with the index.
As stated in the following article https://learn.microsoft.com/en-us/azure/documentdb/documentdb-working-with-dates#indexing-datetimes-for-range-queries I changed the index for the datatype string to RangeIndex, to allow range queries:
DocumentCollection collection = new DocumentCollection { Id = "orders" };
collection.IndexingPolicy = new IndexingPolicy(new RangeIndex(DataType.String) { Precision = -1 });
await client.CreateDocumentCollectionAsync("/dbs/orderdb", collection);
And it seems to work! If there are any undesired side effects I will let you know.

GroovyRowresult object returns different kinds of values when calling size() depending on how object was instantiated

Using Groovy: Not sure if this is intended behavior, but when using size() as applied to a GroovyRowresult object, it will return the number of rows in the object if the object was created by a call to {Sql object}.rows(sql_select_text). However if the GroovyRowresult object was created by a call to {Sql object}.firstRow(sql_select_text), it will return the number of fields in the returned record. Problem seems to be that the returned value of a call to {GroovyRowresult object}.size() is determined by how the GroovyRowresult object came into being and not anything else that would be an obvious or clear distinction. Is this intended behavior or not? I can't find it referred to in the Groovy documantation even for the most current ver. of Groovy. Code example:
/*
Using HyperSQL for database implementation for test, so you need the
HSQLDB package from http://hsqldb.org/ to run this test in the Groovy
runtime classpath or explode the hsqldb.jar file into the same directory as this
.groovy script.
*/
import groovy.sql.Sql
memCon = Sql.newInstance("jdbc:hsqldb:mem", "SA", "","org.hsqldb.jdbcDriver")
memCon.execute("DROP TABLE IF EXISTS TBL")
memCon.execute("""create table TBL (
AAA VARCHAR(50),
BBB VARCHAR(50)
)""")
sql = "INSERT INTO TBL VALUES ('123', 'ABC')"
memCon.execute(sql)
sql = "INSERT INTO TBL VALUES ('456', 'ABC')"
memCon.execute(sql)
sql = "INSERT INTO TBL VALUES ('789', 'ABC')"
memCon.execute(sql)
sql = "select * from TBL"
rows = memCon.rows(sql)
println '(.rows) rows.size() = ' + rows.size() // returns 3
rows = memCon.firstRow(sql)
println '(.firstRow) rows.size() = ' + rows.size() // returns 2
To test: Save code list above to a file named "test.groovy". Place it in the same directory as the hsqldb.jar file (if you altered your Groovy runtime classpath to include a reference to it) or its exploded file structure and run it with "groovy test.groovy".
I submitted this issue for review by the Groovy gods; guess I'll have to see what they say. The issue is found here.
memCon.rows returns a List<GroovyRowResult>
memCon.firstRow returns a GroovyRowResult
So the first size call gets the size of the List (the number of rows)
The second size call gets the size of the GroovyRowResult (the number of fields)

Resources