Querying Project Gutenberg catalog.rdf via rdflib.js - javascript

I am attempting to interpret Project Gutenberg's catalog.rdf file from the browser using rdflib.js. Currently I am able to download a cached copy of the catalogue, parse it, and match a list of books. Next, I'm trying to get all the particulars about the book (title, author, subjects, etc.)
Unfortunately, most of my literal values are coming back as [object NodeList]
Example of book RDF:
<pgterms:etext rdf:ID="etext27785">
<dc:publisher>&pg;</dc:publisher>
<dc:title rdf:parseType="Literal">A Book About Lawyers</dc:title>
<dc:creator rdf:parseType="Literal">Jeaffreson, John Cordy, 1831-1901</dc:creator>
<pgterms:friendlytitle rdf:parseType="Literal">A Book About Lawyers by John Cordy Jeaffreson</pgterms:friendlytitle>
<dc:language><dcterms:ISO639-2><rdf:value>en</rdf:value></dcterms:ISO639-2></dc:language>
<dc:subject><dcterms:LCSH><rdf:value>Lawyers -- Great Britain -- Anecdotes</rdf:value></dcterms:LCSH></dc:subject>
<dc:subject><dcterms:LCC><rdf:value>KD</rdf:value></dcterms:LCC></dc:subject>
<dc:created><dcterms:W3CDTF><rdf:value>2009-01-12</rdf:value></dcterms:W3CDTF></dc:created>
<pgterms:downloads><xsd:nonNegativeInteger><rdf:value>20</rdf:value></xsd:nonNegativeInteger></pgterms:downloads>
<dc:rights rdf:resource="&lic;" />
</pgterms:etext>
Example of code used to parse:
let store = $rdf.graph();
$rdf.parse(stm,store,baseUrl,'application/rdf+xml');
let books = store.match(undefined, types.RDF('type') , types.PGb('etext')).map(t=>t.subject);
let lib = books.map(b=>{
let props = store.match(b, null, undefined);
console.debug("Book: " + schema['_id']);
props.forEach(a=>{
console.debug(a);
});
});
This results in all the triples being returned, however the object portion is often an attempt to serialize a NodeList. For example, the "title" predicate:
{
"subject": {
"termType": "NamedNode",
"value": "http://www.gutenberg.org/feeds/catalog.rdf#etext14600"
},
"predicate": {
"termType": "NamedNode",
"value": "http://purl.org/dc/elements/1.1/title"
},
"object": {
"termType": "Literal",
"value": "[object NodeList]",
"datatype": {
"termType": "NamedNode",
"value": "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral"
}
},
"why": {
"termType": "NamedNode",
"value": "https://example.com/datasets/gutenberg/catalog.rdf.gz"
}
}
How is it possible to get the actual value of a literal object from the RDF query?
I would also be open to other RDF JS libraries, or other query mechanisms (SPARQL for example), if that would be easier.

This behaviour is as a direct result of a defect in the library.
RDF+XML nodes with a parseType='Literal' always result in "[ojbect NodeList]" in the browser.
https://github.com/linkeddata/rdflib.js/issues/75

Related

My Query Quagmire: My node.js program works just fine, except when I try to execute queries, or filtered HTTP requests. Why?

I have been working on the backend of my app. At this point, it can access all data in a data base, and output it. I'm trying to implement some queries, so that the user can filter out the content that is returned. My DAL/DAO, looks like this
let mflix //Creates a variable used to store a ref to our DB
class MflixDAO {
static async injectDB(conn){
if(mflix){
return
}
try{
mflix = await conn.db(process.env.JD_NS).collection("movies")
}catch(e){
console.error('Unable to establish a collection handle in mflixDAO: ' + e)
}
}
// Creates a query to fetch data from the collection/table in the DB
static async getMovies({
mflix.controller
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
let query
if (filters){
// Code
if("year" in filters){
query = {"year": {$eq: filters["year"]}}
}
// Code
}
// Cursor represents the returned data
let cursor
try{
cursor = await mflix.find(query)
}catch(e){
console.error('Unable to issue find command ' + e)
return {moviesList: [], totalNumMovies: 0}
}
const displayCursor = cursor.limit(moviesPerPage).skip(moviesPerPage * page)
try{
const moviesList = await displayCursor.toArray() // Puts data in an array
const totalNumMovies = await mflix.countDocuments(query) // Gets total number of documents
return { moviesList, totalNumMovies}
} catch(e){
console.error('Unable to convert cursor to array or problem counting documents ' + e)
return{moviesList: [], totalNumMovies: 0}
}
}
}
export default MflixDAO
Just so you know, I am using a sample database from MongoDB Atlas. I am using Postman to test HTTP requests. All the data follows JSON format
Anyway, when I execute a basic GET request. The program runs without any problems. All the data outputs as expected. However, if I execute something along the lines of
GET http://localhost:5000/api/v1/mflix?year=1903
Then moviesList returns an empty array [], but no error message.
After debugging, I suspect the problem lies either at cursor = await mflix.find(query) or displayCursor = cursor.limit(moviesPerPage).skip(moviesPerPage * page), but the callstacks for those methods is so complex for me, I don't know what to even look for.
Any suggestions?
Edit: Here is an example of the document I am trying to access:
{
"_id": "573a1390f29313caabcd42e8",
"plot": "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.",
"genres": [
"Short",
"Western"
],
"runtime": 11,
"cast": [
"A.C. Abadie",
"Gilbert M. 'Broncho Billy' Anderson",
"George Barnes",
"Justus D. Barnes"
],
"poster": "https://m.media-amazon.com/images/M/MV5BMTU3NjE5NzYtYTYyNS00MDVmLWIwYjgtMmYwYWIxZDYyNzU2XkEyXkFqcGdeQXVyNzQzNzQxNzI#._V1_SY1000_SX677_AL_.jpg",
"title": "The Great Train Robbery",
"fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.",
"languages": [
"English"
],
"released": "1903-12-01T00:00:00.000Z",
"directors": [
"Edwin S. Porter"
],
"rated": "TV-G",
"awards": {
"wins": 1,
"nominations": 0,
"text": "1 win."
},
"lastupdated": "2015-08-13 00:27:59.177000000",
"year": 1903,
"imdb": {
"rating": 7.4,
"votes": 9847,
"id": 439
},
"countries": [
"USA"
],
"type": "movie",
"tomatoes": {
"viewer": {
"rating": 3.7,
"numReviews": 2559,
"meter": 75
},
"fresh": 6,
"critic": {
"rating": 7.6,
"numReviews": 6,
"meter": 100
},
"rotten": 0,
"lastUpdated": "2015-08-08T19:16:10.000Z"
},
"num_mflix_comments": 0
}
EDIT: It seems to be a datatype problem. When I request a data with a string/varchar type, the program returns values that contain that value. Example:
Input:
GET localhost:5000/api/v1/mflix?rated=TV-G
Output:
{
"_id": "XXXXXXXXXX"
// Data
"rated" = "TV-G"
// Data
}
EDIT: The problem has nothing to do with anything I've posted up to this point it seems. The problem is in this piece of code:
let filters = {}
if(req.query.year){
filters.year = req.query.year // This line needs to be changed
}
const {moviesList, totalNumMovies} = await MflixDAO.getMovies({
filters,
page,
moviesPerPage,
})
I will explain in the answer below
Ok so the problem, as it turns out, is that when I make an HTTP request, the requested value is passed as a string. So in
GET http://localhost:5000/api/v1/mflix?year=1903
the value of year is registered by the program as a string. In other words, the DAO ends up looking for "1903" instead of 1903. Naturally, year = "1903" does not exist. To fix this, the line filters.year = req.query.year must be changed to filters.year = parseInt(req.query.year).

JavaScript parsing nested JSON data

I'm creating a discord bot (using discord.js)
I'm writing a help command using pages in embeds, I have the pages working fine - however, the problem seems to come when trying to get the data from the JSON.
I have the JSON file setup in a larger config file, so it's nested inside of it.
Each command has it's name attached to it
{
"other-json-data": "other data",
"commands": {
"rule": {
"info": "This command gives the rules",
"expectedArgs": "<number>"
},
"invite": {
"info": "Invite new users",
"expectedArgs": "<no-args>"
},
"flip": {
"info": "Flip a coin",
"expectedArgs": "<no-args>"
}
},
"other-json-data": "other data"
}
I need to get the data from the commands area for each page.
I only have a integer input (from the page number), but I haven't got a clue how I would get the data from whatever command needs to be shown.
For something else in my project, I am using this to get the expectedArgs from the JSON object config.commands[arguments].expectedArgs, where the config is just a reference to the JSON file, this works perfectly fine. The arguments is a string input (i.e. rule), which returns whatever the info from that command.
However, would there be a way to get say the second one down (invite). I've tried config.commands[pageNumber].expectedArgs}, however, this doesn't seem to work. pageNumber would be an integer, so would it would get whatever value and then I could grab the expectedArgs.
You can get all keys from an object and select one using their index.
const json = {
"other-json-data": "other data",
"commands": {
"rule": {
"info": "This command gives the rules",
"expectedArgs": "<number>"
},
"invite": {
"info": "Invite new users",
"expectedArgs": "<no-args>"
},
"flip": {
"info": "Flip a coin",
"expectedArgs": "<no-args>"
}
},
"other-json-data": "other data"
}
const pageNumber = 1
// key will be a command name, e.g. 'invite'
const key = Object.keys(json.commands)[pageNumber]
const { expectedArgs } = json.commands[key]
console.log(`${key} expects ${expectedArgs}`)
Remember that indexes range starts at zero.

Optimalization of firebase query. Getting data by ids

I'm new in Firebase. I would like to create an app (using Angular and AngularFire library), which shows current price of some wares. I have list all available wares in Firebase Realtime Database in the following format:
"warehouse": {
"wares": {
"id1": {
"id": "id1",
"name": "name1",
"price": "0.99"
},
"id2": {
"id": "id2",
"name": "name2",
"price": "15.00"
},
... //much more stuff
}
}
I'm using ngrx with my app, so I think that I can load all wares to store as an object not list because normalizing state tree. I wanted load wares to store in this way:
this.db.object('warehouse/wares').valueChanges();
The problem is wares' price will be refresh every 5 minutes. The number og wares is huge (about 3000 items) so one response will be weight about 700kB. I know that I will exceed limit downloaded data in a short time, in this way.
I want limit the loading data to interesing for user, so every user will can choose wares. I will store this choices in following way:
"users": {
"user1": {
"id": "user1",
"wares": {
"id1": {
"order": 1
},
"id27": {
"order": 2
},
"id533": {
"order": 3
}
},
"waresIds": ["id1", "id27", "id533"]
}
}
And my question is:
Is there a way to getting wares based on waresIds' current user? I mean, does it exist way to get only wares, whose ids are in argument array? F.e.
"wares": {
"id1": {
"id": "id1",
"name": "name1",
"price": "0.99"
},
"id27": {
"id": "id27",
"name": "name27",
"price": "0.19"
},
"id533": {
"id": "id533",
"name": "name533",
"price": "1.19"
}
}
for query like:
this.db.object('warehouse/wares').contains(["id1", "id27", "id533"]).valueChanges();
I saw query limits in Angular Fire like equalTo and etc. but every is for list. I'm totally confused. Is there anyone who can help me? Maybe I'm making mistakes in the design of the app structure. If so, I am asking for clarification.
Because you are saving the ids inside user try this way.
wares: Observable<any[]>;
//inside ngOnInit or function
this.wares = this.db.list('users/currentUserId/wares').snapshotChanges().map(changes => {
return changes.map(c => {
const id = c.payload.key; //gets ids under users/wares/ids..
let wares=[];
//now get the wares
this.db.list('warehouse/wares', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(res=>{
res.forEach(data=>{
wares.push(data);
})
});
return wares;
});
});
There are two things you can do. I don't believe Firebase allows you to query for multiple equals values at once. You can however loop over the array of "ids" and query for each one directly.
I am assuming you already queried for "waresIds" and you've stored those ID's in an array named idArray:
for id in idArray {
database.ref('warehouse/wares').orderByChild('id').equalTo(id).once('value').then((snapshot) => {
console.log(snapshot.val());
})
}
In order to use the above query efficiently you'll have to index your data on id.
Your second option would be to use .childChanged to get only the updated data after your initial fetch. This should cut down drastically on the amount of data you need to download.
Yes , you can get exactly data that you want in firebase,
See official Firebase documents about filtering
You need to get each waresID
var waresID = // logic to get waresID
var userId = // logic to get userId
var ref = firebase.database().ref("wares/" + userId).child(waresID);
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.val());
});
this will return only data related to that waresID or userId
Note: this is javascript code, i hope this will work for you.

Error getting a single value from a collection

I have a collection called notification and i am trying to get a single value with findOne()
var allnotices = Notifications.findOne({eventownernumber:"2"},{sort: {noticedate: -1, limit: 1}}).noticemessage;
I want to get the value where the eventownernumber is 2 and i want to get the latest record and i only want one record.
Even though noticemessage is part of the row fields,i get the error that noticemessage is undefined.
This is the schema
{
"_id": "tmkWCydSKZtYdrKTZ",
"eventoriginalid": "3bXvARk6K6yhee6Hi",
"lat": "-1.851881824302658",
"lng": "96.987469482421875",
"eventownernumber": "1",
"eventownernames": "Test 1",
"eventtitle": "ci",
"eventtime": "08:05",
"invited": "0",
"eventduration": "21",
"eventtype": "notification",
"eventcategory": "hackathon",
"eventstatus": "11",
"createdAt": {
"$date": "2016-11-02T12:38:40.378Z"
},
"noticedate": {
"$date": "2016-11-02T16:50:53.394Z"
},
"noticenumber": "2",
"noticenames": "Test 2",
"noticemessage": "Test 2 has joined your event ci",
"noticestatus": "12"
}
Why is noticemessage undefined?.
There are four basic possibilities why Collection.findOne(query).key could yield an error:
There is no document matching the query therefore you're trying to reference undefined.key
The key in question doesn't exist in the returned document
The document exists in the database but isn't being published by the server and being subscribed to by the client
The document exists and is published and subscribed to but the subscription is not yet .ready(), i.e. you need to wait before you can access it.
A common defensive pattern is:
const oneDoc = myCollection.findOne(query);
let myVar = oneDoc && oneDoc.key;
if ( myVar ) {
// do the thing
} else {
// handle the error
}
You need to save the number as integer for eventownernnumber (and please write it like eventOwnerNumber, which is a good practice for readability), not string. Either use input type="number" or convert the value to integer like this:
Number(valueHere);
The rest of your query looks fine to me but you don't need limit since you do findOne() and you find the newest inserted doc with noticedate: -1
Another thing is, you need to save the date like this in your insert():
noticeDate: new Date() //your current query should give you the right document after this change

Iteration in handlebar using backbone

I'm using backbone and handlebars for templating and i'm new to this.
My current json is in the below format and the code works fine.
[
{
"id": "10",
"info": {
"name": "data10"
}
},
{
"id": "11",
"info": {
"name": "data11"
}
}
]
But when i change my json structure to something like shown below i'm having difficulty in getting things to be populated.
{
"total_count": "10",
"dataElements": [
{
"id": "10",
"info": {
"name": "data10"
}
},
{
"id": "11",
"info": {
"name": "data11"
}
}
]
}
How can i populate name, info and total_count keeping the current code structure ?
JSFiddle : http://jsfiddle.net/KTj2K/1/
Any help really appriciated.
A few things that you need to do in order for this to work.
Replace Backbone's core 'reset' on your collection with a custom one that understands the data you are passing to it. For example:
reset: function (data) {
this.totalCount = data.total_count;
Backbone.Collection.prototype.reset.call(this, data.dataElements);
}
Now when you reset your collection, it will pull the total_count out of the object you are resetting it with, and use Backbone's core reset with the dataElement array. Keep in mind you may have to do a similar thing with 'parse' if you're intending on pulling this from the server.
I'd recommend that (if your example looks anything like the real code you're working with) you reset your collection before getting to rendering.
var dataCollectionList = new dataCollection();
dataCollectionList.reset(jsonData);
var App = new AppView({model : dataCollectionList});
Now in your view's "render" method you can grab the 'totalCount' property off the collection -
render : function() {
//Should spit the total count into the element, just as an example
this.$el.append(this.model.totalCount);
//or console.log it
console.log(this.model.totalCount);
return this;
}
Voila. Side note - as someone who works with Backbone a lot, it drives me nuts when people set an attribute of something like "model" (i.e. peopleModel, itemModel, etc) and it ends up being a backbone collection. It's much clearer to name it after what it is - though some MVC purists may disagree a bit.
Also, in this code block:
_.each(this.model.models, function (myData) {
$(this.el).append(new ItemView({model:myData}).render().el);
}, this);
You don't need to do _.each(this.model.models.......). Since you're working with a collection, the collection has a built in 'each' method.
this.model.each(function (myData) { ..... } , this);
Quite a bit cleaner.

Categories

Resources