this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fixes and tests for query strings

+210 -4
+12 -4
search/parse_query.go
··· 4 4 "context" 5 5 "log/slog" 6 6 "strings" 7 + "time" 7 8 8 9 "github.com/bluesky-social/indigo/atproto/identity" 9 10 "github.com/bluesky-social/indigo/atproto/syntax" ··· 97 98 } 98 99 continue 99 100 case "since", "until": 100 - // TODO: special-case handle dates? 101 - dt, err := syntax.ParseDatetimeLenient(tokParts[1]) 102 - if err != nil { 103 - continue 101 + var dt syntax.Datetime 102 + // first try just date 103 + date, err := time.Parse(time.DateOnly, tokParts[1]) 104 + if nil == err { 105 + dt = syntax.Datetime(date.Format(syntax.AtprotoDatetimeLayout)) 106 + } else { 107 + // fallback to formal atproto datetime format 108 + dt, err = syntax.ParseDatetimeLenient(tokParts[1]) 109 + if err != nil { 110 + continue 111 + } 104 112 } 105 113 if tokParts[0] == "since" { 106 114 params.Since = &dt
+198
search/query_test.go
··· 200 200 } 201 201 assert.Equal(1, len(res.Hits.Hits)) 202 202 } 203 + 204 + func TestParsedQuery(t *testing.T) { 205 + assert := assert.New(t) 206 + ctx := context.Background() 207 + escli := testEsClient(t) 208 + dir := identity.NewMockDirectory() 209 + srv := testServer(ctx, t, escli, &dir) 210 + ident := identity.Identity{ 211 + DID: syntax.DID("did:plc:abc111"), 212 + Handle: syntax.Handle("handle.example.com"), 213 + } 214 + other := identity.Identity{ 215 + DID: syntax.DID("did:plc:abc222"), 216 + Handle: syntax.Handle("other.example.com"), 217 + } 218 + dir.Insert(ident) 219 + dir.Insert(other) 220 + 221 + res, err := DoSearchPosts(ctx, &dir, escli, testPostIndex, "english", 0, 20) 222 + if err != nil { 223 + t.Fatal(err) 224 + } 225 + assert.Equal(0, len(res.Hits.Hits)) 226 + 227 + p1 := appbsky.FeedPost{Text: "basic english post", CreatedAt: "2024-01-02T03:04:05.006Z"} 228 + assert.NoError(srv.indexPost(ctx, &ident, &p1, "app.bsky.feed.post/3kpnillluoh2y", cid.Undef)) 229 + p2 := appbsky.FeedPost{Text: "another english post", CreatedAt: "2024-01-02T03:04:05.006Z"} 230 + assert.NoError(srv.indexPost(ctx, &ident, &p2, "app.bsky.feed.post/3kpnilllu2222", cid.Undef)) 231 + p3 := appbsky.FeedPost{ 232 + Text: "#cat post with hashtag", 233 + CreatedAt: "2024-01-02T03:04:05.006Z", 234 + Facets: []*appbsky.RichtextFacet{ 235 + &appbsky.RichtextFacet{ 236 + Features: []*appbsky.RichtextFacet_Features_Elem{ 237 + &appbsky.RichtextFacet_Features_Elem{ 238 + RichtextFacet_Tag: &appbsky.RichtextFacet_Tag{ 239 + Tag: "trick", 240 + }, 241 + }, 242 + }, 243 + Index: &appbsky.RichtextFacet_ByteSlice{ 244 + ByteStart: 0, 245 + ByteEnd: 4, 246 + }, 247 + }, 248 + }, 249 + } 250 + assert.NoError(srv.indexPost(ctx, &ident, &p3, "app.bsky.feed.post/3kpnilllu3333", cid.Undef)) 251 + p4 := appbsky.FeedPost{ 252 + Text: "@other.example.com post with mention", 253 + CreatedAt: "2024-01-02T03:04:05.006Z", 254 + Facets: []*appbsky.RichtextFacet{ 255 + &appbsky.RichtextFacet{ 256 + Features: []*appbsky.RichtextFacet_Features_Elem{ 257 + &appbsky.RichtextFacet_Features_Elem{ 258 + RichtextFacet_Mention: &appbsky.RichtextFacet_Mention{ 259 + Did: "did:plc:abc222", 260 + }, 261 + }, 262 + }, 263 + Index: &appbsky.RichtextFacet_ByteSlice{ 264 + ByteStart: 0, 265 + ByteEnd: 18, 266 + }, 267 + }, 268 + }, 269 + } 270 + assert.NoError(srv.indexPost(ctx, &ident, &p4, "app.bsky.feed.post/3kpnilllu4444", cid.Undef)) 271 + p5 := appbsky.FeedPost{ 272 + Text: "https://bsky.app... post with hashtag #cat", 273 + CreatedAt: "2024-01-02T03:04:05.006Z", 274 + Facets: []*appbsky.RichtextFacet{ 275 + &appbsky.RichtextFacet{ 276 + Features: []*appbsky.RichtextFacet_Features_Elem{ 277 + &appbsky.RichtextFacet_Features_Elem{ 278 + RichtextFacet_Link: &appbsky.RichtextFacet_Link{ 279 + Uri: "htTPS://www.en.wikipedia.org/wiki/CBOR?q=3&a=1&utm_campaign=123", 280 + }, 281 + }, 282 + }, 283 + Index: &appbsky.RichtextFacet_ByteSlice{ 284 + ByteStart: 0, 285 + ByteEnd: 19, 286 + }, 287 + }, 288 + }, 289 + } 290 + assert.NoError(srv.indexPost(ctx, &ident, &p5, "app.bsky.feed.post/3kpnilllu5555", cid.Undef)) 291 + p6 := appbsky.FeedPost{ 292 + Text: "post with lang (deutsch)", 293 + CreatedAt: "2024-01-02T03:04:05.006Z", 294 + Langs: []string{"ja", "de-DE"}, 295 + } 296 + assert.NoError(srv.indexPost(ctx, &ident, &p6, "app.bsky.feed.post/3kpnilllu6666", cid.Undef)) 297 + p7 := appbsky.FeedPost{Text: "post with old date", CreatedAt: "2020-05-03T03:04:05.006Z"} 298 + assert.NoError(srv.indexPost(ctx, &ident, &p7, "app.bsky.feed.post/3kpnilllu7777", cid.Undef)) 299 + 300 + _, err = srv.escli.Indices.Refresh() 301 + assert.NoError(err) 302 + 303 + // expect all to be indexed 304 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "*", 0, 20) 305 + if err != nil { 306 + t.Fatal(err) 307 + } 308 + assert.Equal(7, len(res.Hits.Hits)) 309 + 310 + // check that english matches both 311 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "english", 0, 20) 312 + if err != nil { 313 + t.Fatal(err) 314 + } 315 + assert.Equal(2, len(res.Hits.Hits)) 316 + 317 + // phrase only matches one 318 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "\"basic english\"", 0, 20) 319 + if err != nil { 320 + t.Fatal(err) 321 + } 322 + assert.Equal(1, len(res.Hits.Hits)) 323 + 324 + // posts-by 325 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "from:handle.example.com", 0, 20) 326 + if err != nil { 327 + t.Fatal(err) 328 + } 329 + assert.Equal(7, len(res.Hits.Hits)) 330 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "from:@handle.example.com", 0, 20) 331 + if err != nil { 332 + t.Fatal(err) 333 + } 334 + assert.Equal(7, len(res.Hits.Hits)) 335 + 336 + // hashtag query 337 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "post #trick", 0, 20) 338 + if err != nil { 339 + t.Fatal(err) 340 + } 341 + assert.Equal(1, len(res.Hits.Hits)) 342 + 343 + // mention query 344 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "@other.example.com", 0, 20) 345 + if err != nil { 346 + t.Fatal(err) 347 + } 348 + assert.Equal(1, len(res.Hits.Hits)) 349 + 350 + // URL and domain queries 351 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "https://en.wikipedia.org/wiki/CBOR?a=1&q=3", 0, 20) 352 + if err != nil { 353 + t.Fatal(err) 354 + } 355 + assert.Equal(1, len(res.Hits.Hits)) 356 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "\"https://en.wikipedia.org/wiki/CBOR?a=1&q=3\"", 0, 20) 357 + if err != nil { 358 + t.Fatal(err) 359 + } 360 + assert.Equal(0, len(res.Hits.Hits)) 361 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "https://en.wikipedia.org/wiki/CBOR", 0, 20) 362 + if err != nil { 363 + t.Fatal(err) 364 + } 365 + assert.Equal(0, len(res.Hits.Hits)) 366 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "domain:en.wikipedia.org", 0, 20) 367 + if err != nil { 368 + t.Fatal(err) 369 + } 370 + assert.Equal(1, len(res.Hits.Hits)) 371 + 372 + // lang filter 373 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "lang:de", 0, 20) 374 + if err != nil { 375 + t.Fatal(err) 376 + } 377 + assert.Equal(1, len(res.Hits.Hits)) 378 + 379 + // date range filters 380 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "since:2023-01-01T00:00:00Z", 0, 20) 381 + if err != nil { 382 + t.Fatal(err) 383 + } 384 + assert.Equal(6, len(res.Hits.Hits)) 385 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "since:2023-01-01", 0, 20) 386 + if err != nil { 387 + t.Fatal(err) 388 + } 389 + assert.Equal(6, len(res.Hits.Hits)) 390 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "until:2023-01-01", 0, 20) 391 + if err != nil { 392 + t.Fatal(err) 393 + } 394 + assert.Equal(1, len(res.Hits.Hits)) 395 + res, err = DoSearchPosts(ctx, &dir, escli, testPostIndex, "until:asdf", 0, 20) 396 + if err != nil { 397 + t.Fatal(err) 398 + } 399 + assert.Equal(7, len(res.Hits.Hits)) 400 + }