Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added del and empty methods, general improvements #1

Merged
merged 4 commits into from
Jun 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,29 @@ const store = require('semver-store')()

store.set('1.2.0', { value: 42 })

console.log(store.get('1.2.0')) // { value: 42 }
console.log(
store.get('1.2.0') // { value: 42 }
)
```

## API
#### `set(version, store)`
Add a document to the store with the specified version.<br/>
The version **must** be conform with the [semver](http://semver.org/) specification.

#### `get(version)`
Get a document from the store with the specified version.<br/>
The version string could be a full version string or specify a range, such as `1.x`, in which case the highest version compatible will be returned.

#### `del(version)`
Deletes a document from the store with the specified version.<br/>
The version string could be a full version string or specify a range, such as `1.x`, in which case all the compatible values will be deleted.

#### `empty()`
Empties the store.

### Why is fast?
Internally uses a prefix tree, which allows the search to be extremely performant.
Internally uses a [prefix tree](https://en.wikipedia.org/wiki/Trie), which allows the search to be extremely performant.

## License

Expand Down
59 changes: 41 additions & 18 deletions bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,55 @@
const Benchmark = require('benchmark')
const suite = Benchmark.Suite()

const semver = require('semver')
const store = require('./index')()
store.set('1.2.3', null)
const SemVerStore = require('./index')
const store1 = SemVerStore()
const store2 = SemVerStore()

store2
.set('1.1.1', 1)
.set('1.1.2', 1)
.set('1.1.3', 1)
.set('1.2.1', 1)
.set('1.2.2', 1)
.set('1.2.3', 1)
.set('2.1.1', 1)
.set('2.1.2', 1)
.set('2.1.3', 1)
.set('3.2.1', 1)
.set('3.2.2', 1)
.set('3.2.3', 1)

suite
.add('semver-store - full', function () {
store.get('1.2.3')
.add('set', function () {
store1.set('1.2.3', 1)
})
.add('semver - full', function () {
semver.satisfies('1.2.3', '1.2.3')
.add('get', function () {
store1.get('1.2.3')
})

.add('semver-store - wildcard', function () {
store.get('1.x')
.add('get (minor wildcard)', function () {
store1.get('1.x')
})
.add('semver - wildcard', function () {
semver.satisfies('1.2.3', '1.x')
.add('get (patch wildcard)', function () {
store1.get('1.2.x')
})

.add('semver-store - no patch', function () {
store.get('1.2')
.add('del + set', function () {
store1.del('1.2.3')
store1.set('1.2.3', 1)
})
.add('semver - no patch', function () {
semver.satisfies('1.2.3', '1.2')
.add('del (minor wildcard) + set', function () {
store1.del('1.x')
store1.set('1.2.3', 1)
})
.add('del (patch wildcard) + set', function () {
store1.del('1.2.x')
store1.set('1.2.3', 1)
})
.add('set with other keys already present', function () {
store2.set('1.2.4', 1)
})
.add('get with other keys already present', function () {
store2.get('1.2.4')
})

.on('cycle', function (event) {
console.log(String(event.target))
})
Expand Down
118 changes: 102 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,29 @@ function SemVerStore () {
this.tree = new Node()
}

SemVerStore.prototype.set = function (semverString, store) {
SemVerStore.prototype.set = function (version, store) {
var currentNode = this.tree
semverString = semverString.split('.')
while (semverString.length) {
const node = new Node(semverString.shift())
currentNode = currentNode.addChild(node)
continue
version = version.split('.')
while (version.length) {
currentNode = currentNode.addChild(
new Node(version.shift())
)
}
currentNode.setStore(store)
return this
}

SemVerStore.prototype.get = function (semverString) {
SemVerStore.prototype.get = function (version) {
var node = this.tree
var firstDot = semverString.indexOf('.')
var secondDot = semverString.indexOf('.', firstDot + 1)
var major = semverString.slice(0, firstDot)
var firstDot = version.indexOf('.')
var secondDot = version.indexOf('.', firstDot + 1)
var major = version.slice(0, firstDot)
var minor = secondDot === -1
? semverString.slice(firstDot + 1)
: semverString.slice(firstDot + 1, secondDot + 1)
? version.slice(firstDot + 1)
: version.slice(firstDot + 1, secondDot)
var patch = secondDot === -1
? 'x'
: semverString.slice(secondDot + 1)
: version.slice(secondDot + 1)

node = node.getChild(major)
if (node === null) return null
Expand All @@ -39,6 +40,68 @@ SemVerStore.prototype.get = function (semverString) {
return node.store
}

SemVerStore.prototype.del = function (version) {
var firstDot = version.indexOf('.')
var secondDot = version.indexOf('.', firstDot + 1)
var major = version.slice(0, firstDot)
var minor = secondDot === -1
? version.slice(firstDot + 1)
: version.slice(firstDot + 1, secondDot)
var patch = secondDot === -1
? 'x'
: version.slice(secondDot + 1)

// check existence of major node
var majorNode = this.tree.children[major]
if (majorNode == null) return this

// if minor is the wildcard, then remove the full major node
if (minor === 'x') {
this.tree.removeChild(major)
return this
}

// check existence of minor node
var minorNode = majorNode.children[minor]
if (minorNode == null) return this

// if patch is the wildcard, then remove the full minor node
// and also the major if there are no more children
if (patch === 'x') {
this.tree.children[major].removeChild(minor)
if (this.tree.children[major].length === 0) {
this.tree.removeChild(major)
}
return this
}

// check existence of patch node
var patchNode = minorNode.children[patch]
if (patchNode == null) return this

// Specific delete
this.tree
.children[major]
.children[minor]
.removeChild(patch)

// check if the minor node has no more children, if so removes it
// same for the major node
if (this.tree.children[major].children[minor].length === 0) {
this.tree.children[major].removeChild(minor)
if (this.tree.children[major].length === 0) {
this.tree.removeChild(major)
}
}

return this
}

SemVerStore.prototype.empty = function () {
this.tree = new Node()
return this
}

function Node (prefix, children, store) {
this.prefix = Number(prefix) || 0
this.children = children || null
Expand All @@ -49,24 +112,47 @@ function Node (prefix, children, store) {
Node.prototype.getChild = function (prefix) {
if (this.children === null) return null
if (prefix === 'x') {
const max = Math.max.apply(Math, this.childrenPrefixes)
var max = Math.max.apply(Math, this.childrenPrefixes)
return this.children[max]
}
return this.children[Number(prefix)] || null
return this.children[prefix] || null
}

Node.prototype.addChild = function (node) {
this.children = this.children || {}
const child = this.getChild(node.prefix)
var child = this.getChild(node.prefix)
if (child === null) {
this.children[node.prefix] = node
this.childrenPrefixes.push(node.prefix)
}
return child || node
}

Node.prototype.removeChild = function (prefix) {
if (prefix === 'x') {
this.children = null
this.childrenPrefixes = []
return this
}
if (this.children[prefix] !== undefined) {
prefix = Number(prefix)
delete this.children[prefix]
this.childrenPrefixes.splice(
this.childrenPrefixes.indexOf(prefix), 1
)
}
return this
}

Node.prototype.setStore = function (store) {
this.store = store
return this
}

Object.defineProperty(Node.prototype, 'length', {
get: function () {
return this.childrenPrefixes.length
}
})

module.exports = SemVerStore
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"homepage": "https://github.com/delvedor/semver-store#readme",
"devDependencies": {
"benchmark": "^2.1.4",
"semver": "^5.5.0",
"standard": "^11.0.1",
"tap": "^12.0.1"
}
Expand Down
Loading