Skip to content

Commit d6b7afd

Browse files
authored
fix(orama): break the search circular dependency behind Metro/Expo failure (#961) (#1026)
1 parent 59fa064 commit d6b7afd

4 files changed

Lines changed: 101 additions & 103 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { InternalDocumentID, getDocumentIdFromInternalId } from '../components/internal-document-id-store.js'
2+
import { getNested } from '../utils.js'
3+
import type { AnyOrama, LiteralUnion, Result, SearchableValue, TypedDocument } from '../types.js'
4+
5+
export function fetchDocumentsWithDistinct<T extends AnyOrama, ResultDocument extends TypedDocument<T>>(
6+
orama: T,
7+
uniqueDocsArray: [InternalDocumentID, number][],
8+
offset: number,
9+
limit: number,
10+
distinctOn: LiteralUnion<T['schema']>
11+
): Result<ResultDocument>[] {
12+
const docs = orama.data.docs
13+
14+
// Keep track which values we already seen
15+
const values = new Map<SearchableValue, true>()
16+
17+
// We cannot know how many results we will have in the end,
18+
// so we need cannot pre-allocate the array.
19+
const results: Result<ResultDocument>[] = []
20+
21+
const resultIDs: Set<InternalDocumentID> = new Set()
22+
const uniqueDocsArrayLength = uniqueDocsArray.length
23+
let count = 0
24+
for (let i = 0; i < uniqueDocsArrayLength; i++) {
25+
const idAndScore = uniqueDocsArray[i]
26+
27+
// If there are no more results, just break the loop
28+
if (typeof idAndScore === 'undefined') {
29+
continue
30+
}
31+
32+
const [id, score] = idAndScore
33+
34+
if (resultIDs.has(id)) {
35+
continue
36+
}
37+
38+
const doc = orama.documentsStore.get(docs, id)
39+
const value = getNested(doc as object, distinctOn)
40+
if (typeof value === 'undefined' || values.has(value)) {
41+
continue
42+
}
43+
values.set(value, true)
44+
45+
count++
46+
// We shouldn't consider the document if it's not in the offset range
47+
if (count <= offset) {
48+
continue
49+
}
50+
51+
results.push({ id: getDocumentIdFromInternalId(orama.internalDocumentIDStore, id), score, document: doc! })
52+
resultIDs.add(id)
53+
54+
// reached the limit, break the loop
55+
if (count >= offset + limit) {
56+
break
57+
}
58+
}
59+
60+
return results
61+
}
62+
63+
export function fetchDocuments<T extends AnyOrama, ResultDocument extends TypedDocument<T>>(
64+
orama: T,
65+
uniqueDocsArray: [InternalDocumentID, number][],
66+
offset: number,
67+
limit: number
68+
): Result<ResultDocument>[] {
69+
const docs = orama.data.docs
70+
71+
const results: Result<ResultDocument>[] = Array.from({
72+
length: limit
73+
})
74+
75+
const resultIDs: Set<InternalDocumentID> = new Set()
76+
77+
// We already have the list of ALL the document IDs containing the search terms.
78+
// We loop over them starting from a positional value "offset" and ending at "offset + limit"
79+
// to provide pagination capabilities to the search.
80+
for (let i = offset; i < limit + offset; i++) {
81+
const idAndScore = uniqueDocsArray[i]
82+
83+
// If there are no more results, just break the loop
84+
if (typeof idAndScore === 'undefined') {
85+
break
86+
}
87+
88+
const [id, score] = idAndScore
89+
90+
if (!resultIDs.has(id)) {
91+
// We retrieve the full document only AFTER making sure that we really want it.
92+
// We never retrieve the full document preventively.
93+
const fullDoc = orama.documentsStore.get(docs, id)
94+
results[i] = { id: getDocumentIdFromInternalId(orama.internalDocumentIDStore, id), score, document: fullDoc! }
95+
resultIDs.add(id)
96+
}
97+
}
98+
return results
99+
}

packages/orama/src/methods/search-fulltext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type {
1818
} from '../types.js'
1919
import { getNanosecondsTime, removeVectorsFromHits, sortTokenScorePredicate } from '../utils.js'
2020
import { count } from './docs.js'
21-
import { fetchDocuments, fetchDocumentsWithDistinct } from './search.js'
21+
import { fetchDocuments, fetchDocumentsWithDistinct } from './fetch-documents.js'
2222

2323
export function innerFullTextSearch<T extends AnyOrama>(
2424
orama: T,

packages/orama/src/methods/search-hybrid.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {
1010
import { getNanosecondsTime, formatNanoseconds, removeVectorsFromHits } from '../utils.js'
1111
import { getFacets } from '../components/facets.js'
1212
import { getGroups } from '../components/groups.js'
13-
import { fetchDocuments } from './search.js'
13+
import { fetchDocuments } from './fetch-documents.js'
1414
import { innerFullTextSearch } from './search-fulltext.js'
1515
import { innerVectorSearch } from './search-vector.js'
1616
import { runAfterSearch, runBeforeSearch } from '../components/hooks.js'
Lines changed: 0 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-
import { InternalDocumentID, getDocumentIdFromInternalId } from '../components/internal-document-id-store.js'
21
import { createError } from '../errors.js'
3-
import { getNested } from '../utils.js'
42
import type {
53
AnyOrama,
6-
LiteralUnion,
7-
Result,
84
Results,
95
SearchParams,
106
SearchParamsFullText,
117
SearchParamsHybrid,
128
SearchParamsVector,
13-
SearchableValue,
149
TypedDocument
1510
} from '../types.js'
1611
import { MODE_FULLTEXT_SEARCH, MODE_HYBRID_SEARCH, MODE_VECTOR_SEARCH } from '../constants.js'
@@ -39,99 +34,3 @@ export function search<T extends AnyOrama, ResultDocument = TypedDocument<T>>(
3934

4035
throw createError('INVALID_SEARCH_MODE', mode)
4136
}
42-
43-
export function fetchDocumentsWithDistinct<T extends AnyOrama, ResultDocument extends TypedDocument<T>>(
44-
orama: T,
45-
uniqueDocsArray: [InternalDocumentID, number][],
46-
offset: number,
47-
limit: number,
48-
distinctOn: LiteralUnion<T['schema']>
49-
): Result<ResultDocument>[] {
50-
const docs = orama.data.docs
51-
52-
// Keep track which values we already seen
53-
const values = new Map<SearchableValue, true>()
54-
55-
// We cannot know how many results we will have in the end,
56-
// so we need cannot pre-allocate the array.
57-
const results: Result<ResultDocument>[] = []
58-
59-
const resultIDs: Set<InternalDocumentID> = new Set()
60-
const uniqueDocsArrayLength = uniqueDocsArray.length
61-
let count = 0
62-
for (let i = 0; i < uniqueDocsArrayLength; i++) {
63-
const idAndScore = uniqueDocsArray[i]
64-
65-
// If there are no more results, just break the loop
66-
if (typeof idAndScore === 'undefined') {
67-
continue
68-
}
69-
70-
const [id, score] = idAndScore
71-
72-
if (resultIDs.has(id)) {
73-
continue
74-
}
75-
76-
const doc = orama.documentsStore.get(docs, id)
77-
const value = getNested(doc as object, distinctOn)
78-
if (typeof value === 'undefined' || values.has(value)) {
79-
continue
80-
}
81-
values.set(value, true)
82-
83-
count++
84-
// We shouldn't consider the document if it's not in the offset range
85-
if (count <= offset) {
86-
continue
87-
}
88-
89-
results.push({ id: getDocumentIdFromInternalId(orama.internalDocumentIDStore, id), score, document: doc! })
90-
resultIDs.add(id)
91-
92-
// reached the limit, break the loop
93-
if (count >= offset + limit) {
94-
break
95-
}
96-
}
97-
98-
return results
99-
}
100-
101-
export function fetchDocuments<T extends AnyOrama, ResultDocument extends TypedDocument<T>>(
102-
orama: T,
103-
uniqueDocsArray: [InternalDocumentID, number][],
104-
offset: number,
105-
limit: number
106-
): Result<ResultDocument>[] {
107-
const docs = orama.data.docs
108-
109-
const results: Result<ResultDocument>[] = Array.from({
110-
length: limit
111-
})
112-
113-
const resultIDs: Set<InternalDocumentID> = new Set()
114-
115-
// We already have the list of ALL the document IDs containing the search terms.
116-
// We loop over them starting from a positional value "offset" and ending at "offset + limit"
117-
// to provide pagination capabilities to the search.
118-
for (let i = offset; i < limit + offset; i++) {
119-
const idAndScore = uniqueDocsArray[i]
120-
121-
// If there are no more results, just break the loop
122-
if (typeof idAndScore === 'undefined') {
123-
break
124-
}
125-
126-
const [id, score] = idAndScore
127-
128-
if (!resultIDs.has(id)) {
129-
// We retrieve the full document only AFTER making sure that we really want it.
130-
// We never retrieve the full document preventively.
131-
const fullDoc = orama.documentsStore.get(docs, id)
132-
results[i] = { id: getDocumentIdFromInternalId(orama.internalDocumentIDStore, id), score, document: fullDoc! }
133-
resultIDs.add(id)
134-
}
135-
}
136-
return results
137-
}

0 commit comments

Comments
 (0)