Skip to content

Commit

Permalink
feat: Added .base method
Browse files Browse the repository at this point in the history
  • Loading branch information
bergos committed Oct 30, 2023
1 parent 8e69dc7 commit 812740e
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 0 deletions.
20 changes: 20 additions & 0 deletions Grapoi.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { baseDataset, baseTerm } from './lib/base.js'
import { rebaseDataset } from './lib/rebase.js'
import { replaceDataset } from './lib/replace.js'
import sortByScore from './lib/sortByScore.js'
Expand Down Expand Up @@ -96,6 +97,25 @@ class Grapoi extends PathList {
return super.addOut(this._toTermArray(predicates), this._toTermArray(objects), callback)
}

/**
* Base all terms with a relative IRI with the given base.
* @param {Grapoi|Grapoi[]|Term|Term[]} base Base of the terms
* @returns {Constructor} Instance with a single pointer with the term based
*/
base (base) {
if (!base) {
throw new Error('base parameter is required')
}

base = this._toTerm(base)

for (const ptr of this.ptrs) {
baseDataset(base, { factory: this.factory })(ptr.dataset)
}

return this.node(baseTerm(base, { factory: this.factory })(this.term))
}

/**
* Use the given score function on all pointers and return the pointer with the best score.
* @param {function} score Score function
Expand Down
50 changes: 50 additions & 0 deletions lib/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
function baseDataset (newBaseTerm, { factory }) {
const base = baseQuad(newBaseTerm, { factory })

return dataset => {
for (const quad of [...dataset]) {
const newQuad = base(quad)

if (newQuad !== quad) {
dataset.delete(quad)
dataset.add(newQuad)
}
}
}
}

function baseQuad (newBaseTerm, { factory }) {
const base = baseTerm(newBaseTerm, { factory })

return quad => {
const subject = base(quad.subject)
const predicate = base(quad.predicate)
const object = base(quad.object)

if (subject === quad.subject && predicate === quad.predicate && object === quad.object) {
return quad
}

return factory.quad(subject, predicate, object, quad.graph)
}
}

function baseTerm (newBaseTerm, { factory }) {
return term => {
if (term.termType !== 'NamedNode') {
return term
}

if (/[a-z]+:\/\//.test(term.value)) {

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
return term
}

return factory.namedNode(new URL(term.value, newBaseTerm.value).toString())
}
}

export {
baseDataset,
baseQuad,
baseTerm
}
45 changes: 45 additions & 0 deletions test/Grapoi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,51 @@ describe('Grapoi', () => {
})
})

describe('.base', () => {
it('should be a method', () => {
const { grapoi } = datasets.default()

strictEqual(typeof grapoi.base, 'function')
})

it('should throw an error if no base is given', () => {
const { grapoi } = datasets.default()

throws(() => {
grapoi.base()
}, {
message: 'base parameter is required'
})
})

it('should return a new Grapoi object', () => {
const { expectedTerm, grapoi } = datasets.base()

const result = grapoi.base(expectedTerm)

strictEqual(result instanceof Grapoi, true)
notStrictEqual(result, grapoi)
})

it('should base the dateset', () => {
const { expectedGrapoi, expectedTerm, grapoi } = datasets.base()

const result = grapoi.base(expectedTerm)

grapoiEqual(result, expectedGrapoi)
datasetEqual(result.dataset, expectedGrapoi.dataset)
})

it('should support base argument given as ptr', () => {
const { expectedGrapoi, grapoi } = datasets.base()

const result = grapoi.base(expectedGrapoi)

grapoiEqual(result, expectedGrapoi)
datasetEqual(result.dataset, expectedGrapoi.dataset)
})
})

describe('.best', () => {
it('should be a method', () => {
const { grapoi } = datasets.default()
Expand Down
27 changes: 27 additions & 0 deletions test/support/datasets.multi.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ terms.graphs = [ns.ex.graph1, ns.ex.graph2]

const triples = {}

triples.base = [
[factory.namedNode(''), factory.namedNode('/propertyA'), links[0]],
[links[0], factory.namedNode('/propertyB'), factory.namedNode('/end')],
[ns.other(''), ns.other.propertyA, links[1]],
[links[1], ns.other.propertyB, ns.other.end]
]

triples.based = [
[ns.ex(''), ns.ex.propertyA, links[0]],
[links[0], ns.ex.propertyB, ns.ex.end],
[ns.other(''), ns.other.propertyA, links[1]],
[links[1], ns.other.propertyB, ns.other.end]
]

triples.in = [
[ns.ex.end1, ns.ex.propertyA, ns.ex.start1],
[ns.ex.end1, ns.ex.propertyA, ns.ex.start2],
Expand Down Expand Up @@ -371,6 +385,19 @@ multi.any = () => {
return createPathListDataset([], { terms: [null] })
}

multi.base = () => {
const { ...others } = createPathListDataset(triples.base, { terms: [ns.ex('')] })

const expectedTerm = ns.ex('')

const expectedGrapoi = new Grapoi({
dataset: factory.dataset(triples.based.map(parts => factory.quad(...parts))),
term: expectedTerm
})

return { expectedGrapoi, expectedTerm, ...others }
}

multi.best = () => {
const { dataset, ...others } = createPathListDataset(triples.inBlankNode, {
subjects: [ns.ex.start1, ns.ex.start1]
Expand Down

0 comments on commit 812740e

Please sign in to comment.