Skip to content

Commit

Permalink
Merge pull request #3 from hsu-aut/release
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
aljoshakoecher authored Aug 18, 2020
2 parents a4e9a6a + 617f2a1 commit 2b9fb9e
Show file tree
Hide file tree
Showing 15 changed files with 802 additions and 485 deletions.
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
indent_style = tab
indent_size = 4

[*.md]
trim_trailing_whitespace = false
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
},
rules: {
"semi": ["error","always"],
"indent": ["error", 4],
"indent": ["error", "tab"],
"prefer-const": ["error",{}],
"max-len": ["error", {code: 140, ignoreComments: true}]
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
Expand Down
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
/out-tsc
# Only exists if Bazel was run
/bazel-out
tsconfig.build.tsbuildinfo
tsconfig.tsbuildinfo
*.tsbuildinfo

# Logs
logs
Expand Down
202 changes: 141 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,29 @@ const convertedResult = converter.convert(inputArray, mappingDefinitions);
```
You have to pass two parameters to `convert()`:
1) `inputArray` are the results you get from your SPARQL query
2) `mappingDefinitions` is an array that describes how your converted result should look like. It consists of mapping definitions looking like this:
2) `mappingDefinitions` is a recursive structure that describes how your converted result should look like. It looks like this:

```javascript
const convertStructure = [
const convertStructure: MappingDefinition = [
{
objectToGroup: 'yourObjectToGroup',
name: 'newPropertyName',
childRoot: 'nameOfTheSubordinateArray'
rootName: 'name of the property that this group will be subordinated under'
propertyToGroup: 'a variable in your SPARQL query that you want to group on',
name: 'this can be used to rename the variable',
childMappings: [{
// This is a Partial<MappingDefinition>.
// --> You can define a complete MappingDefinition in case you want to create a nested structure
// --> You can also just set "rootName" if you don't want to group any further but want have the ungrouped "rest" under a defined rootName
}]
}
];
];
```
`objectToGroup`: The element in your query-result that you want to group (corresponds to a variable in the SELECT part of your query)</br>

`propertyToGroup`: The element in your query-result that you want to group (corresponds to a variable in the SELECT part of your query)</br>
`name`: Can be used for mapping the result object name to a new name.</br>
`childRoot`: The name of the root element of the subordinate array. After grouping your `objectToGroup`, the child elements will be added as an array. This array will be given the property name `childRoot`.</br>
`rootName`: The key of the grouped element in the superordinate array. After grouping, the grouped structure will be added as an array with key 'rootName' in the parent element.</br>

## A more detailed description
When you query Triple stores with their REST API, you get a tabular structure which can be somewhat ugly if the result actually is a nested tree-like structure. The REST-API returns a direct representation of the result-table in JSON. This is not very useful when you want to use your query-results in a frontend to dynamically display your data. Let's look at an example:
When you query Triple stores with their REST API, you get a tabular structure which can be somewhat ugly if the result actually is a nested tree-like structure. The REST-API returns a direct representation of the result-table in JSON. This is not very useful when you want to use your query-results e.g. in a frontend to dynamically display your data. Let's look at an example:
![Example Graph](https://github.com/aljoshakoecher/sparql-result-converter/raw/documentation/images/docu-images/example-graph.png)
Expand Down Expand Up @@ -161,62 +167,136 @@ The returned JSON just represents this table, the JSON looks like this:
}
```

The result is an array consisting of objects for each row of the table. Now if you want to show all owners and all pets of each owner, a nested structure is better suited. This is what sparql-result-converter gives you. It converts the flat array into a nested structure by grouping on certain properties. You can decide which properties should be grouped by passing an array representing your desired structure to the converter function. </br>
In this example, we want to group on the owners and would therefore pass the following array:
The result is an array consisting of objects for each row of the table. Now if you want to show all owners and all pets of each owner, a nested structure is better suited. **Converting from the tabular to a nested structure is exactly what sparql-result-converter does**. It converts the flat array into a nested structure by grouping on certain properties. You can decide which properties should be grouped by passing an array representing your desired structure to the converter function. </br>
In a first step of this example, we could want to group on the owners and would therefore pass the mapping definition:
```javascript
const convertStructure = [
{
objectToGroup: 'owner', // property that should be grouped
name: 'ownerName', // new name for the grouped property
childRoot: 'pets' // name of the array that will contain the remaining properties (petName and petType)
}
const mappingDefinition: MappingDefinition[] = [
{
rootName: 'owners',
propertyToGroup: 'owner',
name: 'name',
childMappings: [{
rootName: 'pets',
}]
}
];
```
The converted array will look like this:
As a result, we want an object with key 'owners', therefore `rootName` = owners. Furthermore, we want to group on ?owner (see SPARQL query), therefore we set `propertyToGroup` to be 'owner'. As the key for the grouped array is "owners", we might want to change the owner-property to be 'name'. This is done by setting `name` to 'name'. In this first example, we don't want to group any further, but we want to make sure that the rest (i.e. the pets) is subordinated under the key 'pets', that's why we set `rootName` of the only childMapping to 'pets'.

The converted result will look like this:
```javascript
const expectedResult = [
{
ownerName: 'Peter',
pets: [
{
petName: 'Rex',
petType: 'Dog'
}
]
},
{
ownerName: 'John',
pets: [
{
petName: 'Lassie',
petType: 'Dog'
},
{
petName: 'Oliver',
petType: 'Cat'
}
]
},
{
ownerName: 'Mary',
pets: [
{
petName: 'Huey',
petType: 'Cat'
},
{
petName: 'Dewey',
petType: 'Cat'
},
{
petName: 'Louie',
petType: 'Cat'
}
]
}
];
{
owners: [
{
name: 'Peter',
pets: [
{
petName: 'Rex',
petType: 'Dog'
}
]
},
{
name: 'John',
pets: [
{
petName: 'Lassie',
petType: 'Dog'
},
{
petName: 'Oliver',
petType: 'Cat'
}
]
},
{
name: 'Mary',
pets: [
{
petName: 'Huey',
petType: 'Cat'
},
{
petName: 'Dewey',
petType: 'Cat'
},
{
petName: 'Louie',
petType: 'Cat'
}
]
}
]
};
```

You can use the `name` to map the `objectToGroup` to a new property name.The converter only takes the value and gets rid of the type, therefore you might want to change the property name.

You can add more objects to `convertStructure` in case you want to successively group on more properties (In this example, you could also group on 'petType').
Now let's say we that we want to group on the owners and on the petType afterwards. This can be achieved by a nested mapping definition. Look at the following mapping definition:

```javascript
const twoLayerMappingDefinition: MappingDefinition[] = [
{
rootName: 'owners',
propertyToGroup: 'owner',
name: 'name',
childMappings: [
{
rootName: 'petTypes',
propertyToGroup: 'petType',
name: 'type',
childMappings: [{
rootName: 'pets'
}]
}
]
},
];
```
This nested mapping definition will lead to the following result:
```javascript
{
owners: [
{
name: 'Peter',
petTypes: [
{
type: 'Dog',
pets: [
{ petName: 'Rex' }
]
}
]
},
{
name: 'John',
petTypes: [
{
type: 'Dog',
pets: [
{ petName: 'Lassie' }
]
},
{
type: 'Cat',
pets: [
{ petName: 'Oliver' }
]
}
]
},
{
name: 'Mary',
petTypes: [
{
type: 'Cat',
pets: [
{ petName: 'Huey' },
{ petName: 'Dewey' },
{ petName: 'Louie' },
]
}
]
}
]
};
```
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "sparql-result-converter",
"version": "1.0.3",
"version": "2.0.0",
"description": "Little utility function to that converts the table-like result of a SPARQL query into a JSON tree with a user-defined structure",
"scripts": {
"clean": "rimraf dist",
"clean": "rimraf dist && rimraf *.tsbuildinfo",
"build": "tsc --project tsconfig.build.json",
"test": "npm run lint && npm run coverage",
"test:only": "mocha -r ts-node/register tests/**/*.test.ts",
Expand Down
50 changes: 14 additions & 36 deletions src/ArrayUtil.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,26 @@
export class ArrayUtil {

/**
* Checks whether or not all entries of the array contain the property that is used for grouping
* @param {*} arrayToCheck
* @param {*} groupingProperty
*/
static allEntriesContainGroupingProperty(arrayToCheck: any[], groupingProperty: string): boolean {
for (let i = 0; i < arrayToCheck.length; i++) {
const element = arrayToCheck[i];
if (!Object.prototype.hasOwnProperty.call(element, groupingProperty)) {
return false;
}
}
return true;
}



/**
/**
* Transforms an array of objects to an array of simple datatypes by extracting every value-property.
* @param {*} sparqlResult Array of objects that contain a value-property
*/
static extractValues(sparqlResult: SparqlResultLine[]): TransformedSparqlResultElement[] {
static extractValues(sparqlResult: SparqlResultLine[]): Record<string, string>[] {

const outputArray = new Array<TransformedSparqlResultElement>();
const outputArray = new Array<Record<string, string>>();

// Take every array element and extract all values of all object keys -> flatten the array
sparqlResult.forEach((sparqlResultLine:SparqlResultLine) => {
const objectKeys = Object.keys(sparqlResultLine);
// Take every array element and extract all values of all object keys -> flatten the array
sparqlResult.forEach((sparqlResultLine:SparqlResultLine) => {
const objectKeys = Object.keys(sparqlResultLine);

const outputElement = {};
objectKeys.forEach((key) => {
outputElement[key] = sparqlResultLine[key].value;
});
outputArray.push(outputElement);
});
const outputElement = {};
objectKeys.forEach((key) => {
outputElement[key] = sparqlResultLine[key].value;
});
outputArray.push(outputElement);
});

return outputArray;
}
return outputArray;
}


}
Expand All @@ -48,8 +31,3 @@ export interface SparqlResultLine {
value: string
}
}

export interface TransformedSparqlResultElement {
[propName: string]: string;
}

Loading

0 comments on commit 2b9fb9e

Please sign in to comment.