Skip to content

Commit 3a0316e

Browse files
authored
Merge pull request #323 from ml054/RDBC-615
RDBC-615 Expose ability to load attachments/ cmp exchange values in m…
2 parents fbe9b9e + e71f8b1 commit 3a0316e

File tree

6 files changed

+187
-3
lines changed

6 files changed

+187
-3
lines changed

src/Documents/Attachments/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as stream from "readable-stream";
22
import { HttpResponse } from "../../Primitives/Http";
33
import { closeHttpResponse } from "../../Utility/HttpUtil";
4+
import { CapitalizeType } from "../../Types";
45

56
export type AttachmentType = "Document" | "Revision";
67

@@ -11,6 +12,12 @@ export interface AttachmentName {
1112
size: number;
1213
}
1314

15+
export interface IAttachmentObject extends CapitalizeType<AttachmentName> {
16+
getContentAsString(): string;
17+
getContentAsString(encoding: string): string;
18+
getContentAsStream(): any;
19+
}
20+
1421
export interface AttachmentDetails extends AttachmentName {
1522
changeVector: string;
1623
documentId?: string;

src/Documents/Commands/GetDocumentsCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export class GetDocumentsCommand extends RavenCommand<GetDocumentsResult> {
244244
}
245245

246246
if (this._revisionsIncludeByDateTime) {
247-
query += "&revisionsBefore=" + DateUtil.utc.stringify(this._revisionsIncludeByDateTime);
247+
query += "&revisionsBefore=" + this._urlEncode(DateUtil.utc.stringify(this._revisionsIncludeByDateTime));
248248
}
249249

250250
if (this._compareExchangeValueIncludes) {

src/Documents/Indexes/StronglyTyped.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { FieldIndexing, FieldStorage, FieldTermVector } from "./Enums";
22
import { MetadataObject } from "../Session/MetadataObject";
3-
import { IndexFieldOptions } from "./IndexFieldOptions";
3+
import { AttachmentName, IAttachmentObject } from "../Attachments";
4+
import { CapitalizeType } from "../../Types";
45

56
export type IndexingMapDefinition<TInput, TOutput> = (document: TInput) => TOutput | TOutput[];
67

@@ -22,6 +23,8 @@ export interface IndexingMapUtils {
2223
createSpatialField(wkt: string): SpatialField;
2324
createSpatialField(lat: number, lng: number): SpatialField;
2425
createField(name: string, value: any, options: CreateFieldOptions): void;
26+
attachmentsFor(document: any): CapitalizeType<AttachmentName>[];
27+
loadAttachment(document: any, attachmentName: string): IAttachmentObject;
2528
}
2629

2730
export class StubMapUtils<T> implements IndexingMapUtils {
@@ -31,6 +34,8 @@ export class StubMapUtils<T> implements IndexingMapUtils {
3134
id: (document: any) => string;
3235
createSpatialField: (wktOrLat: string | number, lng?: number) => void;
3336
createField: (name: string, value: any, options?: CreateFieldOptions) => void;
37+
attachmentsFor: (document: any) => CapitalizeType<AttachmentName>[];
38+
loadAttachment: (document: any, attachmentName: string) => IAttachmentObject;
3439
}
3540

3641
interface Group<TDocument, TKey> {

src/Types/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,12 @@ export abstract class PropsBasedObjectLiteralDescriptor<T extends object>
7373
}
7474
}
7575

76+
export type CapitalizeType<T> = { [K in keyof T & string as `${Capitalize<K>}`]: T[K] };
77+
7678
export type Field<T> = keyof T & string | string;
7779

7880
export type ServerResponse<T> = T extends Date|string ? T : {
7981
[K in keyof T]: T[K] extends Date
80-
? string
82+
? string
8183
: ServerResponse<T[K]>;
8284
}

test/Issues/RDBC_615.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { AbstractJavaScriptIndexCreationTask, IDocumentStore, PutCompareExchangeValueOperation } from "../../src";
2+
import { disposeTestDocumentStore, testContext } from "../Utils/TestUtil";
3+
import { Address, User } from "../Assets/Entities";
4+
import { assertThat } from "../Utils/AssertExtensions";
5+
6+
type IndexEntry = {
7+
name: string;
8+
city: string;
9+
}
10+
11+
describe("[RDBC-615] Expose ability to load attachments/cmp exchange in js indexes", function () {
12+
13+
let store: IDocumentStore;
14+
15+
beforeEach(async function () {
16+
store = await testContext.getDocumentStore();
17+
});
18+
19+
afterEach(async () =>
20+
await disposeTestDocumentStore(store));
21+
22+
it("can index compare exchange", async () => {
23+
const user = new User();
24+
user.addressId = "address/1";
25+
user.name = "Marcin";
26+
27+
{
28+
const session = store.openSession();
29+
30+
const address = new Address();
31+
address.city = "Warsaw";
32+
await store.operations.send(new PutCompareExchangeValueOperation("address/1", address, 0));
33+
34+
await session.store(user);
35+
await session.saveChanges();
36+
}
37+
38+
await store.executeIndex(new Users_ByAddress());
39+
40+
await testContext.waitForIndexing(store);
41+
42+
{
43+
const session = store.openSession();
44+
const user1 = await session.query<User>(User, Users_ByAddress)
45+
.whereEquals("name", "Marcin")
46+
.whereEquals("city", "Warsaw")
47+
.firstOrNull();
48+
assertThat(user1)
49+
.isNotNull();
50+
}
51+
});
52+
53+
it("can index attachment names", async () => {
54+
const user = new User();
55+
user.addressId = "address/1";
56+
user.name = "Marcin";
57+
58+
{
59+
const session = store.openSession();
60+
61+
await session.store(user);
62+
63+
const stream = Buffer.from([5, 4, 3, 2, 1]);
64+
session.advanced.attachments.store(user.id, "photo.jpg", stream);
65+
66+
await session.saveChanges();
67+
}
68+
69+
await store.executeIndex(new Users_Attachments());
70+
71+
await testContext.waitForIndexing(store);
72+
73+
{
74+
const session = store.openSession();
75+
const user1 = await session.query<User>(User, Users_Attachments)
76+
.whereEquals("name", "Marcin")
77+
.whereEquals("attachmentName", "photo.jpg")
78+
.firstOrNull();
79+
assertThat(user1)
80+
.isNotNull();
81+
}
82+
});
83+
84+
it("can index attachment content", async () => {
85+
const user = new User();
86+
user.addressId = "address/1";
87+
user.name = "Marcin";
88+
89+
{
90+
const session = store.openSession();
91+
92+
await session.store(user);
93+
94+
const stream = Buffer.from("pizza");
95+
session.advanced.attachments.store(user.id, "text.txt", stream);
96+
97+
await session.saveChanges();
98+
}
99+
100+
await store.executeIndex(new Users_LoadedAttachment());
101+
102+
await testContext.waitForIndexing(store);
103+
104+
{
105+
const session = store.openSession();
106+
const user1 = await session.query<User>(User, Users_LoadedAttachment)
107+
.whereEquals("name", "Marcin")
108+
.whereEquals("text", "pizza")
109+
.firstOrNull();
110+
assertThat(user1)
111+
.isNotNull();
112+
}
113+
});
114+
});
115+
116+
117+
class Users_ByAddress extends AbstractJavaScriptIndexCreationTask<User, IndexEntry> {
118+
119+
constructor() {
120+
super();
121+
122+
const { cmpxchg } = this.mapUtils();
123+
124+
this.map("Users", user => {
125+
const address = cmpxchg<Address>(user.addressId);
126+
return {
127+
city: address.city,
128+
name: user.name
129+
}
130+
});
131+
}
132+
}
133+
134+
class Users_Attachments extends AbstractJavaScriptIndexCreationTask<User> {
135+
constructor() {
136+
super();
137+
138+
const { attachmentsFor } = this.mapUtils();
139+
140+
this.map("Users", user => {
141+
const attachments = attachmentsFor(user);
142+
143+
return attachments.map(a => {
144+
return {
145+
name: user.name,
146+
attachmentName: a.Name
147+
}
148+
});
149+
});
150+
}
151+
}
152+
153+
class Users_LoadedAttachment extends AbstractJavaScriptIndexCreationTask<User> {
154+
155+
constructor() {
156+
super();
157+
158+
const { loadAttachment } = this.mapUtils();
159+
160+
this.map("Users", user => {
161+
const attachment = loadAttachment(user, "text.txt");
162+
163+
return {
164+
name: user.name,
165+
text: attachment.getContentAsString()
166+
}
167+
})
168+
}
169+
}

test/Ported/Documents/CanQueryAndIncludeRevisions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ describe("CanQueryAndIncludeRevisionsTest", function () {
280280
changeVector = metadatas[0]["@change-vector"];
281281
await session.advanced.patch(id, "changeVector", changeVector);
282282
await session.advanced.patch(id, "changeVectors", [changeVector]);
283+
await session.saveChanges();
283284
}
284285

285286
const dateTime = new Date();

0 commit comments

Comments
 (0)