Indexing custom properties with Google Cloud Search API - api

We are having issues indexing metadata with items added to Google Cloud Search with 3rd-party datasources, using the REST API. We are following this guide: https://developers.google.com/cloud-search/docs/guides/schema-guide.
What we tried: uploaded the schema successfully ✅, indexed some items ✅, made them available in the search UI ✅, but it seems the properties and facets not showing up whether it’s in the green metadata bits in search results or in API results -- we get an empty structuredData object. Search results seem to only query the content.inlineContent and nothing else. We’re not getting any errors from the API when we make the index request (using https://developers.google.com/cloud-search/docs/reference/rest/v1/indexing.datasources.items/index_). It seems like it’s just ignoring everything in structuredData.
Other attempts: In addition to the REST API, we’ve also used the official nodejs SDK, (which has additional validation because it’s in TypeScript), to no avail. We’ve verified that auth params are correct (it returns 401 otherwise) and that there are no other validation issues (it returns 400 otherwise). Requests return 200 and do upload successfully, just not the structuredData. The original schema upload was also successful (200), and the response included the full schema we gave.
What are we missing?
schema JSON:
{
"objectDefinitions": [{
"name": "page",
"options": {
"displayOptions": {
"objectDisplayLabel": "Page",
"metalines": [
{
"properties": [
{
"propertyName": "title"
},
{
"propertyName": "content"
},
{
"propertyName": "author"
},
{
"propertyName": "siteSection"
}
]
}
]
}
},
"propertyDefinitions": [
{
"name": "title",
"isReturnable": true,
"isWildcardSearchable": true,
"isSuggestable": true,
"isRepeatable": false,
"isFacetable": false,
"textPropertyOptions": {
"operatorOptions": {
"operatorName": "title"
}
},
"displayOptions": {
"displayLabel": "Title"
}
},
{
"name": "content",
"isReturnable": true,
"isRepeatable": false,
"isFacetable": false,
"htmlPropertyOptions": {
"retrievalImportance": {
"importance": "DEFAULT"
}
},
"displayOptions": {
"displayLabel": "Content"
}
},
{
"name": "author",
"isReturnable": true,
"isRepeatable": true,
"isFacetable": true,
"textPropertyOptions": {
"operatorOptions": {
"operatorName": "author"
}
},
"displayOptions": {
"displayLabel": "Author(s)"
}
},
{
"name": "siteSection",
"isReturnable": true,
"isWildcardSearchable": false,
"isSuggestable": false,
"isRepeatable": false,
"isFacetable": true,
"textPropertyOptions": {
"operatorOptions": {
"operatorName": "sitesection"
}
},
"displayOptions": {
"displayLabel": "Site Section"
}
}
]
}]
}
indexing code:
const version = '4';
const apiUrl = `https://cloudsearch.googleapis.com/v1/indexing/datasources/${sourceId}/items/exampleItem:index`;
const title = "Example Item";
const url = "https://example.com";
fetch(apiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
body: JSON.stringify({
item: {
name: `datasource/${sourceId}/items/exampleItem`,
acl: {
readers: [
{
gsuitePrincipal: {
gsuiteDomain: true,
},
},
],
},
metadata: {
title: title,
sourceRepositoryUrl: url,
objectType: "page",
},
structuredData: {
object: {
properties: [
{
name: "title",
textValues: {
values: [title],
},
},
{
name: "author",
textValues: {
values: ["Unknown Author"],
},
},
{
name: "siteSection",
textValues: {
values: ["exampleSection"],
},
},
{
name: "content",
htmlValues: {
values: [exampleContentHTML],
},
},
],
},
},
content: {
inlineContent: b64(exampleContentHTML),
contentFormat: "TEXT",
},
version: b64(version),
itemType: "CONTENT_ITEM",
},
mode: "SYNCHRONOUS",
}),
})
indexing result:
{
"name": "datasources/DATASOURCE_ID/items/exampleItem",
"acl": {
"readers": [{
"gsuitePrincipal": {
"gsuiteDomain": true
}
}]
},
"metadata": {
"title": "Example Item",
"sourceRepositoryUrl": "https://example.com",
"objectType": "page"
},
"structuredData": {
"object": {}
},
"content": {
"inlineContent": "... base 64 encoded content...",
"contentFormat": "TEXT"
},
"version": "NQ==",
"status": {
"code": "ACCEPTED"
},
"queue": "default",
"itemType": "CONTENT_ITEM"
}

Related

Realm sync not working for embedded objects in react native

This is my collection schema on realm -
{
"title": "testnote",
"properties": {
"_id": {
"bsonType": "objectId"
},
"_syncPartition": {
"bsonType": "string"
},
"title": {
"bsonType": "string"
},
"description": {
"bsonType": "string"
},
"subject": {
"bsonType": "string"
},
"tags": {
"bsonType": "array",
"items": {
"bsonType": "string"
}
},
"pages": {
"bsonType": "array",
"items": {
"title": "notespage",
"bsonType": "object",
"properties": {
"type": {
"bsonType": "string"
},
"data": {
"bsonType": "string"
}
}
}
},
"createdBy": {
"bsonType": "string"
}
}
}
This is how I've declared realm schema in react native. The code to initialize the Realm connection is also added below.
static NotesPage = {
name: "notespage",
embedded: true,
properties: {
type: "string",
data: "string"
}
}
static mainSchema = {
name: "testnote",
properties: {
_id: "objectId?",
_syncPartition: "string?",
createdBy: "string?",
title: "string?",
description: "string?",
subject: "string?",
tags: "string[]",
pages: { type: "list", objectType: "notespage" }
},
primaryKey: "_id"
};
const sampleNotesConfig = {
schema: [mainSchema NotesPage],
sync: {
user,
partitionValue: notesPartition,
newRealmFileBehavior: OpenRealmBehaviorConfiguration,
existingRealmFileBehavior: OpenRealmBehaviorConfiguration
}
};
Realm.open(sampleNotesConfig).then((notesRealm) => {
//// Some relevant code
}).catch((reason) => {
console.log("Error initializing realm");
console.log(reason);
});
When I create the realm object, it gets inserted in the local realm file but never gets synced to the server. I don't see any server errors or errors in local.
notesRealm.write(() => {
notesRealm.create(
SampleNote.schema.name,
{
"subject": "History",
"title": "Local to Remote Note 10",
"description": "Local to Remote Note 10 Description",
"tags": ["Tag 1", "Tag 2", "Tag 3"],
"pages": [{"type": "COVER_PAGE", "data": "Data 1"}, { "type": "PARAGRAPH", "data": "Data 2"}]
});
});
I have checked following things -
Partition values are correct.
User has permission to read/write to that partition.
If I just remove the pages section from the schema, the object starts syncing.

How can i filter multiple fields with OR in Backand?

i am passing filter value but its always matches both field where i need optional matches for this
[ { "fieldName": "Firstname", "operator": "equals", "value":
"Megh" }, { "fieldName": "Lastname", "operator": "contains",
"value": "doot" }]
return $http ({
method: 'GET',
url: Backand.getApiUrl() + '/1/objects/users',
params: {
filter: {
"q": {
{
"$or": [ { "firstName": { "$eq": "Megh" } }, { "lastName": {"$like": "doot"} } ]
}
}
}
}
});
You can read further:
http://docs.backand.com/en/latest/apidocs/nosql_query_language/
There is also the "search" parameter that performs a free text search on all the textual fields in the object. Useful when you want to provide a free text search in your UI.
return $http ({
method: 'GET',
url: Backand.getApiUrl() + '/1/objects/users',
params: {
search: "something"
}
});

Complex array at QML

I got in trouble with very simple code:
property var pagesAllModels: {
ru: [
{ title: qsTr("New"), url: "http://bash.im" },
{ title: qsTr("Random"), url: "http://bash.im/random" },
{ title: qsTr("Best"), url: "http://bash.im/best" },
{ title: qsTr("By rating"), url: "http://bash.im/byrating" },
{ title: qsTr("Abyss"), url: "http://bash.im/abyss" },
{ title: qsTr("Abyss top"), url: "http://bash.im/abysstop" },
{ title: qsTr("Abyss best"), url: "http://bash.im/abyssbest" },
],
eng: [
{ title: "Latest", url: "http://bash.org/?latest" },
{ title: "Browse", url: "http://bash.org/?browse" },
{ title: "Random", url: "http://bash.org/?random" },
{ title: "Top", url: "http://bash.org/?top" }
]
}
That code in QML gives me error at line "eng: [" with error "expected lexem ," but in pure javascript everything work fine. What's wrong?
That's because your code is illegal JSON array definition. Web browsers are accepting it because they aren't strict about JS syntax, but QML engine is really strict :
In a key:value pair in associative array, the key must be a string, so it must have quotes around it, else it would be confused with a (non-existing) variable name.
property var pagesAllModels: {
"ru": [
{ "title": qsTr("New"), "url": "http://bash.im" },
{ "title": qsTr("Random"), "url": "http://bash.im/random" },
{ "title": qsTr("Best"), "url": "http://bash.im/best" },
{ "title": qsTr("By rating"), "url": "http://bash.im/byrating" },
{ "title": qsTr("Abyss"), "url": "http://bash.im/abyss" },
{ "title": qsTr("Abyss top"), "url": "http://bash.im/abysstop" },
{ "title": qsTr("Abyss best"), "url": "http://bash.im/abyssbest" }
],
"eng": [
{ "title": "Latest", "url": "http://bash.org/?latest" },
{ "title": "Browse", "url": "http://bash.org/?browse" },
{ "title": "Random", "url": "http://bash.org/?random" },
{ "title": "Top", "url": "http://bash.org/?top" }
]
}
And it works !
Try this
property var pagesAllModels: {
ru: [
{ title: qsTr("New"), url: "http://bash.im" },
{ title: qsTr("Random"), url: "http://bash.im/random" },
{ title: qsTr("Best"), url: "http://bash.im/best" },
{ title: qsTr("By rating"), url: "http://bash.im/byrating" },
{ title: qsTr("Abyss"), url: "http://bash.im/abyss" },
{ title: qsTr("Abyss top"), url: "http://bash.im/abysstop" },
{ title: qsTr("Abyss best"), url: "http://bash.im/abyssbest" }
],
eng: [
{ title: "Latest", url: "http://bash.org/?latest" },
{ title: "Browse", url: "http://bash.org/?browse" },
{ title: "Random", url: "http://bash.org/?random" },
{ title: "Top", url: "http://bash.org/?top" }
]
}
Hope this will resolve your issue
associative arrays separates by semicolon ";"
Try this
property var pagesAllModels: {
ru: [
{ title: qsTr("New"), url: "http://bash.im" },
{ title: qsTr("Random"), url: "http://bash.im/random" },
{ title: qsTr("Best"), url: "http://bash.im/best" },
{ title: qsTr("By rating"), url: "http://bash.im/byrating" },
{ title: qsTr("Abyss"), url: "http://bash.im/abyss" },
{ title: qsTr("Abyss top"), url: "http://bash.im/abysstop" },
{ title: qsTr("Abyss best"), url: "http://bash.im/abyssbest" },
];
eng: [
{ title: "Latest", url: "http://bash.org/?latest" },
{ title: "Browse", url: "http://bash.org/?browse" },
{ title: "Random", url: "http://bash.org/?random" },
{ title: "Top", url: "http://bash.org/?top" }
]
}

How to specify rootProperty for nested data if it is one level below?

I am fetching nested data to be shown as nested list but whenever I tap on top level item, it again shows same top level list instead of showing children list and a ajax request is fired to fetch json data again. Here is the store:
Ext.define('MyTabApp.store.CategoriesStore',{
extend:'Ext.data.TreeStore',
config:{
model : 'MyTabApp.model.Category',
autoLoad: false,
storeId : 'categoriesStore',
proxy: {
type: 'ajax',
url: 'resources/data/catTree.json',
reader: {
type: 'json',
rootProperty: 'data.categories'
}
},
listeners:{
load: function( me, records, successful, operation, eOpts ){
console.log("categories tree loaded");
console.log(records);
}
}
}
});
and here is the data in that file which I am using to mock service:
{
"data":{
"categories": [
{
"name": "Men",
"categories": [
{
"name": "Footwear",
"categories": [
{ "name": "Casual Shoes", "leaf": true },
{ "name": "Sports Shoes", "leaf": true }
]
},
{
"name": "Clothing",
"categories": [
{ "name": "Casual Shirts", "leaf": true },
{ "name": "Ethnic", "leaf": true }
]
},
{ "name": "Accessories", "leaf": true }
]
},
{
"name": "Women",
"categories": [
{ "name": "Footwear", "leaf": true },
{ "name": "Clothing", "leaf": true },
{ "name": "Accessories", "leaf": true }
]
},
{
"name": "Kids",
"categories": [
{
"name": "Footwear",
"categories": [
{ "name": "Casual Shoes", "leaf": true },
{ "name": "Sports Shoes", "leaf": true }
]
},
{ "name": "Clothing", "leaf": true }
]
}
]
}
}
This is the list:
Ext.define('MyTabApp.view.CategoriesList', {
extend: 'Ext.dataview.NestedList',
alias : 'widget.categorieslist',
config: {
height : '100%',
title : 'Categories',
displayField : 'name',
useTitleAsBackText : true,
style : 'background-color:#999 !important; font-size:75%',
styleHtmlContent : true,
listConfig: {
itemHeight: 47,
itemTpl : '<div class="nestedlist-item"><div>{name}</div></div>',
height : "100%"
}
},
initialize : function() {
this.callParent();
var me = this;
var catStore = Ext.create('MyTabApp.store.CategoriesStore');
catStore.load();
me.setStore(catStore);
}
});
The list starts working properly without any ajax request on each tap if I remove data wrapper over top categories array and change rootProperty to categories instead of data.categories. Since server is actually returning categories in data object I cannot remove it so how do I fix the store in that case? Also why is that additional ajax request to fetch the file?
[EDIT]
Tried to create a fiddle http://www.senchafiddle.com/#d16kl which is similar but not same because it is using 2.0.1 and data is not loaded from external file or server.
Last time I had this exact situation, it was because one of my top level category was a leaf but I had not set leaf:true. Doing so recalled the top level of the nested list as if it was a child.
It seems from your Fiddle that if your data is in this following format, it would work fine:
{
"categories" : [{
"name" : "Foo",
"categories" : [{
...
}]
}]
}
That is, just remove the "data" property and make defaultRootProperty: 'categories' & rootProperty: 'categories'. Check this: http://www.senchafiddle.com/#d16kl#tIhTp
It works with external data file as well.

Sencha Touch Maintain Different Stores From Same Json

I would like to be able to maintain different stores from the same json where the model for each store is the same. Each store would need to be updated based on its root property assignment. Please see below for a sample json, store, and model, in which case each store would be updated based on the json's root property value (category 1, category 2, etc.). The goal is to be able to bind a nested list in my application to different stores on the fly, rather than call setProxy to change the url setting on a single store. Also, the json needs to be in this format. Thanks for your help and please let me know if I can provide clarification or answer any questions.
Json:
{
"items": [
{
"name": "category 1",
"status": "",
"displaytext": "",
"items": [
{
"name": "",
"status": "",
"displaytext": "",
"items": [
{
"name": "",
"status": "",
"displaytext": "",
"items": [
{
"name": "",
"status": "",
"displaytext": "",
"leaf": true
}
]
}
]
}
]
},
{
"name": "category 2",
"status": "",
"displaytext": "",
"items": [
{
"name": "",
"status": "",
"displaytext": "",
"items": [
{
"name": "",
"status": "",
"displaytext": "",
"leaf": true
}
]
}
]
},
{
"name": "cateory 3",
"status": "",
"displaytext": "",
"items": []
},
{
"name": "category 4",
"status": "",
"displaytext": "",
"items": []
}
]
}
Model:
Ext.define('MyApp.model.myModel', {
extend: 'Ext.data.Model',
config: {
fields: [
{
name: 'name',
type: 'string'
},
{
name: 'status',
type: 'string'
},
{
name: 'displaytext',
type: 'string'
}
]
}
});
Store 1, 2, 3, etc:
Ext.define('MyApp.store.storeCategory1', {
extend: 'Ext.data.TreeStore',
requires: [
'MyApp.model.myModel'
],
config: {
model: 'MyApp.model.myModel',
storeId: 'myStore',
autoLoad: false,
proxy: {
type: 'ajax',
url: '/path/to/file.json',
reader: {
type: 'json',
rootProperty: 'items'
}
}
}
});
I think you best bet would be to make a server request independent of the Store's proxy. On success, split up the data into the different stores as needed. It's fine to preprocess data this way, especially if you need to split one large data response into multiple data stores. For example:
Ext.Ajax.request({
url: 'path/to/file.json',
success: function(response){
// process server response here
var json = Ext.decode(response.responseText);
for(var i=0, l=json.items.length, i<l; i++){
// start distributing the data to your different stores here
}
}
});
Hope this helps.