Appengine search language - google-app-engine

I'm trying to implement search.FieldLoadSaver interface to be able to pick field language.
func (p *Product) Save() ([]search.Field, error) {
var fields []search.Field
// Add product.ID
fields = append(fields, search.Field{Name: "ID", Value: search.Atom(p.ID)})
// Add product.Name
fields = append(fields, search.Field{Name: "Name", Value: p.Name, Language: "en"})
return fields, nil
}
And i'm getting this error : errors.errorString{s:"search: INVALID_REQUEST: invalid language . Languages should be two letters."}
It seems that python devserver handles empty language field as an error.
EDIT: so the problem was that i was putting multiple fields with the same name and setting language to be empty. It appears that this isn't allowed, so when you're using multiple fields with same name, make sure you're putting language also.

I'm not sure what exactly your question is but here you can see that what you think (It seems that python devserver handles empty language field as an error.) is not true.
Code snippet from that documentation
type Field struct {
// Name is the field name. A valid field name matches /[A-Z][A-Za-z0-9_]*/.
// A field name cannot be longer than 500 characters.
Name string
// Value is the field value. The valid types are:
// - string,
// - search.Atom,
// - search.HTML,
// - time.Time (stored with millisecond precision),
// - float64,
// - appengine.GeoPoint.
Value interface{}
// Language is a two-letter ISO 693-1 code for the field's language,
// defaulting to "en" if nothing is specified. It may only be specified for
// fields of type string and search.HTML.
Language string
// Derived marks fields that were calculated as a result of a
// FieldExpression provided to Search. This field is ignored when saving a
// document.
Derived bool
}
As you can see, if you specify no language, it defaults to "en"
However, as can be seen in the search api source code:
class Field(object):
"""An abstract base class which represents a field of a document.
This class should not be directly instantiated.
"""
TEXT, HTML, ATOM, DATE, NUMBER, GEO_POINT = ('TEXT', 'HTML', 'ATOM', 'DATE',
'NUMBER', 'GEO_POINT')
_FIELD_TYPES = frozenset([TEXT, HTML, ATOM, DATE, NUMBER, GEO_POINT])
def __init__(self, name, value, language=None):
"""Initializer.
In particular This class should not be directly instantiated.
There are some other Field subclasses that you should be using instead. For your current example you should use (assuming that p.id is a string. Otherwise use NumberField)
class TextField(Field):
"""A Field that has text content.
and
class AtomField(Field):
"""A Field that has content to be treated as a single token for indexing.

Related

Make TranslateBehavior and translatable strings work at the same time

What I want
What I want to do is, based on the Accept-Language sent by the user's browser, to translate both the in-code strings (__('translatable string')) and the fields I have configured from tables that have the TranslateBehavior.
What I did
Added
DispatcherFactory::add('LocaleSelector', ['locales' => ['en_US', 'el_GR']]);
in bootstrap.php in order to set the locale automatically, using the Accept-Language that is sent by the user's browser. This works just fine and sets the locale to either en_US or el_GR.
I also have setup i18n (following http://book.cakephp.org/3.0/en/orm/behaviors/translate.html) because I want some fields of a table of mine to be translatable.
Of course, I have strings in my code that do not come from the database and need to be translated. I use the __() function for that.
The problem
Let's say the user, through Accept-Language, requests Greek (el_GR), then the locale will be set to el_GR. The function __() will work out of the box, because it is exactly what it needs.
But, the TranslateBehavior will not work, because it needs gre, not el_GR.
How can I make those 2 work at the same time, while they expect different locale value for the same language?
The Translate Behavior doesn't impose any restrictions on the locale identifier format. The possible values are only limited by the database column type/length.
So, just use en_US and el_GR for your translation table records and you should be good.
After a long discussion and help from #ndm, I took the following measures to make it work:
After doing the appropriate changes, completely clear your tmp directory and update composer. This solved a problem where LocaleSelector, while it was setting the appropriate locale, my database wasn't being translated.
There is the problem that the Accept-Language header can have many values. You want many of them to be matched with a specific locale. That way you will be able to have the same locale for everything, both for your database and translatable strings. In order to solve this problem, I made my own LocaleSelectorFilter and I placed it in src/Routing/Filter. It overrides the default LocaleSelectorFilter:
namespace Cake\Routing\Filter;
use Cake\Event\Event;
use Cake\I18n\I18n;
use Cake\Routing\DispatcherFilter;
use Locale;
class LocaleSelectorFilter extends DispatcherFilter
{
protected $_locales = [];
public function __construct($config = [])
{
parent::__construct($config);
if (!empty($config['locales'])) {
$this->_locales = $config['locales'];
}
}
private function matchLocaleWithConfigValue($localeFromHeader)
{
foreach ($this->_locales as $locale => $acceptableValues) {
// $acceptableValues is either an array or a single string
if (!$locale) {
// single string
if ($localeFromHeader === $acceptableValues) {
return $acceptableValues;
}
} else {
// array
foreach ($acceptableValues as $acceptableValue) {
if ($localeFromHeader === $acceptableValue) {
return $locale;
}
}
}
}
return false;
}
public function beforeDispatch(Event $event)
{
$request = $event->data['request'];
$locale = Locale::acceptFromHttp($request->header('Accept-Language'));
if (!$locale) {
// no locale set in the headers
return;
}
if (empty($this->_locales)) {
// any locale value allowed
I18n::locale($locale);
return;
}
// search whether the requested language is in the accepted ones
$localeConfigMatch = $this->matchLocaleWithConfigValue($locale);
if (!$localeConfigMatch) {
// no locale matches the header, leave the default one
return;
}
// match was found, switch locale
I18n::locale($localeConfigMatch);
}
}
It is used like this, inside bootstrap.php:
DispatcherFactory::add('LocaleSelector', ['locales' => ['el_GR' => ['el', 'el_GR', 'el-GR'], 'en_US']]);
In the above example, all the values el, el_GR and el-GR of the Accept-Language will result to the locale el_GR being set. Also, if Accept-Language has the en_US value, it will be set. So it supports both many different values to be set to one specific locale and it also supports the default LocaleSelector behavior.
Your i18n table's locale column must be set to 'el_GR' (in this example). This is necessary, because it is what the in-code translatable strings expect (the ones using the __() function). So by setting 'el_GR' (instead of 'gre', mentioned in the documentation) you will have both the in-code translatable strings and database translatable fields work out of the box.
That's it!

Mongoid 3.1 eager loading, json, and field names

Recently updated to Mongoid 3.1 from 3.0.3 and this resulted in some broken code and confusion on my side.
Say you have a pair of classes with a belongs_to/has_many relationship, like so:
class Band
include Mongoid::Document
field :name, type: String
has_many :members, :autosave => true
end
class Member
include Mongoid::Document
field :name, type: String
belongs_to :band
end
Saving all this to the database like so:
b = Band.new
b.name = "Sonny and Cher"
b.members << Member.new(name: "Sonny")
b.members << Member.new(name: "Cher")
b.save
I would in my API, be able to return a 'member' object like so:
m = Member.where(name: "Sonny").first
m.to_json
which yields the following, as expected:
{"_id":"<removed>","band_id":"5151d89f5dd99dd9ec000002","name":"Sonny"}
My client can request the full band object with a subsequent call if it wants to. However, in some cases I DO want to include the referenced item directly. With 3.0.3, I would just do the following:
m = Member.where(name: "Sonny").first
m[:band] = m.band
m.to_json
and this would add a new field with the full band information to it. With 3.1, however (it may have started in earlier versions, but I didn't test), I now get this:
{"_id":"<removed>","band_id":{"_id":"5151dc025dd99d579e000002","name":"Sonny and Cher"},"name":"Sonny"}
So, it looks like the band info has been eager-loaded into the field? Why is it stored under the key ':band_id' and not ':band'? I guess ':band' is protected, but I still don't think the data should be stored under the ':band_id' key. I suspect I am missing something here. Any ideas?
You can specify an :include option for to_json like so:
m.to_json(include: :band)
The JSON will then have a key band with the Band object converted to JSON and band_id will still be present.

Merge multiple columns in bulkloader

I'm using app engine's bulkloader to import a CSV file into my datastore. I've got a number of columns that I want to merge into one, for example they're all URLs, but not all of them are supplied and there is a superseding order, eg:
url_main
url_temp
url_test
I want to say: "Ok, if url_main exists, use that, otherwise user url_test and then use url_temp"
Is it, therefore, possible to create a custom import transform that references columns and merges them into one based on conditions?
Ok, so after reading https://developers.google.com/appengine/docs/python/tools/uploadingdata#Configuring_the_Bulk_Loader I learnt about import_transform and that this can use custom functions.
With that in mind, this pointed me the right way:
... a two-argument function with the keyword argument bulkload_state,
which on return contains useful information about the entity:
bulkload_state.current_entity, which is the current entity being
processed; bulkload_state.current_dictionary, the current export
dictionary ...
So, I created a function that handled two variables, one would be the value of the current entity and the second would be the bulkload_state that allowed me to fetch the current row, like so:
def check_url(value, bulkload_state):
row = bulkload_state.current_dictionary
fields = [ 'Final URL', 'URL', 'Temporary URL' ]
for field in fields:
if field in row:
return row[ field ]
return None
All this does is grab the current row (bulkload_state.current_dictionary) and then checks which URL fields exist, otherwise it just returns None.
In my bulkloader.yaml I call this function simply by setting:
- property: business_url
external_name: URL
import_transform: bulkloader_helper.check_url
Note: the external_name doesn't matter, as long as it exists as I'm not actually using it, I'm making use of multiple columns.
Simples!

Salesforce Custom Object Relationship Creation

I want to create two objects and link them via a parent child relationship in C# using the Metadata API.
I can create objects and 'custom' fields for the objects via the metadata, but the service just ignores the field def for the relationship.
By snipet for the fields are as follows:
CustomField[] fields = new CustomField[] { new CustomField()
{
type = FieldType.Text,
label = "FirstName",
length = 50,
lengthSpecified = true,
fullName = "LJUTestObject__c.FirstName__c"
},
new CustomField()
{
type = FieldType.Text,
label = "LastName",
length = 50,
lengthSpecified = true,
fullName = "LJUTestObject__c.Lastname__c"
},
new CustomField()
{
type = FieldType.Text,
label = "Postcode",
length = 50,
lengthSpecified = true,
fullName = "LJUTestChildObject__c.Postcode__c"
},
new CustomField()
{
type = FieldType.MasterDetail,
relationshipLabel = "PostcodeLookup",
relationshipName = "LJUTestObject__c.LJUTestObject_Id__c",
relationshipOrder = 0,
relationshipOrderSpecified = true,
fullName = "LJUTestChildObject__c.Lookup__r"
}
};
The parent object looks like:
LJUTestObject
ID,
FirstName, Text(50)
LastName, Text(50)
The child objext looks like:
LJUTestChildObject
ID,
Postcode, Text(50)
I want to link the parent to the child so one "LJUTestObject", can have many "LJUTestChildObjects".
What values do I need for FieldType, RelationshipName, and RelationshipOrder to make this happen?
TL;DR:
Use this as a template for accomplishing what you want:
var cf = new CustomField();
cf.fullName = "ChildCustomObject__c.ParentCustomField__c";
cf.type = FieldType.MasterDetail;
cf.typeSpecified = true;
cf.label = "Parent Or Whatever You Want This To Be Called In The UI";
cf.referenceTo = "ParentCustomObject__c";
cf.relationshipName = "ParentOrWhateverYouWantThisToBeCalledInternally";
cf.relationshipLabel = "This is an optional label";
var aUpsertResponse = smc.upsertMetadata(metadataSession, null, null, new Metadata[] { cf });
The key difference:
The natural temptation is to put the CustomField instances into the fields array of a CustomObject, and pass that CustomObject to the Salesforce Metadata API. And this does work for most data fields, but it seems that it does not work for relationship fields.
Instead, pass the CustomField directly to the Salesforce Metadata API, not wrapped in a CustomObject.
Those muted errors:
Turns out that errors are occurring, and the Salesforce Metadata API knows about them, but doesn't bother telling you about them when they occur for CustomFields nested inside a CustomObject.
By passing the CustomField directly to the Metadata API (not wrapped in a CustomObject), the call to upsertMetadata will still return without an exception being thrown (as it was already doing for you), but this time, if something goes wrong, upsertResponse[0].success will be false instead of true, and upsertResponse[0].errors will give you more information.
Other gotchas
Must specify referenceTo, and if it doesn't match the name of an existing built-in or custom object, the error message will be the same as if you had not specified referenceTo at all.
fullName should end in __c not __r. __r is for relationship names, but remember that fullName is specifying the field name, not the relationship name.
relationshipName - I got it working by not including __r on the end, and not including the custom object name at the start. I haven't tested to be sure other ways don't work, but be aware that at the very least, you don't need to have those extra components in the relationshipName.
Remember generally that anything with label in its name is probably for display to users in the UI, and thus can have spaces in it to be nicely formatted the way users expect.
Salesforce... really???
(mini rant warning)
The Salesforce Metadata API is unintuitive and poorly documented. That's why you got stuck on such a simple thing. That's why no-one knew the answer to your question. That's why, four years later, I got stuck on the same thing. Creating relationships is one of the main things you would want to do with the Salesforce Metadata API, and yet it has been this difficult to figure out, for this long. C'mon Salesforce, we know you're a sales company more than a tech company, but you earn trazillions of dollars and are happy to show it off - invest a little more in a better API experience for the developers who invest in learning your platform.
I've not created these through the meta data API like this myself, but I'd suggest that:
relationshipName = "LJUTestObject__c.LJUTestObject_Id__c
Should be:
relationshipName = "LJUTestObject__c.Id
as Id is a standard field, the __c suffix is only used for custom fields (not standard fields on custom objects). Also, it may be that the relationship full name should end in __c not __r, but try the change above first and see how you go.
SELECT
Id,
OwnerId,
WhatId,
Reminder_Date_Time__c,
WhoId,
Record_Type_Name__c,
Task_Type__c,
Assigned_Date__c,
Task_Status__c,
ActivityDate,
Subject,
Attended_By__c,
Is_Assigned__c
FROM Task
WHERE
(NOT Task_Status__c LIKE 'Open') AND
ActivityDate >= 2017-12-13 AND
(NOT Service__r.Service_State__c LIKE 'Karnataka')

Django admin: Format fields in list, but keep sortable?

I keep numeric fields like "size", "width", "height" in my database. Now I would attach units like "KiB" or "pixels" to them when showing them in the change list. This could easily be achieved by adding callables such as "size_formatted" etc to list_display. However, these are no longer sortable.
Is there a way around this limitation?
Read here - ModelAdmin.list_display (read a lot to get to the point ;) )
you need to add admin_order_field attribute to your function
class YourAdminCLass(admin.ModelAdmin)
[...]
def size_formatted(self, obj):
return "whatever you need"
size_formatted.admin_order_field = 'size'

Resources