Adding prefixes to JSON-LD context - fuseki

I was playing with fuseki and JSON-LD and noticed that fuseki removes prefixes from the attributes in the JSON-LD context. Example of JSON-LD context after been loaded from fuseki:
{
"#context": {
"hasPriceSpecification": {
"#id": "http://purl.org/goodrelations/v1#hasPriceSpecification",
"#type": "#id"
},
"acceptedPaymentMethods": {
"#id": "http://purl.org/goodrelations/v1#acceptedPaymentMethods",
"#type": "#id"
},
"includes": {
"#id": "http://purl.org/goodrelations/v1#includes",
"#type": "#id"
},
"page": {
"#id": "http://xmlns.com/foaf/0.1/page",
"#type": "#id"
},
"foaf": "http://xmlns.com/foaf/0.1/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"pto": "http://www.productontology.org/id/",
"gr": "http://purl.org/goodrelations/v1#"
}
}
Is it possible to return prefixed context and JSON-LD from fuseki?
Optionally returned JSON-LD can be formatted back to prefixied form with javascript by writing new context with the prefixes eg. gr:hasPriceSpecification. Is somehow possible to create prefixed context from this one using JSON-LD javascript library?

Well .. i did a simple function that does the latter:
function newPrefixedContext(oldContext) {
var namespaces = [];
var newContext = {};
for(var res in oldContext) {
if(typeof oldContext[res] === 'string') {
var char = oldContext[res].charAt(oldContext[res].length-1);
if(char=="#" || char=="/") {
var o = {prefix:res, namespace:oldContext[res]};
namespaces.push(o);
newContext[res] = oldContext[res];
}
}
}
// Loop context and adds prefixed property names to newContext
for(var res in oldContext) {
if(typeof oldContext[res] != undefined) {
for(var n in namespaces) {
if(oldContext[res]["#id"]) {
if(oldContext[res]["#id"].indexOf(namespaces[n].namespace) == 0) {
newContext[namespaces[n].prefix+":"+res] = oldContext[res];
break;
}
} else {
if(namespaces[n].namespace!==oldContext[res] && oldContext[res].indexOf(namespaces[n].namespace) == 0) {
newContext[namespaces[n].prefix+":"+res] = oldContext[res];
break;
}
}
}
}
}
return newContext;
}
You can use it with JSON-LD javascript library to transform the JSON-LD to prefixed format like:
jsonld.compact(jsonLD, newPrefixedContext(jsonLD["#context"]), function(err, compacted) {
console.log(JSON.stringify(compacted, null, 2));
});

Related

Vue doesn't update when computed data change

Context: I have a list of posts with tags, categories from wordpress api. I display these posts with Vue and using computed with a search box to filter the result based on titre, description, tags, and categories
Problem: I am trying to update a computed list when user click on a list of tag available. I add the get and set for computed data like this:
var vm = new Vue({
el: '#blogs',
data: {
search: '',
posts: [],
filterPosts: []
},
beforeMount: function() {
// It should call the data and update
callData();
},
computed: {
filterPosts: {
get: function() {
var self = this;
return self.posts.filter(function(post){
var query = self.search.toLowerCase();
var title = post.title.toLowerCase();
var content = post.content.toLowerCase();
var date = post.date.toLowerCase();
var categories = '';
post.categories.forEach(function(category) {
categories += category.name.toLowerCase();
});
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
return title.indexOf(query) !== -1 ||content.indexOf(query) !== -1 || date.indexOf(query) !== -1 || categories.indexOf(query) !== -1 || tags.indexOf(query) !== -1;
});
},
set: function (newValue) {
console.log(newValue);
this.filterPosts = Object.assign({}, newValue);
}
}
},
methods: {
filterByTag: function(tag, event) {
event.preventDefault();
var self = this;
self.filterPosts = self.posts.filter(function(post){
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
return tags.indexOf(tag.toLowerCase()) !== -1;
});
}
}
}); // Vue instance
The console.log always output new data based on the function I wrote on methods but Vue didn't re-render the view. I think I didn't do the right way or thought like Vue. Could you please give some insight?
Edit 1
Add full code.
I tried to add filterPosts in data but I received this error from Vue: The computed property "filterPosts" is already defined in data.
Your setter is actually not setting anything, it only logs the new value. You need to store it somewhere.
For example you can store it in the component's data:
data: {
value: 'foo',
},
computed: {
otherValue: {
get() { /*...*/ },
set(newVal) { this.value = newVal },
},
},
But this is definitely not the only possibility, if you use Vuex, the setter can dispatch an action that will then make the computed value get updated. The component will eventually catch the update and show the new value.
computed: {
value: {
get() {
return this.$store.getters.externalData;
},
set(newVal) {
return this.$store.dispatch('modifyingAction', newVal);
},
},
},
The bottomline is you have to trigger a data change in the setter, otherwise your component will not be updated nor will it trigger any rerender.
EDIT (The original answer was updated with full code):
The answer is that unless you want to manually change the list filteredPosts without altering posts, you don't need a get and set function for your computed variable. The behaviour you want can be acheived with this:
const vm = new Vue({
data() {
return {
search: '',
posts: [],
// these should probably be props, or you won't be able to edit the list easily. The result is the same anyway.
};
},
computed: {
filteredPosts() {
return this.posts.filter(function(post) {
... // do the filtering
});
},
},
template: "<ul><li v-for='post in filteredPosts'>{{ post.content }}</li></ul>",
});
This way, if you change the posts or the search variable in data, filteredPosts will get recomputed, and a re-render will be triggered.
After going around and around, I found a solution, I think it may be the right way with Vue now: Update the computed data through its dependencies properties or data.
The set method didn't work for this case so I add an activeTag in data, when I click on a tag, it will change the activeTag and notify the computed filterPost recheck and re-render. Please tell me if we have another way to update the computed data.
var vm = new Vue({
el: '#blogs',
data: {
search: '',
posts: [],
tags: [],
activeTag: ''
},
beforeMount: function() {
// It should call the data and update
callData();
},
computed: {
filterPosts: {
get: function() {
var self = this;
return self.posts.filter(function(post){
var query = self.search.toLowerCase();
var title = post.title.toLowerCase();
var content = post.content.toLowerCase();
var date = post.date.toLowerCase();
var categories = '';
post.categories.forEach(function(category) {
categories += category.name.toLowerCase();
});
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
var activeTag = self.activeTag;
if (activeTag !== '') {
return tags.indexOf(activeTag.toLowerCase()) !== -1;
}else{
return title.indexOf(query) !== -1 ||content.indexOf(query) !== -1 || date.indexOf(query) !== -1 || categories.indexOf(query) !== -1 || tags.indexOf(query) !== -1;
}
});
},
set: function (newValue) {
console.log(newValue);
}
}
},
methods: {
filterByTag: function(tag, event) {
event.preventDefault();
var self = this;
self.activeTag = tag;
}
}
}); // Vue instance
Try something like:
data: {
myValue: 'OK'
},
computed: {
filterPosts: {
get: function () {
return this.myValue + ' is OK'
}
set: function (newValue) {
this.myValue = newValue
}
}
}
More:
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

Can I group methods in Meteor?

I'm starting with Meteor, and want to organize my methods...
By example, I have 2 collecations: 'a' and 'b', both have the insert method.. I want to do something like that:
Meteor.methods({
a: {
insert : function(){
console.log("insert in Collection a");
}
},
b: {
insert : function(){
console.log("insert in Collection b");
}
}
});
And then call
Meteor.call('a.insert');
It's possible to do this? Or how can I organize my methods?
I don't wanna make methods like: 'insertA' and 'insertB'
You could use this syntax :
Meteor.methods({
"a.insert": function(){
console.log("insert in Collection a");
}
"b.insert": function(){
console.log("insert in Collection b");
}
});
Which allows you to do Meteor.call("a.insert");.
Building on saimeunt's idea, you could also add a helper function to your code if you are concerned with the elegance of these groups in your code. Then you can use the notation you liked and even nest groups arbitrarily:
var methods = {
a: {
insert : function(){
console.log("insert in Collection a");
}
},
b: {
insert : function(){
console.log("insert in Collection b");
},
b2: {
other2: function() {
console.log("other2");
},
other3: function() {
console.log("other3");
}
}
},
};
function flatten(x, prefix, agg) {
if (typeof(x) == "function") {
agg[prefix] = x;
} else {
// x is a (sub-)group
_.each(x, function(sub, name) {
flatten(sub, prefix + (prefix.length > 0 ? "." : "") + name, agg);
});
}
return agg;
}
Meteor.methods(flatten(methods, "", {}));
You would then call with the dot notation, like:
Meteor.call('b.b2.other2');

Meteor.js : SearchSource + Publish composite

I'm currently creating a research engine for my app.
Until now, I used Publish composite + iron router : The user could had filters to search for some specific set of users.
Now, I want him to be able to look for some keywords too. For that I downloaded and tested the great SearchSource package.
The problem is that the SearchSource server side definition only seems to allow to return one cursor.
How could I combine the two logics ? Even if it's tricky, please, share.
Of course I could make an autorunned subscription where I look for every users loaded on the client and then subscribe to the additionnal documents, but it is not really the most performant and beautifull thing to do.
Some data :
Here is my current Publish Composite for filters :
Meteor.publishComposite("findTalkers", function(page, langs){
//console.log("Find Talkers");
//console.log("page : " + page);
//console.log("langs : " + langs);
if (langs.length)
{
return ({
find: function()
{
if (langs && langs.length)
{
var test = {$in: langs};
preSelectedUsers = [],
selector = {
_id: {$ne: this.userId},
"profile.completed": true,
"profile.firstName": {$exists: true},
"profile.languages.native": {$exists: false},
"profile.languages.lang": test
};
Counts.publish(this, "nbUsers", Meteor.users.find(selector, {
fields: {_id: 1}
}), {noReady: false, nonReactive: true});
if (page > 1)
{
preSelectedUsers = Meteor.users.find(selector, {
sort: {'profile.talkname': 1},
limit: 25,
skip: (25 * (page || 1)),
fields: {_id: 1}
}).fetch();
var i = -1;
while (preSelectedUsers[++i])
preSelectedUsers[i] = preSelectedUsers[i]._id;
}
if (page > 1)
selector._id = {$in: preSelectedUsers};
return Meteor.users.find(selector, {
fields: userFields,
sort: {'profile.talkname': 1},
limit: 25
});
}
},
children: [
{
// Finding user's profile picture if it is not url
find: function(user)
{
if (user && user.profile && user.profile.avatar.type != "url")
return Images.find({_id: user.profile.avatar.data}, {sort: {uploadedAt: -1}, limit: 1});
}
}
]
});
}
else
{
return ({
find: function()
{
return Meteor.users.find({_id: "flush"});
}
});
}
});
Here is my research with SearchSource :
Client :
var searchOptions = {
keepHistory: 1000 * 60 * 5,
localSearch: true
},
SearchSources = {
talkersSearch: new SearchSource('users', ['profile.talkname'], searchOptions)
};
Router.map(function(){
this.route('talkers/:page?',
{
template: "talkers",
onBeforeAction: function(pause){
(Meteor.user() && Meteor.user().profile.completed)
? this.next()
: this.render('/profile');
},
waitOn: function(){
var filters = MatesFilter.find().fetch(),
i = -1;
while (filters[++i])
filters[i] = filters[i].value;
if (filters.length)
{
return Meteor.subscribe("findTalkers", (this.params.page || 1), filters, function(){
Session.set('numberuser', Counts.get("nbUsers"));
});
}
return Meteor.subscribe('myself');
}
});
}
Template.talkers.helpers({
getPackages: function() {
return SearchSources.talkersSearch.getData({
transform: function(matchText, regExp) {
return matchText.replace(regExp, "<b>$&</b>")
},
sort: {isoScore: -1}
});
}
}
Template.talkers.events({
"keyup #header-search": _.throttle(function(e) {
Session.set("matesSearch", $(e.target).val().trim());
console.log("Searching for : " + text);
SearchSources.talkersSearch.search(Session.get("matesSearch"), {
page: (this.params.page || 1),
filters: filters
});
}, 200)
}
SERVER :
SearchSource.defineSource('users', function(searchText, options) {
var options = {sort: {"profile.talkname": -1}, limit: 25};
if(searchText)
{
var regExp = buildRegExp(searchText);
selector = { $or: [
{ "profile.talkname": regExp },
{ "profile.bio": regExp }
] };
return Meteor.users.find(selector, options).fetch();
}
return ;
});
All this Gives me two sources from which I can get users. I'd want to get a mean to merge the two ides (a composition of publication INSIDE the search, for example).
Thanks you.

Can URI templates be used to match URIs to routes?

Frameworks like ASP.NET or Nancy provide a syntax that can be used for specifying routes, such as:
MapRoute("/customers/{id}/invoices/{invoiceId}", ...)
In ASP.NET routes work in two directions. They can match a request URI such as /customers/32/invoices/19 to a route, and they can resolve parameters such as { id: 37, invoiceId: 19 } into a URI.
RFC 6570: URI Templates also defines a similar, though much richer, specification for URI's that are often used to resolve URI's. For example:
UriTemplate("/customers/{id}/invoices{/invoiceId}{?sort}", { id: 37, invoiceId: 19, sort: 'asc' } )
// returns: /customers/37/invoices/19?sort=asc
My question is, can the syntax specified in RFC 6570 be used to match request URI's to routes? Is there a part of the syntax that would make it ambiguous to match a given URI to a given URI template? Are there any libraries that support matching a URI to a URI template?
I suspect it would be very difficult. Certainly things like the prefix syntax would make it impossible to regenerate the original parameters.
For things like path segment expansion
{/list*} /red/green/blue
How would you know which parts of the path were literals and which parts were part of the parameter? There are lots of fairly freaky behavior in the URITemplate spec, I suspect even if it is possible to match, it would be fairly expensive.
Are you interested in doing this for the purposes of routing?
It is simple regarding match but regarding resolve you need to replace the ASP.net part by the RFC 6570.
Unfortunately I am doing this in node with express js and this might not be helpful, but I am sure something like https://github.com/geraintluff/uri-templates (for resolving) is also available in ASP.
Here is some .js code to illustrate the rewriting of a hyperschema
USING RFC 6570 to use with express js (the advantage of the use within schema is that you could also define regexes for your uri templates):
var deref = require('json-schema-deref');
var tv4 = require('tv4');
var url = require('url');
var rql = require('rql/parser');
var hyperschema = {
"$schema": "http://json-schema.org/draft-04/hyper-schema",
"links": [
{
"href": "{/id}{/ooo*}{#q}",
"method": "GET",
"rel": "self",
"schema": {
"type": "object",
"properties": {
"params": {
"type": "object",
"properties": {
"id": {"$ref": "#/definitions/id"}
},
"additionalProperties": false
}
},
"additionalProperties": true
}
}
],
"definitions": {
"id": {
"type": "string",
"pattern": "[a-z]{0,3}"
}
}
}
// DOJO lang AND _
function getDottedProperty(object, parts, create) {
var key;
var i = 0;
while (object && (key = parts[i++])) {
if (typeof object !== 'object') {
return undefined;
}
object = key in object ? object[key] : (create ? object[key] = {} : undefined);
}
return object;
}
function getProperty(object, propertyName, create) {
return getDottedProperty(object, propertyName.split('.'), create);
}
function _rEscape(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function getPattern(k, ldo, customCat) {
// ...* = explode = array
// ...: = maxLength
var key = ((k.slice(-1) === '*') ? k.slice(0,-1) : k).split(':')[0];
var cat = (customCat) ? customCat : 'params'; // becomes default of customCat in TS
var pattern = '';
if (typeof ldo === 'object' && ldo.hasOwnProperty('schema')) {
var res = getProperty(ldo.schema, ['properties',cat,'properties',key,'pattern'].join('.'));
if (res) {
console.log(['properties',cat,'properties',key,'pattern'].join('.'),res);
return ['(',res,')'].join('');
}
}
return pattern;
}
function ldoToRouter(ldo) {
var expression = ldo.href.replace(/(\{\+)/g, '{') // encoding
.replace(/(\{\?.*\})/g, '') // query
.replace(/\{[#]([^}]*)\}/g, function(_, arg) {
// crosshatch
//console.log(arg);
return ['(?:[/]*)?#:',arg,getPattern(arg,ldo,'anchor')].join('');
})
.replace(/\{([./])?([^}]*)\}/g, function(_, op, arg) {
// path seperator
//console.log(op, '::', arg, '::', ldo.schema);
return [op,':',arg,getPattern(arg,ldo)].join('');
});
return {method: ldo.method.toLowerCase(), args:[expression]};
}
deref(hyperschema, function(err, fullSchema) {
console.log('deref hyperschema:',JSON.stringify(fullSchema));
var router = fullSchema.links.map(ldoToRouter);
console.log('router:',JSON.stringify(router));
});

Make a http get request to rest api and convert to a collection

Is it possible to make a Meteor.http.get("localhost:4000/api/resource.json") and use the returned response as Meteor.collection?
Sure. Let's say the JSON looks like:
{
'results': [
{
'name': 'bob',
'eyes': 'brown'
},
{
'name': 'sue',
'eyes': 'blue'
}
]
}
To insert that into a collection you would do:
Meteor.http.get("localhost:4000/api/resource.json", function(error, result) {
if (result.statusCode === 200) {
for (var i = 0; i < result.data.results.length; i++) {
MyCollection.insert(result.data.results[i])
}
}
else {
console.log(result);
}
});

Resources