Once you’ve first indexed your data, you need to keep your index up to date as the data changes.
One way of doing so is via incremental updates, by tracking changes and forwarding them to Algolia. Whenever you add, edit, or delete data from your source, you want to update the corresponding records so they reflect the latest state of your data.
Tracking each record with a unique identifier
To perform incremental updates, you need to declare a unique identifier for each record so you can track what data matches what record. This identifier should map to a “key” you store on your side. For instance, if you’re selling books and you have one Algolia record per book, you could use the ISBN.
You need to store the unique identifier in the objectID
attribute so you can leverage it to perform updates or deletions.
Adding records
Whenever you add new data in your data source, you can add them to Algolia with a regular call to saveObjects
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $index->saveObjects(
[
[
'objectID' => 'myID1',
'firstname' => 'Jimmie',
'lastname' => 'Barninger'
],
[
'objectID' => 'myID2',
'firstname' => 'Warren',
'lastname' => 'Speach'
]
]
);
|
1
2
3
4
5
6
7
8
9
| res = index.save_objects([{
firstname: 'Jimmie',
lastname: 'Barninger',
objectID: 'myID1'
}, {
firstname: 'Warren',
lastname: 'Speach',
objectID: 'myID2'
}])
|
1
2
3
4
5
6
7
8
9
10
11
12
| const objects = [{
objectID: 'myID1',
firstname: 'Jimmie',
lastname: 'Barninger'
}, {
objectID: 'myID2',
firstname: 'Warren',
lastname: 'Speach'
}];
index.saveObjects(objects).then(({ objectIDs }) => {
console.log(objectIDs);
});
|
1
2
3
4
| res = index.save_objects([
{"objectID": "myID1", "firstname": "Jimmie", "lastname": "Barninger"},
{"objectID": "myID2", "firstname": "Warren", "lastname": "Speach"}
])
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| struct Contact: Encodable {
let objectID: ObjectID
let firstname: String
let lastname: String
}
let contacts: [Contact] = [
.init(objectID: "myID1", firstname: "Jimmie", lastname: "Barninger"),
.init(objectID: "myID2", firstname: "Warren", lastname: "Speach"),
]
try index.saveObjects(contacts) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
8
| List<Contact> contacts = new List<Contact>
{
new Contact { ObjectID = "myID1", Firstname = "Jimmie", Lastname = "Barninger" },
new Contact { ObjectID = "myID2", Firstname = "Warren", Lastname = "Speach" }
};
index.SaveObjects(contacts);
// Asynchronous
await index.SaveObjectsAsync(contacts);
|
1
2
3
4
5
6
7
8
| List<Contact> contacts = Arrays.asList(
new Contact().setObjectID("myID1").setFirstName("Jimmie").setLastName("Barninger"),
new Contact().setObjectID("myID2").setFirstName("Warren").setLastName("Speach"));
// Sync version
index.saveObjects(contacts);
// Async version
index.saveObjectsAsync(contacts);
|
1
2
3
4
5
6
7
8
9
10
| type Contact struct {
ObjectID string `json:"objectID"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}
contacts := []Contact{
{ObjectID: "myID1", Firstname: "Jimmie", Lastname: "Barninger"},
{ObjectID: "myID2", Firstname: "Ray", Lastname: "Charles"},
}
res, err := index.SaveObjects(contacts)
|
1
2
3
4
5
6
| client.execute {
index into "index1" objects Seq(
Contact("myID1", "Jimmie", "Barninger"),
Contact("myID2", "Warren", "Speach")
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // With JsonObject
val json = listOf(
json {
"objectID" to ObjectID("myID1")
"firstname" to "Jimmie"
"lastname" to "Barninger"
},
json {
"objectID" to ObjectID("myID2")
"firstname" to "Warren"
"lastname" to "Speach"
}
)
index.saveObjects(json)
// With serializable class
@Serializable
data class Contact(
val firstname: String,
val lastname: String,
override val objectID: ObjectID
) : Indexable
val contacts = listOf(
Contact("Jimmie", "Barninger", ObjectID("myID")),
Contact("Jimmie", "Barninger", ObjectID("myID"))
)
index.saveObjects(Contact.serializer(), contacts)
|
Note that every record has a custom objectID
.
Updating records
There are two ways to incrementally update records in Algolia:
- Fully replacing the old record with a new one
- Updating only a subset of the existing record with the changed data
Replacing the old record
When saving a record in Algolia, if a record with the specified objectID
already exists in the index, the engine replaces it. You can replace an existing record by using the method and specifying the objectID
. This technique is useful when you’re updating data in a single place in your system, or when you don’t know what changed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $res = $index->saveObjects(
[
[
'objectID' => 'myID1',
'firstname' => 'Jimmie',
'lastname' => 'Barninger'
],
[
'objectID' => 'myID2',
'firstname' => 'Warren',
'lastname' => 'Speach'
]
]
);
|
1
2
3
4
5
6
7
8
9
| res = index.save_objects([{
firstname: 'Jimmie',
lastname: 'Barninger',
objectID: 'myID1'
}, {
firstname: 'Warren',
lastname: 'Speach',
objectID: 'myID2'
}])
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| const objects = [{
firstname: 'Jimmie',
lastname: 'Barninger',
objectID: 'myID1'
}, {
firstname: 'Warren',
lastname: 'Speach',
objectID: 'myID2'
}];
index.saveObjects(objects).then(({ objectIDs }) => {
console.log(objectIDs);
});
|
1
2
3
4
| res = index.save_objects([
{'firstname': 'Jimmie', 'lastname': 'Barninger', 'objectID': 'myID1'},
{'firstname': 'Warren', 'lastname': 'Speach', 'objectID': 'myID2'}
])
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| struct Contact: Encodable {
let objectID: ObjectID
let firstname: String
let lastname: String
}
let contacts: [Contact] = [
.init(objectID: "myID1", firstname: "Jimmie", lastname: "Barninger"),
.init(objectID: "myID2", firstname: "Warren", lastname: "Speach"),
]
let replacements = contacts.map {($0.objectID, $0) }
index.replaceObjects(replacements: replacements) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
8
9
10
| List<Contact> contacts = new List<Contact>
{
new Contact { ObjectID = "myID1", Firstname = "Jimmie", Lastname = "Barninger" },
new Contact { ObjectID = "myID2", Firstname = "Warren", Lastname = "Speach" }
};
index.SaveObjects(contacts);
// Asynchronous
await index.SaveObjectsAsync(contacts);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // Sync version
index.saveObjects(Arrays.asList(
new Contact()
.setFirstName("Jimmie")
.setLastName("Barninger")
.setObjectID("myID"),
new Contact()
.setFirstName("Warren")
.setLastName("Speach")
.setObjectID("myID2")
));
// Async version
index.saveObjectsAsync(Arrays.asList(
new Contact()
.setFirstName("Jimmie")
.setLastName("Barninger")
.setObjectID("myID"),
new Contact()
.setFirstName("Warren")
.setLastName("Speach")
.setObjectID("myID2")
));
|
1
2
3
4
5
6
7
8
9
10
11
12
| type Contact struct {
ObjectID string `json:"objectID"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}
contacts := []Contact{
{ObjectID: "myID1", Firstname: "Jimmie", Lastname: "Barninger"},
{ObjectID: "myID2", Firstname: "Ray", Lastname: "Charles"},
}
res, err := index.SaveObjects(contacts)
|
1
2
3
4
5
6
| client.execute {
index into "index1" objects Seq(
Contact("myID1", "Jimmie", "Barninger"),
Contact("myID2", "Warren", "Speach")
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| // With JsonObject
val json = listOf(
ObjectID("myID1") to json {
"firstname" to "Jimmie"
"lastname" to "Barninger"
},
ObjectID("myID1") to json {
"firstname" to "Warren"
"lastname" to "Speach"
}
)
index.replaceObjects(json)
// With serializable class
@Serializable
data class Contact(
val firstname: String,
val lastname: String,
override val objectID: ObjectID
) : Indexable
val contacts = listOf(
Contact("Jimmie", "Barninger", ObjectID("myID")),
Contact("Jimmie", "Barninger", ObjectID("myID"))
)
index.replaceObjects(Contact.serializer(), contacts)
|
Note that every record contains the objectID
of the record to delete.
Updating a subset of the record
Sometimes, you may want to update a subset of the attributes of a record and leave the rest untouched. This is useful when you update data in multiple places. With the books example, you may have two different systems to handle metadata and to manage stock, both updating the index, with no knowledge about the rest of the data.
For this, you can use the partialUpdateObjects
method and only pass the changed data. Anything you don’t specify remains untouched.
1
2
3
4
5
6
7
8
9
10
11
12
| $index->partialUpdateObjects(
[
[
'objectID' => 'myID1',
'firstname' => 'Jimmie'
],
[
'objectID' => 'myID2',
'firstname' => 'Warren'
]
]
);
|
1
2
3
4
5
6
7
| index.partial_update_objects([{
firstname: 'Jimmie',
objectID: 'myID'
}, {
firstname: 'Warren',
objectID: 'myID2'
}])
|
1
2
3
4
5
6
7
8
9
10
11
| const objects = [{
firstname: 'Jimmie',
objectID: 'myID1'
}, {
firstname: 'Warren',
objectID: 'myID2'
}];
index.partialUpdateObjects(objects).then(({ objectIDs }) => {
console.log(objectIDs);
});
|
1
2
3
4
| index.partial_update_objects([
{'objectID': 'myID1', 'firstname': 'Jimmie'},
{'objectID': 'myID2', 'firstname': 'Warren'}
])
|
1
2
3
4
5
6
7
8
9
10
| let updates: [(ObjectID, PartialUpdate)] = [
("myID1", .update(attribute: "firstname", value: "Jimmie")),
("myID2", .update(attribute: "firstname", value: "Warren"))
]
index.partialUpdateObjects(updates: updates) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
8
9
10
| List<Contact> contacts = new List<Contact>
{
new Contact { ObjectID = "myID1", Firstname = "Jimmie" },
new Contact { ObjectID = "myID2", Firstname = "Warren" }
};
index.PartialUpdateObjects(contacts);
// Asynchronous
await index.PartialUpdateObjectsAsync(contacts);
|
1
2
3
4
5
6
7
8
9
10
| List<Contact> contacts = Arrays.asList(
new Contact().setCity("San Francisco").setObjectID("MyID"),
new Contact().setCity("Paris").setObjectID("MyID2")
);
// Sync version
index.partialUpdateObjects(contacts);
// Async version
index.partialUpdateObjectsAsync(contacts);
|
1
2
3
4
5
6
| objects := []map[string]string{
{"objectID": "myID1", "lastname": "Barninger"},
{"objectID": "myID2", "firstname": "Ray"},
}
res, err := index.PartialUpdateObjects(objects)
|
1
2
3
4
5
6
| client.execute {
partialUpdate from "index" objects Seq(
Contact("myID", "Jimmie", "Barninger"),
Contact("myID", "Speach")
)
}
|
1
2
3
4
5
6
7
| val firstname = Attribute("firstname")
val partials = listOf(
ObjectID("myID1") to Partial.Update(firstname, "Jimmie"),
ObjectID("myID2") to Partial.Update(firstname, "Warren")
)
index.partialUpdateObjects(partials)
|
Note that every record contains the objectID
of the record to delete.
Deleting records
Whenever you delete data in your data source, you can delete the corresponding record from Algolia with deleteObjects
.
1
| $index->deleteObjects(["myID1", "myID2"]);
|
1
| index.delete_objects(['myID1', 'myID2'])
|
1
2
3
| index.deleteObjects(['myID1', 'myID2']).then(({ objectIDs }) => {
console.log(objectIDs);
});
|
1
| index.delete_objects(['myID1', 'myID2'])
|
1
2
3
4
5
| index.deleteObjects(withIDs: ["myID1", "myID2"]) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
| List<string> ids = new List<string> { "myID1", "myID2" };
index.DeleteObjects(ids);
// Asynchronous
await index.DeleteObjectsAsync(ids);
|
1
2
3
4
5
| // Sync version
index.deleteObjects(Arrays.asList("myID1", "myID2"));
// Async version
index.deleteObjectsAsync(Arrays.asList("myID1", "myID2"));
|
1
2
3
| res, err := index.DeleteObjects(
[]string{"myID1", "myID2"},
)
|
1
2
3
| client.execute {
delete from "test1" objectIds Seq("1", "2")
}
|
1
2
3
| val objectIDS = listOf(ObjectID("myID1"), ObjectID("myID2"))
index.deleteObjects(objectIDS)
|
Note that every record contains the objectID
of the record to delete.
Delete by query
Sometimes, you may need to delete all records matching a certain filter. Back to the book example, if you stop selling books from a specific publisher, you might want to delete all records matching this publisher
in your index. To do this, you can use the deleteBy
method.
The deleteBy
method is an expensive operation for the engine. For better performance, use the deleteObjects
method instead.
1
2
3
4
5
| $index->deleteBy([
'filters' => 'category:cars',
'aroundLatLng' => '40.71, -74.01'
/* add any filter parameters */
]);
|
1
2
3
4
5
| params = {
filters: 'category:cars',
aroundLatLng: '40.71, -74.01'
}
index.delete_by(params)
|
1
2
3
4
5
6
| index.deleteBy({
filters: 'category:cars',
aroundLatLng: '40.71, -74.01'
}).then(() => {
// done
});
|
1
2
3
4
| index.delete_by({
'filters': 'category:cars',
'aroundLatLng': '40.71, -74.01'
})
|
1
2
3
4
5
6
7
8
9
10
| var query = DeleteByQuery()
query.filters = "category:cars"
query.aroundLatLng = .init(latitude: 40.71, longitude: -74.01)
index.deleteObjects(byQuery: query) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
| var query = new Query { Filters = "category:cars", AroundLatLng = "40.71, -74.01" };
index.DeleteBy(query);
// Asynchronous
await index.DeleteByAsync(query);
|
1
2
3
4
5
6
7
8
9
| Query query = new Query("query")
.setFilters("category:cars")
.setAroundLatLng("40.71, -74.01")
// Sync version
index.deleteBy(query);
// Async version
index.deleteByAsync(query);
|
1
2
3
4
| res, err := index.DeleteBy(
opt.Filters("category:cars"),
opt.AroundLatLng("40.71, -74.01"),
)
|
1
2
3
4
5
6
| delete from "testDeleteBy" by Query(
filters = Some("price > 10"),
aroundLatLng = Some(
AroundLatLng("40.71, -74.01")
)
)
|
1
2
3
4
5
6
| val query = DeleteByQuery(
filters = "category:car",
aroundLatLng = Point(40.71f, -74.01f)
)
index.deleteObjectsBy(query)
|
Any attribute you’re using to delete by needs to be in your searchableAttributes
.