Skip to content

Commit 99b11d2

Browse files
donomiialiszka
authored andcommitted
Adds support for hybrid search
1 parent 2e8726f commit 99b11d2

File tree

7 files changed

+208
-2
lines changed

7 files changed

+208
-2
lines changed

ci/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
version: '3.4'
33
services:
44
weaviate:
5-
image: semitechnologies/weaviate:preview-replace-shardingconfig-replicas-with-replication-factor-29e987d
5+
image: semitechnologies/weaviate:1.17.0-prealpha-e8b5ccb
66
restart: on-failure:0
77
ports:
88
- "8080:8080"

cluster/journey.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const weaviate = require("../index");
22
const { createTestFoodSchemaAndData, cleanupTestFood, PIZZA_CLASS_NAME, SOUP_CLASS_NAME } = require("../utils/testData");
33

44
const EXPECTED_WEAVIATE_VERSION = "1.17.0-prealpha"
5-
const EXPECTED_WEAVIATE_GIT_HASH = "29e987d"
5+
const EXPECTED_WEAVIATE_GIT_HASH = "e8b5ccb"
66

77
describe("cluster nodes endpoint", () => {
88
const client = weaviate.client({

graphql/bm25.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
export default class GraphQLBm25 {
2+
constructor(bm25Obj) {
3+
this.source = bm25Obj;
4+
}
5+
6+
toString(wrap = true) {
7+
this.parse();
8+
this.validate();
9+
10+
let args = [`query:${JSON.stringify(this.query)}`]; // query must always be set
11+
12+
if (this.properties) {
13+
args = [...args, `properties:${JSON.stringify(this.properties)}`];
14+
}
15+
16+
return `{${args.join(",")}}`;
17+
}
18+
19+
validate() {
20+
if (!this.query) {
21+
throw new Error("bm25 filter: query cannot be empty");
22+
}
23+
}
24+
25+
parse() {
26+
for (let key in this.source) {
27+
switch (key) {
28+
case "query":
29+
this.parseQuery(this.source[key]);
30+
break;
31+
case "properties":
32+
this.parseProperties(this.source[key]);
33+
break;
34+
default:
35+
throw new Error("bm25 filter: unrecognized key '" + key + "'");
36+
}
37+
}
38+
}
39+
40+
41+
parseProperties(properties) {
42+
if (!Array.isArray(properties)) {
43+
throw new Error("bm25 filter: properties must be an array");
44+
}
45+
46+
this.properties = properties;
47+
}
48+
49+
parseQuery(query) {
50+
if (typeof query !== "string") {
51+
throw new Error("bm25 filter: query must be a number");
52+
}
53+
54+
this.query = query;
55+
}
56+
57+
}

graphql/getter.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Where from "./where";
22
import NearText from "./nearText";
33
import NearVector from "./nearVector";
4+
import Bm25 from "./bm25";
5+
import Hybrid from "./hybrid";
46
import NearObject from "./nearObject";
57
import NearImage from "./nearImage";
68
import Ask from "./ask";
@@ -60,6 +62,27 @@ export default class Getter {
6062
return this;
6163
};
6264

65+
withBm25 = (bm25Obj) => {
66+
try {
67+
this.bm25String = new Bm25(bm25Obj).toString();
68+
} catch (e) {
69+
this.errors = [...this.errors, e];
70+
}
71+
72+
return this;
73+
};
74+
75+
withHybrid = (hybridObj) => {
76+
try {
77+
this.hybridString = new Hybrid(hybridObj).toString();
78+
} catch (e) {
79+
this.errors = [...this.errors, e];
80+
}
81+
82+
return this;
83+
};
84+
85+
6386
withNearObject = (nearObjectObj) => {
6487
if (this.includesNearMediaFilter) {
6588
throw new Error(

graphql/hybrid.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
export default class GraphQLHybrid {
2+
constructor(hybridObj) {
3+
this.source = hybridObj;
4+
}
5+
6+
toString(wrap = true) {
7+
this.parse();
8+
this.validate();
9+
10+
let args = [`query:${JSON.stringify(this.query)}`]; // query must always be set
11+
12+
if (this.alpha) {
13+
args = [...args, `alpha:${JSON.stringify(this.alpha)}`];
14+
}
15+
16+
if (this.vector) {
17+
args = [...args, `vector:${JSON.stringify(this.vector)}`];
18+
}
19+
20+
if (!wrap) {
21+
return `${args.join(",")}`;
22+
}
23+
return `{${args.join(",")}}`;
24+
}
25+
26+
validate() {
27+
if (!this.query) {
28+
throw new Error("hybrid filter: query cannot be empty");
29+
}
30+
}
31+
32+
parse() {
33+
for (let key in this.source) {
34+
switch (key) {
35+
case "query":
36+
this.parseQuery(this.source[key]);
37+
break;
38+
case "alpha":
39+
this.parseAlpha(this.source[key]);
40+
break;
41+
case "vector":
42+
this.parseVector(this.source[key]);
43+
break;
44+
default:
45+
throw new Error("hybrid filter: unrecognized key '" + key + "'");
46+
}
47+
}
48+
}
49+
50+
parseVector(vector) {
51+
if (!Array.isArray(vector)) {
52+
throw new Error("hybrid filter: vector must be an array");
53+
}
54+
55+
this.vector = vector;
56+
}
57+
58+
parseQuery(query) {
59+
if (typeof query !== "string") {
60+
throw new Error("hybrid filter: query must be a string");
61+
}
62+
63+
this.query = query;
64+
}
65+
66+
parseAlpha(alpha) {
67+
if (typeof alpha !== "number") {
68+
throw new Error("hybrid filter: alpha must be a number");
69+
}
70+
71+
this.alpha = alpha;
72+
}
73+
}

graphql/journey.test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,58 @@ describe("the graphql journey", () => {
151151
.catch((e) => fail("it should not have error'd" + e));
152152
});
153153

154+
test("graphql get bm25 with query (without properties)", () => {
155+
return client.graphql
156+
.get()
157+
.withClassName("Article")
158+
.withBm25({ query: "Article", })
159+
.withFields("_additional { id }")
160+
.do()
161+
.then((res) => {
162+
expect(res.data.Get.Article.length).toBe(3);
163+
})
164+
.catch((e) => fail("it should not have error'd" + e));
165+
});
166+
167+
test("graphql get bm25 with query (with properties)", () => {
168+
return client.graphql
169+
.get()
170+
.withClassName("Article")
171+
.withBm25({ query: "Article", properties: ["title", "url"]})
172+
.withFields("_additional { id }")
173+
.do()
174+
.then((res) => {
175+
expect(res.data.Get.Article.length).toBe(3);
176+
})
177+
.catch((e) => fail("it should not have error'd" + e));
178+
});
179+
180+
test("graphql get hybrid with query (no vector)", () => {
181+
return client.graphql
182+
.get()
183+
.withClassName("Article")
184+
.withHybrid({ query: "Apple", alpha: 3})
185+
.withFields("_additional { id }")
186+
.do()
187+
.then((res) => {
188+
expect(res.data.Get.Article.length).toBe(3);
189+
})
190+
.catch((e) => fail("it should not have error'd" + e));
191+
});
192+
193+
test("graphql get hybrid with query (with vector)", () => {
194+
return client.graphql
195+
.get()
196+
.withClassName("Article")
197+
.withHybrid({ query: "Apple", alpha: 0.3, vector: [0.1, 0.2, 0.3]})
198+
.withFields("_additional { id }")
199+
.do()
200+
.then((res) => {
201+
expect(res.data.Get.Article.length).toBe(3);
202+
})
203+
.catch((e) => fail("it should not have error'd" + e));
204+
});
205+
154206
test("graphql get with nearText (with certainty)", () => {
155207
return client.graphql
156208
.get()

schema/journey.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ function newClassObject(className) {
451451
desiredVirtualCount: 128,
452452
function: "murmur3",
453453
key: "_id",
454+
replicas: 1,
454455
strategy: "hash",
455456
virtualPerPhysical: 128,
456457
},

0 commit comments

Comments
 (0)