Skip to content
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
7 changes: 5 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3.4'
services:
weaviate:
image: semitechnologies/weaviate:1.0.0-rc1
image: semitechnologies/weaviate:1.2.1
restart: on-failure:0
ports:
- "8080:8080"
Expand All @@ -11,11 +11,14 @@ services:
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: "./weaviate-data"
DEFAULT_VECTORIZER_MODULE: text2vec-contextionary
ENABLE_MODULES: text2vec-contextionary
contextionary:
image: semitechnologies/contextionary:en0.16.0-v0.4.21
image: semitechnologies/contextionary:en0.16.0-v1.0.2
ports:
- "9999:9999"
environment:
OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75
EXTENSIONS_STORAGE_MODE: weaviate
EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080
NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5
ENABLE_COMPOUND_SPLITTING: 'false'
14 changes: 14 additions & 0 deletions graphql/explorer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import NearText from "./nearText";
import NearVector from "./nearVector";
import NearObject from "./nearObject";
import { DEFAULT_KIND, validateKind } from "../kinds";

export default class Explorer {
Expand Down Expand Up @@ -28,6 +29,15 @@ export default class Explorer {
return this;
};

withNearObject = (nearObjectObj) => {
try {
this.nearObjectString = new NearObject(nearObjectObj).toString();
} catch (e) {
this.errors = [...this.errors, e];
}
return this;
};

withNearVector = (nearVectorObj) => {
try {
this.nearVectorString = new NearVector(nearVectorObj).toString();
Expand Down Expand Up @@ -85,6 +95,10 @@ export default class Explorer {
args = [...args, `nearText:${this.nearTextString}`];
}

if (this.nearObjectString) {
args = [...args, `nearObject:${this.nearObjectString}`];
}

if (this.nearVectorString) {
args = [...args, `nearVector:${this.nearVectorString}`];
}
Expand Down
15 changes: 15 additions & 0 deletions graphql/getter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Where from "./where";
import NearText from "./nearText";
import NearVector from "./nearVector";
import NearObject from "./nearObject";
import Group from "./group";

export default class Getter {
Expand Down Expand Up @@ -47,6 +48,15 @@ export default class Getter {
return this;
};

withNearObject = (nearObjectObj) => {
try {
this.nearObjectString = new NearObject(nearObjectObj).toString();
} catch (e) {
this.errors = [...this.errors, e];
}
return this;
};

withNearVector = (nearVectorObj) => {
try {
this.nearVectorString = new NearVector(nearVectorObj).toString();
Expand Down Expand Up @@ -92,6 +102,7 @@ export default class Getter {
if (
this.whereString ||
this.nearTextString ||
this.nearObjectString ||
this.nearVectorString ||
this.limit ||
this.groupString
Expand All @@ -106,6 +117,10 @@ export default class Getter {
args = [...args, `nearText:${this.nearTextString}`];
}

if (this.nearObjectString) {
args = [...args, `nearObject:${this.nearObjectString}`];
}

if (this.nearVectorString) {
args = [...args, `nearVector:${this.nearVectorString}`];
}
Expand Down
100 changes: 100 additions & 0 deletions graphql/getter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,103 @@ describe("nearVector searchers", () => {
});
});
});

describe("nearObject searchers", () => {
test("a query with a valid nearObject with id", () => {
const mockClient = {
query: jest.fn(),
};

const expectedQuery =
`{Get{Person` + `(nearObject:{id:"some-uuid"})` + `{name}}}`;

new Getter(mockClient)
.withClassName("Person")
.withFields("name")
.withNearObject({ id: "some-uuid" })
.do();

expect(mockClient.query).toHaveBeenCalledWith(expectedQuery);
});

test("a query with a valid nearObject with beacon", () => {
const mockClient = {
query: jest.fn(),
};

const expectedQuery =
`{Get{Person` + `(nearObject:{beacon:"weaviate/some-uuid"})` + `{name}}}`;

new Getter(mockClient)
.withClassName("Person")
.withFields("name")
.withNearObject({ beacon: "weaviate/some-uuid" })
.do();

expect(mockClient.query).toHaveBeenCalledWith(expectedQuery);
});

test("a query with a valid nearObject with all params", () => {
const mockClient = {
query: jest.fn(),
};

const expectedQuery =
`{Get{Person` + `(nearObject:{id:"some-uuid",beacon:"weaviate/some-uuid",certainty:0.7})` + `{name}}}`;

new Getter(mockClient)
.withClassName("Person")
.withFields("name")
.withNearObject({
id: "some-uuid",
beacon: "weaviate/some-uuid",
certainty: 0.7
})
.do();

expect(mockClient.query).toHaveBeenCalledWith(expectedQuery);
});

describe("queries with invalid nearObject searchers", () => {
const mockClient = {
query: jest.fn(),
};

const tests = [
{
title: "an empty nearObject",
nearObject: {},
msg: "nearObject filter: id or beacon needs to be set",
},
{
title: "id of wrong type",
nearObject: { id: {} },
msg: "nearObject filter: id must be a string",
},
{
title: "beacon of wrong type",
nearObject: { beacon: {} },
msg: "nearObject filter: beacon must be a string",
},
{
title: "certainty of wrong type",
nearObject: { id: "foo", certainty: "foo" },
msg: "nearObject filter: certainty must be a number",
}
];

tests.forEach((t) => {
test(t.title, () => {
new Getter(mockClient)
.withClassName("Person")
.withFields("name")
.withNearObject(t.nearObject)
.do()
.then(() => fail("it should have error'd"))
.catch((e) => {
expect(e.toString()).toContain(t.msg);
});
});
});
});
});
13 changes: 13 additions & 0 deletions graphql/journey.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ describe("the graphql journey", () => {
.catch((e) => fail("it should not have error'd" + e));
});

test("graphql explore with nearObject field", () => {
return client.graphql
.explore()
.withNearObject({ id: "abefd256-8574-442b-9293-9205193737ee" })
.withFields("beacon certainty className")
.do()
.then((res) => {
expect(res.data.Explore.length).toBeGreaterThan(0);
})
.catch((e) => fail("it should not have error'd" + e));
});

it("tears down and cleans up", () => {
return Promise.all([
client.schema.classDeleter().withClassName("Article").do(),
Expand Down Expand Up @@ -150,6 +162,7 @@ const setup = async (client) => {

const toImport = [
{
id: "abefd256-8574-442b-9293-9205193737ee",
class: "Article",
properties: {
wordCount: 60,
Expand Down
77 changes: 77 additions & 0 deletions graphql/nearObject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
export default class GraphQLNearObject {
constructor(nearObjectObj) {
this.source = nearObjectObj;
}

toString(wrap = true) {
this.parse();
this.validate();

let args = [];

if (this.id) {
args = [...args, `id:${JSON.stringify(this.id)}`];
}

if (this.beacon) {
args = [...args, `beacon:${JSON.stringify(this.beacon)}`];
}

if (this.certainty) {
args = [...args, `certainty:${this.certainty}`];
}

if (!wrap) {
return `${args.join(",")}`;
}
return `{${args.join(",")}}`;
}

validate() {
if (!this.id && !this.beacon) {
throw new Error("nearObject filter: id or beacon needs to be set");
}
}

parse() {
for (let key in this.source) {
switch (key) {
case "id":
this.parseID(this.source[key]);
break;
case "beacon":
this.parseBeacon(this.source[key]);
break;
case "certainty":
this.parseCertainty(this.source[key]);
break;
default:
throw new Error("nearObject filter: unrecognized key '" + key + "'");
}
}
}

parseID(id) {
if (typeof id !== "string") {
throw new Error("nearObject filter: id must be a string");
}

this.id = id;
}

parseBeacon(beacon) {
if (typeof beacon !== "string") {
throw new Error("nearObject filter: beacon must be a string");
}

this.beacon = beacon;
}

parseCertainty(cert) {
if (typeof cert !== "number") {
throw new Error("nearObject filter: certainty must be a number");
}

this.certainty = cert;
}
}
52 changes: 47 additions & 5 deletions schema/journey.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,36 @@ describe("schema", () => {

it("creates a thing class (implicitly)", () => {
const classObj = {
class: "MyThingClass",
class: 'MyThingClass',
properties: [
{
dataType: ["string"],
name: "stringProp",
},
name: 'stringProp',
moduleConfig: {
'text2vec-contextionary': {
skip: false,
vectorizePropertyName: false
}
}
}
],
vectorIndexType: "hnsw",
vectorizer: "text2vec-contextionary",
vectorIndexType: 'hnsw',
vectorizer: 'text2vec-contextionary',
vectorIndexConfig: {
cleanupIntervalSeconds: 300,
maxConnections: 64,
efConstruction: 128,
vectorCacheMaxObjects: 500000
},
invertedIndexConfig: {
cleanupIntervalSeconds: 60
},
moduleConfig: {
'text2vec-contextionary':
{
vectorizeClassName: true
}
}
};

return client.schema
Expand Down Expand Up @@ -58,6 +79,12 @@ describe("schema", () => {
{
dataType: ["string"],
name: "stringProp",
moduleConfig: {
'text2vec-contextionary': {
skip: false,
vectorizePropertyName: false
}
}
},
{
dataType: ["string"],
Expand All @@ -66,6 +93,21 @@ describe("schema", () => {
],
vectorIndexType: "hnsw",
vectorizer: "text2vec-contextionary",
vectorIndexConfig: {
cleanupIntervalSeconds: 300,
maxConnections: 64,
efConstruction: 128,
vectorCacheMaxObjects: 500000
},
invertedIndexConfig: {
cleanupIntervalSeconds: 60
},
moduleConfig: {
'text2vec-contextionary':
{
vectorizeClassName: true
}
}
},
],
});
Expand Down