I'd like to change the row color of a spreadsheet using the Google Spreadsheet API.
I'm using JAVA, I saw it working in JavaScript but I don't found it in JAVA.
Google Sheet API Documentation is not the best to say the least but after some fiddling here is python code that works:
http = credentials.authorize(httplib2.Http())
discovery_url = ('https://sheets.googleapis.com/$discovery/rest?'
'version=v4')
service = discovery.build('sheets', 'v4', http=http, discoveryServiceUrl=discovery_url, cache_discovery=False)
spreadsheet = service.spreadsheets().get(spreadsheetId=ss.id).execute()
requests = []
for sheet in spreadsheet.get('sheets'):
sheetId = sheet.get('properties').get('sheetId')
requests.append({
"updateCells": {
"rows": [
{
"values": [{
"userEnteredFormat": {
"backgroundColor": {
"red": 1,
"green": 0,
"blue": 0,
"alpha": 1
}}}
]
}
],
"fields": 'userEnteredFormat.backgroundColor',
"range": {
"sheetId": sheetId,
"startRowIndex": 0,
"endRowIndex": 1,
"startColumnIndex": 0,
"endColumnIndex": 1
}}})
body = {
'requests': requests
}
response = service.spreadsheets().batchUpdate(spreadsheetId=ss.id, body=body).execute()
With JavaScript API you could use this:
const range = {
sheetId: 250062959, // find your own
startRowIndex: 0,
endRowIndex: 1,
startColumnIndex: 0,
endColumnIndex: 1,
};
const request = {
spreadsheetId, // fill with your own
resource: {
requests: [
{
updateCells: {
range,
fields: '*',
rows: [
{
values: [
{
userEnteredValue: { stringValue: 'message' },
userEnteredFormat: {
backgroundColor: { red: 1, green: 0, blue: 0 },
},
},
],
},
],
},
},
],
},
};
try {
const result = await client.spreadsheets.batchUpdate(request);
console.log(result);
} catch (error) {
throw `update row error ${error}`;
}
I know this is a long time since you originally asked but according to the v4 API you could technically set a conditional format that is always true with spreadsheets.batchUpdate
eg. https://developers.google.com/sheets/api/samples/conditional-formatting
May not be the easiest thing to manage but is 'technically' possible
object = {
"updateCells": {
"range": {
"sheetId": sheetId,
"startRowIndex":startRowIndex,
"endRowIndex": endRowIndex,
"startColumnIndex": startColumnIndex,
"endColumnIndex": endColumnIndex
}
"rows": [{
"values": [{
"textFormatRuns": [
{"format": {
"foregroundColor": {
"red": 0.0,
"green": 255.0,
"blue": 31.0
},
},"startIndex": 0
},
]
}
]
}]
"fields": "textFormatRuns(format)"
}
}
Set cell color:
https://developers.google.com/apps-script/reference/spreadsheet/range#setBackground(String)
google-apps-script (JavaScript) is the only option as far as I know. It can't be done with the spreadsheet-API (gdata)
Related
I try to change the cells background color with java, and try to use the official tutorial:
https://developers.google.cn/sheets/api/guides/conditional-format?hl=ko
But after code executing, I got the error:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid requests[0].addConditionalFormatRule: Invalid ConditionValue.userEnteredValue: =LT($D2,median($D$2:$D$11))",
"reason" : "badRequest"
} ],
"message" : "Invalid requests[0].addConditionalFormatRule: Invalid ConditionValue.userEnteredValue: =LT($D2,median($D$2:$D$11))",
"status" : "INVALID_ARGUMENT"
}
I do not understand what means "=LT($D2,median($D$2:$D$11))" and where the range sets. On the screen-shot range is A2:D5 - so how to this range should be set and what wrong with this example?
Regarding where do we set the range, It is being set in AddConditionalFormatRuleRequest -> ConditionalFormatRule -> ranges
Regarding what this formula means =LT($D2,median($D$2:$D$11)),
Since this is a custom formula, $D2 will increment based on the range provided. From D2 up to D11. It will check if the current Column D row is less than the median value of range D2:D11. See LT() and MEDIAN()
Sample Request via API Explorer:
{
"requests": [
{
"addConditionalFormatRule": {
"index": 0,
"rule": {
"ranges": [
{
"sheetId": 116889903,
"startRowIndex": 1,
"startColumnIndex": 0,
"endColumnIndex": 4,
"endRowIndex": 11
}
],
"booleanRule": {
"condition": {
"type": "CUSTOM_FORMULA",
"values": [
{
"userEnteredValue": "=LT($D2,median($D$2:$D$11))"
}
]
},
"format": {
"backgroundColor": {
"red": 1,
"blue": 0,
"green": 0
}
}
}
}
}
}
]
}
In this example, the range I set is starting from rowIndex 1 which is equivalent to sheets row 2. (GridRange object is zero-based). start columnIndex is 0 since we want it to start at sheet column 1.
Notice that endRowIndex is 11 (Sheet row 12) and endColumnIndex is 4 (Sheet column 5). It is because based on GridRange, endRowIndex and endColumnIndex are exclusive which means it is not included in the range.
Output:
For a java code sample code, please refer to the official document example here.
But if you just want to change the cell background color without conditional formatting, you can use UpdateCellsRequest or RepeatCellRequest.
The difference between the 2 is that, In UpdateCellsRequest you will provide the backgroundColor in each cell in your range. While in RepeatCellRequest you only need to set the backgroundColor once and it will be reflected in all your range.
Sample Update Cells Request via API Explorer:
{
"requests": [
{
"updateCells": {
"range": {
"sheetId": 116889903,
"startRowIndex": 0,
"startColumnIndex": 0,
"endRowIndex": 1,
"endColumnIndex": 4
},
"rows": [
{
"values": [
{
"userEnteredFormat": {
"backgroundColor": {
"red": 0,
"blue": 1,
"green": 0
}
}
},
{
"userEnteredFormat": {
"backgroundColor": {
"red": 0,
"blue": 0,
"green": 1
}
}
}
]
}
],
"fields": "userEnteredFormat"
}
}
]
}
In this example, even though I set the range to A1:D1, but since I only provided 2 CellData values with background color blue and green. Only 2 cells were updated
Output:
Sample Repeat Cell Request via API Explorer:
{
"requests": [
{
"repeatCell": {
"range": {
"sheetId": 116889903,
"startRowIndex": 0,
"endRowIndex": 1,
"startColumnIndex": 0,
"endColumnIndex": 4
},
"cell": {
"userEnteredFormat": {
"backgroundColor": {
"red": 0,
"blue": 0,
"green": 1
}
}
},
"fields": "userEnteredFormat"
}
}
]
}
I only entered 1 CellData which will reflect to all the cells within the range provided.
Output:
I'm using elasticsearch for the first time. I'm trying to use completion suggester in multi-field key, although I don't see any error but I don't get the response.
Mapping creation:
PUT /products5/
{
"mappings":{
"products" : {
"properties" : {
"name" : {
"type":"text",
"fields":{
"text":{
"type":"keyword"
},
"suggest":{
"type" : "completion"
}
}
}
}
}
}
}
Indexing:
PUT /products5/product/1
{
"name": "Apple iphone 5"
}
PUT /products5/product/2
{
"name": "iphone 4 16GB"
}
PUT /products5/product/3
{
"name": "iphone 3 SS 16GB black"
}
PUT /products5/product/4
{
"name": "Apple iphone 4 S 16 GB white"
}
PUT /products5/product/5
{
"name": "Apple iphone case"
}
Query:
POST /products5/product/_search
{
"suggest":{
"my-suggestion":{
"prefix":"i",
"completion":{
"field":"name.suggest"
}
}
}
}
Output:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 0,
"max_score": 0,
"hits": []
},
"suggest": {
"my-suggestion": [
{
"text": "i",
"offset": 0,
"length": 1,
"options": []
}
]
}
}
Please guide me what is the mistake, I tried every possible options.
From the first perspective this looks accurate. Probably the reason why you don't have correct response is that you added documents in the index before you created mapping in the index. And documents are not indexed according to the mapping you specified
I have found an issue in your mapping name. There is an inconsistency between name of the mapping and value which you specifies in the url when you're creating new documents. You create a mapping in the index with the name products. And when you add new documents you're specifying product as a name of the mapping of your index and it doesn't end with s. You have a typo.
Is there a way to apply group by on two fields in elasticsearch??
TermsBuilder yearAgg = AggregationBuilders.terms("by_year").field("year").subAggregation(AggregationBuilders.terms("by_name")).field("Name").subAggregation(sumMarks);
// create the bool filter for the condition above
String[] names = { "stokes", "roshan" };
BoolQueryBuilder aggFilter = QueryBuilders.boolQuery().must(QueryBuilders.termsQuery("Name", names));
// create the filter aggregation and add the year sub-aggregation
FilterAggregationBuilder aggregation = AggregationBuilders.filter("agg").filter(aggFilter).subAggregation(yearAgg);
// create the request and execute it
SearchResponse response = client.prepareSearch("bighalf").setTypes("excel").addAggregation(aggregation).execute().actionGet();
System.out.println(response.toString());
I tried to apply group by on two different terms but Am not getting the expected result.
Response after grouping:
{
"aggregations": {
"agg": {
"doc_count": 2,
"by_year": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "roshan",
"doc_count": 1,
"by_name": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "roshan",
"doc_count": 1
}
]
},
"sum_marks": {
"value": 85
}
},
{
"key": "stokes",
"doc_count": 1,
"by_name": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "stokes",
"doc_count": 1
}
]
},
"sum_marks": {
"value": 91
}
}
]
}
}
}
}
I could only see the document count under "by_name" grouping. Is there a better way to apply grouping on two different fields in elasticsearch.
You have an error in the way you build your aggregation, you're using the same field Name for both the by_year and by_name aggregation.
// your code
TermsBuilder yearAgg = AggregationBuilders.terms("by_year")
.field("year").subAggregation(AggregationBuilders.terms("by_name")).field("Name").subAggregation(sumMarks);
^
|
This parenthesis is wrong, it should go at the end
Do it like this instead
TermsBuilder yearAgg = AggregationBuilders.terms("by_year").field("year")
.subAggregation(AggregationBuilders.terms("by_name").field("Name").subAggregation(sumMarks));
I use Vaadin 7. I want to save a Chart configuration and restore it later. I found an interesting thing in com.vaadin.addon.charts.model.Configuration is that you can serialize the configuration into JSON object.
Code :
chart.getConfiguration().toString();
Result:
{
"type": "column"
},
"title": {
"text": "Chart"
},
"xAxis": {
"categories": [
"f",
"e"
],
"axisIndex": 0
},
"yAxis": {
"min": 0,
"title": {
"text": "Quantity"
},
"axisIndex": 0
},
"tooltip": {
"_fn_formatter": "this.series.name +\u0027: \u0027+ this.y +\u0027 (\u0027+ Math.round(this.percentage) +\u0027%)\u0027"
},
"plotOptions": {
"column": {
"stacking": "normal"
}
},
"series": [
{
"data": [
1,
2
],
"name": "d",
"visible": true
}
],
"exporting": {
"enabled": false
}
}
What I want now is build Configuration from that JSON object. Is there a way to that ?
Found it, pretty simple actually :
Chart chart = new Chart();
//json is the string containing your JSON object
chart.setJsonConfig(json);
//you'll have to draw the chart to update it if needed
//chart.drawChart();
How can I have the tokens of a particular field returned in the result
For example, A GET request
curl -XGET 'http://localhost:9200/twitter/tweet/1'
returns
{
"_index" : "twitter",
"_type" : "tweet",
"_id" : "1",
"_source" : {
"user" : "kimchy",
"postDate" : "2009-11-15T14:12:12",
"message" : "trying out Elastic Search"
}
}
I would like to have the tokens of '_source.message' field included in the result
There is also another way to do it using the following script_fields script:
curl -H 'Content-Type: application/json' -XPOST 'http://localhost:9200/test-idx/_search?pretty=true' -d '{
"query" : {
"match_all" : { }
},
"script_fields": {
"terms" : {
"script": "doc[field].values",
"params": {
"field": "message"
}
}
}
}'
It's important to note that while this script returns the actual terms that were indexed, it also caches all field values and on large indices can use a lot of memory. So, on large indices, it might be more useful to retrieve field values from stored fields or source and reparse them again on the fly using the following MVEL script:
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.StringReader;
// Cache analyzer for further use
cachedAnalyzer=(isdef cachedAnalyzer)?cachedAnalyzer:doc.mapperService().documentMapper(doc._type.value).mappers().indexAnalyzer();
terms=[];
// Get value from Fields Lookup
//val=_fields[field].values;
// Get value from Source Lookup
val=_source[field];
if(val != null) {
tokenStream=cachedAnalyzer.tokenStream(field, new StringReader(val));
CharTermAttribute termAttribute = tokenStream.addAttribute(CharTermAttribute);
while(tokenStream.incrementToken()) {
terms.add(termAttribute.toString())
};
tokenStream.close();
}
terms
This MVEL script can be stored as config/scripts/analyze.mvel and used with the following query:
curl 'http://localhost:9200/test-idx/_search?pretty=true' -d '{
"query" : {
"match_all" : { }
},
"script_fields": {
"terms" : {
"script": "analyze",
"params": {
"field": "message"
}
}
}
}'
If you mean the tokens that have been indexed you can make a terms facet on the message field. Increase the size value in order to get more entries back, or set to 0 to get all terms.
Lucene provides the ability to store the term vectors, but there's no way to have access to it with elasticsearch by now (as far as I know).
Why do you need that? If you only want to check what you're indexing you can have a look at the analyze api.
Nowadays, it's possible with the Term vectors API:
curl http://localhost:9200/twitter/_termvectors/1?fields=message
Result:
{
"_index": "twitter",
"_id": "1",
"_version": 1,
"found": true,
"took": 0,
"term_vectors": {
"message": {
"field_statistics": {
"sum_doc_freq": 4,
"doc_count": 1,
"sum_ttf": 4
},
"terms": {
"elastic": {
"term_freq": 1,
"tokens": [
{
"position": 2,
"start_offset": 11,
"end_offset": 18
}
]
},
"out": {
"term_freq": 1,
"tokens": [
{
"position": 1,
"start_offset": 7,
"end_offset": 10
}
]
},
"search": {
"term_freq": 1,
"tokens": [
{
"position": 3,
"start_offset": 19,
"end_offset": 25
}
]
},
"trying": {
"term_freq": 1,
"tokens": [
{
"position": 0,
"start_offset": 0,
"end_offset": 6
}
]
}
}
}
}
}
Note: Mapping types (here: tweets) have been removed in Elasticsearch 8.x (see migration guide).