Auto-suggest entities using the Wikipedia API - semantic-web

I want to provide an auto-suggest feature to my users, where they can choose from a list of known "things" from a semantic entity database.
I'm looking at using the Wikipedia Media API instead of setting up my own:
https://www.mediawiki.org/wiki/API:Main_page
There is an API tool for testing requests:
https://www.mediawiki.org/wiki/Special:ApiSandbox
For example if a user likes cats:
https://www.wikidata.org/wiki/Q146
The requests would be:
https://en.wikipedia.org/w/api.php?action=query&format=jsonfm&prop=pageterms&list=&meta=&titles=C
https://en.wikipedia.org/w/api.php?action=query&format=jsonfm&prop=pageterms&list=&meta=&titles=Ca
https://en.wikipedia.org/w/api.php?action=query&format=jsonfm&prop=pageterms&list=&meta=&titles=Cat
The user would select Cat from the dropdown, and I would save the ID.
Is this a good approach? How could I improve it?

I managed to load autosuggestions using the Wikipedia REST api:
https://en.wikipedia.org/w/api.php?action=query&format=json&gsrlimit=15&generator=search&origin=*&gsrsearch=
The Angular code:
this.resultsCtrl = new FormControl();
this.resultsCtrl.valueChanges
.debounceTime(400)
.subscribe(name => {
if (name) {
this.filteredResults = this.filterResults(name);
}
});
filterResults(name: string) {
return Observable.create(obs => {
const displaySuggestions = function (response) {
if (!response) {
obs.error(status);
} else {
obs.next(Object.keys(response.query.pages).map(key => response.query.pages[key]));
obs.complete();
}
};
this.http.get('https://en.wikipedia.org/w/api.php?action=query&format=json&gsrlimit=15&generator=search&origin=*&gsrsearch='
+ encodeURI(name))
.subscribe(displaySuggestions);
});
}

Related

How to remove irrelevant languages for categories which are assigned to a sales channel?

We set up a Shopware 6.4.16.0 site for Germany with the languages de_DE and en_GB.
Now we want to setup an American sales channel with only English content.
We selected only English as a language for that sales channel, duplicated the category tree and assigned the new root category to the American sales channel.
Sales channel details look like this:
Unfortunately, in the categories all languages are selectable, which is confusing:
Should the irrelevant languages be hidden or is this working as intended. I did not find any GitHub issue for this yet. Or is this intended?
Probably we need to extend the admin UI to determine which languages are relevant for a (sub)category (via the sales channel assignment of the root category) and hide the irrelevant languages? What is a good starting point for this?
The language dropdown in your second screenshot in fact works as expected. The languages are globally.
In theory there are two different translations systems inside shopware:
Content translation:
Every entity that you can edit in the admin in different languages uses this translation mechanism. The values in the different languages are stored in the DB and the correct value will be read according to the language in the context. This translation system is used in the second screenshot for the categories.
UI translation:
This translation system is used for the "snippet" system in the storefront, where you provide translations for text snippets that are displayed in the storefront. This is what your first screenshot shows.
Both translation systems are only loosely coupled in a way that the content will be displayed with the same locale as the rest of the UI translations whereever content from the DB is displayed in the storefront.
With the admin extension of a plugin you could override both of the corresponding components and restrict the criteria for fetching the available languages in the language switcher.
const { Component } = Shopware;
const { Criteria } = Shopware.Data;
Component.override('sw-language-switch', {
props: {
salesChannelIds: {
type: Array,
default: () => {
return [];
},
},
},
computed: {
languageCriteria() {
if (this.salesChannelIds.length) {
const criteria = this.$super('languageCriteria');
criteria.addFilter(Criteria.equalsAny(
'salesChannels.id',
this.salesChannelIds
));
return criteria;
}
return this.$super('languageCriteria');
}
}
});
Component.override('sw-category-detail', {
template: `{% block sw_category_language_switch %}
<sw-language-switch
:key="rootCategory?.id"
:sales-channel-ids="salesChannelIds"
:save-changes-function="saveOnLanguageChange"
:abort-change-function="abortOnLanguageChange"
:disabled="landingPageId === 'create'"
#on-change="onChangeLanguage"
/>
{% endblock %}`,
data() {
return {
rootCategoryId: null,
rootCategory: null,
};
},
computed: {
salesChannelIds() {
if (!this.rootCategory) {
return [];
}
return this.rootCategory.navigationSalesChannels.map((salesChannel) => {
return salesChannel.id;
});
}
},
watch: {
category(category) {
if (!category || !category.path) {
this.rootCategoryId = null;
return;
}
const parentIds = category.path.split('|');
this.rootCategoryId = parentIds[1];
},
rootCategoryId(rootCategoryId) {
if (!rootCategoryId) {
this.rootCategory = null;
return;
}
const criteria = new Criteria();
criteria.addAssociation('navigationSalesChannels');
this.categoryRepository.get(rootCategoryId, Shopware.Context.api, criteria)
.then((rootCategory) => {
this.rootCategory = rootCategory;
});
},
},
});
You could still tweak this a little. For example when you click on a category in the tree that won't feature the currently selected language, you could automatically switch to the relevant language instead. For now this example just restricts the options in the dropdown on category-by-category basis.

GraphQL stitch and union

I have a need to 'aggregate' multiple graphQl services (with same schema) into single read-only (query only) service exposing data from all services. For example:
---- domain 1 ----
"posts": [
{
"title": "Domain 1 - First post",
"description": "Content of the first post"
},
{
"title": "Domain 1 - Second post",
"description": "Content of the second post"
}
]
---- domain 2 ----
"posts": [
{
"title": "Domain 2 - First post",
"description": "Content of the first post"
},
{
"title": "Domain 2 - Second post",
"description": "Content of the second post"
}
]
I understand that 'stitching' is not meant for UC's like this but more to combine different micro-services into same API. In order to have same types (names) into single API, I implemented 'poor man namespaces' by on-the-fly' appending domain name to all data types. However, I'm able only to make a query with two different types like this:
query {
domain_1_posts {
title
description
}
domain_2_posts {
title
description
}
}
but, it results with data set consist out of two arrays:
{
"data": {
"domain_1_posts": [
{ ...},
],
"domain_2_posts": [
{ ...},
]
}
}
I would like to hear your ideas what I can do to combine it into single dataset containing only posts?
One idea is to add own resolver that can call actual resolvers and combine results into single array (if that is supported at all).
Also, as a plan B, I could live with sending 'domain' param to query and then construct query toward first or second domain (but, to keep initial query 'domain-agnostic', e.g. without using domain namses in query itself?
Thanks in advance for all suggestions...
I manage to find solution for my use-case so, I'll leave it here in case that anyone bump into this thread...
As already mentioned, stitching should be used to compose single endpoint from multiple API segments (microservices). In case that you try to stitch schemas containing same types or queries, your request will be 'routed' to pre-selected instance (so, only one).
As #xadm suggested, key for 'merging' data from multiple schemas into singe data set is in using custom fetch logic for Link used for remote schema, as explained:
1) Define custom fetch function matching your business needs (simplified example):
const customFetch = async (uri, options) => {
// do not merge introspection query results!!!
// for introspection query always use predefined (first?) instance
if( operationType === 'IntrospectionQuery'){
return fetch(services[0].uri, options);
}
// array fecth calls to different endpoints
const calls = [
fetch(services[0].uri, options),
fetch(services[1].uri, options),
fetch(services[2].uri, options),
...
];
// execute calls in parallel
const data = await Promise.all(fetchCalls);
// do whatever you need to merge data according to your needs
const retData = customBusinessLogic();
// return new response containing merged data
return new fetch.Response(JSON.stringify(retData),{ "status" : 200 });
}
2) Define link using custom fetch function. If you are using identical schemas you don't need to create links to each instance, just one should be enough.
const httpLink = new HttpLink(services[0].uri, fetch: customFetch });
3) Use Link to create remote executable schema:
const schema = await introspectSchema(httpLink );
return makeRemoteExecutableSchema({
schema,
link: httpLink,
context: ({ req }) => {
// inject http request headers into context if you need them
return {
headers: {
...req.headers,
}
}
},
})
4) If you want to forward http headers all the way to the fetch function, use apollo ContextLink:
// link for forwarding headers through context
const contextLink = setContext( (request, previousContext) => {
if( previousContext.graphqlContext ){
return {
headers: {
...previousContext.graphqlContext.headers
}
}
}
}).concat(http);
Just to mention, dependencies used for this one:
const { introspectSchema, makeRemoteExecutableSchema, ApolloServer } = require('apollo-server');
const fetch = require('node-fetch');
const { setContext } = require('apollo-link-context');
const { HttpLink } = require('apollo-link-http');
I hope that it will be helfull to someone...

error handling in angular 5, catch errors from backend api in frontend

I need advise for handling errors in front-end of web application.
When I call a service to get the community according to community in web app, I want it to catch an error. For example for catching errors like 404.
There is a service for getting community according to id provided.
getCommunity(id: number) {
return this.http.get(`${this.api}/communities/` + id + ``);
}
that is called in events.ts file
setCommunityBaseUrl() {
this.listingService.getCommunity(environment.communityId).subscribe((data: any) => {
this.communityUrl = data.url + `/` + data.domain;
});
}
The id is provided in environment. Let's say there are 20 communities in total. When I provide id = 1 the events according to community = 1 appears.
export const environment = {
production: ..,
version: 'v2',
apiUrl: '...',
organization: '...',
websiteTitle: '...',
communityId: 1,
googleMapsApiKey: '...'
};
The problem is that when I provide id = null all community events are occurring | all events list in the backend is occurring.
Please, help ^^
When you subscribe you subscribe with an Observer pattern. So the first function you pass in
.subscribe(() => {} );
fires when the Observable calls .next(...)
and after that you can provide another function which will fire whenever the Observable calls .error(...)
so
.subscribe(() => {}, (error) => { handleTheError(error); } );
The this.http.get(...); returns an Observable which will fire the .error(...) on http error
We also know that this.http.get(...) completes or "errors" and it's not an endless one (a one that never completes). So you can make it a promise and manipulate on it promise like.
async getMeSomething(...) {
try {
this.mydata = await this.http.get(...).toPromise();
}
catch(error) {
handleTheError(error)
}
}
But what I really recommend is to use Swagger for your backend and then generate the API Client class with NSwagStudio so you don't have to write the client manually or adjust it or deal with error catching. I use it all the time and it saves us an enormous amount of time
Because you are using ".subscribe" you can create your own error handler and catch the errors like this, directly on the method.
This is an example on how you can use this:
constructor(
private _suiteAPIService: SuitesAPIService,
private _testcaseService: TestcaseService,
public _tfsApiService: TfsApiService,
private _notificationService: NotificationService) { }
errorHandler(error: HttpErrorResponse) {
return observableThrowError(error.message || "Server Error")
}
public something = "something";
GetTestcasesFromSuiteSubscriber(Project, BuildNumber, SuiteId) {
this._suiteAPIService.GetTestResults(Project, BuildNumber, SuiteId).subscribe(
data => {
console.log(data);
this._testcaseService.ListOfTestcases = data;
//Notofication service to get data.
this._notificationService.TestcasesLoaded();
},
error => {
//Here we write som error
return this.something;
}
);
}

How to get a collection from firestore using data from another collection in react-Native.....What Am i Doing Wrong?

I have tried searching everywhere, from stackoverflow to GitHub but i can get a solution. I am trying to get list of users by using their userid that I get from a collection of businesses. What Am i doing wrong?
componentWillMount() {
//Loading all the business collections.
firebase.firestore().collection("business").onSnapshot((snapshot) => {
var bizs = [];
snapshot.forEach((bdt) => {
var userdt = [];
//get document id of a certain user in the business collections
firebase.firestore().collection('users').where("userid", "==", bdt.data().userid).get()
.then((snap) => {
snap.forEach(dc => {
//loading details of the user from a specific ID
firebase.firestore().collection("users").doc(dc.id).onSnapshot((udt) => {
userdt.push({
name: udt.data().fullname,
photourl: udt.data().photoURL,
location: bdt.data().location,
openhrs: bdt.data().openHrs,
likes: '20',
reviews: '3002',
call: bdt.data().contacts
});
console.log(userdt); //this one works
})
console.log(userdt); // but this one doesnt diplay anything just []
})
}).catch((dterr) => {
console.log(dterr)
})
});
this.setState({bizdata: bizs,loading: false
});
});
}
I am using react-native and firestore
Put log with some number,
like,
console.log('1',userdt);
console.log('2',userdt);
and check weather which one is appearing first, Maybe '2' is executing before updating the data

Apply filter to API response - vue.js

I have this method to get data from an API, which sends me information of many furniture pieces:
loadPieces() {
this.isLoading = true;
axios.get(this.galleryRoute)
.then(r => {
this.gallery = r.data;
this.isLoading = false;
})
.catch(error => {
this.$nextTick(() => this.loadPieces());
});
console.log(this.galleryRoute);
},
This is a part of the response I get, which represents only one piece:
[[{"id":266,"name":" Tray 7x45x32, white stained ash","thumbnail":{"width":840,"height":840,"urls":{"raw":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a.jpeg","small":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a#140.jpeg","medium":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a#420.jpeg"}}},
Now I want to create a filter so that I can get a specific piece from the JSON object, using it's id. I've tried searching but so far I have no idea how to do this.
Thanks in advance!
Add a computed property which applies the filter to this.gallery:
computed: {
filteredGallery() {
if (!this.gallery) return []; // handle gallery being unset in whatever way
return this.gallery.filter(picture =>
// some reason to show picture
);
}
}
I'm assuming gallery is an array, but you could apply a similar technique to it if it was an object, using e.g. Object.keys(this.gallery).
Then in your template, use filteredGallery instead of gallery.